aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OTP_VERSION2
-rw-r--r--erts/doc/src/notes.xml2047
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml2
-rw-r--r--lib/common_test/doc/src/notes.xml183
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/notes.xml17
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn18
-rw-r--r--lib/snmp/doc/src/notes.xml17
-rw-r--r--lib/snmp/include/snmp_types.hrl5
-rw-r--r--lib/snmp/src/agent/depend.mk19
-rw-r--r--lib/snmp/src/agent/modules.mk26
-rw-r--r--lib/snmp/src/agent/snmp_view_based_acm_mib.erl16
-rw-r--r--lib/snmp/src/agent/snmpa_agent.erl1136
-rw-r--r--lib/snmp/src/agent/snmpa_app.erl4
-rw-r--r--lib/snmp/src/agent/snmpa_get.erl1150
-rw-r--r--lib/snmp/src/agent/snmpa_get_lib.erl254
-rw-r--r--lib/snmp/src/agent/snmpa_get_mechanism.erl79
-rw-r--r--lib/snmp/src/agent/snmpa_set_lib.erl2
-rw-r--r--lib/snmp/src/agent/snmpa_supervisor.erl64
-rw-r--r--lib/snmp/src/agent/snmpa_trap.erl2
-rw-r--r--lib/snmp/src/app/snmp.app.src3
-rw-r--r--lib/snmp/src/app/snmp.config1
-rw-r--r--lib/snmp/src/app/snmp.erl24
-rw-r--r--lib/snmp/test/modules.mk1
-rw-r--r--lib/snmp/test/snmp_agent_test_get.erl58
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl1
-rw-r--r--lib/snmp/test/snmp_manager_test.erl7
-rw-r--r--lib/ssh/doc/src/notes.xml360
-rw-r--r--lib/tools/doc/src/notes.xml16
-rw-r--r--make/otp_patch_solve_forward_merge_version2
-rw-r--r--make/otp_version_tickets9
-rw-r--r--make/otp_version_tickets_in_merge11
-rw-r--r--otp_versions.table1
34 files changed, 4377 insertions, 1154 deletions
diff --git a/OTP_VERSION b/OTP_VERSION
index a011a9766c..5ba2cf5ac5 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-21.3.8
+21.3.7
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index e8a7d88f9e..8336c1e3d6 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,2053 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Add missing documentation of new external tags
+ <c>NEW_PID</c>, <c>NEW_PORT</c> and
+ <c>NEWER_REFERENCE</c> introduced in OTP 19.</p> <p>These
+ new tags are planned to be "activated" in OTP 23 when
+ distribution capability flag <c>DFLAG_BIG_CREATION</c>
+ becomes mandatory. Older nodes (>= 19) are able to decode
+ these new tags and send them back to the new node. Nodes
+ older than OTP 23 will however never encode their own
+ local pids, ports and references using the new tags.</p>
+ <p>
+ Own Id: OTP-15766</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>erlang:open_port/2</c> with the <c>fd</c> option
+ to correctly cleanup the pollset when the port is closed.
+ Before this fix there would be error printouts sent to
+ logger when the same fd was reused in a new port.</p>
+ <p>
+ Own Id: OTP-15753 Aux Id: ERL-900 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a bug in <c>seq_trace:reset_trace/0</c> that
+ could crash the emulator.</p>
+ <p>
+ Own Id: OTP-15704</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>process_info(reductions)</c> causing it
+ to sometimes return invalid results.</p>
+ <p>
+ Own Id: OTP-15709 Aux Id: ERIERL-337 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>If a suspend/resume signal pair was sent to a process
+ while it was executing dirty, the receiving process could
+ later end up in a suspended state indefinitely. This bug
+ was introduced in ERTS version 10.0 (OTP 21.0).</p>
+ <p>Suspend/resume signals are sent from <seealso
+ marker="erts:erlang#suspend_process/1"><c>erlang:suspend_process()</c></seealso>/<seealso
+ marker="erts:erlang#resume_process/1"><c>erlang:resume_process()</c></seealso>.
+ The <seealso
+ marker="runtime_tools:dbg"><c>dbg</c></seealso> trace
+ tool utilize this functionality and could thus trigger
+ this bug.</p>
+ <p>
+ Own Id: OTP-15688</p>
+ </item>
+ <item>
+ <p>
+ Fix a possible deadlock when terminating the ERTS caused
+ by a dirty scheduler not releasing it's run-queue lock
+ when terminating.</p>
+ <p>
+ Own Id: OTP-15690 Aux Id: PR-2172 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>When multiplying a number by itself, a word beyond the
+ number on the heap could be read (and ignored). This bug
+ was extremely unlikely to actually cause a real
+ problem.</p>
+ <p>
+ Own Id: OTP-15484</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where doing <c>seq_trace:reset_trace()</c> while
+ another process was doing a garbage collection could
+ cause the run-time system to segfault.</p>
+ <p>
+ Own Id: OTP-15490</p>
+ </item>
+ <item>
+ <p>
+ Fix reading of ancillary data from packet oriented
+ sockets on old Linux kernel versions. Without this fix,
+ getting the data would cause the port to enter an
+ infinite loop.</p>
+ <p>
+ Own Id: OTP-15494</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where crash dumping or doing
+ <c>erlang:system_info(procs)</c> while another process
+ was doing a garbage collection could cause the run-time
+ system to segfault.</p>
+ <p>
+ Own Id: OTP-15527</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>erlang:system_info(kernel_poll)</c> to return
+ correct value. Before this fix, the call always returned
+ <c>false</c>.</p>
+ <p>
+ Own Id: OTP-15556</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>enif_make_map_from_arrays</c> that would
+ produce broken maps when number of keys were 32. Bug
+ exists since OTP 21.0.</p>
+ <p>
+ Own Id: OTP-15567</p>
+ </item>
+ <item>
+ <p>
+ Fix a bug in <c>binary:encode_unsigned</c> that may cause
+ a read of uninitialized memory.</p>
+ <p>
+ The bug existed since the function was added (OTP
+ R16B02).</p>
+ <p>
+ Own Id: OTP-15583 Aux Id: PR-2118 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug that could cause <c>heart</c> to kill an
+ exiting node before it had time to flush all buffered
+ writes. If environment variable
+ <c>HEART_KILL_SIGNAL=SIGABRT</c> was set a superfluous
+ core dump could also be generated.</p>
+ <p>
+ Own Id: OTP-15599 Aux Id: ERIERL-298 </p>
+ </item>
+ <item>
+ <p>
+ Fix <c>enif_consume_timeslice</c> to be a no-op on dirty
+ scheduler and not crash debug compiled emulator.</p>
+ <p>
+ Own Id: OTP-15604</p>
+ </item>
+ <item>
+ <p>
+ Fixed macro redefinition warnings.</p>
+ <p>
+ Own Id: OTP-15629</p>
+ </item>
+ <item>
+ <p>
+ <c>to_erl</c> fixed to not garble terminal input beyond
+ 7-bit ASCII.</p>
+ <p>
+ Own Id: OTP-15650 Aux Id: ERL-854, PR-2161 </p>
+ </item>
+ <item>
+ <p>
+ Minor fixes for <c>make clean</c>.</p>
+ <p>
+ Own Id: OTP-15657</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in all <c>ets:select*</c> and
+ <c>ets:match*</c> functions that could in some rare cases
+ lead to very poor performance.</p>
+ <p>
+ Own Id: OTP-15660 Aux Id: ERL-869 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add <c>erlang:system_flag(system_logger, Pid)</c> and
+ <c>erlang:system_info(system_logger)</c>. This
+ system_flag can be used to set the process that will
+ receive the logging messages generated by ERTS.</p>
+ <p>
+ Own Id: OTP-15375</p>
+ </item>
+ <item>
+ <p><c>integer_to_list/2</c> and
+ <c>integer_to_binary/2</c> are now implemented in C,
+ improving their performance.</p>
+ <p>
+ Own Id: OTP-15503 Aux Id: PR-2052 </p>
+ </item>
+ <item>
+ <p>
+ Improved <c>term_to_binary</c> to do more fair reduction
+ count and yielding when encoding large byte lists
+ (strings).</p>
+ <p>
+ Own Id: OTP-15514 Aux Id: ERL-774 </p>
+ </item>
+ <item>
+ <p>
+ Made internal port drivers more robust against
+ <c>erlang:port_control</c> with invalid arguments and
+ added documentation warnings about such abuse.</p>
+ <p>
+ Own Id: OTP-15555 Aux Id: ERIERL-231 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug on NetBSD where the <c>exit_status</c> from a
+ port program would never be sent.</p>
+ <p>
+ Own Id: OTP-15558 Aux Id: ERL-725 </p>
+ </item>
+ <item>
+ <p>There is a new function <c>persistent:term(Key,
+ Default)</c> to allow specifying a default when looking
+ up a persistent term.</p>
+ <p>
+ Own Id: OTP-15576 Aux Id: ERL-843 </p>
+ </item>
+ <item>
+ <p>A transitory emulator option '<c>+ztma true</c>' has
+ been added to allow running existing BEAM code that
+ relies on "tuple calls" (dispatch on parameterized
+ modules) which has been compiled under OTP 20 or earlier.
+ This option will be removed in OTP 22, so such modules
+ should eventually be recompiled with the
+ <c>+tuple_calls</c> option.</p>
+ <p>
+ Own Id: OTP-15580 Aux Id: PR-2113 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.2.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixes of install/release phase in build system.</p>
+ <list> <item>The source tree was modified when
+ installing/releasing and/or applying a patch.</item>
+ <item>Some files were installed with wrong access
+ rights.</item> <item>If applying a patch (using
+ <c>otp_patch_apply</c>) as another user (except root)
+ than the user that built the source, the documentation
+ was not properly updated.</item> </list>
+ <p>
+ Own Id: OTP-15551</p>
+ </item>
+ <item>
+ <p>
+ Setting the <c>recbuf</c> size of an inet socket the
+ <c>buffer</c> is also automatically increased. Fix a bug
+ where the auto adjustment of inet buffer size would be
+ triggered even if an explicit inet buffer size had
+ already been set.</p>
+ <p>
+ Own Id: OTP-15651 Aux Id: ERIERL-304 </p>
+ </item>
+ <item>
+ <p>
+ Reading from UDP using active <c>true</c> or active
+ <c>N</c> mode has been optimized when more packets than
+ specified by <c>read_packets</c> are available on the
+ socket.</p>
+ <p>
+ Own Id: OTP-15652 Aux Id: ERIERL-304 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When using the <c>{linger,{true,T}}</c> option;
+ <c>gen_tcp:listen/2</c> used the full linger time before
+ returning for example <c>eaddrinuse</c>. This bug has now
+ been corrected.</p>
+ <p>
+ Own Id: OTP-14728 Aux Id: ERIERL-303 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug where doing a <c>gen_tcp:send</c> on a socket
+ with <c>delay_send</c> set to true could cause a segfault
+ if the other side closes the connection.</p>
+ <p>
+ Bug was introduced in erts-10.2 (OTP-21.2).</p>
+ <p>
+ Own Id: OTP-15536 Aux Id: ERL-827 </p>
+ </item>
+ <item>
+ <p>
+ Fix a race condition when a port program closes that
+ could result in the next started port to hang during
+ startup.</p>
+ <p>
+ When this fault happens the following error is normally
+ (but not always) logged:</p>
+ <p>
+ <c> =ERROR REPORT==== 14-Jan-2019::10:45:52.868246
+ ===</c><br/><c> Bad input fd in erts_poll()! fd=11,
+ port=#Port&lt;0.505>, driver=spawn, name=/bin/sh -s
+ unix:cmd </c></p>
+ <p>
+ Bug was introduced in erts-10.0 (OTP-21.0).</p>
+ <p>
+ Own Id: OTP-15537</p>
+ </item>
+ <item>
+ <p>
+ Fix a bug where polling for external events could be
+ delayed for a very long time if all active schedulers
+ were 100% loaded.</p>
+ <p>
+ Bug was introduced in erts-10.2 (OTP-21.2).</p>
+ <p>
+ Own Id: OTP-15538 Aux Id: ERIERL-229 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a crash when dangling files were closed after
+ <c>init:restart/0</c>.</p>
+ <p>
+ Own Id: OTP-15495 Aux Id: ERL-821 </p>
+ </item>
+ <item>
+ <p>
+ A bug that could cause dirty schedulers to become
+ unresponsive has been fixed.</p>
+ <p>
+ Own Id: OTP-15509 Aux Id: PR-2027, PR-2093 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed bug on big endian architectures when changing file
+ permissions or ownership with <c>file:change_mode</c>,
+ <c>change_owner</c>, <c>change_group</c> or
+ <c>write_file_info</c>. Bug exists since OTP-21.0.</p>
+ <p>
+ Own Id: OTP-15485</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>atomics</c> with option
+ <c>{signed,false}</c> when returned values are <c>(1 bsl
+ 63)</c> or larger. Could cause heap corruption leading to
+ VM crash or other unpleasant symptoms. Bug exists since
+ OTP-21.2 when module <c>atomics</c> was introduced.</p>
+ <p>
+ Own Id: OTP-15486 Aux Id: PR-2061 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in operator <c>band</c> of two negative
+ operands causing erroneous result if the absolute value
+ of one of the operands have the lowest <c>N*W</c> bits as
+ zero and the other absolute value is not larger than
+ <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger
+ and <c>W</c> is 32 or 64 depending on word size.</p>
+ <p>
+ Own Id: OTP-15487 Aux Id: ERL-804 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When a process was waiting for a TCP socket send
+ operation to complete, and another process closed the
+ socket during that send, the sending process could hang.
+ This bug has now been corrected.</p>
+ <p>
+ Own Id: OTP-12242 Aux Id: ERL-561 </p>
+ </item>
+ <item>
+ <p>
+ Document <c>bit_size</c> in match specifications and
+ allow it in <c>ets:fun2ms</c>.</p>
+ <p>
+ Own Id: OTP-15343 Aux Id: PR-1962 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>ets:select_replace</c> when called with a
+ fully bound key could cause a following call to
+ <c>ets:next</c> or <c>ets:prev</c> to crash the emulator
+ or return invalid result.</p>
+ <p>
+ Own Id: OTP-15346</p>
+ </item>
+ <item>
+ <p>When a module has been purged from memory, any
+ literals belonging to that module will be copied to all
+ processes that hold references to them. The max heap size
+ limit would be ignored in the garbage collection
+ initiated when copying literals to a process. If the max
+ heap size was exceeded, the process would typically be
+ terminated in the following garbage collection. Corrected
+ to terminate the process directly if copying a literal
+ would exceed the max heap size.</p>
+ <p>
+ Own Id: OTP-15360</p>
+ </item>
+ <item>
+ <p>
+ Fix compilation of run_erl on Solaris 11.4 and later.</p>
+ <p>
+ Own Id: OTP-15389</p>
+ </item>
+ <item>
+ <p>Fixed a bug where <c>lists:reverse/1-2</c> could use
+ far too many reductions. This bug was introduced in
+ <c>OTP 21.1</c>.</p>
+ <p>
+ Own Id: OTP-15436</p>
+ </item>
+ <item>
+ <p>Fixed a bug where a dirty scheduler could stay awake
+ forever if a distribution entry was removed as part of a
+ dirty GC.</p>
+ <p>
+ Own Id: OTP-15446 Aux Id: PR-2024 </p>
+ </item>
+ <item>
+ <p>
+ Fix microstate accounting handing in various places. Most
+ importantly the GC states when the GC is run on a dirty
+ scheduler are now managed correctly.</p>
+ <p>
+ Own Id: OTP-15450 Aux Id: ERIERL-229 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>file:sendfile</c> when the send operation
+ failed. For sockets in <c>active</c> modes it could cause
+ emulator crash or a hanging call. For sockets with
+ <c>{active,false}</c> an unexpected <c>{inet_reply, _,
+ _}</c> message could be sent to the calling process. The
+ bug exists since OTP-21.0.</p>
+ <p>
+ Own Id: OTP-15461 Aux Id: ERL-784 </p>
+ </item>
+ <item>
+ <p>
+ The erts configure script has been updated to reject any
+ CFLAGS that does not have <c>-O</c>. This in order to
+ prevent the common mistake of forgetting to add
+ <c>-O2</c> to custom CFLAGS.</p>
+ <p>
+ Own Id: OTP-15465</p>
+ </item>
+ <item>
+ <p>
+ Fix reduction count in lists:member/2</p>
+ <p>
+ Own Id: OTP-15474 Aux Id: ERIERL-229 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New <c>counters</c> and <c>atomics</c> modules supplies
+ access to highly efficient operations on mutable fixed
+ word sized variables.</p>
+ <p>
+ Own Id: OTP-13468</p>
+ </item>
+ <item>
+ <p>There is a new module <c>persistent_term</c> that
+ implements a term storage suitable for terms that are
+ frequently used but never or infrequently updated.
+ Lookups are done in constant time without copying the
+ terms.</p>
+ <p>
+ Own Id: OTP-14669 Aux Id: PR-1989 </p>
+ </item>
+ <item>
+ <p>
+ A function <c>inet:getifaddrs/1</c> that takes a list
+ with a namespace option has been added, for platforms
+ that support that feature, for example Linux (only?).</p>
+ <p>
+ Own Id: OTP-15121 Aux Id: ERIERL-189, PR-1974 </p>
+ </item>
+ <item>
+ <p>Added the <c>nopush</c> option for TCP sockets, which
+ corresponds to <c>TCP_NOPUSH</c> on *BSD and
+ <c>TCP_CORK</c> on Linux.</p>
+ <p>This is also used internally in <c>file:sendfile</c>
+ to reduce latency on subsequent send operations.</p>
+ <p>
+ Own Id: OTP-15357 Aux Id: ERL-698 </p>
+ </item>
+ <item>
+ <p>List subtraction (The <c>--</c> operator) will now
+ yield properly on large inputs.</p>
+ <p>
+ Own Id: OTP-15371</p>
+ </item>
+ <item>
+ <p>
+ Optimize handling of send_delay for tcp sockes to better
+ work with the new pollthread implementation introduced in
+ OTP-21.</p>
+ <p>
+ Own Id: OTP-15471 Aux Id: ERIERL-229 </p>
+ </item>
+ <item>
+ <p>
+ Optimize driver_set_timer with a zero timeout to
+ short-circuit and not create any timer structure, but
+ instead schedule the timer immediately.</p>
+ <p>
+ Own Id: OTP-15472 Aux Id: ERIERL-229 </p>
+ </item>
+ <item>
+ <p>
+ Add <c>erl_xcomp_code_model_small</c> as a cross
+ configure variable in order to let the emulator be build
+ with the assumption that a small code model will be used
+ on the target machine.</p>
+ <p>
+ Own Id: OTP-15473 Aux Id: ERIERL-229 </p>
+ </item>
+ <item>
+ <p>
+ Add a new pollset that is made to handle sockets that use
+ <c>{active, true}</c> or <c>{active, N}</c>. The new
+ pollset will not be polled by a pollthread, but instead
+ polled by a normal scheduler.</p>
+ <p>
+ This change was made because of the overhead associated
+ with constantly having to re-apply the ONESHOT mechanism
+ on fds that all input events were interesting.</p>
+ <p>
+ The new pollset is only active on platforms that support
+ concurrent kernel poll updates, i.e. Linux and BSD.</p>
+ <p>
+ Own Id: OTP-15475 Aux Id: ERIERL-229 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug where emulator would segfault if a literal
+ message was sent when sequence tracing was enabled.</p>
+ <p>
+ Own Id: OTP-15478 Aux Id: ERL-741 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.1.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Added an optional <c>./configure</c> flag to compile
+ the emulator with spectre mitigation:
+ <c>--with-spectre-mitigation</c></p>
+ <p>Note that this requires a recent version of GCC with
+ support for spectre mitigation and the
+ <c>--mindirect-branch=thunk</c> flag, such as
+ <c>8.1</c>.</p>
+ <p>
+ Own Id: OTP-15430 Aux Id: ERIERL-237 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a rare bug where files could be closed on a
+ normal instead of an IO scheduler, resulting in system
+ instability if the operation blocked.</p>
+ <p>
+ Own Id: OTP-15421</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug where the socket option 'pktoptions' caused a read
+ of uninitialized memory has been fixed. Would cause
+ malfunction on FreeBSD.</p>
+ <p>
+ Own Id: OTP-14297 Aux Id: OTP-15141 </p>
+ </item>
+ <item>
+ <p>Fixed a memory leak on errors when reading files.</p>
+ <p>
+ Own Id: OTP-15318</p>
+ </item>
+ <item>
+ <p>File access through UNC paths works again on Windows.
+ This regression was introduced in OTP 21.</p>
+ <p>
+ Own Id: OTP-15333 Aux Id: ERL-737 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix the seq_trace token to not be cleared when a process
+ receives messages sent by erts. Some examples of when
+ this could happen is all port BIFs, i.e.
+ <c>open_port</c>, <c>port_command</c> etc etc.</p>
+ <p>
+ Fix so that messages sent by nifs can be traced using
+ normal and <c>seq_trace</c> tracing.</p>
+ <p>
+ Own Id: OTP-15038 Aux Id: ERL-602 </p>
+ </item>
+ <item>
+ <p>
+ Fixed specs and documentation for <c>process_info</c>
+ item <c>monitored_by</c> to include port identifiers and
+ nif resources as possible types.</p>
+ <p>
+ Own Id: OTP-15180 Aux Id: ERL-648 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug in generation of erl_crash.dump, which could
+ cause VM to crash.</p>
+ <p>
+ Bug exist since erts-9.2 (OTP-20.2).</p>
+ <p>
+ Own Id: OTP-15181</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where ctrl-break or ctrl-c would not trigger the
+ break mode properly on Windows. This bug was introduced
+ in erts-10.0 (OTP-21).</p>
+ <p>
+ Own Id: OTP-15205</p>
+ </item>
+ <item>
+ <p>
+ Fix a performance bug for reception of UDP packages,
+ where a memory buffer would be reallocated when it should
+ not have been.</p>
+ <p>
+ Introduce a limit on the maximum automatic increase of
+ the UDP user-space buffer to the theoretical max of the
+ network PATH, i.e. 65535.</p>
+ <p>
+ Own Id: OTP-15206</p>
+ </item>
+ <item>
+ <p>
+ Fix alignment of erts allocator state internally in erts.
+ With the improper alignment the emulator would refuse to
+ start when compiled with clang on 32-bit systems.</p>
+ <p>
+ Own Id: OTP-15208 Aux Id: PR-1897 ERL-677 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug where too many concurrent calls to
+ <c>erlang:open_port({spawn,"cmd"},...)</c> would result
+ in the emulator terminating with the reason "Failed to
+ write to erl_child_setup: ". After this fix the
+ <c>open_port</c> call will throw an <c>emfile</c>
+ exception instead.</p>
+ <p>
+ Own Id: OTP-15210</p>
+ </item>
+ <item>
+ <p>
+ Upgraded the ERTS internal PCRE library from version 8.41
+ to version 8.42. 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-15217</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>open_port({fd,X,Y}, ...)</c> to release the file
+ descriptors from the pollset when closing the port.
+ Without this fix the same file descriptor number could
+ not be reused when doing multiple open_port and
+ port_close sequences.</p>
+ <p>
+ Own Id: OTP-15236 Aux Id: ERL-692 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>float_to_list/2</c> and
+ <c>float_to_binary/2</c> with options
+ <c>[{decimals,0},compact]</c> causing totally wrong
+ results. Bug exists since OTP-21.0.</p>
+ <p>
+ Own Id: OTP-15276 Aux Id: PR-1920 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>erlang:memory</c> causing <c>ets</c> to
+ report too much. This small false memory leak (16 bytes
+ each time) can only happen when a specific race condition
+ occurs between scheduler threads on a table with option
+ <c>write_concurrency</c>.</p>
+ <p>
+ Own Id: OTP-15278</p>
+ </item>
+ <item>
+ <p>
+ Minor <c>configure</c> test fixes</p>
+ <p>
+ Own Id: OTP-15282</p>
+ </item>
+ <item>
+ <p>
+ Improved robustness of distribution connection setup. In
+ OTP-21.0 a truly asynchronous connection setup was
+ introduced. This is further improvement on that work to
+ make the emulator more robust and also be able to recover
+ in cases when involved Erlang processes misbehave.</p>
+ <p>
+ Own Id: OTP-15297 Aux Id: OTP-15279, OTP-15280 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The socket options <c>recvtos</c>, <c>recvttl</c>,
+ <c>recvtclass</c> and <c>pktoptions</c> have been
+ implemented in the socket modules. See the documentation
+ for the <c>gen_tcp</c>, <c>gen_udp</c> and <c>inet</c>
+ modules. Note that support for these in the runtime
+ system is platform dependent. Especially for
+ <c>pktoptions</c> which is very Linux specific and
+ obsoleted by the RFCs that defined it.</p>
+ <p>
+ Own Id: OTP-15145 Aux Id: ERIERL-187 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ As of ERTS version 10.0 (OTP 21.0) the
+ <c>erl_child_setup</c> program, which creates port
+ programs, ignores <c>TERM</c> signals. This setting was
+ unintentionally inherited by port programs. Handling of
+ <c>TERM</c> signals in port programs has now been
+ restored to the default behavior. That is, terminate the
+ process.</p>
+ <p>
+ Own Id: OTP-15289 Aux Id: ERIERL-235, OTP-14943, ERL-576 </p>
+ </item>
+ <item>
+ <p>
+ The fix made for OTP-15279 in erts-10.07 (OTP-21.0.8) was
+ not complete. It could cause a new connection attempt to
+ be incorrectly aborted in certain cases. This fix will
+ amend that flaw.</p>
+ <p>
+ Own Id: OTP-15296 Aux Id: OTP-15279, ERIERL-226 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A process could get stuck in an infinite rescheduling
+ loop between normal and dirty schedulers. This bug was
+ introduced in ERTS version 10.0.</p>
+ <p>
+ Thanks to Maxim Fedorov for finding and fixing this
+ issue.</p>
+ <p>
+ Own Id: OTP-15275 Aux Id: PR-1943 </p>
+ </item>
+ <item>
+ <p>
+ Garbage collection of a distribution entry could cause an
+ emulator crash if <c>net_kernel</c> had not brought
+ previous connection attempts on it down properly.</p>
+ <p>
+ Own Id: OTP-15279 Aux Id: ERIERL-226 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A race between termination of a process and resume of the
+ same process via <c>erlang:resume_process/1</c> could
+ cause the VM to crash. This bug was introduced in erts
+ version 10.0 (OTP 21.0).</p>
+ <p>
+ Own Id: OTP-15237</p>
+ </item>
+ <item>
+ <p>
+ When tracing on <c>running</c>, <c>in</c> trace events
+ could be lost when a process was rescheduled between a
+ dirty and a normal scheduler.</p>
+ <p>
+ Own Id: OTP-15269 Aux Id: ERL-713 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug which caused an emulator crash when
+ <c>enif_send()</c> was called by a NIF that executed on a
+ dirty scheduler. The bug was either triggered when the
+ NIF called <c>enif_send()</c> without a message
+ environment, or when the process executing the NIF was
+ <c>send</c> traced.</p>
+ <p>
+ Own Id: OTP-15223</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug causing some Erlang references to be
+ inconsistently ordered. This could for example cause
+ failure to look up certain elements with references as
+ keys in search data structures. This bug was introduced
+ in R13B02.</p>
+ <p>
+ Thanks to Simon Cornish for finding the bug and supplying
+ a fix.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15225</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a bug that prevented the <c>noshell</c> option
+ from working correctly on Mac OS X and BSD.</p>
+ <p>
+ Own Id: OTP-15169</p>
+ </item>
+ <item>
+ <p>Fixed a crash when matching directly against a literal
+ map using a single key that had been saved on the
+ stack.</p>
+ <p>
+ Own Id: OTP-15184</p>
+ </item>
+ <item>
+ <p>Fix node crash when passing a bad time option to
+ <c>file:read_file_info/2</c>.</p>
+ <p>
+ Own Id: OTP-15196</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a scheduler bug that caused normal schedulers to
+ run dirty code.</p>
+ <p>
+ Own Id: OTP-15154</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in <c>erlang:trace_info/2</c> which caused
+ the emulator to crash when a bad argument was passed. The
+ bug was introduced in ERTS version 10.0.</p>
+ <p>
+ Own Id: OTP-15183 Aux Id: ERL-670 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a rare bug that could cause processes to be
+ scheduled after they had been freed.</p>
+ <p>
+ Own Id: OTP-15067 Aux Id: ERL-573 </p>
+ </item>
+ <item>
+ <p>Fixed a race condition in the inet driver that could
+ cause receive to hang when the emulator was compiled with
+ gcc 8.</p>
+ <p>
+ Own Id: OTP-15158 Aux Id: ERL-654 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The keys used in <c>os:getenv</c> and <c>os:putenv</c>
+ are case-insensitive again on Windows.</p>
+ <p>
+ Own Id: OTP-15147 Aux Id: ERL-644 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The type specifications for <c>file:posix/0</c> and
+ <c>inet:posix/0</c> have been updated according to which
+ errors file and socket operations should be able to
+ return.</p>
+ <p>
+ Own Id: OTP-14019 Aux Id: ERL-550 </p>
+ </item>
+ <item>
+ <p>
+ Fix error printout from run_erl and a bug that could
+ cause unintended fds to be leaked into the started
+ program.</p>
+ <p>
+ Own Id: OTP-14537 Aux Id: PR1529 </p>
+ </item>
+ <item>
+ <p> File operations used to accept <seealso
+ marker="kernel:file#type-name_all">filenames</seealso>
+ containing null characters (integer value zero). This
+ caused the name to be truncated and in some cases
+ arguments to primitive operations to be mixed up.
+ Filenames containing null characters inside the filename
+ are now <em>rejected</em> and will cause primitive file
+ operations to fail. </p> <p> Also environment variable
+ operations used to accept <seealso
+ marker="kernel:os#type-env_var_name">names</seealso> and
+ <seealso
+ marker="kernel:os#type-env_var_value">values</seealso> of
+ environment variables containing null characters (integer
+ value zero). This caused operations to silently produce
+ erroneous results. Environment variable names and values
+ containing null characters inside the name or value are
+ now <em>rejected</em> and will cause environment variable
+ operations to fail. </p> <p>Primitive environment
+ variable operations also used to accept the <c>$=</c>
+ character in environment variable names causing various
+ problems. <c>$=</c> characters in environment variable
+ names are now also <em>rejected</em>. </p> <p>Also
+ <seealso
+ marker="kernel:os#cmd/1"><c>os:cmd/1</c></seealso> now
+ reject null characters inside its <seealso
+ marker="kernel:os#type-os_command">command</seealso>.
+ </p> <p><seealso
+ marker="erts:erlang#open_port/2"><c>erlang:open_port/2</c></seealso>
+ will also reject null characters inside the port name
+ from now on.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14543 Aux Id: ERL-370 </p>
+ </item>
+ <item>
+ <p>
+ Fix bugs related to the bookkeeping of microstate
+ accounting states.</p>
+ <p>
+ Own Id: OTP-14652</p>
+ </item>
+ <item>
+ <p><c>os:putenv</c> and <c>os:getenv</c> no longer access
+ the process environment directly and instead work on a
+ thread-safe emulation. The only observable difference is
+ that it's <em>not</em> kept in sync with libc
+ <c>getenv(3)</c> / <c>putenv(3)</c>, so those who relied
+ on that behavior in drivers or NIFs will need to add
+ manual synchronization.</p> <p>On Windows this means that
+ you can no longer resolve DLL dependencies by modifying
+ the <c>PATH</c> just before loading the driver/NIF. To
+ make this less of a problem, the emulator now adds the
+ target DLL's folder to the DLL search path.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14666</p>
+ </item>
+ <item>
+ <p>Corrected <c>erlang:is_builtin(erlang, M, F)</c> to
+ return <c>true</c> for <c>apply/2</c> and
+ <c>yield/0</c>.</p>
+ <p>
+ Own Id: OTP-14713 Aux Id: ERL-500 </p>
+ </item>
+ <item>
+ <p>Fixed a bug where the PATH environment variable wasn't
+ updated correctly on a release downgrade, effectively
+ keeping the PATH of the new release.</p>
+ <p>
+ Own Id: OTP-14719</p>
+ </item>
+ <item>
+ <p>A receive optimization that avoids scanning the entire
+ message queue when receiving a message containing a
+ freshly created reference could in rare circumstances
+ (involving recursive calls to the functions that does the
+ receive) cause the receive to hang. This has been
+ corrected.</p>
+ <p>
+ Own Id: OTP-14782 Aux Id: ERL-511 </p>
+ </item>
+ <item>
+ <p>
+ Fix building of Erlang/OTP on platforms which have small
+ data area with short addressing. For example the
+ PowerPC/RTEMS platform.</p>
+ <p>
+ Own Id: OTP-14909 Aux Id: PR-1692 </p>
+ </item>
+ <item>
+ <p>Fixed a crash when <c>enif_make_binary</c> is called
+ with a binary produced by <c>enif_inspect_binary</c> in a
+ different environment.</p>
+ <p>
+ Own Id: OTP-14931</p>
+ </item>
+ <item>
+ <p>Fixed a crash when <c>enif_make_binary</c> is called
+ more than once with a binary that had previously been
+ added to an <c>enif_ioq</c>.</p>
+ <p>
+ Own Id: OTP-14932</p>
+ </item>
+ <item>
+ <p>
+ The erl_child_setup program now ignores SIGTERM signals.</p>
+ <p>
+ Own Id: OTP-14943 Aux Id: ERL-576 </p>
+ </item>
+ <item>
+ <p>
+ Force 64-bit alignment on pre-allocators on architectures
+ which needs it.</p>
+ <p>
+ Own Id: OTP-14977</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where dirty scheduler picked up non-dirty
+ work.</p>
+ <p>
+ Own Id: OTP-14978</p>
+ </item>
+ <item>
+ <p>
+ Calls to <c>gen_tcp:send/2</c> on closed sockets now
+ returns <c>{error, closed}</c> instead of
+ <c>{error,enotconn}</c>.</p>
+ <p>
+ Own Id: OTP-15001</p>
+ </item>
+ <item>
+ <p>
+ <c>erlang:monotonic_time/1</c> failed with <c>badarg</c>
+ when passing the <c>perf_counter</c> time unit as
+ argument.</p>
+ <p>
+ Own Id: OTP-15008</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where rapid <c>init:restart()</c> calls would
+ sometimes crash because a code load request leaked in
+ between the restarts.</p>
+ <p>
+ Own Id: OTP-15013</p>
+ </item>
+ <item>
+ <p>
+ Improve <c>float_to_list(F, [{decimals,D}])</c> to closer
+ conform with <c>io_lib:format("~.*f", [D,F])</c>.</p>
+ <p>
+ There are however, still cases when <c>float_to_list</c>
+ does not produce the exact same result as
+ <c>io_lib:format</c>, especially for large values
+ <c>F</c> and/or many decimals <c>D</c>.</p>
+ <p>
+ Own Id: OTP-15015 Aux Id: OTP-14890 </p>
+ </item>
+ <item>
+ <p>Fixed a deadlock that would occur on certain
+ allocators when a reallocation failed with <c>+ramv</c>
+ enabled.</p>
+ <p>
+ Own Id: OTP-15024</p>
+ </item>
+ <item>
+ <p>
+ Fix bug that made it impossible to use an erl_tracer as
+ the seq_trace trace receiver.</p>
+ <p>
+ Own Id: OTP-15029</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where a large (> 1 GB) emulator generated error
+ logger message would cause the emulator to crash.</p>
+ <p>
+ Own Id: OTP-15032</p>
+ </item>
+ <item>
+ <p>The emulator will no longer crash when reading the
+ file information of an ordinary file that has an NTFS
+ reparse point, such as files stored in a OneDrive-mapped
+ folder.</p>
+ <p>
+ Own Id: OTP-15062 Aux Id: ERL-615 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>enif_binary_to_term</c> which could cause
+ memory corruption for immediate terms (atoms, small
+ integers, pids, ports, empty lists).</p>
+ <p>
+ Own Id: OTP-15080</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>erlang:system_profile/2</c> that could
+ cause superfluous <c>{profile,_,active,_,_}</c> messages
+ for terminating processes.</p>
+ <p>
+ Own Id: OTP-15085</p>
+ </item>
+ <item>
+ <p>
+ On OSs with per thread CPU time support, change
+ <c>cpu_timestamp</c> in <seealso
+ marker="erlang#trace/3">erlang:trace/3</seealso> to use
+ it instead of per process CPU time. This makes this
+ option useable on such OSs when running multiple
+ schedulers.</p>
+ <p>
+ Own Id: OTP-15090</p>
+ </item>
+ <item>
+ <p>
+ Fix segfault in abort_signal_task which could happen if a
+ port terminated while there were outstanding port tasks
+ that were not signals, for example a
+ ready_input/ready_output event.</p>
+ <p>
+ Own Id: OTP-15108 Aux Id: ERL-621 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>ets</c> that could cause VM crash if
+ process A terminates after fixating a table and process B
+ deletes the table at "the same time". The table fixation
+ could be done with <c>ets:safe_fixtable</c> or if process
+ A terminates in the middle of a long running
+ <c>select</c> or <c>match</c> call.</p>
+ <p>
+ Own Id: OTP-15109</p>
+ </item>
+ <item>
+ <p>Owner and group changes through
+ <c>file:write_file_info</c>, <c>file:change_owner</c>,
+ and <c>file:change_group</c> will no longer report
+ success on permission errors.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15118</p>
+ </item>
+ <item>
+ <p>
+ Fix a bug error reporting from escripts on windows where
+ the error message would get garbled.</p>
+ <p>
+ Own Id: OTP-15119 Aux Id: PR-1826 </p>
+ </item>
+ <item>
+ <p>
+ Fix segfault when a process is interally re-scheduled
+ while being traced for in out events. This bug was
+ introduced in erts-8.0 (OTP-19.0).</p>
+ <p>
+ Own Id: OTP-15125</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>It is now possible to open device files and FIFOs with
+ <c>file:open/2</c>.</p>
+ <p>
+ Own Id: OTP-11462</p>
+ </item>
+ <item>
+ <p>
+ The <c>erlang:system_flag(scheduler_wall_time,Bool)</c>
+ call is now reference counted and will be turned off if
+ the (last) process that started the performance
+ statistics dies. Thus it is no longer possible to start
+ the statistics with <c>rpc:call(Node, erlang,
+ system_flag, [scheduler_wall_time, true])</c> since it
+ will be turned off directly afterwards when the rpc
+ process dies.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11694</p>
+ </item>
+ <item>
+ <p>A new logging API is added to Erlang/OTP, see the
+ <seealso
+ marker="kernel:logger"><c>logger(3)</c></seealso> manual
+ page, and section <seealso
+ marker="kernel:logger_chapter">Logging</seealso> in the
+ Kernel User's Guide.</p>
+ <p>Calls to <c>error_logger</c> are automatically
+ redirected to the new API, and legacy error logger event
+ handlers can still be used. It is, however, recommended
+ to use the Logger API directly when writing new code.</p>
+ <p>Notice the following potential incompatibilities:</p>
+ <list> <item><p>Kernel configuration parameters
+ <c>error_logger</c> still works, but is overruled if the
+ default handler's output destination is configured with
+ Kernel configuration parameter <c>logger</c>.</p> <p>In
+ general, parameters for configuring error logger are
+ overwritten by new parameters for configuring
+ Logger.</p></item> <item><p>The concept of SASL error
+ logging is deprecated, meaning that by default the SASL
+ application does not affect which log events are
+ logged.</p> <p>By default, supervisor reports and crash
+ reports are logged by the default Logger handler started
+ by Kernel, and end up at the same destination (terminal
+ or file) as other standard log event from Erlang/OTP.</p>
+ <p>Progress reports are not logged by default, but can be
+ enabled by setting the primary log level to info, for
+ example with the Kernel configuration parameter
+ <c>logger_level</c>.</p> <p>To obtain backwards
+ compatibility with the SASL error logging functionality
+ from earlier releases, set Kernel configuration parameter
+ <c>logger_sasl_compatible</c> to <c>true</c>. This
+ prevents the default Logger handler from logging any
+ supervisor-, crash-, or progress reports. Instead, SASL
+ adds a separate Logger handler during application start,
+ which takes care of these log events. The SASL
+ configuration parameters <c>sasl_error_logger</c> and
+ <c>sasl_errlog_type</c> specify the destination (terminal
+ or file) and severity level to log for these
+ events.</p></item></list>
+ <p>
+ Since Logger is new in Erlang/OTP 21.0, we do reserve the
+ right to introduce changes to the Logger API and
+ functionality in patches following this release. These
+ changes might or might not be backwards compatible with
+ the initial version.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13295</p>
+ </item>
+ <item>
+ <p>
+ <c>gen_sctp:connect_init/4</c> or rather connect in
+ <c>inet_drv.c</c> for SCTP has been fixed to not check
+ the write file descriptor for writeability after a
+ connect, since for SCTP (SOCK_SEQPACKET) that property
+ does not seem to be any kind of indicator for when a
+ connect has finished. This fixes connects that the OS
+ returned as "in progress" that was misinterpreted by
+ <c>gen_sctp:connect_init</c> as failed.</p>
+ <p>
+ Own Id: OTP-13760 Aux Id: PR-1592 </p>
+ </item>
+ <item>
+ <p>The file driver has been rewritten as a NIF,
+ decreasing the latency of file operations. Notable
+ incompatibilities are:</p> <list> <item><p>The
+ <c>use_threads</c> option for <c>file:sendfile/5</c> no
+ longer has any effect; we either use non-blocking
+ <c>sendfile(2)</c> or fall back to <c>file:read</c> +
+ <c>gen_tcp:send</c>. </p></item> <item><p>The
+ file-specific DTrace probes have been removed. The same
+ effect can be achieved with normal tracing together with
+ the <c>nif__entry</c>/<c>nif__return</c> probes to track
+ scheduling.</p></item> </list>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14256</p>
+ </item>
+ <item>
+ <p>The I/O polling functionality of erts has been
+ re-written to better make use of the OSs polling
+ mechanisms. This change means that erts will now always
+ prefer to use a kernel-polling mechanism if possible.
+ Also all of the I/O polling has been moved to dedicated
+ threads instead of being placed in the scheduler
+ loops.</p> <p>As a result of this, the <c>erl</c> options
+ <c>+K</c> and <c>+secio</c> have been removed. It is
+ still possible to disable kernel-poll, but it has to be
+ done at compile time through the configure option
+ <c>--disable-kernel-poll</c>.</p> <p>The new <c>erl</c>
+ options <seealso marker="erl#+IOt"><c>+IOt</c></seealso>
+ and <seealso marker="erl#+IOp"><c>+IOp</c></seealso> can
+ be used to change how many IO poll threads and poll sets
+ that erts should use. See their respective documentation
+ for more details.</p>
+ <p>
+ Own Id: OTP-14346</p>
+ </item>
+ <item>
+ <p>Truly asynchronous auto-connect. Earlier, when
+ <c>erlang:send</c> was aimed toward an unconnected node,
+ the function would not return until the connection setup
+ had completed (or failed). Now the function returns
+ directly after the message has been enqueued and the
+ connection setup started.</p>
+ <p>The same applies to all distributed operations that
+ may trigger auto-connect, i.e. <c>'!'</c>, <c>send</c>,
+ <c>link</c>, <c>monitor</c>, <c>monitor_node</c>,
+ <c>exit/2</c> and <c>group_leader</c>.</p>
+ <p>The interface for all these functions are unchanged as
+ they do not return connection failures. The only
+ exception is <c>erlang:monitor</c> where a <em>possible
+ incompatibility</em> is introduced: An attempt to monitor
+ a process on a primitive node (such as erl_interface or
+ jinterface), where remote process monitoring is not
+ implemented, will no longer fail with <c>badarg</c>
+ exception. Instead a monitor will be created, but it will
+ only supervise the connection to the node.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14370</p>
+ </item>
+ <item>
+ <p>Changed the default behaviour of <c>.erlang</c>
+ loading: <c>.erlang</c> is no longer loaded from the
+ current directory. <c>c:erlangrc(PathList)</c> can be
+ used to search and load an <c>.erlang</c> file from user
+ specified directories.</p> <p><c>escript</c>,
+ <c>erlc</c>, <c>dialyzer</c> and <c>typer</c> no longer
+ load an <c>.erlang</c> at all.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14439</p>
+ </item>
+ <item>
+ <p>
+ New functionality for implementation of alternative
+ carriers for the Erlang distribution has been introduced.
+ This mainly consists of support for usage of distribution
+ controller processes (previously only ports could be used
+ as distribution controllers). For more information see
+ <seealso marker="erts:alt_dist#distribution_module">ERTS
+ User's Guide ➜ How to implement an Alternative Carrier
+ for the Erlang Distribution ➜ Distribution
+ Module</seealso>.</p>
+ <p>
+ Own Id: OTP-14459</p>
+ </item>
+ <item>
+ <p>
+ Add support for the lcc compiler and in extension the
+ Elbrus 2000 platform.</p>
+ <p>
+ Own Id: OTP-14492</p>
+ </item>
+ <item>
+ <p>Support for "tuple calls" have been removed from the
+ run-time system. Tuple calls was an undocumented and
+ unsupported feature which allowed the module argument for
+ an apply operation to be a tuple: <c>Var = dict:new(),
+ Var:size()</c>. This "feature" frequently caused
+ confusion, especially when such call failed. The
+ stacktrace would point out functions that don't exist in
+ the source code.</p>
+ <p>For legacy code that need to use parameterized modules
+ or tuple calls for some other reason, there is a new
+ compiler option called <c>tuple_calls</c>. When this
+ option is given, the compiler will generate extra code
+ that emulates the old behavior for calls where the module
+ is a variable.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14497</p>
+ </item>
+ <item>
+ <p>Creation of small maps with literal keys has been
+ optimized to be faster and potentially use less memory.
+ The keys are combined into a literal key tuple which is
+ put into the literal pool. The key tuple can be shared
+ between many instances of maps having the same keys.</p>
+ <p>
+ Own Id: OTP-14502</p>
+ </item>
+ <item>
+ <p>
+ When an exception is thrown, include the arguments of the
+ call in the stacktrace for BIFs <c>band</c>, <c>bor</c>,
+ <c>bsl</c>, <c>bsr</c>, <c>bxor</c>, <c>div</c>,
+ <c>rem</c> and the operators <c>+</c>, <c>-</c>, <c>*</c>
+ and <c>/</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14508</p>
+ </item>
+ <item>
+ <p>
+ The non-smp emulators have been removed. This means that
+ the configure options <c>--disable-threads</c> and
+ <c>--enable-plain-emulator</c> have been removed and
+ configure will now refuse to build Erlang/OTP on
+ platforms without thread support.</p>
+ <p>
+ In order to achieve a similar setup as the non-smp
+ emulator, it is possible to start Erlang/OTP with the
+ <c>+S 1</c> option.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14518</p>
+ </item>
+ <item>
+ <p>Modules that use floating point constants compiled
+ with R15 or earlier will need to be re-compiled before
+ they can be loaded.</p>
+ <p>
+ Own Id: OTP-14575</p>
+ </item>
+ <item>
+ <p>
+ Implementation of true asynchronous signaling between
+ processes in order to improve scalability. Signals
+ affected include exit, monitor, demonitor, monitor
+ triggered, link, unlink, and group leader.</p>
+ <p>
+ Own Id: OTP-14589</p>
+ </item>
+ <item>
+ <p>
+ Added a PGO (profile guided optimization) pass to the
+ build step of erts. This can be disabled by passing
+ --disable-pgo to configure.</p>
+ <p>
+ Own Id: OTP-14604</p>
+ </item>
+ <item>
+ <p>
+ Improved the performance of <c>binary:split</c> and
+ <c>binary:match</c>.</p>
+ <p>
+ Own Id: OTP-14610 Aux Id: PR-1480 </p>
+ </item>
+ <item>
+ <p>
+ It is not longer possible to disable dirty schedulers
+ when building erlang.</p>
+ <p>
+ Own Id: OTP-14613</p>
+ </item>
+ <item>
+ <p>Loaded BEAM code in a 64-bit system requires less
+ memory because of better packing of operands for
+ instructions.</p>
+ <p>These memory savings were achieved by major
+ improvements to the <c>beam_makeops</c> scripts used when
+ building the run time system and BEAM compiler. There is
+ also new for documentation for <c>beam_makeops</c> that
+ describes how new BEAM instructions and loader
+ transformations can be implemented. The documentation is
+ found in here in a source directory or git repository:
+ erts/emulator/internal_doc/beam_makeops.md. An online
+ version can be found here:
+ https://github.com/erlang/otp/blob/master/erts/emulator/internal_doc/beam_makeops.md</p>
+ <p>
+ Own Id: OTP-14626</p>
+ </item>
+ <item>
+ <p><c>file:read_file</c> has been changed to read the
+ content of files that report a size of 0 even when data
+ can be read from them. An example of such a file is
+ <c>/proc/cpuinfo</c> on Linux.</p>
+ <p>
+ Own Id: OTP-14637 Aux Id: ERL-327 PR-1524 </p>
+ </item>
+ <item>
+ <p>
+ It is no longer possible to disable the <c>temp_alloc</c>
+ allocator. Disabling it caused serious performance
+ degradations and was never what was wanted.</p>
+ <p>
+ Own Id: OTP-14651</p>
+ </item>
+ <item>
+ <p>The reduction cost of sending messages is now
+ constant. It will no longer scale according to the length
+ of the receiving process' message queue.</p>
+ <p>
+ Own Id: OTP-14667</p>
+ </item>
+ <item>
+ <p>
+ Improved loading of modules with <c>-on_load</c>
+ directive, to no longer block all schedulers when the
+ load operation is completed.</p>
+ <p>
+ Own Id: OTP-14680</p>
+ </item>
+ <item>
+ <p>
+ On platforms with real-time signals available, SIGRTMIN+1
+ is now used as the internal scheduler suspend signal
+ instead of SIGUSR2.</p>
+ <p>
+ Own Id: OTP-14682</p>
+ </item>
+ <item>
+ <p>When the value returned from a '<c>catch</c>'
+ expression is ignored, no stacktrace will be built if an
+ exception is caught. That will save time and produce less
+ garbage. There are also some minor optimizations of
+ '<c>try</c>/<c>catch</c>' both in the compiler and
+ run-time system.</p>
+ <p>
+ Own Id: OTP-14683</p>
+ </item>
+ <item>
+ <p>The guarantees and non-guarantees of
+ <c>erlang:get_stacktrace/0</c> are now documented.</p>
+ <p>
+ Own Id: OTP-14687</p>
+ </item>
+ <item>
+ <p>There is a new syntax in '<c>try/catch</c>' for
+ retrieving the stacktrace without calling
+ '<c>erlang:get_stacktrace/0</c>'. See the reference
+ manual for a description of the new syntax. The
+ '<c>erlang:get_stacktrace/0</c>' BIF is now
+ deprecated.</p>
+ <p>
+ Own Id: OTP-14692</p>
+ </item>
+ <item>
+ <p>
+ New 'used' option for <c>binary_to_term/2</c> that will
+ also return number of bytes actually read from the
+ binary. This enables easy access to any extra data in the
+ binary located directly after the returned term.</p>
+ <p>
+ Own Id: OTP-14780</p>
+ </item>
+ <item>
+ <p>
+ Added more statistics for
+ <c>erlang:system_info({allocator,A})</c> in the
+ <c>mbcs_pool</c> section.</p>
+ <p>
+ Own Id: OTP-14795 Aux Id: ERL-88 </p>
+ </item>
+ <item>
+ <p>Added <c>enif_ioq_peek_head</c> to retrieve Erlang
+ terms from NIF IO queues without having to resort to
+ copying.</p>
+ <p>
+ Own Id: OTP-14797</p>
+ </item>
+ <item>
+ <p>There is a new option '<c>makedep_side_effect</c>' for
+ the compiler and <c>-MMD</c> for '<c>erlc</c>' that
+ generates dependencies and continues to compile as
+ normal.</p>
+ <p>
+ Own Id: OTP-14830</p>
+ </item>
+ <item>
+ <p>Added <c>ets:whereis/1</c> for retrieving the table
+ identifier of a named table.</p>
+ <p>
+ Own Id: OTP-14884</p>
+ </item>
+ <item>
+ <p><c>seq_trace</c> labels may now be any erlang
+ term.</p>
+ <p>
+ Own Id: OTP-14899</p>
+ </item>
+ <item>
+ <p>
+ Optimized the common case of <c>monitor</c> followed by
+ <c>send</c> to the same local process. The monitor signal
+ is now delayed in order to be piggybacked with the sent
+ message and thereby only get one lock operation on the
+ message queue of the receiver. A delayed monitor signal
+ is flushed if no <c>send</c> has been done at the latest
+ when the process is scheduled out.</p>
+ <p>
+ Own Id: OTP-14901</p>
+ </item>
+ <item>
+ <p>
+ Make hipe compiled code work on x86_64 (amd64) with OS
+ security feature PIE, where executable code can be loaded
+ into a random location. Old behavior, if hipe was
+ enabled, was to disable PIE build options for the VM.</p>
+ <p>
+ Own Id: OTP-14903</p>
+ </item>
+ <item>
+ <p>The number of driver async threads will now default to
+ 1 as the standard drivers do not use them anymore. Users
+ that changed this value to tweak the file driver should
+ replace <c>+A</c> with <c>+SDio</c> since it now uses
+ dirty IO schedulers instead of async threads.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14928</p>
+ </item>
+ <item>
+ <p>
+ Optimize <c>==</c> and <c>/=</c> for binaries with
+ different sizes to be constant in time instead of
+ proportional to the size of their common prefix.</p>
+ <p>
+ Own Id: OTP-14934 Aux Id: PR-1708 </p>
+ </item>
+ <item>
+ <p>
+ Refactorings making some internal process flags available
+ for other usage.</p>
+ <p>
+ Own Id: OTP-14948</p>
+ </item>
+ <item>
+ <p>
+ Removed need for HiPE to allocate native executable
+ memory in low 2GB address space on x86_64. Command line
+ option <c>+MXscs</c> is thereby obsolete and ignored.</p>
+ <p>
+ Own Id: OTP-14951</p>
+ </item>
+ <item>
+ <p>Added <c>enif_make_map_from_arrays</c> for creating a
+ populated map, analogous to
+ <c>enif_make_list_from_array</c>.</p>
+ <p>
+ Own Id: OTP-14954</p>
+ </item>
+ <item>
+ <p>Added configuration switches for busy-wait and wake up
+ thresholds for dirty schedulers, and changing these
+ settings for normal schedulers will no longer affect
+ dirty schedulers. </p> <p>Refer to the documentation for
+ details. The new switches are <seealso
+ marker="erl#+sbwtdcpu">+sbwtdcpu</seealso>, <seealso
+ marker="erl#+sbwtdio">+sbwtdio</seealso>, <seealso
+ marker="erl#+swtdcpu">+swtdcpu</seealso>, and <seealso
+ marker="erl#+swtdio">+swtdio</seealso>.</p> <p>The
+ default busy wait threshold for dirty scheduler threads
+ has also been lowered to <c>short</c>.</p>
+ <p>
+ Own Id: OTP-14959</p>
+ </item>
+ <item>
+ <p>
+ The list of "taints" now also includes dynamic loaded
+ drivers in addition to NIF libraries. Statically linked
+ drivers and NIF libraries that are part of erts are not
+ included. The "taints" are returned by
+ <c>system_info(taints)</c> and printed in the header of
+ <c>erl_crash.dump</c> files.</p>
+ <p>
+ Own Id: OTP-14960</p>
+ </item>
+ <item>
+ <p>Added <c>instrument:allocations</c> and
+ <c>instrument:carriers</c> for retrieving information
+ about memory utilization and fragmentation.</p>
+ <p>The old <c>instrument</c> interface has been removed,
+ as have the related options <c>+Mim</c> and
+ <c>+Mis</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14961</p>
+ </item>
+ <item>
+ <p>The process suspend functionality used by the <seealso
+ marker="erlang#suspend_process/2">erlang:suspend_process/2</seealso>
+ BIF has been reimplemented using the newly introduced
+ true asynchronous signaling between processes. This
+ mainly to reduce memory usage in the process control
+ block of all processes, but also in order to simplify the
+ implementation.</p> <warning> <p>You can easily create
+ deadlocks if processes suspends each other (directly or
+ in circles). In ERTS versions prior to ERTS version 10.0,
+ the runtime system prevented such deadlocks, but this
+ prevention has now been removed due to performance
+ reasons.</p> </warning> <p>Other ERTS internal
+ functionality that used the previous process suspend
+ functionality have also been reimplemented to use
+ asynchronous signaling instead.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14964 Aux Id: OTP-14589 </p>
+ </item>
+ <item>
+ <p>Added the <c>nifs</c> option to
+ <c>?MODULE:module_info/1</c> for listing a module's
+ installed NIF functions.</p>
+ <p>
+ Own Id: OTP-14965</p>
+ </item>
+ <item>
+ <p>
+ New implementation of <c>erlang:process_info/[1,2]</c>.</p>
+ <p>
+ In the general case when inspecting another process, the
+ new implementation sends an asynchronous process-info
+ request signal to the other process and waits for the
+ result instead of locking the other process and reading
+ the result directly. In some special cases where no
+ conflicts occur, signal order wont be violated, and the
+ amount of data requested is guaranteed to be small, the
+ inspected process may be inspected directly.</p>
+ <p>
+ Appropriate amount of reductions are now also bumped when
+ inspecting a process.</p>
+ <p>
+ Own Id: OTP-14966</p>
+ </item>
+ <item>
+ <p>
+ Removed process start time from crash dump in order to
+ save memory in process control block.</p>
+ <p>
+ Own Id: OTP-14975 Aux Id: PR-1597 </p>
+ </item>
+ <item>
+ <p>
+ Optimize <c>erlang:put/2</c> when updating existing key
+ with a new immediate value (atom, small integer, pid,
+ port).</p>
+ <p>
+ Own Id: OTP-14976</p>
+ </item>
+ <item>
+ <p>
+ <c>erlang:process_info/1</c> has been changed to no
+ longer include <c>messages</c> by default. Instead
+ <c>erlang:process_info/2</c> should be used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14986 Aux Id: PR-1745 </p>
+ </item>
+ <item>
+ <p>
+ New <c>erlang:system_info(ets_count)</c> to get total
+ number of ets tables existing at the local node.</p>
+ <p>
+ Own Id: OTP-14987</p>
+ </item>
+ <item>
+ <p>
+ New NIF functions: <c>enif_mutex_name</c>,
+ <c>enif_cond_name</c>, <c>enif_rwlock_name</c>,
+ <c>enif_thread_name</c>, <c>enif_vfprintf</c>,
+ <c>enif_vsnprintf</c>.</p>
+ <p>
+ Own Id: OTP-14994</p>
+ </item>
+ <item>
+ <p>When <c>erlang:system_flag(backtrace_depth, 0)</c> has
+ been called, all exceptions will now contain the entry
+ for <em>one</em> function (despite the zero). It used to
+ be that a hand-made stack backtrace passed to
+ <c>erlang:raise/3</c> would be be truncated to an empty
+ list.</p>
+ <p>
+ Own Id: OTP-15026</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug for named <c>ets</c> tables which could cause
+ unexpected results from matchspec iteration functions
+ (<c>ets:select*</c> and <c>ets:match*</c>) if the table
+ was deleted and recreated with the same name during the
+ iteration. The iteration could incorrectly continue
+ through the recreated table. The expected correct
+ behavior is now for the iteration call to fail with a
+ <c>badarg</c> exception if the table is deleted before
+ the iteration has completed.</p>
+ <p>
+ Own Id: OTP-15031</p>
+ </item>
+ <item>
+ <p>Two new guards BIFs operating on maps have been added:
+ <c>map_get/2</c> and <c>is_map_key/2</c>. They do the
+ same as <c>maps:get/2</c> and <c>maps:is_key/2</c>,
+ respectively, except that they are allowed to be used in
+ guards.</p>
+ <p>
+ Own Id: OTP-15037 Aux Id: PR-1784, PR-1802 </p>
+ </item>
+ <item>
+ <p>
+ Release run-queue lock while cleaning up terminated dirty
+ process.</p>
+ <p>
+ Own Id: OTP-15081</p>
+ </item>
+ <item>
+ <p>The callback module passed as <c>-epmd_module</c> to
+ erl has been expanded to be able to do name and port
+ resolving.</p> <p>Documentation has also been added in
+ the <seealso
+ marker="kernel:erl_epmd"><c>erl_epmd</c></seealso>
+ reference manual and ERTS User's Guide <seealso
+ marker="erts:alt_disco">How to Implement an Alternative
+ Service Discovery for Erlang Distribution</seealso>.</p>
+ <p>
+ Own Id: OTP-15086 Aux Id: PR-1694 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 9.3.3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixes of install/release phase in build system.</p>
+ <list> <item>The source tree was modified when
+ installing/releasing and/or applying a patch.</item>
+ <item>Some files were installed with wrong access
+ rights.</item> <item>If applying a patch (using
+ <c>otp_patch_apply</c>) as another user (except root)
+ than the user that built the source, the documentation
+ was not properly updated.</item> </list>
+ <p>
+ Own Id: OTP-15551</p>
+ </item>
+ <item>
+ <p>
+ Minor fixes for <c>make clean</c>.</p>
+ <p>
+ Own Id: OTP-15657</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in all <c>ets:select*</c> and
+ <c>ets:match*</c> functions that could in some rare cases
+ lead to very poor performance.</p>
+ <p>
+ Own Id: OTP-15660 Aux Id: ERL-869 </p>
+ </item>
+ <item>
+ <p>
+ Fix a possible deadlock when terminating the ERTS caused
+ by a dirty scheduler not releasing it's run-queue lock
+ when terminating.</p>
+ <p>
+ Own Id: OTP-15690 Aux Id: PR-2172 </p>
+ </item>
+ <item>
+ <p>Add missing documentation of new external tags
+ <c>NEW_PID</c>, <c>NEW_PORT</c> and
+ <c>NEWER_REFERENCE</c> introduced in OTP 19.</p> <p>These
+ new tags are planned to be "activated" in OTP 23 when
+ distribution capability flag <c>DFLAG_BIG_CREATION</c>
+ becomes mandatory. Older nodes (>= 19) are able to decode
+ these new tags and send them back to the new node. Nodes
+ older than OTP 23 will however never encode their own
+ local pids, ports and references using the new tags.</p>
+ <p>
+ Own Id: OTP-15766</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.3.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/vsn.mk b/erts/vsn.mk
index c57f8746f6..fafcdf3b28 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.3.5
+VSN = 10.3.4
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index 7664dcc9d1..30dd1eb296 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -787,5 +787,3 @@
</funcs>
</erlref>
-
-
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 9c74146d73..fac1b09e44 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,189 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.17.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ OTP internal test improvements.</p>
+ <p>
+ Own Id: OTP-15716</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.17</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug caused <c>ct:encrypt_config_file/3</c> and
+ <c>ct:decrypt_config_file/3</c> to fail with
+ <c>badmatch</c> if input parameter <c>KeyOrFile</c> was
+ <c>{key,string()}</c>. This is now corrected.</p>
+ <p>
+ Own Id: OTP-15540</p>
+ </item>
+ <item>
+ <p>
+ The status of a test case which failed with timetrap
+ timeout in <c>end_per_testcase</c> could not be modified
+ by returning <c>{fail,Reason}</c> from a
+ <c>post_end_per_testcase</c> hook function. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15584 Aux Id: ERIERL-282 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A new variant of the <c>newline</c> option to
+ <c>ct_telnet:cmd/3</c> and <c>ct_telnet:send/3</c> is
+ added, which allows to specify a string to append as
+ newline indicator on a command. By default, the value is
+ "\n", but in some cases it is required to be "\r\n",
+ which this option allows.</p>
+ <p>
+ A faulty regular expression given as parameter to
+ <c>ct_telnet:expect/2,3</c> would earlier crash and look
+ like an internal error in common_test. A better error
+ indication is now given, but the test case will still
+ fail.</p>
+ <p>
+ Own Id: OTP-15229 Aux Id: ERIERL-203 </p>
+ </item>
+ <item>
+ <p>
+ Since the yang RFC allows more than one top element of
+ config data in an <c>edit-config</c> element,
+ <c>ct_netconfc:edit_config/3,4,5</c> can now take a list
+ of XML elements.</p>
+ <p>
+ Own Id: OTP-15298</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.16.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The Logger handler cth_log_redirect earlier called the
+ report callback (report_cb) before calling the logger
+ formatter. In some cases this would fail, since
+ cth_log_redirect could not handle report callbacks with
+ two arguments. This is now corrected, so only the
+ formatter will call the report callback.</p>
+ <p>
+ Own Id: OTP-15307</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.16</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Use the compiler option <c>nowarn_export_all</c> to
+ disable <c>export_all</c> warnings when automatically
+ compiling test suites.</p>
+ <p>
+ Own Id: OTP-14810</p>
+ </item>
+ <item>
+ <p>
+ Use uri_string module instead of http_uri.</p>
+ <p>
+ Own Id: OTP-14902</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.15.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The test result when a hook function fails is in general
+ the same as if the function that the hook is associated
+ with fails. For example, if <c>post_init_per_testcase</c>
+ fails the result is that the test case is skipped, as is
+ the case when <c>init_per_testcase</c> fails.This,
+ however, was earlier not true for timetrap timeouts or
+ other error situations where the process running the hook
+ function was killed. This is now corrected, so the error
+ handling should be the same no matter how the hook
+ function fails.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15717 Aux Id: ERIERL-334 </p>
+ </item>
+ <item>
+ <p>
+ In some rare cases, when two common_test nodes used the
+ same log directory, a timing problem could occur which
+ caused common_test to crash because it's log cache file
+ was unexpectedly empty. This is now corrected.</p>
+ <p>
+ Own Id: OTP-15758 Aux Id: ERIERL-342 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Two new common_test hook functions are introduced:</p>
+ <p>
+ <c>post_groups/2</c>, which is called after
+ <c>Suite:groups/0</c><br/> <c>post_all/3</c>, which is
+ called after <c>Suite:all/0</c></p>
+ <p>
+ These functions allow modifying the return values from
+ the <c>groups/0</c> and <c>all/0</c> functions,
+ respectively.</p>
+ <p>
+ A new term, <c>{testcase,TestCase,RepeatProperties}</c>
+ is now also allowed in the return from <c>all/0</c>. This
+ can be used for repeating a single test case a specific
+ number of times, or until it fails or succeeds once.</p>
+ <p>
+ Own Id: OTP-14746 Aux Id: ERIERL-143 </p>
+ </item>
+ <item>
+ <p>
+ OTP internal test improvements.</p>
+ <p>
+ Own Id: OTP-15716</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.17.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 73a442a29c..14a3622a00 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.17.2
+COMMON_TEST_VSN = 1.17.1
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 1f95382704..b331fd81b6 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -202,6 +202,22 @@
</section>
+<section><title>Erl_Interface 3.10.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix handling of Makefile dependencies so that parallel
+ make works properly.</p>
+ <p>
+ Own Id: OTP-15757</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.10.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -1652,4 +1668,3 @@
</section>
</section>
</chapter>
-
diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index 9bcd99fba3..ff3250b383 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -233,9 +233,13 @@ countryName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
-- regarding how to handle and sometimes accept incorrect certificates
-- we define and use the type below instead of X520countryName
+ -- We accept utf8String encoding of the US-ASCII
+ -- country name code and the mix up with other country code systems
+ -- that uses three characters instead of two.
+
OTP-X520countryname ::= CHOICE {
- printableString PrintableString (SIZE (2)),
- utf8String UTF8String (SIZE (2))
+ printableString PrintableString (SIZE (2..3)),
+ utf8String UTF8String (SIZE (2..3))
}
serialNumber ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 423d90fef6..a6c3d57148 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -73,6 +73,23 @@
</section>
+ <section><title>SNMP 5.2.11.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ [snmp|agent] Add a get-mechanism callback module (and a
+ corresponding behaviour). The agent calls this module to
+ handle each get (get, get-next and get-bulk) request.</p>
+ <p>
+ Own Id: OTP-15691 Aux Id: ERIERL-324 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SNMP 5.2.11</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/snmp/include/snmp_types.hrl b/lib/snmp/include/snmp_types.hrl
index ffe30996dc..eff17a13a3 100644
--- a/lib/snmp/include/snmp_types.hrl
+++ b/lib/snmp/include/snmp_types.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -349,6 +349,9 @@
-define(view_included, 1).
-define(view_excluded, 2).
+-define(view_wildcard, 0).
+-define(view_exact, 1).
+
%%-----------------------------------------------------------------
%% From SNMPv2-SMI
diff --git a/lib/snmp/src/agent/depend.mk b/lib/snmp/src/agent/depend.mk
index 8eba50fa3b..49c7669e41 100644
--- a/lib/snmp/src/agent/depend.mk
+++ b/lib/snmp/src/agent/depend.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2016. All Rights Reserved.
+# Copyright Ericsson AB 2004-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.
@@ -24,6 +24,9 @@ $(EBIN)/snmpa_authentication_service.$(EMULATOR): \
$(EBIN)/snmpa_error_report.$(EMULATOR): \
snmpa_error_report.erl
+$(EBIN)/snmpa_get_mechanism.$(EMULATOR): \
+ snmpa_get_mechanism.erl
+
$(EBIN)/snmpa_network_interface.$(EMULATOR): \
snmpa_network_interface.erl
@@ -78,6 +81,20 @@ $(EBIN)/snmpa_error_logger.$(EMULATOR): \
snmpa_error_report.erl \
snmpa_error_logger.erl
+$(EBIN)/snmpa_set.$(EMULATOR): \
+ snmpa_set_mechanism.erl \
+ snmpa_set.erl \
+ ../misc/snmp_verbosity.hrl
+
+$(EBIN)/snmpa_get.$(EMULATOR): \
+ snmpa_get_mechanism.erl \
+ snmpa_get.erl \
+ ../misc/snmp_verbosity.hrl
+
+$(EBIN)/snmpa_get_lib.$(EMULATOR): \
+ snmpa_get_lib.erl \
+ ../misc/snmp_verbosity.hrl
+
$(EBIN)/snmpa_local_db.$(EMULATOR): \
snmpa_local_db.erl \
../misc/snmp_debug.hrl \
diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk
index 0f8615588a..49cc158c2e 100644
--- a/lib/snmp/src/agent/modules.mk
+++ b/lib/snmp/src/agent/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2016. All Rights Reserved.
+# Copyright Ericsson AB 2004-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.
@@ -22,6 +22,7 @@ BEHAVIOUR_MODULES = \
snmpa_authentication_service \
snmpa_discovery_handler \
snmpa_error_report \
+ snmpa_get_mechanism \
snmpa_mib_storage \
snmpa_mib_data \
snmpa_network_interface \
@@ -30,12 +31,24 @@ BEHAVIOUR_MODULES = \
snmpa_notification_filter \
snmpa_set_mechanism
+MIB_MODULES = \
+ snmp_community_mib \
+ snmp_framework_mib \
+ snmp_notification_mib \
+ snmp_standard_mib \
+ snmp_target_mib \
+ snmp_user_based_sm_mib \
+ snmp_view_based_acm_mib
+
# snmpa is "plain" interface module but also defines some agent specific types
# and therefor must be compiled before the modules that use them, including
# the behaviour modules...
+# Some of the MIB modules also define types used elsewhere and therefor
+# has to be built before the other mods.
# snmpa_mib_data_ttln
MODULES = \
snmpa \
+ $(MIB_MODULES) \
$(BEHAVIOUR_MODULES) \
snmpa_acm \
snmpa_agent \
@@ -46,6 +59,8 @@ MODULES = \
snmpa_error \
snmpa_error_io \
snmpa_error_logger \
+ snmpa_get \
+ snmpa_get_lib \
snmpa_local_db \
snmpa_mib_storage_ets \
snmpa_mib_storage_dets \
@@ -66,17 +81,10 @@ MODULES = \
snmpa_trap \
snmpa_usm \
snmpa_vacm \
- snmp_community_mib \
- snmp_framework_mib \
snmp_generic \
snmp_generic_mnesia \
snmp_index \
- snmp_notification_mib \
- snmp_shadow_table \
- snmp_standard_mib \
- snmp_target_mib \
- snmp_user_based_sm_mib \
- snmp_view_based_acm_mib
+ snmp_shadow_table
INTERNAL_HRL_FILES = \
diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
index 02415e8036..c6eeb7cea2 100644
--- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
+++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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.
@@ -37,6 +37,12 @@
%%
-export([emask2imask/1]).
+-export_type([
+ mibview/0,
+ internal_view_mask/0,
+ internal_view_mask_element/0,
+ internal_view_type/0
+ ]).
-include("snmp_types.hrl").
-include("SNMPv2-TC.hrl").
@@ -53,7 +59,13 @@
-type internal_view_mask() :: null | [internal_view_mask_element()].
--type internal_view_mask_element() :: 0 | 1.
+-type internal_view_mask_element() :: ?view_wildcard |
+ ?view_exact.
+-type internal_view_type() :: ?view_included | ?view_excluded.
+
+-type mibview() :: [{SubTree :: snmp:oid(),
+ Mask :: internal_view_mask(),
+ Type :: internal_view_type()}].
-type external_view_mask() :: octet_string(). % At most length of 16 octet
-type octet_string() :: [octet()].
diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl
index 458b88359b..a521b3773b 100644
--- a/lib/snmp/src/agent/snmpa_agent.erl
+++ b/lib/snmp/src/agent/snmpa_agent.erl
@@ -28,7 +28,8 @@
%% External exports
-export([start_link/4, start_link/5, stop/1]).
--export([subagent_set/2,
+-export([subagent_get/3, subagent_get_next/3,
+ subagent_set/2,
load_mibs/3, unload_mibs/3,
which_mibs/1, whereis_mib/2, info/1,
register_subagent/3, unregister_subagent/2,
@@ -362,12 +363,19 @@ do_init(Prio, Parent, Ref, Options) ->
"~n Options: ~p",[Prio, Parent, Ref, Options]),
Mibs = get_mibs(Options),
+
SetModule = get_set_mechanism(Options),
put(set_module, SetModule),
+ ?vtrace("set-module: ~w", [SetModule]),
+
+ GetModule = get_get_mechanism(Options),
+ put(get_module, GetModule),
+ ?vtrace("get-module: ~w", [GetModule]),
%% OTP-3324. For AXD301.
AuthModule = get_authentication_service(Options),
put(auth_module, AuthModule),
+ ?vtrace("auth-module: ~w", [AuthModule]),
MultiT = get_multi_threaded(Options),
Vsns = get_versions(Options),
@@ -1133,7 +1141,7 @@ handle_call({subagent_get_next, MibView, Varbinds, PduData}, _From, S) ->
"~n PduData: ~p",
[MibView,Varbinds,PduData]),
put_pdu_data(PduData),
- {reply, do_get_next(MibView, Varbinds, infinity), S};
+ {reply, do_get_next(MibView, Varbinds), S};
handle_call({subagent_set, Arguments, PduData}, _From, S) ->
?vlog("[handle_call] subagent set:"
"~n Arguments: ~p"
@@ -1174,7 +1182,7 @@ handle_call({get_next, Vars, Context}, _From, S) ->
?vdebug("Varbinds: ~p",[Varbinds]),
MibView = snmpa_acm:get_root_mib_view(),
Reply =
- case do_get_next(MibView, Varbinds, infinity) of
+ case do_get_next(MibView, Varbinds) of
{noError, 0, NewVarbinds} ->
Vbs = lists:keysort(#varbind.org_index, NewVarbinds),
[{Oid,Val} || #varbind{oid = Oid, value = Val} <- Vbs];
@@ -2559,7 +2567,7 @@ process_pdu(#pdu{type = 'get-next-request', request_id = ReqId, varbinds = Vbs},
"~n ReqId: ~p"
"~n Vbs: ~p"
"~n MibView: ~p",[ReqId, Vbs, MibView]),
- Res = get_err(do_get_next(MibView, Vbs, infinity)),
+ Res = get_err(do_get_next(MibView, Vbs)),
?vtrace("get-next result: "
"~n ~p",[Res]),
{ErrStatus, ErrIndex, ResVarbinds} =
@@ -2650,8 +2658,7 @@ validate_next_v1_2([Vb | _Vbs], _MibView, _Res)
{noSuchName, Vb#varbind.org_index};
validate_next_v1_2([Vb | Vbs], MibView, Res)
when Vb#varbind.variabletype =:= 'Counter64' ->
- case validate_next_v1(
- do_get_next(MibView, [mk_next_oid(Vb)], infinity), MibView) of
+ case validate_next_v1( do_get_next(MibView, [mk_next_oid(Vb)]), MibView) of
{noError, 0, [NVb]} ->
validate_next_v1_2(Vbs, MibView, [NVb | Res]);
{Error, Index, _OrgVb} ->
@@ -2693,6 +2700,20 @@ mk_next_oid(Vb) ->
%%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
+%% Func: do_get/2
+%% Purpose: Handles all VBs in a request that is inside the
+%% mibview (local).
+%% Returns: {noError, 0, ListOfNewVarbinds} |
+%% {ErrorStatus, ErrorIndex, []}
+%%-----------------------------------------------------------------
+
+do_get(UnsortedVarbinds, IsNotification) ->
+ Extra = get(net_if_data),
+ GetModule = get(get_module),
+ GetModule:do_get(UnsortedVarbinds, IsNotification, Extra).
+
+
+%%-----------------------------------------------------------------
%% Func: do_get/3
%% Purpose: do_get handles "getRequests".
%% Pre: incoming varbinds have type == 'NULL', value == unSpecified
@@ -2700,390 +2721,24 @@ mk_next_oid(Vb) ->
%% {ErrorStatus, ErrorIndex, []}
%%-----------------------------------------------------------------
-%% If this function is called from a worker-process, we *may*
-%% need to tunnel into the master-agent and let it do the
-%% work
+%% If this function is called from a worker-process (or other process),
+%% we *may* need to tunnel into the master-agent and let it do the work.
do_get(MibView, UnsortedVarbinds, IsNotification) ->
- do_get(MibView, UnsortedVarbinds, IsNotification, false).
+ Extra = get(net_if_data),
+ GetModule = get(get_module),
+ GetModule:do_get(MibView, UnsortedVarbinds, IsNotification, Extra).
do_get(MibView, UnsortedVarbinds, IsNotification, ForceMaster) ->
- ?vtrace("do_get -> entry with"
- "~n MibView: ~p"
- "~n UnsortedVarbinds: ~p"
- "~n IsNotification: ~p",
- [MibView, UnsortedVarbinds, IsNotification]),
case (whereis(snmp_master_agent) =:= self()) of
false when (ForceMaster =:= true) ->
- %% I am a lowly worker process, handoff to the master agent
PduData = get_pdu_data(),
call(snmp_master_agent,
{do_get, MibView, UnsortedVarbinds, IsNotification, PduData});
-
- _ ->
- %% This is me, the master, so go ahead
- {OutSideView, InSideView} =
- split_vbs_view(UnsortedVarbinds, MibView),
- {Error, Index, NewVbs} =
- do_get(InSideView, IsNotification),
- {Error, Index, NewVbs ++ OutSideView}
-
- end.
-
-
-split_vbs_view(Vbs, MibView) ->
- ?vtrace("split the varbinds view", []),
- split_vbs_view(Vbs, MibView, [], []).
-
-split_vbs_view([Vb | Vbs], MibView, Out, In) ->
- case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of
- true -> split_vbs_view(Vbs, MibView, Out, [Vb | In]);
- false -> split_vbs_view(Vbs, MibView,
- [Vb#varbind{value = noSuchObject} | Out], In)
- end;
-split_vbs_view([], _MibView, Out, In) ->
- {Out, In}.
-
-do_get(UnsortedVarbinds, IsNotification) ->
- {MyVarbinds, SubagentVarbinds} = sort_varbindlist(UnsortedVarbinds),
- case do_get_local(MyVarbinds, [], IsNotification) of
- {noError, 0, NewMyVarbinds} ->
- case do_get_subagents(SubagentVarbinds, IsNotification) of
- {noError, 0, NewSubagentVarbinds} ->
- {noError, 0, NewMyVarbinds ++ NewSubagentVarbinds};
- {ErrorStatus, ErrorIndex, _} ->
- {ErrorStatus, ErrorIndex, []}
- end;
- {ErrorStatus, ErrorIndex, _} ->
- {ErrorStatus, ErrorIndex, []}
+ _ ->
+ do_get(MibView, UnsortedVarbinds, IsNotification)
end.
-%%-----------------------------------------------------------------
-%% Func: do_get_local/3
-%% Purpose: Loop the variablebindings list. We know that each varbind
-%% in that list belongs to us.
-%% Returns: {noError, 0, ListOfNewVarbinds} |
-%% {ErrorStatus, ErrorIndex, []}
-%%-----------------------------------------------------------------
-do_get_local([Vb | Vbs], Res, IsNotification) ->
- case try_get(Vb, IsNotification) of
- NewVb when is_record(NewVb, varbind) ->
- do_get_local(Vbs, [NewVb | Res], IsNotification);
- ListOfNewVb when is_list(ListOfNewVb) ->
- do_get_local(Vbs, lists:append(ListOfNewVb, Res), IsNotification);
- {error, Error, OrgIndex} ->
- {Error, OrgIndex, []}
- end;
-do_get_local([], Res, _IsNotification) ->
- {noError, 0, Res}.
-
-%%-----------------------------------------------------------------
-%% Func: do_get_subagents/2
-%% Purpose: Loop the list of varbinds for different subagents.
-%% For each of them, call sub_agent_get to retreive
-%% the values for them.
-%% Returns: {noError, 0, ListOfNewVarbinds} |
-%% {ErrorStatus, ErrorIndex, []}
-%%-----------------------------------------------------------------
-do_get_subagents(SubagentVarbinds, IsNotification) ->
- do_get_subagents(SubagentVarbinds, [], IsNotification).
-do_get_subagents([{SubAgentPid, SAVbs} | Tail], Res, IsNotification) ->
- {_SAOids, Vbs} = sa_split(SAVbs),
- case catch subagent_get(SubAgentPid, Vbs, IsNotification) of
- {noError, 0, NewVbs} ->
- do_get_subagents(Tail, lists:append(NewVbs, Res), IsNotification);
- {ErrorStatus, ErrorIndex, _} ->
- {ErrorStatus, ErrorIndex, []};
- {'EXIT', Reason} ->
- user_err("Lost contact with subagent (get) ~w. Using genErr",
- [Reason]),
- {genErr, 0, []}
- end;
-do_get_subagents([], Res, _IsNotification) ->
- {noError, 0, Res}.
-
-
-%%-----------------------------------------------------------------
-%% Func: try_get/2
-%% Returns: {error, ErrorStatus, OrgIndex} |
-%% #varbind |
-%% List of #varbind
-%%-----------------------------------------------------------------
-try_get(IVb, IsNotification) when is_record(IVb, ivarbind) ->
- ?vtrace("try_get(ivarbind) -> entry with"
- "~n IVb: ~p", [IVb]),
- get_var_value_from_ivb(IVb, IsNotification);
-try_get({TableOid, TableVbs}, IsNotification) ->
- ?vtrace("try_get(table) -> entry with"
- "~n TableOid: ~p"
- "~n TableVbs: ~p", [TableOid, TableVbs]),
- [#ivarbind{mibentry = MibEntry}|_] = TableVbs,
- {NoAccessVbs, AccessVbs} =
- check_all_table_vbs(TableVbs, IsNotification, [], []),
- case get_tab_value_from_mib(MibEntry, TableOid, AccessVbs) of
- {error, ErrorStatus, OrgIndex} ->
- {error, ErrorStatus, OrgIndex};
- NVbs ->
- NVbs ++ NoAccessVbs
- end.
-
-%%-----------------------------------------------------------------
-%% Make sure all requested columns are accessible.
-%%-----------------------------------------------------------------
-check_all_table_vbs([IVb| IVbs], IsNotification, NoA, A) ->
- #ivarbind{mibentry = Me, varbind = Vb} = IVb,
- case Me#me.access of
- 'not-accessible' ->
- NNoA = [Vb#varbind{value = noSuchInstance} | NoA],
- check_all_table_vbs(IVbs, IsNotification, NNoA, A);
- 'accessible-for-notify' when IsNotification =:= false ->
- NNoA = [Vb#varbind{value = noSuchInstance} | NoA],
- check_all_table_vbs(IVbs, IsNotification, NNoA, A);
- 'write-only' ->
- NNoA = [Vb#varbind{value = noSuchInstance} | NoA],
- check_all_table_vbs(IVbs, IsNotification, NNoA, A);
- _ ->
- check_all_table_vbs(IVbs, IsNotification, NoA, [IVb | A])
- end;
-check_all_table_vbs([], _IsNotification, NoA, A) -> {NoA, A}.
-
-%%-----------------------------------------------------------------
-%% Returns: {error, ErrorStatus, OrgIndex} |
-%% #varbind
-%%-----------------------------------------------------------------
-get_var_value_from_ivb(IVb, IsNotification)
- when IVb#ivarbind.status =:= noError ->
- ?vtrace("get_var_value_from_ivb(noError) -> entry", []),
- #ivarbind{mibentry = Me, varbind = Vb} = IVb,
- #varbind{org_index = OrgIndex, oid = Oid} = Vb,
- case Me#me.access of
- 'not-accessible' ->
- Vb#varbind{value = noSuchInstance};
- 'accessible-for-notify' when IsNotification =:= false ->
- Vb#varbind{value = noSuchInstance};
- 'write-only' ->
- Vb#varbind{value = noSuchInstance};
- _ ->
- case get_var_value_from_mib(Me, Oid) of
- {value, Type, Value} ->
- Vb#varbind{variabletype = Type, value = Value};
- {error, ErrorStatus} ->
- {error, ErrorStatus, OrgIndex}
- end
- end;
-get_var_value_from_ivb(#ivarbind{status = Status, varbind = Vb}, _) ->
- ?vtrace("get_var_value_from_ivb(~p) -> entry", [Status]),
- Vb#varbind{value = Status}.
-
-%%-----------------------------------------------------------------
-%% Func: get_var_value_from_mib/1
-%% Purpose:
-%% Returns: {error, ErrorStatus} |
-%% {value, Type, Value}
-%%-----------------------------------------------------------------
-%% Pre: Oid is a correct instance Oid (lookup checked that).
-%% Returns: A correct return value (see make_value_a_correct_value)
-get_var_value_from_mib(#me{entrytype = variable,
- asn1_type = ASN1Type,
- mfa = {Mod, Func, Args}},
- _Oid) ->
- ?vtrace("get_var_value_from_mib(variable) -> entry when"
- "~n Mod: ~p"
- "~n Func: ~p"
- "~n Args: ~p", [Mod, Func, Args]),
- Result = (catch dbg_apply(Mod, Func, [get | Args])),
- % mib shall return {value, <a-nice-value-within-range>} |
- % {noValue, noSuchName} (v1) |
- % {noValue, noSuchObject | noSuchInstance} (v2, v1)
- % everything else (including 'genErr') will generate 'genErr'.
- make_value_a_correct_value(Result, ASN1Type, {Mod, Func, Args});
-
-get_var_value_from_mib(#me{entrytype = table_column,
- oid = MeOid,
- asn1_type = ASN1Type,
- mfa = {Mod, Func, Args}},
- Oid) ->
- ?vtrace("get_var_value_from_mib(table_column) -> entry when"
- "~n MeOid: ~p"
- "~n Mod: ~p"
- "~n Func: ~p"
- "~n Args: ~p"
- "~n Oid: ~p", [MeOid, Mod, Func, Args, Oid]),
- Col = lists:last(MeOid),
- Indexes = snmp_misc:diff(Oid, MeOid),
- [Result] = (catch dbg_apply(Mod, Func, [get, Indexes, [Col] | Args])),
- make_value_a_correct_value(Result, ASN1Type,
- {Mod, Func, Args, Indexes, Col}).
-
-
-%% For table operations we need to pass RestOid down to the table-function.
-%% Its up to the table-function to check for noSuchInstance (ex: a
-%% non-existing row).
-%% Returns: {error, ErrorStatus, OrgIndex} |
-%% {value, Type, Value}
-get_tab_value_from_mib(#me{mfa = {Mod, Func, Args}}, TableOid, TableVbs) ->
- ?vtrace("get_tab_value_from_mib -> entry when"
- "~n Mod: ~p"
- "~n Func: ~p"
- "~n Args: ~p", [Mod, Func, Args]),
- TableOpsWithShortOids = deletePrefixes(TableOid, TableVbs),
- SortedVBsRows = snmpa_svbl:sort_varbinds_rows(TableOpsWithShortOids),
- case get_value_all_rows(SortedVBsRows, Mod, Func, Args, []) of
- {Error, Index} ->
- #ivarbind{varbind = Vb} = lists:nth(Index, TableVbs),
- {error, Error, Vb#varbind.org_index};
- ListOfValues ->
- merge_varbinds_and_value(TableVbs, ListOfValues)
- end.
-
-%%-----------------------------------------------------------------
-%% Values is a scrambled list of {CorrectValue, Index}, where Index
-%% is index into the #ivarbind list. So for each Value, we must
-%% find the corresponding #ivarbind, and merge them into a new
-%% #varbind.
-%% The Values list comes from validate_tab_res.
-%%-----------------------------------------------------------------
-merge_varbinds_and_value(IVbs, [{{value, Type, Value}, Index} | Values]) ->
- #ivarbind{varbind = Vb} = lists:nth(Index, IVbs),
- [Vb#varbind{variabletype = Type, value = Value} |
- merge_varbinds_and_value(IVbs, Values)];
-merge_varbinds_and_value(_, []) -> [].
-
-get_value_all_rows([{[], OrgCols} | Rows], Mod, Func, Args, Res) ->
- ?vtrace("get_value_all_rows -> entry when"
- "~n OrgCols: ~p", [OrgCols]),
- Cols = [{{value, noValue, noSuchInstance}, Index} ||
- {_Col, _ASN1Type, Index} <- OrgCols],
- NewRes = lists:append(Cols, Res),
- get_value_all_rows(Rows, Mod, Func, Args, NewRes);
-get_value_all_rows([{RowIndex, OrgCols} | Rows], Mod, Func, Args, Res) ->
- ?vtrace("get_value_all_rows -> entry when"
- "~n RowIndex: ~p"
- "~n OrgCols: ~p", [RowIndex, OrgCols]),
- {DOrgCols, Dup} = remove_duplicates(OrgCols),
- Cols = delete_index(DOrgCols),
- Result = (catch dbg_apply(Mod, Func, [get, RowIndex, Cols | Args])),
- case validate_tab_res(Result, DOrgCols, {Mod, Func, Args}) of
- Values when is_list(Values) ->
- NVals = restore_duplicates(Dup, Values),
- NewRes = lists:append(NVals, Res),
- get_value_all_rows(Rows, Mod, Func, Args, NewRes);
- {error, ErrorStatus, Index} ->
- validate_err(row_set, {ErrorStatus, Index}, {Mod, Func, Args})
- end;
-get_value_all_rows([], _Mod, _Func, _Args, Res) ->
- ?vtrace("get_value_all_rows -> entry when done"
- "~n Res: ~p", [Res]),
- Res.
-
-%%-----------------------------------------------------------------
-%% Returns: list of {ShortOid, ASN1TYpe}
-%%-----------------------------------------------------------------
-deletePrefixes(Prefix, [#ivarbind{varbind = Varbind, mibentry = ME} | Vbs]) ->
- #varbind{oid = Oid} = Varbind,
- [{snmp_misc:diff(Oid, Prefix), ME#me.asn1_type} |
- deletePrefixes(Prefix, Vbs)];
-deletePrefixes(_Prefix, []) -> [].
-
-%%-----------------------------------------------------------------
-%% Args: {RowIndex, list of {ShortOid, ASN1Type}}
-%% Returns: list of Col
-%%-----------------------------------------------------------------
-delete_index([{Col, _Val, _OrgIndex} | T]) ->
- [Col | delete_index(T)];
-delete_index([]) -> [].
-
-%%-----------------------------------------------------------------
-%% This function is called before 'get' on a table, and removes
-%% any duplicate columns. It returns {Cols, DupInfo}. The Cols
-%% are the unique columns. The instrumentation function is
-%% called to get the values. These values, together with the
-%% DupInfo, is later passed to restore_duplicates, which uses
-%% the retrieved values to reconstruct the original column list,
-%% but with the retrieved value for each column.
-%%-----------------------------------------------------------------
-remove_duplicates(Cols) ->
- remove_duplicates(Cols, [], []).
-
-
-remove_duplicates([{Col, V1, OrgIdx1}, {Col, V2, OrgIdx2} | T], NCols, Dup) ->
- remove_duplicates([{Col, V1, OrgIdx1} | T], NCols,
- [{Col, V2, OrgIdx2} | Dup]);
-remove_duplicates([Col | T], NCols, Dup) ->
- remove_duplicates(T, [Col | NCols], Dup);
-remove_duplicates([], NCols, Dup) ->
- {lists:reverse(NCols), lists:reverse(Dup)}.
-
-restore_duplicates([], Cols) ->
- [{Val, OrgIndex} || {_Col, Val, OrgIndex} <- Cols];
-restore_duplicates([{Col, _Val2, OrgIndex2} | Dup],
- [{Col, NVal, OrgIndex1} | Cols]) ->
- [{NVal, OrgIndex2} |
- restore_duplicates(Dup, [{Col, NVal, OrgIndex1} | Cols])];
-restore_duplicates(Dup, [{_Col, Val, OrgIndex} | T]) ->
- [{Val, OrgIndex} | restore_duplicates(Dup, T)].
-
-%% Maps the column number to Index.
-% col_to_index(0, _) -> 0;
-% col_to_index(Col, [{Col, _, Index}|_]) ->
-% Index;
-% col_to_index(Col, [_|Cols]) ->
-% col_to_index(Col, Cols).
-
-%%-----------------------------------------------------------------
-%% Three cases:
-%% 1) All values ok
-%% 2) table_func returned {Error, ...}
-%% 3) Some value in Values list is erroneous.
-%% Args: Value is a list of values from table_func(get..)
-%% OrgCols is a list with {Col, ASN1Type, OrgIndex}
-%% each element in Values and OrgCols correspond to each
-%% other.
-%%-----------------------------------------------------------------
-validate_tab_res(Values, OrgCols, Mfa) when is_list(Values) ->
- {_Col, _ASN1Type, OneIdx} = hd(OrgCols),
- validate_tab_res(Values, OrgCols, Mfa, [], OneIdx);
-validate_tab_res({noValue, Error}, OrgCols, Mfa) ->
- Values = lists:duplicate(length(OrgCols), {noValue, Error}),
- validate_tab_res(Values, OrgCols, Mfa);
-validate_tab_res({genErr, Col}, OrgCols, Mfa) ->
- case lists:keysearch(Col, 1, OrgCols) of
- {value, {_Col, _ASN1Type, Index}} ->
- {error, genErr, Index};
- _ ->
- user_err("Invalid column in {genErr, ~w} from ~w (get)",
- [Col, Mfa]),
- [{_Col, _ASN1Type, Index} | _] = OrgCols,
- {error, genErr, Index}
- end;
-validate_tab_res(genErr, [{_Col, __ASN1Type, Index} | _OrgCols], _Mfa) ->
- {error, genErr, Index};
-validate_tab_res(Error, [{_Col, _ASN1Type, Index} | _OrgCols], Mfa) ->
- user_err("Invalid return value ~w from ~w (get)",[Error, Mfa]),
- {error, genErr, Index}.
-
-validate_tab_res([Value | Values],
- [{Col, ASN1Type, Index} | OrgCols],
- Mfa, Res, I) ->
- %% This one makes it possible to return a list of genErr, which
- %% is not allowed according to the manual. But that's ok, as
- %% everything else will generate a genErr! (the only problem is
- %% that it won't generate a user_error).
- case make_value_a_correct_value(Value, ASN1Type, Mfa) of
- {error, ErrorStatus} ->
- {error, ErrorStatus, Index};
- CorrectValue ->
- NewRes = [{Col, CorrectValue, Index} | Res],
- validate_tab_res(Values, OrgCols, Mfa, NewRes, I)
- end;
-validate_tab_res([], [], _Mfa, Res, _I) ->
- lists:reverse(Res);
-validate_tab_res([], [{_Col, _ASN1Type, Index}|_], Mfa, _Res, _I) ->
- user_err("Too few values returned from ~w (get)", [Mfa]),
- {error, genErr, Index};
-validate_tab_res(_TooMany, [], Mfa, _Res, I) ->
- user_err("Too many values returned from ~w (get)", [Mfa]),
- {error, genErr, I}.
%%%-----------------------------------------------------------------
@@ -3125,491 +2780,12 @@ validate_tab_res(_TooMany, [], Mfa, _Res, I) ->
%% subagent must be considered to be very rare.
%%-----------------------------------------------------------------
-%% It may be a bit agressive to check this already,
-%% but since it is a security measure, it makes sense.
-do_get_next(_MibView, UnsortedVarbinds, GbMaxVBs)
- when (is_integer(GbMaxVBs) andalso (length(UnsortedVarbinds) > GbMaxVBs)) ->
- {tooBig, 0, []}; % What is the correct index in this case?
-do_get_next(MibView, UnsortedVBs, GbMaxVBs) ->
- ?vt("do_get_next -> entry when"
- "~n MibView: ~p"
- "~n UnsortedVBs: ~p", [MibView, UnsortedVBs]),
- SortedVBs = oid_sort_vbs(UnsortedVBs),
- ?vt("do_get_next -> "
- "~n SortedVBs: ~p", [SortedVBs]),
- next_loop_varbinds([], SortedVBs, MibView, [], [], GbMaxVBs).
-
-oid_sort_vbs(Vbs) ->
- lists:keysort(#varbind.oid, Vbs).
-
-next_loop_varbinds(_, Vbs, _MibView, Res, _LAVb, GbMaxVBs)
- when (is_integer(GbMaxVBs) andalso
- ((length(Vbs) + length(Res)) > GbMaxVBs)) ->
- {tooBig, 0, []}; % What is the correct index in this case?
-
-%% LAVb is Last Accessible Vb
-next_loop_varbinds([], [Vb | Vbs], MibView, Res, LAVb, GbMaxVBs) ->
- ?vt("next_loop_varbinds -> entry when"
- "~n Vb: ~p"
- "~n MibView: ~p", [Vb, MibView]),
- case varbind_next(Vb, MibView) of
- endOfMibView ->
- ?vt("next_loop_varbind -> endOfMibView", []),
- RVb = if LAVb =:= [] -> Vb;
- true -> LAVb
- end,
- NewVb = RVb#varbind{variabletype = 'NULL', value = endOfMibView},
- next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [], GbMaxVBs);
-
- {variable, ME, VarOid} when ((ME#me.access =/= 'not-accessible') andalso
- (ME#me.access =/= 'write-only') andalso
- (ME#me.access =/= 'accessible-for-notify')) ->
- ?vt("next_loop_varbind -> variable: "
- "~n ME: ~p"
- "~n VarOid: ~p", [ME, VarOid]),
- case try_get_instance(Vb, ME) of
- {value, noValue, _NoSuchSomething} ->
- ?vt("next_loop_varbind -> noValue", []),
- %% Try next one
- NewVb = Vb#varbind{oid = VarOid,
- value = 'NULL'},
- next_loop_varbinds([], [NewVb | Vbs], MibView, Res, [],
- GbMaxVBs);
- {value, Type, Value} ->
- ?vt("next_loop_varbind -> value"
- "~n Type: ~p"
- "~n Value: ~p", [Type, Value]),
- NewVb = Vb#varbind{oid = VarOid,
- variabletype = Type,
- value = Value},
- next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [],
- GbMaxVBs);
- {error, ErrorStatus} ->
- ?vdebug("next loop varbinds:"
- "~n ErrorStatus: ~p",[ErrorStatus]),
- {ErrorStatus, Vb#varbind.org_index, []}
- end;
- {variable, _ME, VarOid} ->
- ?vt("next_loop_varbind -> variable: "
- "~n VarOid: ~p", [VarOid]),
- RVb = if LAVb =:= [] -> Vb;
- true -> LAVb
- end,
- NewVb = Vb#varbind{oid = VarOid, value = 'NULL'},
- next_loop_varbinds([], [NewVb | Vbs], MibView, Res, RVb, GbMaxVBs);
- {table, TableOid, TableRestOid, ME} ->
- ?vt("next_loop_varbind -> table: "
- "~n TableOid: ~p"
- "~n TableRestOid: ~p"
- "~n ME: ~p", [TableOid, TableRestOid, ME]),
- next_loop_varbinds({table, TableOid, ME,
- [{tab_oid(TableRestOid), Vb}]},
- Vbs, MibView, Res, [], GbMaxVBs);
- {subagent, SubAgentPid, SAOid} ->
- ?vt("next_loop_varbind -> subagent: "
- "~n SubAgentPid: ~p"
- "~n SAOid: ~p", [SubAgentPid, SAOid]),
- NewVb = Vb#varbind{variabletype = 'NULL', value = 'NULL'},
- next_loop_varbinds({subagent, SubAgentPid, SAOid, [NewVb]},
- Vbs, MibView, Res, [], GbMaxVBs)
- end;
-next_loop_varbinds({table, TableOid, ME, TabOids},
- [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) ->
- ?vt("next_loop_varbinds(table) -> entry with"
- "~n TableOid: ~p"
- "~n Vb: ~p", [TableOid, Vb]),
- case varbind_next(Vb, MibView) of
- {table, TableOid, TableRestOid, _ME} ->
- next_loop_varbinds({table, TableOid, ME,
- [{tab_oid(TableRestOid), Vb} | TabOids]},
- Vbs, MibView, Res, [], GbMaxVBs);
- _ ->
- case get_next_table(ME, TableOid, TabOids, MibView) of
- {ok, TabRes, TabEndOfTabVbs} ->
- NewVbs = lists:append(TabEndOfTabVbs, [Vb | Vbs]),
- NewRes = lists:append(TabRes, Res),
- next_loop_varbinds([], NewVbs, MibView, NewRes, [],
- GbMaxVBs);
- {ErrorStatus, OrgIndex} ->
- ?vdebug("next loop varbinds: next varbind"
- "~n ErrorStatus: ~p"
- "~n OrgIndex: ~p",
- [ErrorStatus,OrgIndex]),
- {ErrorStatus, OrgIndex, []}
- end
- end;
-next_loop_varbinds({table, TableOid, ME, TabOids},
- [], MibView, Res, _LAVb, GbMaxVBs) ->
- ?vt("next_loop_varbinds(table) -> entry with"
- "~n TableOid: ~p", [TableOid]),
- case get_next_table(ME, TableOid, TabOids, MibView) of
- {ok, TabRes, TabEndOfTabVbs} ->
- ?vt("next_loop_varbinds(table) -> get_next_table result:"
- "~n TabRes: ~p"
- "~n TabEndOfTabVbs: ~p", [TabRes, TabEndOfTabVbs]),
- NewRes = lists:append(TabRes, Res),
- next_loop_varbinds([], TabEndOfTabVbs, MibView, NewRes, [],
- GbMaxVBs);
- {ErrorStatus, OrgIndex} ->
- ?vdebug("next loop varbinds: next table"
- "~n ErrorStatus: ~p"
- "~n OrgIndex: ~p",
- [ErrorStatus,OrgIndex]),
- {ErrorStatus, OrgIndex, []}
- end;
-next_loop_varbinds({subagent, SAPid, SAOid, SAVbs},
- [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) ->
- ?vt("next_loop_varbinds(subagent) -> entry with"
- "~n SAPid: ~p"
- "~n SAOid: ~p"
- "~n Vb: ~p", [SAPid, SAOid, Vb]),
- case varbind_next(Vb, MibView) of
- {subagent, _SubAgentPid, SAOid} ->
- next_loop_varbinds({subagent, SAPid, SAOid,
- [Vb | SAVbs]},
- Vbs, MibView, Res, [], GbMaxVBs);
- _ ->
- case get_next_sa(SAPid, SAOid, SAVbs, MibView) of
- {ok, SARes, SAEndOfMibViewVbs} ->
- NewVbs = lists:append(SAEndOfMibViewVbs, [Vb | Vbs]),
- NewRes = lists:append(SARes, Res),
- next_loop_varbinds([], NewVbs, MibView, NewRes, [],
- GbMaxVBs);
- {noSuchName, OrgIndex} ->
- %% v1 reply, treat this Vb as endOfMibView, and try again
- %% for the others.
- case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of
- {value, EVb} ->
- NextOid = next_oid(SAOid),
- EndOfVb =
- EVb#varbind{oid = NextOid,
- value = {endOfMibView, NextOid}},
- case lists:delete(EVb, SAVbs) of
- [] ->
- next_loop_varbinds([], [EndOfVb, Vb | Vbs],
- MibView, Res, [],
- GbMaxVBs);
- TryAgainVbs ->
- next_loop_varbinds({subagent, SAPid, SAOid,
- TryAgainVbs},
- [EndOfVb, Vb | Vbs],
- MibView, Res, [],
- GbMaxVBs)
- end;
- false ->
- %% bad index from subagent
- {genErr, (hd(SAVbs))#varbind.org_index, []}
- end;
- {ErrorStatus, OrgIndex} ->
- ?vdebug("next loop varbinds: next subagent"
- "~n Vb: ~p"
- "~n ErrorStatus: ~p"
- "~n OrgIndex: ~p",
- [Vb,ErrorStatus,OrgIndex]),
- {ErrorStatus, OrgIndex, []}
- end
- end;
-next_loop_varbinds({subagent, SAPid, SAOid, SAVbs},
- [], MibView, Res, _LAVb, GbMaxVBs) ->
- ?vt("next_loop_varbinds(subagent) -> entry with"
- "~n SAPid: ~p"
- "~n SAOid: ~p", [SAPid, SAOid]),
- case get_next_sa(SAPid, SAOid, SAVbs, MibView) of
- {ok, SARes, SAEndOfMibViewVbs} ->
- NewRes = lists:append(SARes, Res),
- next_loop_varbinds([], SAEndOfMibViewVbs, MibView, NewRes, [],
- GbMaxVBs);
- {noSuchName, OrgIndex} ->
- %% v1 reply, treat this Vb as endOfMibView, and try again for
- %% the others.
- case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of
- {value, EVb} ->
- NextOid = next_oid(SAOid),
- EndOfVb = EVb#varbind{oid = NextOid,
- value = {endOfMibView, NextOid}},
- case lists:delete(EVb, SAVbs) of
- [] ->
- next_loop_varbinds([], [EndOfVb], MibView, Res, [],
- GbMaxVBs);
- TryAgainVbs ->
- next_loop_varbinds({subagent, SAPid, SAOid,
- TryAgainVbs},
- [EndOfVb], MibView, Res, [],
- GbMaxVBs)
- end;
- false ->
- %% bad index from subagent
- {genErr, (hd(SAVbs))#varbind.org_index, []}
- end;
- {ErrorStatus, OrgIndex} ->
- ?vdebug("next loop varbinds: next subagent"
- "~n ErrorStatus: ~p"
- "~n OrgIndex: ~p",
- [ErrorStatus,OrgIndex]),
- {ErrorStatus, OrgIndex, []}
- end;
-next_loop_varbinds([], [], _MibView, Res, _LAVb, _GbMaxVBs) ->
- ?vt("next_loop_varbinds -> entry when done", []),
- {noError, 0, Res}.
-
-try_get_instance(_Vb, #me{mfa = {M, F, A}, asn1_type = ASN1Type}) ->
- ?vtrace("try_get_instance -> entry with"
- "~n M: ~p"
- "~n F: ~p"
- "~n A: ~p", [M,F,A]),
- Result = (catch dbg_apply(M, F, [get | A])),
- % mib shall return {value, <a-nice-value-within-range>} |
- % {noValue, noSuchName} (v1) |
- % {noValue, noSuchObject | noSuchInstance} (v2, v1)
- % everything else (including 'genErr') will generate 'genErr'.
- make_value_a_correct_value(Result, ASN1Type, {M, F, A}).
-
-tab_oid([]) -> [0];
-tab_oid(X) -> X.
-
-
-%%-----------------------------------------------------------------
-%% Perform a next, using the varbinds Oid if value is simple
-%% value. If value is {endOf<something>, NextOid}, use NextOid.
-%% This case happens when a table has returned endOfTable, or
-%% a subagent has returned endOfMibView.
-%%-----------------------------------------------------------------
-varbind_next(#varbind{value = Value, oid = Oid}, MibView) ->
- ?vt("varbind_next -> entry with"
- "~n Value: ~p"
- "~n Oid: ~p"
- "~n MibView: ~p", [Value, Oid, MibView]),
- case Value of
- {endOfTable, NextOid} ->
- snmpa_mib:next(get(mibserver), NextOid, MibView);
- {endOfMibView, NextOid} ->
- snmpa_mib:next(get(mibserver), NextOid, MibView);
- _ ->
- snmpa_mib:next(get(mibserver), Oid, MibView)
- end.
-
-get_next_table(#me{mfa = {M, F, A}}, TableOid, TableOids, MibView) ->
- % We know that all TableOids have at least a column number as oid
- ?vt("get_next_table -> entry with"
- "~n M: ~p"
- "~n F: ~p"
- "~n A: ~p"
- "~n TableOid: ~p"
- "~n TableOids: ~p"
- "~n MibView: ~p", [M, F, A, TableOid, TableOids, MibView]),
- Sorted = snmpa_svbl:sort_varbinds_rows(TableOids),
- case get_next_values_all_rows(Sorted, M,F,A, [], TableOid) of
- NewVbs when is_list(NewVbs) ->
- ?vt("get_next_table -> "
- "~n NewVbs: ~p", [NewVbs]),
- % We must now check each Vb for endOfTable and that it is
- % in the MibView. If not, it becomes a endOfTable. We
- % collect all of these together.
- transform_tab_next_result(NewVbs, {[], []}, MibView);
- {ErrorStatus, OrgIndex} ->
- {ErrorStatus, OrgIndex}
- end.
-
-get_next_values_all_rows([Row | Rows], M, F, A, Res, TabOid) ->
- {RowIndex, TableOids} = Row,
- Cols = delete_index(TableOids),
- ?vt("get_next_values_all_rows -> "
- "~n Cols: ~p", [Cols]),
- Result = (catch dbg_apply(M, F, [get_next, RowIndex, Cols | A])),
- ?vt("get_next_values_all_rows -> "
- "~n Result: ~p", [Result]),
- case validate_tab_next_res(Result, TableOids, {M, F, A}, TabOid) of
- Values when is_list(Values) ->
- ?vt("get_next_values_all_rows -> "
- "~n Values: ~p", [Values]),
- NewRes = lists:append(Values, Res),
- get_next_values_all_rows(Rows, M, F, A, NewRes, TabOid);
- {ErrorStatus, OrgIndex} ->
- {ErrorStatus, OrgIndex}
- end;
-get_next_values_all_rows([], _M, _F, _A, Res, _TabOid) ->
- Res.
-
-transform_tab_next_result([Vb | Vbs], {Res, EndOfs}, MibView) ->
- case Vb#varbind.value of
- {endOfTable, _} ->
-%% ?vtrace("transform_tab_next_result -> endOfTable: "
-%% "split varbinds",[]),
-%% R = split_varbinds(Vbs, Res, [Vb | EndOfs]),
-%% ?vtrace("transform_tab_next_result -> "
-%% "~n R: ~p", [R]),
-%% R;
- split_varbinds(Vbs, Res, [Vb | EndOfs]);
- _ ->
- case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of
- true ->
- transform_tab_next_result(Vbs, {[Vb|Res], EndOfs},MibView);
- _ ->
- Oid = Vb#varbind.oid,
- NewEndOf = Vb#varbind{value = {endOfTable, Oid}},
- transform_tab_next_result(Vbs, {Res, [NewEndOf | EndOfs]},
- MibView)
- end
- end;
-transform_tab_next_result([], {Res, EndOfs}, _MibView) ->
- ?vt("transform_tab_next_result -> entry with: "
- "~n Res: ~p"
- "~n EndIfs: ~p",[Res, EndOfs]),
- {ok, Res, EndOfs}.
-
-%%-----------------------------------------------------------------
-%% Three cases:
-%% 1) All values ok
-%% 2) table_func returned {Error, ...}
-%% 3) Some value in Values list is erroneous.
-%% Args: Value is a list of values from table_func(get_next, ...)
-%% TableOids is a list of {TabRestOid, OrgVb}
-%% each element in Values and TableOids correspond to each
-%% other.
-%% Returns: List of NewVarbinds |
-%% {ErrorStatus, OrgIndex}
-%% (In the NewVarbinds list, the value may be endOfTable)
-%%-----------------------------------------------------------------
-validate_tab_next_res(Values, TableOids, Mfa, TabOid) ->
- ?vt("validate_tab_next_res -> entry with: "
- "~n Values: ~p"
- "~n TableOids: ~p"
- "~n Mfa: ~p"
- "~n TabOid: ~p", [Values, TableOids, Mfa, TabOid]),
- {_Col, _ASN1Type, OneIdx} = hd(TableOids),
- validate_tab_next_res(Values, TableOids, Mfa, [], TabOid,
- next_oid(TabOid), OneIdx).
-validate_tab_next_res([{NextOid, Value} | Values],
- [{_ColNo, OrgVb, _Index} | TableOids],
- Mfa, Res, TabOid, TabNextOid, I) ->
- ?vt("validate_tab_next_res -> entry with: "
- "~n NextOid: ~p"
- "~n Value: ~p"
- "~n Values: ~p"
- "~n TableOids: ~p"
- "~n Mfa: ~p"
- "~n TabOid: ~p",
- [NextOid, Value, Values, TableOids, Mfa, TabOid]),
- #varbind{org_index = OrgIndex} = OrgVb,
- ?vt("validate_tab_next_res -> OrgIndex: ~p", [OrgIndex]),
- NextCompleteOid = lists:append(TabOid, NextOid),
- case snmpa_mib:lookup(get(mibserver), NextCompleteOid) of
- {table_column, #me{asn1_type = ASN1Type}, _TableEntryOid} ->
- ?vt("validate_tab_next_res -> ASN1Type: ~p", [ASN1Type]),
- case make_value_a_correct_value({value, Value}, ASN1Type, Mfa) of
- {error, ErrorStatus} ->
- ?vt("validate_tab_next_res -> "
- "~n ErrorStatus: ~p", [ErrorStatus]),
- {ErrorStatus, OrgIndex};
- {value, Type, NValue} ->
- ?vt("validate_tab_next_res -> "
- "~n Type: ~p"
- "~n NValue: ~p", [Type, NValue]),
- NewVb = OrgVb#varbind{oid = NextCompleteOid,
- variabletype = Type, value = NValue},
- validate_tab_next_res(Values, TableOids, Mfa,
- [NewVb | Res], TabOid, TabNextOid, I)
- end;
- Error ->
- user_err("Invalid oid ~w from ~w (get_next). Using genErr => ~p",
- [NextOid, Mfa, Error]),
- {genErr, OrgIndex}
- end;
-validate_tab_next_res([endOfTable | Values],
- [{_ColNo, OrgVb, _Index} | TableOids],
- Mfa, Res, TabOid, TabNextOid, I) ->
- ?vt("validate_tab_next_res(endOfTable) -> entry with: "
- "~n Values: ~p"
- "~n OrgVb: ~p"
- "~n TableOids: ~p"
- "~n Mfa: ~p"
- "~n Res: ~p"
- "~n TabOid: ~p"
- "~n TabNextOid: ~p"
- "~n I: ~p",
- [Values, OrgVb, TableOids, Mfa, Res, TabOid, TabNextOid, I]),
- NewVb = OrgVb#varbind{value = {endOfTable, TabNextOid}},
- validate_tab_next_res(Values, TableOids, Mfa, [NewVb | Res],
- TabOid, TabNextOid, I);
-validate_tab_next_res([], [], _Mfa, Res, _TabOid, _TabNextOid, _I) ->
- Res;
-validate_tab_next_res([], [{_Col, _OrgVb, Index}|_], Mfa, _Res, _, _, _I) ->
- user_err("Too few values returned from ~w (get_next)", [Mfa]),
- {genErr, Index};
-validate_tab_next_res({genErr, ColNumber}, OrgCols,
- Mfa, _Res, _TabOid, _TabNextOid, _I) ->
- OrgIndex = snmpa_svbl:col_to_orgindex(ColNumber, OrgCols),
- validate_err(table_next, {genErr, OrgIndex}, Mfa);
-validate_tab_next_res({error, Reason}, [{_ColNo, OrgVb, _Index} | _TableOids],
- Mfa, _Res, _TabOid, _TabNextOid, _I) ->
- #varbind{org_index = OrgIndex} = OrgVb,
- user_err("Erroneous return value ~w from ~w (get_next)",
- [Reason, Mfa]),
- {genErr, OrgIndex};
-validate_tab_next_res(Error, [{_ColNo, OrgVb, _Index} | _TableOids],
- Mfa, _Res, _TabOid, _TabNextOid, _I) ->
- #varbind{org_index = OrgIndex} = OrgVb,
- user_err("Invalid return value ~w from ~w (get_next)",
- [Error, Mfa]),
- {genErr, OrgIndex};
-validate_tab_next_res(TooMany, [], Mfa, _Res, _, _, I) ->
- user_err("Too many values ~w returned from ~w (get_next)",
- [TooMany, Mfa]),
- {genErr, I}.
-
-%%-----------------------------------------------------------------
-%% Func: get_next_sa/4
-%% Purpose: Loop the list of varbinds for the subagent.
-%% Call subagent_get_next to retreive
-%% the next varbinds.
-%% Returns: {ok, ListOfNewVbs, ListOfEndOfMibViewsVbs} |
-%% {ErrorStatus, ErrorIndex}
-%%-----------------------------------------------------------------
-get_next_sa(SAPid, SAOid, SAVbs, MibView) ->
- case catch subagent_get_next(SAPid, MibView, SAVbs) of
- {noError, 0, NewVbs} ->
- NewerVbs = transform_sa_next_result(NewVbs,SAOid,next_oid(SAOid)),
- split_varbinds(NewerVbs, [], []);
- {ErrorStatus, ErrorIndex, _} ->
- {ErrorStatus, ErrorIndex};
- {'EXIT', Reason} ->
- user_err("Lost contact with subagent (next) ~w. Using genErr",
- [Reason]),
- {genErr, 0}
- end.
-%%-----------------------------------------------------------------
-%% Check for wrong prefix returned or endOfMibView, and convert
-%% into {endOfMibView, SANextOid}.
-%%-----------------------------------------------------------------
-transform_sa_next_result([Vb | Vbs], SAOid, SANextOid)
- when Vb#varbind.value =:= endOfMibView ->
- [Vb#varbind{value = {endOfMibView, SANextOid}} |
- transform_sa_next_result(Vbs, SAOid, SANextOid)];
-transform_sa_next_result([Vb | Vbs], SAOid, SANextOid) ->
- case lists:prefix(SAOid, Vb#varbind.oid) of
- true ->
- [Vb | transform_sa_next_result(Vbs, SAOid, SANextOid)];
- _ ->
- [Vb#varbind{oid = SANextOid, value = {endOfMibView, SANextOid}} |
- transform_sa_next_result(Vbs, SAOid, SANextOid)]
- end;
-transform_sa_next_result([], _SAOid, _SANextOid) ->
- [].
-
-split_varbinds([Vb | Vbs], Res, EndOfs) ->
- case Vb#varbind.value of
- {endOfMibView, _} -> split_varbinds(Vbs, Res, [Vb | EndOfs]);
- {endOfTable, _} -> split_varbinds(Vbs, Res, [Vb | EndOfs]);
- _ -> split_varbinds(Vbs, [Vb | Res], EndOfs)
- end;
-split_varbinds([], Res, EndOfs) -> {ok, Res, EndOfs}.
+do_get_next(MibView, UnsortedVarbinds) ->
+ Extra = get(net_if_data),
+ GetModule = get(get_module),
+ GetModule:do_get_next(MibView, UnsortedVarbinds, Extra).
-next_oid(Oid) ->
- case lists:reverse(Oid) of
- [H | T] -> lists:reverse([H+1 | T]);
- [] -> []
- end.
%%%-----------------------------------------------------------------
@@ -3623,200 +2799,12 @@ next_oid(Oid) ->
%%%-----------------------------------------------------------------
do_get_bulk(MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs) ->
- ?vtrace("do_get_bulk -> entry with"
- "~n MibView: ~p"
- "~n NonRepeaters: ~p"
- "~n MaxRepetitions: ~p"
- "~n PduMS: ~p"
- "~n Varbinds: ~p"
- "~n GbMaxVBs: ~p",
- [MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs]),
- {NonRepVbs, RestVbs} = split_vbs(NonRepeaters, Varbinds, []),
- ?vt("do_get_bulk -> split: "
- "~n NonRepVbs: ~p"
- "~n RestVbs: ~p", [NonRepVbs, RestVbs]),
- case do_get_next(MibView, NonRepVbs, GbMaxVBs) of
- {noError, 0, UResNonRepVbs} ->
- ?vt("do_get_bulk -> next noError: "
- "~n UResNonRepVbs: ~p", [UResNonRepVbs]),
- ResNonRepVbs = lists:keysort(#varbind.org_index, UResNonRepVbs),
- %% Decode the first varbinds, produce a reversed list of
- %% listOfBytes.
- case (catch enc_vbs(PduMS - ?empty_pdu_size, ResNonRepVbs)) of
- {error, Idx, Reason} ->
- user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]),
- {genErr, Idx, []};
- {SizeLeft, Res} when is_integer(SizeLeft) and is_list(Res) ->
- ?vtrace("do_get_bulk -> encoded: "
- "~n SizeLeft: ~p"
- "~n Res: ~w", [SizeLeft, Res]),
- case (catch do_get_rep(SizeLeft, MibView, MaxRepetitions,
- RestVbs, Res,
- length(UResNonRepVbs), GbMaxVBs)) of
- {error, Idx, Reason} ->
- user_err("failed encoding varbind ~w:~n~p",
- [Idx, Reason]),
- {genErr, Idx, []};
- Res when is_list(Res) ->
- ?vtrace("do get bulk -> Res: "
- "~n ~w", [Res]),
- {noError, 0, conv_res(Res)};
- {noError, 0, Data} = OK ->
- ?vtrace("do get bulk -> OK: "
- "~n length(Data): ~w", [length(Data)]),
- OK;
- Else ->
- ?vtrace("do get bulk -> Else: "
- "~n ~w", [Else]),
- Else
- end;
- Res when is_list(Res) ->
- {noError, 0, conv_res(Res)}
- end;
+ Extra = get(net_if_data),
+ GetModule = get(get_module),
+ GetModule:do_get_bulk(MibView, NonRepeaters, MaxRepetitions,
+ PduMS, Varbinds, GbMaxVBs,
+ Extra).
- {ErrorStatus, Index, _} ->
- ?vdebug("do get bulk: "
- "~n ErrorStatus: ~p"
- "~n Index: ~p",[ErrorStatus, Index]),
- {ErrorStatus, Index, []}
- end.
-
-% sz(L) when list(L) -> length(L);
-% sz(B) when binary(B) -> size(B);
-% sz(_) -> unknown.
-
-split_vbs(N, Varbinds, Res) when N =< 0 -> {Res, Varbinds};
-split_vbs(N, [H | T], Res) -> split_vbs(N-1, T, [H | Res]);
-split_vbs(_N, [], Res) -> {Res, []}.
-
-enc_vbs(SizeLeft, Vbs) ->
- ?vt("enc_vbs -> entry with"
- "~n SizeLeft: ~w", [SizeLeft]),
- Fun = fun(Vb, {Sz, Res}) when Sz > 0 ->
- ?vt("enc_vbs -> (fun) entry with"
- "~n Vb: ~p"
- "~n Sz: ~p"
- "~n Res: ~w", [Vb, Sz, Res]),
- case (catch snmp_pdus:enc_varbind(Vb)) of
- {'EXIT', Reason} ->
- ?vtrace("enc_vbs -> encode failed: "
- "~n Reason: ~p", [Reason]),
- throw({error, Vb#varbind.org_index, Reason});
- X ->
- ?vt("enc_vbs -> X: ~w", [X]),
- Lx = length(X),
- ?vt("enc_vbs -> Lx: ~w", [Lx]),
- if
- Lx < Sz ->
- {Sz - length(X), [X | Res]};
- true ->
- throw(Res)
- end
- end;
- (_Vb, {_Sz, [_H | T]}) ->
- ?vt("enc_vbs -> (fun) entry with"
- "~n T: ~p", [T]),
- throw(T);
- (_Vb, {_Sz, []}) ->
- ?vt("enc_vbs -> (fun) entry", []),
- throw([])
- end,
- lists:foldl(Fun, {SizeLeft, []}, Vbs).
-
-do_get_rep(Sz, MibView, MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs)
- when MaxRepetitions >= 0 ->
- do_get_rep(Sz, MibView, 0, MaxRepetitions, Varbinds, Res,
- GbNumVBs, GbMaxVBs);
-do_get_rep(Sz, MibView, _MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs) ->
- do_get_rep(Sz, MibView, 0, 0, Varbinds, Res, GbNumVBs, GbMaxVBs).
-
-conv_res(ResVarbinds) ->
- conv_res(ResVarbinds, []).
-conv_res([VbListOfBytes | T], Bytes) ->
- conv_res(T, VbListOfBytes ++ Bytes);
-conv_res([], Bytes) ->
- Bytes.
-
-%% The only other value, then a positive integer, is infinity.
-do_get_rep(_Sz, _MibView, Count, Max, _, _Res, GbNumVBs, GbMaxVBs)
- when (is_integer(GbMaxVBs) andalso (GbNumVBs > GbMaxVBs)) ->
- ?vinfo("Max Get-BULK VBs limit (~w) exceeded (~w) when:"
- "~n Count: ~p"
- "~n Max: ~p", [GbMaxVBs, GbNumVBs, Count, Max]),
- {tooBig, 0, []};
-do_get_rep(_Sz, _MibView, Max, Max, _, Res, _GbNumVBs, _GbMaxVBs) ->
- ?vt("do_get_rep -> done when: "
- "~n Res: ~p", [Res]),
- {noError, 0, conv_res(Res)};
-do_get_rep(Sz, MibView, Count, Max, Varbinds, Res, GbNumVBs, GbMaxVBs) ->
- ?vt("do_get_rep -> entry when: "
- "~n Sz: ~p"
- "~n Count: ~p"
- "~n Res: ~w", [Sz, Count, Res]),
- case try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) of
- {noError, NextVarbinds, SizeLeft, Res2} ->
- ?vt("do_get_rep -> noError: "
- "~n SizeLeft: ~p"
- "~n Res2: ~p", [SizeLeft, Res2]),
- do_get_rep(SizeLeft, MibView, Count+1, Max, NextVarbinds,
- Res2 ++ Res,
- GbNumVBs + length(Varbinds), GbMaxVBs);
- {endOfMibView, _NextVarbinds, _SizeLeft, Res2} ->
- ?vt("do_get_rep -> endOfMibView: "
- "~n Res2: ~p", [Res2]),
- {noError, 0, conv_res(Res2 ++ Res)};
- {ErrorStatus, Index} ->
- ?vtrace("do_get_rep -> done when error: "
- "~n ErrorStatus: ~p"
- "~n Index: ~p", [ErrorStatus, Index]),
- {ErrorStatus, Index, []}
- end.
-
-org_index_sort_vbs(Vbs) ->
- lists:keysort(#varbind.org_index, Vbs).
-
-try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) ->
- ?vt("try_get_bulk -> entry with"
- "~n Sz: ~w"
- "~n MibView: ~w"
- "~n Varbinds: ~w", [Sz, MibView, Varbinds]),
- case do_get_next(MibView, Varbinds, GbMaxVBs) of
- {noError, 0, UNextVarbinds} ->
- ?vt("try_get_bulk -> noError: "
- "~n UNextVarbinds: ~p", [UNextVarbinds]),
- NextVarbinds = org_index_sort_vbs(UNextVarbinds),
- case (catch enc_vbs(Sz, NextVarbinds)) of
- {error, Idx, Reason} ->
- user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]),
- ?vtrace("try_get_bulk -> encode error: "
- "~n Idx: ~p"
- "~n Reason: ~p", [Idx, Reason]),
- {genErr, Idx};
- {SizeLeft, Res} when is_integer(SizeLeft) andalso
- is_list(Res) ->
- ?vt("try get bulk -> encode ok: "
- "~n SizeLeft: ~w"
- "~n Res: ~w", [SizeLeft, Res]),
- {check_end_of_mibview(NextVarbinds),
- NextVarbinds, SizeLeft, Res};
- Res when is_list(Res) ->
- ?vt("try get bulk -> Res: "
- "~n ~w", [Res]),
- {endOfMibView, [], 0, Res}
- end;
- {ErrorStatus, Index, _} ->
- ?vt("try_get_bulk -> error: "
- "~n ErrorStatus: ~p"
- "~n Index: ~p", [ErrorStatus, Index]),
- {ErrorStatus, Index}
- end.
-
-%% If all variables in this pass are endOfMibView,
-%% there is no reason to continue.
-check_end_of_mibview([#varbind{value = endOfMibView} | T]) ->
- check_end_of_mibview(T);
-check_end_of_mibview([]) -> endOfMibView;
-check_end_of_mibview(_) -> noError.
%%%--------------------------------------------------
@@ -3834,14 +2822,11 @@ do_subagent_set(Arguments) ->
SetModule = get(set_module),
apply(SetModule, do_subagent_set, [Arguments]).
+
+
%%%-----------------------------------------------------------------
%%% 7. Misc functions
%%%-----------------------------------------------------------------
-sort_varbindlist(Varbinds) ->
- snmpa_svbl:sort_varbindlist(get(mibserver), Varbinds).
-
-sa_split(SubagentVarbinds) ->
- snmpa_svbl:sa_split(SubagentVarbinds).
make_response_pdu(ReqId, ErrStatus, ErrIndex, OrgVarbinds, _ResponseVarbinds)
when ErrIndex =/= 0 ->
@@ -4139,6 +3124,7 @@ report_err(Val, Mfa, Err) ->
user_err("Got ~p from ~w. Using ~w", [Val, Mfa, Err]),
{error, Err}.
+
is_valid_pdu_type('get-request') -> true;
is_valid_pdu_type('get-next-request') -> true;
is_valid_pdu_type('get-bulk-request') -> true;
@@ -4176,33 +3162,8 @@ mapfoldl(F, Eas, Accu0, [Hd|Tail]) ->
mapfoldl(_F, _Eas, Accu, []) -> {Accu,[]}.
-%%-----------------------------------------------------------------
-%% Runtime debugging of the agent.
-%%-----------------------------------------------------------------
-
-dbg_apply(M,F,A) ->
- case get(verbosity) of
- silence ->
- apply(M,F,A);
- _ ->
- ?vlog("~n apply: ~w,~w,~p~n", [M,F,A]),
- Res = (catch apply(M,F,A)),
- case Res of
- {'EXIT', Reason} ->
- ?vinfo("Call to: "
- "~n Module: ~p"
- "~n Function: ~p"
- "~n Args: ~p"
- "~n"
- "~nresulted in an exit"
- "~n"
- "~n ~p", [M, F, A, Reason]);
- _ ->
- ?vlog("~n returned: ~p", [Res])
- end,
- Res
- end.
+%% ---------------------------------------------------------------------
short_name(none) -> ma;
short_name(_Pid) -> sa.
@@ -4450,6 +3411,9 @@ get_mib_storage(Opts) ->
get_set_mechanism(Opts) ->
get_option(set_mechanism, Opts, snmpa_set).
+get_get_mechanism(Opts) ->
+ get_option(get_mechanism, Opts, snmpa_get).
+
get_authentication_service(Opts) ->
get_option(authentication_service, Opts, snmpa_acm).
diff --git a/lib/snmp/src/agent/snmpa_app.erl b/lib/snmp/src/agent/snmpa_app.erl
index 86ff145e93..c00929c334 100644
--- a/lib/snmp/src/agent/snmpa_app.erl
+++ b/lib/snmp/src/agent/snmpa_app.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -67,6 +67,7 @@ convert_config(Opts) ->
SaVerb = get_sub_agent_verbosity(Opts),
[{agent_type, AgentType},
{agent_verbosity, SaVerb},
+ {get_mechanism, snmpa_get},
{set_mechanism, SetModule},
{authentication_service, AuthModule},
{priority, Prio},
@@ -97,6 +98,7 @@ convert_config(Opts) ->
{verbosity, ConfVerb}],
[{agent_type, AgentType},
{agent_verbosity, MaVerb},
+ {get_mechanism, snmpa_get},
{set_mechanism, SetModule},
{authentication_service, AuthModule},
{db_dir, DbDir},
diff --git a/lib/snmp/src/agent/snmpa_get.erl b/lib/snmp/src/agent/snmpa_get.erl
new file mode 100644
index 0000000000..e67975a67d
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_get.erl
@@ -0,0 +1,1150 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpa_get).
+
+-behaviour(snmpa_get_mechanism).
+
+
+%%%-----------------------------------------------------------------
+%%% snmpa_get_mechanism exports
+%%%-----------------------------------------------------------------
+
+-export([
+ do_get/3, do_get/4,
+ do_get_next/3,
+ do_get_bulk/7
+ ]).
+
+-define(VMODULE,"GET").
+-include("snmpa_internal.hrl").
+-include("snmp_types.hrl").
+-include("snmp_debug.hrl").
+-include("snmp_verbosity.hrl").
+
+-ifndef(default_verbosity).
+-define(default_verbosity,silence).
+-endif.
+
+-define(empty_pdu_size, 21).
+
+-ifdef(snmp_extended_verbosity).
+-define(vt(F,A), ?vtrace(F, A)).
+-else.
+-define(vt(_F, _A), ok).
+-endif.
+
+
+-define(AGENT, snmpa_agent).
+-define(LIB, snmpa_get_lib).
+
+
+
+%%%-----------------------------------------------------------------
+%%% 3. GET REQUEST
+%%% --------------
+%%% According to RFC1157, section 4.1.2 and RFC1905, section 4.2.1.
+%%% In rfc1157:4.1.2 it isn't specified if noSuchName should be
+%%% returned even if some other varbind generates a genErr.
+%%% In rfc1905:4.2.1 this is not a problem since exceptions are
+%%% used, and thus a genErr will be returned anyway.
+%%%-----------------------------------------------------------------
+
+%%-----------------------------------------------------------------
+%% Func: do_get/2
+%% Purpose: Handles all VBs in a request that is inside the
+%% mibview (local).
+%% Returns: {noError, 0, ListOfNewVarbinds} |
+%% {ErrorStatus, ErrorIndex, []}
+%%-----------------------------------------------------------------
+
+do_get(UnsortedVarbinds, IsNotification, _Extra) ->
+ {MyVarbinds, SubagentVarbinds} = ?LIB:agent_sort_vbs(UnsortedVarbinds),
+ case do_get_local(MyVarbinds, IsNotification) of
+ {noError, 0, NewMyVarbinds} ->
+ case do_get_subagents(SubagentVarbinds, IsNotification) of
+ {noError, 0, NewSubagentVarbinds} ->
+ {noError, 0, NewMyVarbinds ++ NewSubagentVarbinds};
+ {ErrorStatus, ErrorIndex, _} ->
+ {ErrorStatus, ErrorIndex, []}
+ end;
+ {ErrorStatus, ErrorIndex, _} ->
+ {ErrorStatus, ErrorIndex, []}
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Func: do_get/3
+%% Purpose: do_get handles "getRequests".
+%% Pre: incoming varbinds have type == 'NULL', value == unSpecified
+%% Returns: {noError, 0, ListOfNewVarbinds} |
+%% {ErrorStatus, ErrorIndex, []}
+%%-----------------------------------------------------------------
+
+do_get(MibView, UnsortedVarbinds, IsNotification, Extra) ->
+ ?vtrace("do_get -> entry with"
+ "~n MibView: ~p"
+ "~n UnsortedVarbinds: ~p"
+ "~n IsNotification: ~p",
+ [MibView, UnsortedVarbinds, IsNotification]),
+ %% This is me, the master, so go ahead
+ {OutSideView, InSideView} = ?LIB:split_vbs_view(UnsortedVarbinds, MibView),
+ {Error, Index, NewVbs} = do_get(InSideView, IsNotification, Extra),
+ {Error, Index, NewVbs ++ OutSideView}.
+
+
+
+%%-----------------------------------------------------------------
+%% Func: do_get_local/2,3
+%% Purpose: Loop the variablebindings list. We know that each varbind
+%% in that list belongs to us.
+%% Returns: {noError, 0, ListOfNewVarbinds} |
+%% {ErrorStatus, ErrorIndex, []}
+%%-----------------------------------------------------------------
+
+do_get_local(VBs, IsNotification) ->
+ do_get_local(VBs, [], IsNotification).
+
+do_get_local([Vb | Vbs], Res, IsNotification) ->
+ case try_get(Vb, IsNotification) of
+ NewVb when is_record(NewVb, varbind) ->
+ do_get_local(Vbs, [NewVb | Res], IsNotification);
+ ListOfNewVb when is_list(ListOfNewVb) ->
+ do_get_local(Vbs, lists:append(ListOfNewVb, Res), IsNotification);
+ {error, Error, OrgIndex} ->
+ {Error, OrgIndex, []}
+ end;
+do_get_local([], Res, _IsNotification) ->
+ {noError, 0, Res}.
+
+
+
+%%-----------------------------------------------------------------
+%% Func: do_get_subagents/2
+%% Purpose: Loop the list of varbinds for different subagents.
+%% For each of them, call sub_agent_get to retreive
+%% the values for them.
+%% Returns: {noError, 0, ListOfNewVarbinds} |
+%% {ErrorStatus, ErrorIndex, []}
+%%-----------------------------------------------------------------
+do_get_subagents(SubagentVarbinds, IsNotification) ->
+ do_get_subagents(SubagentVarbinds, [], IsNotification).
+do_get_subagents([{SubAgentPid, SAVbs} | Tail], Res, IsNotification) ->
+ {_SAOids, Vbs} = ?LIB:sa_split(SAVbs),
+ case catch ?AGENT:subagent_get(SubAgentPid, Vbs, IsNotification) of
+ {noError, 0, NewVbs} ->
+ do_get_subagents(Tail, lists:append(NewVbs, Res), IsNotification);
+ {ErrorStatus, ErrorIndex, _} ->
+ {ErrorStatus, ErrorIndex, []};
+ {'EXIT', Reason} ->
+ ?LIB:user_err("Lost contact with subagent (get) ~w. Using genErr",
+ [Reason]),
+ {genErr, 0, []}
+ end;
+do_get_subagents([], Res, _IsNotification) ->
+ {noError, 0, Res}.
+
+
+%%-----------------------------------------------------------------
+%% Func: try_get/2
+%% Returns: {error, ErrorStatus, OrgIndex} |
+%% #varbind |
+%% List of #varbind
+%%-----------------------------------------------------------------
+try_get(IVb, IsNotification) when is_record(IVb, ivarbind) ->
+ ?vtrace("try_get(ivarbind) -> entry with"
+ "~n IVb: ~p", [IVb]),
+ get_var_value_from_ivb(IVb, IsNotification);
+try_get({TableOid, TableVbs}, IsNotification) ->
+ ?vtrace("try_get(table) -> entry with"
+ "~n TableOid: ~p"
+ "~n TableVbs: ~p", [TableOid, TableVbs]),
+ [#ivarbind{mibentry = MibEntry}|_] = TableVbs,
+ {NoAccessVbs, AccessVbs} =
+ check_all_table_vbs(TableVbs, IsNotification, [], []),
+ case get_tab_value_from_mib(MibEntry, TableOid, AccessVbs) of
+ {error, ErrorStatus, OrgIndex} ->
+ {error, ErrorStatus, OrgIndex};
+ NVbs ->
+ NVbs ++ NoAccessVbs
+ end.
+
+%%-----------------------------------------------------------------
+%% Make sure all requested columns are accessible.
+%%-----------------------------------------------------------------
+check_all_table_vbs([IVb| IVbs], IsNotification, NoA, A) ->
+ #ivarbind{mibentry = Me, varbind = Vb} = IVb,
+ case Me#me.access of
+ 'not-accessible' ->
+ NNoA = [Vb#varbind{value = noSuchInstance} | NoA],
+ check_all_table_vbs(IVbs, IsNotification, NNoA, A);
+ 'accessible-for-notify' when IsNotification =:= false ->
+ NNoA = [Vb#varbind{value = noSuchInstance} | NoA],
+ check_all_table_vbs(IVbs, IsNotification, NNoA, A);
+ 'write-only' ->
+ NNoA = [Vb#varbind{value = noSuchInstance} | NoA],
+ check_all_table_vbs(IVbs, IsNotification, NNoA, A);
+ _ ->
+ check_all_table_vbs(IVbs, IsNotification, NoA, [IVb | A])
+ end;
+check_all_table_vbs([], _IsNotification, NoA, A) -> {NoA, A}.
+
+%%-----------------------------------------------------------------
+%% Returns: {error, ErrorStatus, OrgIndex} |
+%% #varbind
+%%-----------------------------------------------------------------
+get_var_value_from_ivb(IVb, IsNotification)
+ when IVb#ivarbind.status =:= noError ->
+ ?vtrace("get_var_value_from_ivb(noError) -> entry", []),
+ #ivarbind{mibentry = Me, varbind = Vb} = IVb,
+ #varbind{org_index = OrgIndex, oid = Oid} = Vb,
+ case Me#me.access of
+ 'not-accessible' ->
+ Vb#varbind{value = noSuchInstance};
+ 'accessible-for-notify' when IsNotification =:= false ->
+ Vb#varbind{value = noSuchInstance};
+ 'write-only' ->
+ Vb#varbind{value = noSuchInstance};
+ _ ->
+ case get_var_value_from_mib(Me, Oid) of
+ {value, Type, Value} ->
+ Vb#varbind{variabletype = Type, value = Value};
+ {error, ErrorStatus} ->
+ {error, ErrorStatus, OrgIndex}
+ end
+ end;
+get_var_value_from_ivb(#ivarbind{status = Status, varbind = Vb}, _) ->
+ ?vtrace("get_var_value_from_ivb(~p) -> entry", [Status]),
+ Vb#varbind{value = Status}.
+
+%%-----------------------------------------------------------------
+%% Func: get_var_value_from_mib/1
+%% Purpose:
+%% Returns: {error, ErrorStatus} |
+%% {value, Type, Value}
+%%-----------------------------------------------------------------
+%% Pre: Oid is a correct instance Oid (lookup checked that).
+%% Returns: A correct return value (see ?AGENT:make_value_a_correct_value)
+get_var_value_from_mib(#me{entrytype = variable,
+ asn1_type = ASN1Type,
+ mfa = {Mod, Func, Args}},
+ _Oid) ->
+ ?vtrace("get_var_value_from_mib(variable) -> entry when"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Args: ~p", [Mod, Func, Args]),
+ Result = (catch ?LIB:dbg_apply(Mod, Func, [get | Args])),
+ %% mib shall return {value, <a-nice-value-within-range>} |
+ %% {noValue, noSuchName} (v1) |
+ %% {noValue, noSuchObject | noSuchInstance} (v2, v1)
+ %% everything else (including 'genErr') will generate 'genErr'.
+ ?AGENT:make_value_a_correct_value(Result, ASN1Type, {Mod, Func, Args});
+
+get_var_value_from_mib(#me{entrytype = table_column,
+ oid = MeOid,
+ asn1_type = ASN1Type,
+ mfa = {Mod, Func, Args}},
+ Oid) ->
+ ?vtrace("get_var_value_from_mib(table_column) -> entry when"
+ "~n MeOid: ~p"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Args: ~p"
+ "~n Oid: ~p", [MeOid, Mod, Func, Args, Oid]),
+ Col = lists:last(MeOid),
+ Indexes = snmp_misc:diff(Oid, MeOid),
+ [Result] = (catch ?LIB:dbg_apply(Mod, Func, [get, Indexes, [Col] | Args])),
+ ?AGENT:make_value_a_correct_value(Result, ASN1Type,
+ {Mod, Func, Args, Indexes, Col}).
+
+
+%% For table operations we need to pass RestOid down to the table-function.
+%% Its up to the table-function to check for noSuchInstance (ex: a
+%% non-existing row).
+%% Returns: {error, ErrorStatus, OrgIndex} |
+%% {value, Type, Value}
+get_tab_value_from_mib(#me{mfa = {Mod, Func, Args}}, TableOid, TableVbs) ->
+ ?vtrace("get_tab_value_from_mib -> entry when"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Args: ~p", [Mod, Func, Args]),
+ TableOpsWithShortOids = ?LIB:delete_prefixes(TableOid, TableVbs),
+ SortedVBsRows = snmpa_svbl:sort_varbinds_rows(TableOpsWithShortOids),
+ case get_value_all_rows(SortedVBsRows, Mod, Func, Args, []) of
+ {Error, Index} ->
+ #ivarbind{varbind = Vb} = lists:nth(Index, TableVbs),
+ {error, Error, Vb#varbind.org_index};
+ ListOfValues ->
+ merge_varbinds_and_value(TableVbs, ListOfValues)
+ end.
+
+%%-----------------------------------------------------------------
+%% Values is a scrambled list of {CorrectValue, Index}, where Index
+%% is index into the #ivarbind list. So for each Value, we must
+%% find the corresponding #ivarbind, and merge them into a new
+%% #varbind.
+%% The Values list comes from validate_tab_res.
+%%-----------------------------------------------------------------
+merge_varbinds_and_value(IVbs, [{{value, Type, Value}, Index} | Values]) ->
+ #ivarbind{varbind = Vb} = lists:nth(Index, IVbs),
+ [Vb#varbind{variabletype = Type, value = Value} |
+ merge_varbinds_and_value(IVbs, Values)];
+merge_varbinds_and_value(_, []) -> [].
+
+get_value_all_rows([{[], OrgCols} | Rows], Mod, Func, Args, Res) ->
+ ?vtrace("get_value_all_rows -> entry when"
+ "~n OrgCols: ~p", [OrgCols]),
+ Cols = [{{value, noValue, noSuchInstance}, Index} ||
+ {_Col, _ASN1Type, Index} <- OrgCols],
+ NewRes = lists:append(Cols, Res),
+ get_value_all_rows(Rows, Mod, Func, Args, NewRes);
+get_value_all_rows([{RowIndex, OrgCols} | Rows], Mod, Func, Args, Res) ->
+ ?vtrace("get_value_all_rows -> entry when"
+ "~n RowIndex: ~p"
+ "~n OrgCols: ~p", [RowIndex, OrgCols]),
+ {DOrgCols, Dup} = remove_duplicates(OrgCols),
+ Cols = delete_index(DOrgCols),
+ Result = (catch ?LIB:dbg_apply(Mod, Func, [get, RowIndex, Cols | Args])),
+ case validate_tab_res(Result, DOrgCols, {Mod, Func, Args}) of
+ Values when is_list(Values) ->
+ NVals = restore_duplicates(Dup, Values),
+ NewRes = lists:append(NVals, Res),
+ get_value_all_rows(Rows, Mod, Func, Args, NewRes);
+ {error, ErrorStatus, Index} ->
+ ?AGENT:validate_err(row_set, {ErrorStatus, Index}, {Mod, Func, Args})
+ end;
+get_value_all_rows([], _Mod, _Func, _Args, Res) ->
+ ?vtrace("get_value_all_rows -> entry when done"
+ "~n Res: ~p", [Res]),
+ Res.
+
+%%-----------------------------------------------------------------
+%% Args: {RowIndex, list of {ShortOid, ASN1Type}}
+%% Returns: list of Col
+%%-----------------------------------------------------------------
+delete_index([{Col, _Val, _OrgIndex} | T]) ->
+ [Col | delete_index(T)];
+delete_index([]) -> [].
+
+%%-----------------------------------------------------------------
+%% This function is called before 'get' on a table, and removes
+%% any duplicate columns. It returns {Cols, DupInfo}. The Cols
+%% are the unique columns. The instrumentation function is
+%% called to get the values. These values, together with the
+%% DupInfo, is later passed to restore_duplicates, which uses
+%% the retrieved values to reconstruct the original column list,
+%% but with the retrieved value for each column.
+%%-----------------------------------------------------------------
+remove_duplicates(Cols) ->
+ remove_duplicates(Cols, [], []).
+
+
+remove_duplicates([{Col, V1, OrgIdx1}, {Col, V2, OrgIdx2} | T], NCols, Dup) ->
+ remove_duplicates([{Col, V1, OrgIdx1} | T], NCols,
+ [{Col, V2, OrgIdx2} | Dup]);
+remove_duplicates([Col | T], NCols, Dup) ->
+ remove_duplicates(T, [Col | NCols], Dup);
+remove_duplicates([], NCols, Dup) ->
+ {lists:reverse(NCols), lists:reverse(Dup)}.
+
+restore_duplicates([], Cols) ->
+ [{Val, OrgIndex} || {_Col, Val, OrgIndex} <- Cols];
+restore_duplicates([{Col, _Val2, OrgIndex2} | Dup],
+ [{Col, NVal, OrgIndex1} | Cols]) ->
+ [{NVal, OrgIndex2} |
+ restore_duplicates(Dup, [{Col, NVal, OrgIndex1} | Cols])];
+restore_duplicates(Dup, [{_Col, Val, OrgIndex} | T]) ->
+ [{Val, OrgIndex} | restore_duplicates(Dup, T)].
+
+
+
+%%-----------------------------------------------------------------
+%% Three cases:
+%% 1) All values ok
+%% 2) table_func returned {Error, ...}
+%% 3) Some value in Values list is erroneous.
+%% Args: Value is a list of values from table_func(get..)
+%% OrgCols is a list with {Col, ASN1Type, OrgIndex}
+%% each element in Values and OrgCols correspond to each
+%% other.
+%%-----------------------------------------------------------------
+validate_tab_res(Values, OrgCols, Mfa) when is_list(Values) ->
+ {_Col, _ASN1Type, OneIdx} = hd(OrgCols),
+ validate_tab_res(Values, OrgCols, Mfa, [], OneIdx);
+validate_tab_res({noValue, Error}, OrgCols, Mfa) ->
+ Values = lists:duplicate(length(OrgCols), {noValue, Error}),
+ validate_tab_res(Values, OrgCols, Mfa);
+validate_tab_res({genErr, Col}, OrgCols, Mfa) ->
+ case lists:keysearch(Col, 1, OrgCols) of
+ {value, {_Col, _ASN1Type, Index}} ->
+ {error, genErr, Index};
+ _ ->
+ ?LIB:user_err("Invalid column in {genErr, ~w} from ~w (get)",
+ [Col, Mfa]),
+ [{_Col, _ASN1Type, Index} | _] = OrgCols,
+ {error, genErr, Index}
+ end;
+validate_tab_res(genErr, [{_Col, __ASN1Type, Index} | _OrgCols], _Mfa) ->
+ {error, genErr, Index};
+validate_tab_res(Error, [{_Col, _ASN1Type, Index} | _OrgCols], Mfa) ->
+ ?LIB:user_err("Invalid return value ~w from ~w (get)",[Error, Mfa]),
+ {error, genErr, Index}.
+
+validate_tab_res([Value | Values],
+ [{Col, ASN1Type, Index} | OrgCols],
+ Mfa, Res, I) ->
+ %% This one makes it possible to return a list of genErr, which
+ %% is not allowed according to the manual. But that's ok, as
+ %% everything else will generate a genErr! (the only problem is
+ %% that it won't generate a user_error).
+ case ?AGENT:make_value_a_correct_value(Value, ASN1Type, Mfa) of
+ {error, ErrorStatus} ->
+ {error, ErrorStatus, Index};
+ CorrectValue ->
+ NewRes = [{Col, CorrectValue, Index} | Res],
+ validate_tab_res(Values, OrgCols, Mfa, NewRes, I)
+ end;
+validate_tab_res([], [], _Mfa, Res, _I) ->
+ lists:reverse(Res);
+validate_tab_res([], [{_Col, _ASN1Type, Index}|_], Mfa, _Res, _I) ->
+ ?LIB:user_err("Too few values returned from ~w (get)", [Mfa]),
+ {error, genErr, Index};
+validate_tab_res(_TooMany, [], Mfa, _Res, I) ->
+ ?LIB:user_err("Too many values returned from ~w (get)", [Mfa]),
+ {error, genErr, I}.
+
+
+
+%%%-----------------------------------------------------------------
+%%% 4. GET-NEXT REQUEST
+%%% --------------
+%%% According to RFC1157, section 4.1.3 and RFC1905, section 4.2.2.
+%%%-----------------------------------------------------------------
+%%-----------------------------------------------------------------
+%% Func: do_get_next/3
+%% Purpose: do_get_next handles "getNextRequests".
+%% Note: Even if it is SNMPv1, a varbind's value can be
+%% endOfMibView. This is converted to noSuchName in process_pdu.
+%% Returns: {noError, 0, ListOfNewVarbinds} |
+%% {ErrorStatus, ErrorIndex, []}
+%% Note2: ListOfNewVarbinds is not sorted in any order!!!
+%% Alg: First, the variables are sorted in OID order.
+%%
+%% Second, next in the MIB is performed for each OID, and
+%% the result is collected as: if next oid is a variable,
+%% perform a get to retrieve its value; if next oid is in a
+%% table, save this value and continue until we get an oid
+%% outside this table. Then perform get_next on the table,
+%% and continue with all endOfTables and the oid outside the
+%% table; if next oid is an subagent, save this value and
+%% continue as in the table case.
+%%
+%% Third, each response is checked for endOfMibView, or (for
+%% subagents) that the Oid returned has the correct prefix.
+%% (This is necessary since an SA can be registered under many
+%% separated subtrees, and if the last variable in the first
+%% subtree is requested in a next, the SA will return the first
+%% variable in the second subtree. This might be working, since
+%% there may be a variable in between these subtrees.) For each
+%% of these, a new get-next is performed, one at a time.
+%% This alg. might be optimised in several ways. The most
+%% striking one is that the same SA might be called several
+%% times, when one time should be enough. But it isn't clear
+%% that this really matters, since many nexts across the same
+%% subagent must be considered to be very rare.
+%%-----------------------------------------------------------------
+
+do_get_next(MibView, UnsortedVBs, _Extra) ->
+ do_get_next2(MibView, UnsortedVBs, infinity).
+
+%% The third argument is only used if we are called as result
+%% of a get-bulk request.
+do_get_next2(_MibView, UnsortedVarbinds, GbMaxVBs)
+ when (is_integer(GbMaxVBs) andalso (length(UnsortedVarbinds) > GbMaxVBs)) ->
+ {tooBig, 0, []}; % What is the correct index in this case?
+do_get_next2(MibView, UnsortedVBs, GbMaxVBs) ->
+ ?vt("do_get_next2 -> entry when"
+ "~n MibView: ~p"
+ "~n UnsortedVBs: ~p", [MibView, UnsortedVBs]),
+ SortedVBs = ?LIB:oid_sort_vbs(UnsortedVBs),
+ ?vt("do_get_next -> "
+ "~n SortedVBs: ~p", [SortedVBs]),
+ next_loop_varbinds([], SortedVBs, MibView, [], [], GbMaxVBs).
+
+next_loop_varbinds(_, Vbs, _MibView, Res, _LAVb, GbMaxVBs)
+ when (is_integer(GbMaxVBs) andalso
+ ((length(Vbs) + length(Res)) > GbMaxVBs)) ->
+ {tooBig, 0, []}; % What is the correct index in this case?
+
+%% LAVb is Last Accessible Vb
+next_loop_varbinds([], [Vb | Vbs], MibView, Res, LAVb, GbMaxVBs) ->
+ ?vt("next_loop_varbinds -> entry when"
+ "~n Vb: ~p"
+ "~n MibView: ~p", [Vb, MibView]),
+ case varbind_next(Vb, MibView) of
+ endOfMibView ->
+ ?vt("next_loop_varbind -> endOfMibView", []),
+ RVb = if LAVb =:= [] -> Vb;
+ true -> LAVb
+ end,
+ NewVb = RVb#varbind{variabletype = 'NULL', value = endOfMibView},
+ next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [], GbMaxVBs);
+
+ {variable, ME, VarOid} when ((ME#me.access =/= 'not-accessible') andalso
+ (ME#me.access =/= 'write-only') andalso
+ (ME#me.access =/= 'accessible-for-notify')) ->
+ ?vt("next_loop_varbind -> variable: "
+ "~n ME: ~p"
+ "~n VarOid: ~p", [ME, VarOid]),
+ case try_get_instance(Vb, ME) of
+ {value, noValue, _NoSuchSomething} ->
+ ?vt("next_loop_varbind -> noValue", []),
+ %% Try next one
+ NewVb = Vb#varbind{oid = VarOid,
+ value = 'NULL'},
+ next_loop_varbinds([], [NewVb | Vbs], MibView, Res, [],
+ GbMaxVBs);
+ {value, Type, Value} ->
+ ?vt("next_loop_varbind -> value"
+ "~n Type: ~p"
+ "~n Value: ~p", [Type, Value]),
+ NewVb = Vb#varbind{oid = VarOid,
+ variabletype = Type,
+ value = Value},
+ next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [],
+ GbMaxVBs);
+ {error, ErrorStatus} ->
+ ?vdebug("next loop varbinds:"
+ "~n ErrorStatus: ~p",[ErrorStatus]),
+ {ErrorStatus, Vb#varbind.org_index, []}
+ end;
+ {variable, _ME, VarOid} ->
+ ?vt("next_loop_varbind -> variable: "
+ "~n VarOid: ~p", [VarOid]),
+ RVb = if LAVb =:= [] -> Vb;
+ true -> LAVb
+ end,
+ NewVb = Vb#varbind{oid = VarOid, value = 'NULL'},
+ next_loop_varbinds([], [NewVb | Vbs], MibView, Res, RVb, GbMaxVBs);
+ {table, TableOid, TableRestOid, ME} ->
+ ?vt("next_loop_varbind -> table: "
+ "~n TableOid: ~p"
+ "~n TableRestOid: ~p"
+ "~n ME: ~p", [TableOid, TableRestOid, ME]),
+ next_loop_varbinds({table, TableOid, ME,
+ [{tab_oid(TableRestOid), Vb}]},
+ Vbs, MibView, Res, [], GbMaxVBs);
+ {subagent, SubAgentPid, SAOid} ->
+ ?vt("next_loop_varbind -> subagent: "
+ "~n SubAgentPid: ~p"
+ "~n SAOid: ~p", [SubAgentPid, SAOid]),
+ NewVb = Vb#varbind{variabletype = 'NULL', value = 'NULL'},
+ next_loop_varbinds({subagent, SubAgentPid, SAOid, [NewVb]},
+ Vbs, MibView, Res, [], GbMaxVBs)
+ end;
+next_loop_varbinds({table, TableOid, ME, TabOids},
+ [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) ->
+ ?vt("next_loop_varbinds(table) -> entry with"
+ "~n TableOid: ~p"
+ "~n Vb: ~p", [TableOid, Vb]),
+ case varbind_next(Vb, MibView) of
+ {table, TableOid, TableRestOid, _ME} ->
+ next_loop_varbinds({table, TableOid, ME,
+ [{tab_oid(TableRestOid), Vb} | TabOids]},
+ Vbs, MibView, Res, [], GbMaxVBs);
+ _ ->
+ case get_next_table(ME, TableOid, TabOids, MibView) of
+ {ok, TabRes, TabEndOfTabVbs} ->
+ NewVbs = lists:append(TabEndOfTabVbs, [Vb | Vbs]),
+ NewRes = lists:append(TabRes, Res),
+ next_loop_varbinds([], NewVbs, MibView, NewRes, [],
+ GbMaxVBs);
+ {ErrorStatus, OrgIndex} ->
+ ?vdebug("next loop varbinds: next varbind"
+ "~n ErrorStatus: ~p"
+ "~n OrgIndex: ~p",
+ [ErrorStatus,OrgIndex]),
+ {ErrorStatus, OrgIndex, []}
+ end
+ end;
+next_loop_varbinds({table, TableOid, ME, TabOids},
+ [], MibView, Res, _LAVb, GbMaxVBs) ->
+ ?vt("next_loop_varbinds(table) -> entry with"
+ "~n TableOid: ~p", [TableOid]),
+ case get_next_table(ME, TableOid, TabOids, MibView) of
+ {ok, TabRes, TabEndOfTabVbs} ->
+ ?vt("next_loop_varbinds(table) -> get_next_table result:"
+ "~n TabRes: ~p"
+ "~n TabEndOfTabVbs: ~p", [TabRes, TabEndOfTabVbs]),
+ NewRes = lists:append(TabRes, Res),
+ next_loop_varbinds([], TabEndOfTabVbs, MibView, NewRes, [],
+ GbMaxVBs);
+ {ErrorStatus, OrgIndex} ->
+ ?vdebug("next loop varbinds: next table"
+ "~n ErrorStatus: ~p"
+ "~n OrgIndex: ~p",
+ [ErrorStatus,OrgIndex]),
+ {ErrorStatus, OrgIndex, []}
+ end;
+next_loop_varbinds({subagent, SAPid, SAOid, SAVbs},
+ [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) ->
+ ?vt("next_loop_varbinds(subagent) -> entry with"
+ "~n SAPid: ~p"
+ "~n SAOid: ~p"
+ "~n Vb: ~p", [SAPid, SAOid, Vb]),
+ case varbind_next(Vb, MibView) of
+ {subagent, _SubAgentPid, SAOid} ->
+ next_loop_varbinds({subagent, SAPid, SAOid,
+ [Vb | SAVbs]},
+ Vbs, MibView, Res, [], GbMaxVBs);
+ _ ->
+ case get_next_sa(SAPid, SAOid, SAVbs, MibView) of
+ {ok, SARes, SAEndOfMibViewVbs} ->
+ NewVbs = lists:append(SAEndOfMibViewVbs, [Vb | Vbs]),
+ NewRes = lists:append(SARes, Res),
+ next_loop_varbinds([], NewVbs, MibView, NewRes, [],
+ GbMaxVBs);
+ {noSuchName, OrgIndex} ->
+ %% v1 reply, treat this Vb as endOfMibView, and try again
+ %% for the others.
+ case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of
+ {value, EVb} ->
+ NextOid = next_oid(SAOid),
+ EndOfVb =
+ EVb#varbind{oid = NextOid,
+ value = {endOfMibView, NextOid}},
+ case lists:delete(EVb, SAVbs) of
+ [] ->
+ next_loop_varbinds([], [EndOfVb, Vb | Vbs],
+ MibView, Res, [],
+ GbMaxVBs);
+ TryAgainVbs ->
+ next_loop_varbinds({subagent, SAPid, SAOid,
+ TryAgainVbs},
+ [EndOfVb, Vb | Vbs],
+ MibView, Res, [],
+ GbMaxVBs)
+ end;
+ false ->
+ %% bad index from subagent
+ {genErr, (hd(SAVbs))#varbind.org_index, []}
+ end;
+ {ErrorStatus, OrgIndex} ->
+ ?vdebug("next loop varbinds: next subagent"
+ "~n Vb: ~p"
+ "~n ErrorStatus: ~p"
+ "~n OrgIndex: ~p",
+ [Vb,ErrorStatus,OrgIndex]),
+ {ErrorStatus, OrgIndex, []}
+ end
+ end;
+next_loop_varbinds({subagent, SAPid, SAOid, SAVbs},
+ [], MibView, Res, _LAVb, GbMaxVBs) ->
+ ?vt("next_loop_varbinds(subagent) -> entry with"
+ "~n SAPid: ~p"
+ "~n SAOid: ~p", [SAPid, SAOid]),
+ case get_next_sa(SAPid, SAOid, SAVbs, MibView) of
+ {ok, SARes, SAEndOfMibViewVbs} ->
+ NewRes = lists:append(SARes, Res),
+ next_loop_varbinds([], SAEndOfMibViewVbs, MibView, NewRes, [],
+ GbMaxVBs);
+ {noSuchName, OrgIndex} ->
+ %% v1 reply, treat this Vb as endOfMibView, and try again for
+ %% the others.
+ case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of
+ {value, EVb} ->
+ NextOid = next_oid(SAOid),
+ EndOfVb = EVb#varbind{oid = NextOid,
+ value = {endOfMibView, NextOid}},
+ case lists:delete(EVb, SAVbs) of
+ [] ->
+ next_loop_varbinds([], [EndOfVb], MibView, Res, [],
+ GbMaxVBs);
+ TryAgainVbs ->
+ next_loop_varbinds({subagent, SAPid, SAOid,
+ TryAgainVbs},
+ [EndOfVb], MibView, Res, [],
+ GbMaxVBs)
+ end;
+ false ->
+ %% bad index from subagent
+ {genErr, (hd(SAVbs))#varbind.org_index, []}
+ end;
+ {ErrorStatus, OrgIndex} ->
+ ?vdebug("next loop varbinds: next subagent"
+ "~n ErrorStatus: ~p"
+ "~n OrgIndex: ~p",
+ [ErrorStatus,OrgIndex]),
+ {ErrorStatus, OrgIndex, []}
+ end;
+next_loop_varbinds([], [], _MibView, Res, _LAVb, _GbMaxVBs) ->
+ ?vt("next_loop_varbinds -> entry when done", []),
+ {noError, 0, Res}.
+
+try_get_instance(_Vb, #me{mfa = {M, F, A}, asn1_type = ASN1Type}) ->
+ ?vtrace("try_get_instance -> entry with"
+ "~n M: ~p"
+ "~n F: ~p"
+ "~n A: ~p", [M,F,A]),
+ Result = (catch ?LIB:dbg_apply(M, F, [get | A])),
+ % mib shall return {value, <a-nice-value-within-range>} |
+ % {noValue, noSuchName} (v1) |
+ % {noValue, noSuchObject | noSuchInstance} (v2, v1)
+ % everything else (including 'genErr') will generate 'genErr'.
+ ?AGENT:make_value_a_correct_value(Result, ASN1Type, {M, F, A}).
+
+tab_oid([]) -> [0];
+tab_oid(X) -> X.
+
+
+%%-----------------------------------------------------------------
+%% Perform a next, using the varbinds Oid if value is simple
+%% value. If value is {endOf<something>, NextOid}, use NextOid.
+%% This case happens when a table has returned endOfTable, or
+%% a subagent has returned endOfMibView.
+%%-----------------------------------------------------------------
+varbind_next(#varbind{value = Value, oid = Oid}, MibView) ->
+ ?vt("varbind_next -> entry with"
+ "~n Value: ~p"
+ "~n Oid: ~p"
+ "~n MibView: ~p", [Value, Oid, MibView]),
+ case Value of
+ {endOfTable, NextOid} ->
+ snmpa_mib:next(get(mibserver), NextOid, MibView);
+ {endOfMibView, NextOid} ->
+ snmpa_mib:next(get(mibserver), NextOid, MibView);
+ _ ->
+ snmpa_mib:next(get(mibserver), Oid, MibView)
+ end.
+
+get_next_table(#me{mfa = {M, F, A}}, TableOid, TableOids, MibView) ->
+ % We know that all TableOids have at least a column number as oid
+ ?vt("get_next_table -> entry with"
+ "~n M: ~p"
+ "~n F: ~p"
+ "~n A: ~p"
+ "~n TableOid: ~p"
+ "~n TableOids: ~p"
+ "~n MibView: ~p", [M, F, A, TableOid, TableOids, MibView]),
+ Sorted = snmpa_svbl:sort_varbinds_rows(TableOids),
+ case get_next_values_all_rows(Sorted, M,F,A, [], TableOid) of
+ NewVbs when is_list(NewVbs) ->
+ ?vt("get_next_table -> "
+ "~n NewVbs: ~p", [NewVbs]),
+ % We must now check each Vb for endOfTable and that it is
+ % in the MibView. If not, it becomes a endOfTable. We
+ % collect all of these together.
+ transform_tab_next_result(NewVbs, {[], []}, MibView);
+ {ErrorStatus, OrgIndex} ->
+ {ErrorStatus, OrgIndex}
+ end.
+
+get_next_values_all_rows([Row | Rows], M, F, A, Res, TabOid) ->
+ {RowIndex, TableOids} = Row,
+ Cols = delete_index(TableOids),
+ ?vt("get_next_values_all_rows -> "
+ "~n Cols: ~p", [Cols]),
+ Result = (catch ?LIB:dbg_apply(M, F, [get_next, RowIndex, Cols | A])),
+ ?vt("get_next_values_all_rows -> "
+ "~n Result: ~p", [Result]),
+ case validate_tab_next_res(Result, TableOids, {M, F, A}, TabOid) of
+ Values when is_list(Values) ->
+ ?vt("get_next_values_all_rows -> "
+ "~n Values: ~p", [Values]),
+ NewRes = lists:append(Values, Res),
+ get_next_values_all_rows(Rows, M, F, A, NewRes, TabOid);
+ {ErrorStatus, OrgIndex} ->
+ {ErrorStatus, OrgIndex}
+ end;
+get_next_values_all_rows([], _M, _F, _A, Res, _TabOid) ->
+ Res.
+
+transform_tab_next_result([Vb | Vbs], {Res, EndOfs}, MibView) ->
+ case Vb#varbind.value of
+ {endOfTable, _} ->
+ {ResVBs, EndOfVBs} = ?LIB:split_vbs(Vbs, Res, [Vb | EndOfs]),
+ {ok, ResVBs, EndOfVBs};
+ _ ->
+ case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of
+ true ->
+ transform_tab_next_result(Vbs, {[Vb|Res], EndOfs},MibView);
+ _ ->
+ Oid = Vb#varbind.oid,
+ NewEndOf = Vb#varbind{value = {endOfTable, Oid}},
+ transform_tab_next_result(Vbs, {Res, [NewEndOf | EndOfs]},
+ MibView)
+ end
+ end;
+transform_tab_next_result([], {Res, EndOfs}, _MibView) ->
+ ?vt("transform_tab_next_result -> entry with: "
+ "~n Res: ~p"
+ "~n EndIfs: ~p",[Res, EndOfs]),
+ {ok, Res, EndOfs}.
+
+
+
+%%-----------------------------------------------------------------
+%% Three cases:
+%% 1) All values ok
+%% 2) table_func returned {Error, ...}
+%% 3) Some value in Values list is erroneous.
+%% Args: Value is a list of values from table_func(get_next, ...)
+%% TableOids is a list of {TabRestOid, OrgVb}
+%% each element in Values and TableOids correspond to each
+%% other.
+%% Returns: List of NewVarbinds |
+%% {ErrorStatus, OrgIndex}
+%% (In the NewVarbinds list, the value may be endOfTable)
+%%-----------------------------------------------------------------
+validate_tab_next_res(Values, TableOids, Mfa, TabOid) ->
+ ?vt("validate_tab_next_res -> entry with: "
+ "~n Values: ~p"
+ "~n TableOids: ~p"
+ "~n Mfa: ~p"
+ "~n TabOid: ~p", [Values, TableOids, Mfa, TabOid]),
+ {_Col, _ASN1Type, OneIdx} = hd(TableOids),
+ validate_tab_next_res(Values, TableOids, Mfa, [], TabOid,
+ next_oid(TabOid), OneIdx).
+validate_tab_next_res([{NextOid, Value} | Values],
+ [{_ColNo, OrgVb, _Index} | TableOids],
+ Mfa, Res, TabOid, TabNextOid, I) ->
+ ?vt("validate_tab_next_res -> entry with: "
+ "~n NextOid: ~p"
+ "~n Value: ~p"
+ "~n Values: ~p"
+ "~n TableOids: ~p"
+ "~n Mfa: ~p"
+ "~n TabOid: ~p",
+ [NextOid, Value, Values, TableOids, Mfa, TabOid]),
+ #varbind{org_index = OrgIndex} = OrgVb,
+ ?vt("validate_tab_next_res -> OrgIndex: ~p", [OrgIndex]),
+ NextCompleteOid = lists:append(TabOid, NextOid),
+ case snmpa_mib:lookup(get(mibserver), NextCompleteOid) of
+ {table_column, #me{asn1_type = ASN1Type}, _TableEntryOid} ->
+ ?vt("validate_tab_next_res -> ASN1Type: ~p", [ASN1Type]),
+ case ?AGENT:make_value_a_correct_value({value, Value}, ASN1Type, Mfa) of
+ {error, ErrorStatus} ->
+ ?vt("validate_tab_next_res -> "
+ "~n ErrorStatus: ~p", [ErrorStatus]),
+ {ErrorStatus, OrgIndex};
+ {value, Type, NValue} ->
+ ?vt("validate_tab_next_res -> "
+ "~n Type: ~p"
+ "~n NValue: ~p", [Type, NValue]),
+ NewVb = OrgVb#varbind{oid = NextCompleteOid,
+ variabletype = Type, value = NValue},
+ validate_tab_next_res(Values, TableOids, Mfa,
+ [NewVb | Res], TabOid, TabNextOid, I)
+ end;
+ Error ->
+ ?LIB:user_err("Invalid oid ~w from ~w (get_next). Using genErr => ~p",
+ [NextOid, Mfa, Error]),
+ {genErr, OrgIndex}
+ end;
+validate_tab_next_res([endOfTable | Values],
+ [{_ColNo, OrgVb, _Index} | TableOids],
+ Mfa, Res, TabOid, TabNextOid, I) ->
+ ?vt("validate_tab_next_res(endOfTable) -> entry with: "
+ "~n Values: ~p"
+ "~n OrgVb: ~p"
+ "~n TableOids: ~p"
+ "~n Mfa: ~p"
+ "~n Res: ~p"
+ "~n TabOid: ~p"
+ "~n TabNextOid: ~p"
+ "~n I: ~p",
+ [Values, OrgVb, TableOids, Mfa, Res, TabOid, TabNextOid, I]),
+ NewVb = OrgVb#varbind{value = {endOfTable, TabNextOid}},
+ validate_tab_next_res(Values, TableOids, Mfa, [NewVb | Res],
+ TabOid, TabNextOid, I);
+validate_tab_next_res([], [], _Mfa, Res, _TabOid, _TabNextOid, _I) ->
+ Res;
+validate_tab_next_res([], [{_Col, _OrgVb, Index}|_], Mfa, _Res, _, _, _I) ->
+ ?LIB:user_err("Too few values returned from ~w (get_next)", [Mfa]),
+ {genErr, Index};
+validate_tab_next_res({genErr, ColNumber}, OrgCols,
+ Mfa, _Res, _TabOid, _TabNextOid, _I) ->
+ OrgIndex = snmpa_svbl:col_to_orgindex(ColNumber, OrgCols),
+ ?AGENT:validate_err(table_next, {genErr, OrgIndex}, Mfa);
+validate_tab_next_res({error, Reason}, [{_ColNo, OrgVb, _Index} | _TableOids],
+ Mfa, _Res, _TabOid, _TabNextOid, _I) ->
+ #varbind{org_index = OrgIndex} = OrgVb,
+ ?LIB:user_err("Erroneous return value ~w from ~w (get_next)",
+ [Reason, Mfa]),
+ {genErr, OrgIndex};
+validate_tab_next_res(Error, [{_ColNo, OrgVb, _Index} | _TableOids],
+ Mfa, _Res, _TabOid, _TabNextOid, _I) ->
+ #varbind{org_index = OrgIndex} = OrgVb,
+ ?LIB:user_err("Invalid return value ~w from ~w (get_next)",
+ [Error, Mfa]),
+ {genErr, OrgIndex};
+validate_tab_next_res(TooMany, [], Mfa, _Res, _, _, I) ->
+ ?LIB:user_err("Too many values ~w returned from ~w (get_next)",
+ [TooMany, Mfa]),
+ {genErr, I}.
+
+%%-----------------------------------------------------------------
+%% Func: get_next_sa/4
+%% Purpose: Loop the list of varbinds for the subagent.
+%% Call subagent_get_next to retreive
+%% the next varbinds.
+%% Returns: {ok, ListOfNewVbs, ListOfEndOfMibViewsVbs} |
+%% {ErrorStatus, ErrorIndex}
+%%-----------------------------------------------------------------
+get_next_sa(SAPid, SAOid, SAVbs, MibView) ->
+ case catch ?AGENT:subagent_get_next(SAPid, MibView, SAVbs) of
+ {noError, 0, NewVbs} ->
+ NewerVbs = transform_sa_next_result(NewVbs,SAOid,next_oid(SAOid)),
+ {ResVBs, EndOfVBs} = ?LIB:split_vbs(NewerVbs),
+ {ok, ResVBs, EndOfVBs};
+ {ErrorStatus, ErrorIndex, _} ->
+ {ErrorStatus, ErrorIndex};
+ {'EXIT', Reason} ->
+ ?LIB:user_err("Lost contact with subagent (next) ~w. Using genErr",
+ [Reason]),
+ {genErr, 0}
+ end.
+
+%%-----------------------------------------------------------------
+%% Check for wrong prefix returned or endOfMibView, and convert
+%% into {endOfMibView, SANextOid}.
+%%-----------------------------------------------------------------
+transform_sa_next_result([Vb | Vbs], SAOid, SANextOid)
+ when Vb#varbind.value =:= endOfMibView ->
+ [Vb#varbind{value = {endOfMibView, SANextOid}} |
+ transform_sa_next_result(Vbs, SAOid, SANextOid)];
+transform_sa_next_result([Vb | Vbs], SAOid, SANextOid) ->
+ case lists:prefix(SAOid, Vb#varbind.oid) of
+ true ->
+ [Vb | transform_sa_next_result(Vbs, SAOid, SANextOid)];
+ _ ->
+ [Vb#varbind{oid = SANextOid, value = {endOfMibView, SANextOid}} |
+ transform_sa_next_result(Vbs, SAOid, SANextOid)]
+ end;
+transform_sa_next_result([], _SAOid, _SANextOid) ->
+ [].
+
+
+next_oid(Oid) ->
+ case lists:reverse(Oid) of
+ [H | T] -> lists:reverse([H+1 | T]);
+ [] -> []
+ end.
+
+
+
+%%%-----------------------------------------------------------------
+%%% 5. GET-BULK REQUEST
+%%%
+%%% In order to prevent excesses in reply sizes there are two
+%%% preventive methods in place. One is to check that the encode
+%%% size does not exceed Max PDU size (this is mentioned in the
+%%% standard). The other is a simple VBs limit. That is, the
+%%% resulting response cannot contain more then this number of VBs.
+%%%-----------------------------------------------------------------
+
+do_get_bulk(MibView, NonRepeaters, MaxRepetitions,
+ PduMS, Varbinds, GbMaxVBs, _Extra) ->
+ ?vtrace("do_get_bulk -> entry with"
+ "~n MibView: ~p"
+ "~n NonRepeaters: ~p"
+ "~n MaxRepetitions: ~p"
+ "~n PduMS: ~p"
+ "~n Varbinds: ~p"
+ "~n GbMaxVBs: ~p",
+ [MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs]),
+ {NonRepVbs, RestVbs} = ?LIB:split_vbs_gb(NonRepeaters, Varbinds),
+ ?vt("do_get_bulk -> split: "
+ "~n NonRepVbs: ~p"
+ "~n RestVbs: ~p", [NonRepVbs, RestVbs]),
+ case do_get_next2(MibView, NonRepVbs, GbMaxVBs) of
+ {noError, 0, UResNonRepVbs} ->
+ ?vt("do_get_bulk -> next noError: "
+ "~n UResNonRepVbs: ~p", [UResNonRepVbs]),
+ ResNonRepVbs = lists:keysort(#varbind.org_index, UResNonRepVbs),
+ %% Decode the first varbinds, produce a reversed list of
+ %% listOfBytes.
+ case (catch enc_vbs(PduMS - ?empty_pdu_size, ResNonRepVbs)) of
+ {error, Idx, Reason} ->
+ ?LIB:user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]),
+ {genErr, Idx, []};
+ {SizeLeft, Res} when is_integer(SizeLeft) and is_list(Res) ->
+ ?vtrace("do_get_bulk -> encoded: "
+ "~n SizeLeft: ~p"
+ "~n Res: ~w", [SizeLeft, Res]),
+ case (catch do_get_rep(SizeLeft, MibView, MaxRepetitions,
+ RestVbs, Res,
+ length(UResNonRepVbs), GbMaxVBs)) of
+ {error, Idx, Reason} ->
+ ?LIB:user_err("failed encoding varbind ~w:~n~p",
+ [Idx, Reason]),
+ {genErr, Idx, []};
+ Res when is_list(Res) ->
+ ?vtrace("do get bulk -> Res: "
+ "~n ~w", [Res]),
+ {noError, 0, conv_res(Res)};
+ {noError, 0, Data} = OK ->
+ ?vtrace("do get bulk -> OK: "
+ "~n length(Data): ~w", [length(Data)]),
+ OK;
+ Else ->
+ ?vtrace("do get bulk -> Else: "
+ "~n ~w", [Else]),
+ Else
+ end;
+ Res when is_list(Res) ->
+ {noError, 0, conv_res(Res)}
+ end;
+
+ {ErrorStatus, Index, _} ->
+ ?vdebug("do get bulk: "
+ "~n ErrorStatus: ~p"
+ "~n Index: ~p",[ErrorStatus, Index]),
+ {ErrorStatus, Index, []}
+ end.
+
+enc_vbs(SizeLeft, Vbs) ->
+ ?vt("enc_vbs -> entry with"
+ "~n SizeLeft: ~w", [SizeLeft]),
+ Fun = fun(Vb, {Sz, Res}) when Sz > 0 ->
+ ?vt("enc_vbs -> (fun) entry with"
+ "~n Vb: ~p"
+ "~n Sz: ~p"
+ "~n Res: ~w", [Vb, Sz, Res]),
+ case (catch snmp_pdus:enc_varbind(Vb)) of
+ {'EXIT', Reason} ->
+ ?vtrace("enc_vbs -> encode failed: "
+ "~n Reason: ~p", [Reason]),
+ throw({error, Vb#varbind.org_index, Reason});
+ X ->
+ ?vt("enc_vbs -> X: ~w", [X]),
+ Lx = length(X),
+ ?vt("enc_vbs -> Lx: ~w", [Lx]),
+ if
+ Lx < Sz ->
+ {Sz - length(X), [X | Res]};
+ true ->
+ throw(Res)
+ end
+ end;
+ (_Vb, {_Sz, [_H | T]}) ->
+ ?vt("enc_vbs -> (fun) entry with"
+ "~n T: ~p", [T]),
+ throw(T);
+ (_Vb, {_Sz, []}) ->
+ ?vt("enc_vbs -> (fun) entry", []),
+ throw([])
+ end,
+ lists:foldl(Fun, {SizeLeft, []}, Vbs).
+
+do_get_rep(Sz, MibView, MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs)
+ when MaxRepetitions >= 0 ->
+ do_get_rep(Sz, MibView, 0, MaxRepetitions, Varbinds, Res,
+ GbNumVBs, GbMaxVBs);
+do_get_rep(Sz, MibView, _MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs) ->
+ do_get_rep(Sz, MibView, 0, 0, Varbinds, Res, GbNumVBs, GbMaxVBs).
+
+conv_res(ResVarbinds) ->
+ conv_res(ResVarbinds, []).
+conv_res([VbListOfBytes | T], Bytes) ->
+ conv_res(T, VbListOfBytes ++ Bytes);
+conv_res([], Bytes) ->
+ Bytes.
+
+%% The only other value, then a positive integer, is infinity.
+do_get_rep(_Sz, _MibView, Count, Max, _, _Res, GbNumVBs, GbMaxVBs)
+ when (is_integer(GbMaxVBs) andalso (GbNumVBs > GbMaxVBs)) ->
+ ?vinfo("Max Get-BULK VBs limit (~w) exceeded (~w) when:"
+ "~n Count: ~p"
+ "~n Max: ~p", [GbMaxVBs, GbNumVBs, Count, Max]),
+ {tooBig, 0, []};
+do_get_rep(_Sz, _MibView, Max, Max, _, Res, _GbNumVBs, _GbMaxVBs) ->
+ ?vt("do_get_rep -> done when: "
+ "~n Res: ~p", [Res]),
+ {noError, 0, conv_res(Res)};
+do_get_rep(Sz, MibView, Count, Max, Varbinds, Res, GbNumVBs, GbMaxVBs) ->
+ ?vt("do_get_rep -> entry when: "
+ "~n Sz: ~p"
+ "~n Count: ~p"
+ "~n Res: ~w", [Sz, Count, Res]),
+ case try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) of
+ {noError, NextVarbinds, SizeLeft, Res2} ->
+ ?vt("do_get_rep -> noError: "
+ "~n SizeLeft: ~p"
+ "~n Res2: ~p", [SizeLeft, Res2]),
+ do_get_rep(SizeLeft, MibView, Count+1, Max, NextVarbinds,
+ Res2 ++ Res,
+ GbNumVBs + length(Varbinds), GbMaxVBs);
+ {endOfMibView, _NextVarbinds, _SizeLeft, Res2} ->
+ ?vt("do_get_rep -> endOfMibView: "
+ "~n Res2: ~p", [Res2]),
+ {noError, 0, conv_res(Res2 ++ Res)};
+ {ErrorStatus, Index} ->
+ ?vtrace("do_get_rep -> done when error: "
+ "~n ErrorStatus: ~p"
+ "~n Index: ~p", [ErrorStatus, Index]),
+ {ErrorStatus, Index, []}
+ end.
+
+try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) ->
+ ?vt("try_get_bulk -> entry with"
+ "~n Sz: ~w"
+ "~n MibView: ~w"
+ "~n Varbinds: ~w", [Sz, MibView, Varbinds]),
+ case do_get_next2(MibView, Varbinds, GbMaxVBs) of
+ {noError, 0, UNextVarbinds} ->
+ ?vt("try_get_bulk -> noError: "
+ "~n UNextVarbinds: ~p", [UNextVarbinds]),
+ NextVarbinds = ?LIB:org_index_sort_vbs(UNextVarbinds),
+ case (catch enc_vbs(Sz, NextVarbinds)) of
+ {error, Idx, Reason} ->
+ ?LIB:user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]),
+ ?vtrace("try_get_bulk -> encode error: "
+ "~n Idx: ~p"
+ "~n Reason: ~p", [Idx, Reason]),
+ {genErr, Idx};
+ {SizeLeft, Res} when is_integer(SizeLeft) andalso
+ is_list(Res) ->
+ ?vt("try get bulk -> encode ok: "
+ "~n SizeLeft: ~w"
+ "~n Res: ~w", [SizeLeft, Res]),
+ {check_end_of_mibview(NextVarbinds),
+ NextVarbinds, SizeLeft, Res};
+ Res when is_list(Res) ->
+ ?vt("try get bulk -> Res: "
+ "~n ~w", [Res]),
+ {endOfMibView, [], 0, Res}
+ end;
+ {ErrorStatus, Index, _} ->
+ ?vt("try_get_bulk -> error: "
+ "~n ErrorStatus: ~p"
+ "~n Index: ~p", [ErrorStatus, Index]),
+ {ErrorStatus, Index}
+ end.
+
+%% If all variables in this pass are endOfMibView,
+%% there is no reason to continue.
+check_end_of_mibview([#varbind{value = endOfMibView} | T]) ->
+ check_end_of_mibview(T);
+check_end_of_mibview([]) -> endOfMibView;
+check_end_of_mibview(_) -> noError.
+
+
+
diff --git a/lib/snmp/src/agent/snmpa_get_lib.erl b/lib/snmp/src/agent/snmpa_get_lib.erl
new file mode 100644
index 0000000000..eaf7fe2641
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_get_lib.erl
@@ -0,0 +1,254 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Note that most of these functions *assume* that they are executed
+%% by the agent. If they are not they may note work as they require
+%% some properties to be set in the process dictionary!
+%%
+
+-module(snmpa_get_lib).
+
+-export([
+ split_vbs/1, split_vbs/3,
+ split_vbs_view/2,
+ split_vbs_gb/2,
+
+ agent_sort_vbs/1,
+ oid_sort_vbs/1, org_index_sort_vbs/1,
+
+ sa_split/1,
+
+ delete_prefixes/2,
+
+ dbg_apply/3,
+
+ user_err/2
+ ]).
+
+-define(VMODULE,"GET-LIB").
+-include("snmpa_internal.hrl").
+-include("snmp_types.hrl").
+-include("snmp_debug.hrl").
+-include("snmp_verbosity.hrl").
+
+
+
+
+%%-----------------------------------------------------------------
+%% split_vbs/1,3
+%%
+%% Splits the list of varbinds (basically) into two lists. One
+%% of 'end of'-varbinds (mib view and tables) and then the rest
+%% of the varbinds.
+%%-----------------------------------------------------------------
+
+-spec split_vbs(VBs :: [snmp:varbind()]) ->
+ {ResVBs :: [snmp:varbind()],
+ EndOfVBs :: [snmp:varbind()]}.
+
+split_vbs(VBs) ->
+ split_vbs(VBs, [], []).
+
+-spec split_vbs(VBs :: [snmp:varbind()],
+ Res :: [snmp:varbind()],
+ EndOfs :: [snmp:varbind()]) ->
+ {ResVBs :: [snmp:varbind()],
+ EndOfVBs :: [snmp:varbind()]}.
+
+split_vbs([], ResVBs, EndOfVBs) ->
+ {ResVBs, EndOfVBs};
+split_vbs([VB | VBs], Res, EndOfs) ->
+ case VB#varbind.value of
+ {endOfMibView, _} -> split_vbs(VBs, Res, [VB | EndOfs]);
+ {endOfTable, _} -> split_vbs(VBs, Res, [VB | EndOfs]);
+ _ -> split_vbs(VBs, [VB | Res], EndOfs)
+ end.
+
+
+
+%%-----------------------------------------------------------------
+%% split_vbs_view/2
+%%
+%% Splits a list of varbinds into two lists based on the provided
+%% MibView. One list of varbinds inside the MibView and one of
+%% varbinds outside the MibView.
+%%-----------------------------------------------------------------
+
+-spec split_vbs_view(VBs :: [snmp:varbind()],
+ MibView :: snmp_view_based_acm_mib:mibview()) ->
+ {OutSideView :: [snmp:varbind()],
+ InSideView :: [snmp:varbind()]}.
+
+split_vbs_view(VBs, MibView) ->
+ ?vtrace("split the varbinds view", []),
+ split_vbs_view(VBs, MibView, [], []).
+
+split_vbs_view([], _MibView, Out, In) ->
+ {Out, In};
+split_vbs_view([VB | VBs], MibView, Out, In) ->
+ case snmpa_acm:validate_mib_view(VB#varbind.oid, MibView) of
+ true ->
+ split_vbs_view(VBs, MibView, Out, [VB | In]);
+ false ->
+ VB2 = VB#varbind{value = noSuchObject},
+ split_vbs_view(VBs, MibView, [VB2 | Out], In)
+ end.
+
+
+
+%%-----------------------------------------------------------------
+%% split_vbs_gb/2
+%%
+%% Performs a get-bulk split of the varbinds
+%%-----------------------------------------------------------------
+
+-spec split_vbs_gb(NonRepeaters :: integer(),
+ VBs :: [snmp:varbind()]) ->
+ {NonRepVBs :: [snmp:varbind()],
+ RestVBs :: [snmp:varbind()]}.
+
+split_vbs_gb(N, VBs) ->
+ split_vbs_gb(N, VBs, []).
+
+split_vbs_gb(N, Varbinds, Res) when N =< 0 ->
+ {Res, Varbinds};
+split_vbs_gb(N, [H | T], Res) ->
+ split_vbs_gb(N-1, T, [H | Res]);
+split_vbs_gb(_N, [], Res) ->
+ {Res, []}.
+
+
+
+%%-----------------------------------------------------------------
+%% agent_sort_vbs/1
+%%
+%% Sorts the varbinds into two categories. The first is varbinds
+%% belonging to "our" agent and the other is varbinds for
+%% subagents.
+%%-----------------------------------------------------------------
+
+-spec agent_sort_vbs(VBs :: [snmp:varbind()]) ->
+ {AgentVBs :: [snmp:varbind()],
+ SubAgentVBs :: [snmp:varbind()]}.
+
+agent_sort_vbs(VBs) ->
+ snmpa_svbl:sort_varbindlist(get(mibserver), VBs).
+
+
+%%-----------------------------------------------------------------
+%% oid_sort_vbs/1
+%%
+%% Sorts the varbinds based on their oid.
+%%-----------------------------------------------------------------
+
+-spec oid_sort_vbs(VBs :: [snmp:varbind()]) -> SortedVBs :: [snmp:varbind()].
+
+oid_sort_vbs(VBs) ->
+ lists:keysort(#varbind.oid, VBs).
+
+
+%%-----------------------------------------------------------------
+%% org_index_sort_vbs/1
+%%
+%% Sorts the varbinds based on their org_index.
+%%-----------------------------------------------------------------
+
+-spec org_index_sort_vbs(VBs :: [snmp:varbind()]) -> SortedVBs :: [snmp:varbind()].
+
+org_index_sort_vbs(Vbs) ->
+ lists:keysort(#varbind.org_index, Vbs).
+
+
+
+%%-----------------------------------------------------------------
+%% sa_split/1
+%%
+%% Splits a list of {oid(), varbind()} into two lists of oid()
+%% and varbind. The resulting lists are reversed!
+%%-----------------------------------------------------------------
+
+-spec sa_split(SAVBs :: [{SAOid :: snmp:oid(), snmp:varbind()}]) ->
+ {Oids :: [snmp:oid()], VBs :: [snmp:varbind()]}.
+
+sa_split(SAVBs) ->
+ snmpa_svbl:sa_split(SAVBs).
+
+
+
+%%-----------------------------------------------------------------
+%% delete_prefixes/2
+%%
+%% Takes an Oid prefix and a list of ivarbinds and produces a list
+%% of {ShortOid, ASN1Type}. The ShortOid is basically the oid with
+%% the OidPrefix removed.
+%%-----------------------------------------------------------------
+
+-spec delete_prefixes(OidPrefix :: snmp:oid(),
+ VBs :: [snmp:ivarbind()]) ->
+ [{ShortOid :: snmp:oid(),
+ ASN1Type :: snmp:asn1_type()}].
+
+delete_prefixes(OidPrefix, IVBs) ->
+ [{snmp_misc:diff(Oid, OidPrefix), ME#me.asn1_type} ||
+ #ivarbind{varbind = #varbind{oid = Oid}, mibentry = ME} <- IVBs].
+
+
+
+%%-----------------------------------------------------------------
+%% dbg_apply/3
+%%
+%% Call instrumentation functions, but allow for debug printing
+%% of useful debug info.
+%%-----------------------------------------------------------------
+
+-spec dbg_apply(M :: atom(), F :: atom(), A :: list()) ->
+ any().
+
+dbg_apply(M, F, A) ->
+ case get(verbosity) of
+ silence ->
+ apply(M,F,A);
+ _ ->
+ ?vlog("~n apply: ~w, ~w, ~p~n", [M,F,A]),
+ Res = (catch apply(M,F,A)),
+ case Res of
+ {'EXIT', Reason} ->
+ ?vinfo("Call to: "
+ "~n Module: ~p"
+ "~n Function: ~p"
+ "~n Args: ~p"
+ "~n"
+ "~nresulted in an exit"
+ "~n"
+ "~n ~p~n", [M, F, A, Reason]);
+ _ ->
+ ?vlog("~n returned: ~p~n", [Res])
+ end,
+ Res
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+user_err(F, A) ->
+ snmpa_error:user_err(F, A).
+
+
diff --git a/lib/snmp/src/agent/snmpa_get_mechanism.erl b/lib/snmp/src/agent/snmpa_get_mechanism.erl
new file mode 100644
index 0000000000..744a6529e1
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_get_mechanism.erl
@@ -0,0 +1,79 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpa_get_mechanism).
+
+%%
+%% This module defines the behaviour for the undocumented (hidden)
+%% get-mechanism feature. This allows for implementing your own
+%% handling of get, get-next and get-bulk requests.
+%% Probably only useful for special cases (e.g. optimization).
+%%
+
+
+
+%% ----------- do_get/2,3 -----------------------------------------------------
+
+%% Purpose: Handles all VBs in a request that is inside the
+%% mibview (local).
+
+-callback do_get(UnsortedVBs :: [snmp:varbind()],
+ IsNotification :: boolean(),
+ Extra :: term()) ->
+ {noError, 0, ResVBs :: [snmp:varbind()]} |
+ {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}.
+
+
+%% Purpose: Handles "get-requests".
+
+-callback do_get(MibView :: snmp_view_based_acm_mib:mibview(),
+ UnsortedVBs :: [snmp:varbind()],
+ IsNotification :: boolean(),
+ Extra :: term()) ->
+ {noError, 0, ResVBs :: [snmp:varbind()]} |
+ {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}.
+
+
+
+
+%% ----------- do_get_next/2 ------------------------------------------------
+
+%% Purpose: Handles "get-next-requests".
+
+-callback do_get_next(MibView :: snmp_view_based_acm_mib:mibview(),
+ UnsortedVBs :: [snmp:varbind()],
+ Extra :: term()) ->
+ {noError, 0, ResVBs :: [snmp:varbind()]} |
+ {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}.
+
+
+
+
+%% ----------- do_get_bulk/6 ------------------------------------------------
+
+-callback do_get_bulk(MibView :: snmp_view_based_acm_mib:mibview(),
+ NonRepeaters :: non_neg_integer(),
+ MaxRepetitions :: non_neg_integer(),
+ PduMS :: pos_integer(),
+ VBs :: [snmp:varbind()],
+ MaxVBs :: pos_integer(),
+ Extra :: term()) ->
+ {noError, 0, ResVBs :: [snmp:varbind()]} |
+ {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}.
diff --git a/lib/snmp/src/agent/snmpa_set_lib.erl b/lib/snmp/src/agent/snmpa_set_lib.erl
index 57507a36e8..3dcf49cbe6 100644
--- a/lib/snmp/src/agent/snmpa_set_lib.erl
+++ b/lib/snmp/src/agent/snmpa_set_lib.erl
@@ -390,7 +390,7 @@ dbg_apply(M,F,A) ->
{'EXIT', {function_clause, [{M, F, A} | _]}} ->
{'EXIT', {hook_function_clause, {M, F, A}}};
- % XYZ: Older format for compatibility
+ %% XYZ: Older format for compatibility
{'EXIT', {undef, {M, F, A}}} ->
{'EXIT', {hook_undef, {M, F, A}}};
{'EXIT', {function_clause, {M, F, A}}} ->
diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl
index cdb5ca840d..2cb0556001 100644
--- a/lib/snmp/src/agent/snmpa_supervisor.erl
+++ b/lib/snmp/src/agent/snmpa_supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -193,36 +193,36 @@ init([AgentType, Opts]) ->
?vdebug("agent restart type: ~w", [Restart]),
%% -- Agent type --
- ets:insert(snmp_agent_table, {agent_type, AgentType}),
+ store(agent_type, AgentType),
%% -- Prio --
Prio = get_opt(priority, Opts, normal),
?vdebug("[agent table] store priority: ~p",[Prio]),
- ets:insert(snmp_agent_table, {priority, Prio}),
+ store(priority, Prio),
%% -- Versions --
Vsns = get_opt(versions, Opts, [v1,v2,v3]),
?vdebug("[agent table] store versions: ~p",[Vsns]),
- ets:insert(snmp_agent_table, {versions, Vsns}),
+ store(versions, Vsns),
%% -- Max number of VBs in a Get-BULK response --
GbMaxVBs = get_gb_max_vbs(Opts),
?vdebug("[agent table] Get-BULK max VBs: ~p", [GbMaxVBs]),
- ets:insert(snmp_agent_table, {gb_max_vbs, GbMaxVBs}),
+ store(gb_max_vbs, GbMaxVBs),
%% -- DB-directory --
DbDir = get_opt(db_dir, Opts),
?vdebug("[agent table] store db_dir: ~n ~p",[DbDir]),
- ets:insert(snmp_agent_table, {db_dir, filename:join([DbDir])}),
+ store(db_dir, filename:join([DbDir])),
DbInitError = get_opt(db_init_error, Opts, terminate),
?vdebug("[agent table] store db_init_error: ~n ~p",[DbInitError]),
- ets:insert(snmp_agent_table, {db_init_error, DbInitError}),
+ store(db_init_error, DbInitError),
%% -- Error report module --
ErrorReportMod = get_opt(error_report_mod, Opts, snmpa_error_logger),
?vdebug("[agent table] store error report module: ~w",[ErrorReportMod]),
- ets:insert(snmp_agent_table, {error_report_mod, ErrorReportMod}),
+ store(error_report_mod, ErrorReportMod),
%% -- mib storage --
%% MibStorage has only one mandatory part: module
@@ -320,31 +320,31 @@ init([AgentType, Opts]) ->
end,
?vdebug("[agent table] store mib storage: ~w", [MibStorage]),
- ets:insert(snmp_agent_table, {mib_storage, MibStorage}),
+ store(mib_storage, MibStorage),
%% -- Agent mib storage --
AgentMibStorage = get_opt(agent_mib_storage, Opts, persistent),
%% ?vdebug("[agent table] store agent mib storage: ~w",[AgentMibStorage]),
- ets:insert(snmp_agent_table, {agent_mib_storage, AgentMibStorage}),
+ store(agent_mib_storage, AgentMibStorage),
%% -- System start time --
?vdebug("[agent table] store system start time",[]),
- ets:insert(snmp_agent_table, {system_start_time, snmp_misc:now(cs)}),
+ store(system_start_time, snmp_misc:now(cs)),
%% -- Symbolic store options --
SsOpts = get_opt(symbolic_store, Opts, []),
?vdebug("[agent table] store symbolic store options: ~w",[SsOpts]),
- ets:insert(snmp_agent_table, {symbolic_store, SsOpts}),
+ store(symbolic_store, SsOpts),
%% -- Local DB options --
LdbOpts = get_opt(local_db, Opts, []),
?vdebug("[agent table] store local db options: ~w",[LdbOpts]),
- ets:insert(snmp_agent_table, {local_db, LdbOpts}),
+ store(local_db, LdbOpts),
%% -- Target cache options --
TargetCacheOpts = get_opt(target_cache, Opts, []),
?vdebug("[agent table] store target cache options: ~w",[TargetCacheOpts]),
- ets:insert(snmp_agent_table, {target_cache, TargetCacheOpts}),
+ store(target_cache, TargetCacheOpts),
%% -- Specs --
SupFlags = {one_for_all, 0, 3600},
@@ -377,7 +377,7 @@ init([AgentType, Opts]) ->
%% -- Config --
ConfOpts = get_opt(config, Opts, []),
?vdebug("[agent table] store config options: ~p", [ConfOpts]),
- ets:insert(snmp_agent_table, {config, ConfOpts}),
+ store(config, ConfOpts),
ConfigArgs = [Vsns, ConfOpts],
ConfigSpec =
@@ -390,43 +390,46 @@ init([AgentType, Opts]) ->
%% -- Discovery processing --
DiscoOpts = get_opt(discovery, Opts, []),
?vdebug("[agent table] store discovery options: ~p", [DiscoOpts]),
- ets:insert(snmp_agent_table, {discovery, DiscoOpts}),
+ store(discovery, DiscoOpts),
%% -- Mibs --
Mibs = get_mibs(get_opt(mibs, Opts, []), Vsns),
?vdebug("[agent table] store mibs: ~n ~p",[Mibs]),
- ets:insert(snmp_agent_table, {mibs, Mibs}),
+ store(mibs, Mibs),
Ref = make_ref(),
+ %% -- Get module --
+ GetModule = get_opt(get_mechanism, Opts, snmpa_get),
+ ?vdebug("[agent table] store get-module: ~p", [GetModule]),
+ store(get_mechanism, GetModule),
+
%% -- Set module --
SetModule = get_opt(set_mechanism, Opts, snmpa_set),
?vdebug("[agent table] store set-module: ~p",[SetModule]),
- ets:insert(snmp_agent_table, {set_mechanism, ConfOpts}),
+ store(set_mechanism, SetModule),
%% -- Authentication service --
AuthModule = get_opt(authentication_service, Opts, snmpa_acm),
?vdebug("[agent table] store authentication service: ~w",
[AuthModule]),
- ets:insert(snmp_agent_table,
- {authentication_service, AuthModule}),
+ store(authentication_service, AuthModule),
%% -- Multi-threaded --
MultiT = get_opt(multi_threaded, Opts, false),
- ?vdebug("[agent table] store multi-threaded: ~p",[MultiT]),
- ets:insert(snmp_agent_table, {multi_threaded, MultiT}),
+ ?vdebug("[agent table] store multi-threaded: ~p", [MultiT]),
+ store(multi_threaded, MultiT),
%% -- Audit trail log --
case get_opt(audit_trail_log, Opts, not_found) of
not_found ->
- ?vdebug("[agent table] no audit trail log",[]),
+ ?vdebug("[agent table] no audit trail log", []),
ok;
AtlOpts ->
?vdebug("[agent table] "
"store audit trail log options: ~p",
[AtlOpts]),
- ets:insert(snmp_agent_table,
- {audit_trail_log, AtlOpts}),
+ store(audit_trail_log, AtlOpts),
ok
end,
@@ -434,24 +437,25 @@ init([AgentType, Opts]) ->
MibsOpts = get_opt(mib_server, Opts, []),
?vdebug("[agent table] store mib-server options: "
"~n ~p", [MibsOpts]),
- ets:insert(snmp_agent_table, {mib_server, MibsOpts}),
+ store(mib_server, MibsOpts),
%% -- Network interface --
NiOpts = get_opt(net_if, Opts, []),
?vdebug("[agent table] store net-if options: "
"~n ~p", [NiOpts]),
- ets:insert(snmp_agent_table, {net_if, NiOpts}),
+ store(net_if, NiOpts),
%% -- Note store --
NsOpts = get_opt(note_store, Opts, []),
?vdebug("[agent table] store note-store options: "
"~n ~p",[NsOpts]),
- ets:insert(snmp_agent_table, {note_store, NsOpts}),
+ store(note_store, NsOpts),
AgentOpts =
[{verbosity, AgentVerb},
{mibs, Mibs},
{mib_storage, MibStorage},
+ {get_mechanism, GetModule},
{set_mechanism, SetModule},
{authentication_service, AuthModule},
{multi_threaded, MultiT},
@@ -480,6 +484,10 @@ init([AgentType, Opts]) ->
{ok, {SupFlags, [MiscSupSpec, SymStoreSpec, LocalDbSpec, TargetCacheSpec |
Rest]}}.
+
+store(Key, Value) ->
+ ets:insert(snmp_agent_table, {Key, Value}).
+
get_mibs(Mibs, Vsns) ->
MibDir = filename:join(code:priv_dir(snmp), "mibs"),
StdMib =
diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl
index e75016f7ec..31805c3bee 100644
--- a/lib/snmp/src/agent/snmpa_trap.erl
+++ b/lib/snmp/src/agent/snmpa_trap.erl
@@ -917,7 +917,7 @@ do_send_v2_trap(Recvs, Vbs, ExtraInfo, NetIf) ->
TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'),
AddrCommunities = mk_addr_communities(Recvs),
lists:foreach(fun({Community, Addrs}) ->
- ?vtrace("~n send v2 trap to ~p",[Addrs]),
+ ?vtrace("send v2 trap to ~p",[Addrs]),
NetIf ! {send_pdu, 'version-2', TrapPdu,
{community, Community}, Addrs, ExtraInfo}
end, AddrCommunities),
diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src
index d4bf0de61a..178309b488 100644
--- a/lib/snmp/src/app/snmp.app.src
+++ b/lib/snmp/src/app/snmp.app.src
@@ -49,6 +49,9 @@
snmpa_error_io,
snmpa_error_logger,
snmpa_error_report,
+ snmpa_get,
+ snmpa_get_lib,
+ snmpa_get_mechanism,
snmpa_local_db,
snmpa_mib,
snmpa_mib_data,
diff --git a/lib/snmp/src/app/snmp.config b/lib/snmp/src/app/snmp.config
index b66ef5d7df..f35a636157 100644
--- a/lib/snmp/src/app/snmp.config
+++ b/lib/snmp/src/app/snmp.config
@@ -8,6 +8,7 @@
%% {agent_verbosity, verbosity()} |
%% {versions, versions()} |
%% {priority, atom()} |
+%% {get_mechanism, module()} |
%% {set_mechanism, module()} |
%% {authentication_service, module()} |
%% {multi_threaded, bool()} |
diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl
index 8a736f688b..216452afdd 100644
--- a/lib/snmp/src/app/snmp.erl
+++ b/lib/snmp/src/app/snmp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -116,7 +116,10 @@
pdu/0,
trappdu/0,
mib/0,
- mib_name/0,
+ mib_name/0,
+
+ error_status/0,
+ error_index/0,
void/0
]).
@@ -208,6 +211,23 @@
-type pdu() :: #pdu{}.
-type trappdu() :: #trappdu{}.
+%% We should really specify all of these, but they are so numerous...
+%% See the validate_err/1 function in the snmpa_agent.
+%% Here are a number of them:
+%% badValue |
+%% commitFailed |
+%% genErr |
+%% inconsistentName | inconsistentValue |
+%% noAccess | noCreation |
+%% noSuchInstance | noSuchName | noSuchObject |
+%% notWritable |
+%% resourceUnavailable |
+%% undoFailed |
+%% wrongValue
+
+-type error_status() :: atom().
+-type error_index() :: pos_integer().
+
-type void() :: term().
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 0f54e67c65..8b6547f9a9 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -31,6 +31,7 @@ SUITE_MODULES = \
snmp_agent_mibs_test \
snmp_agent_nfilter_test \
snmp_agent_test \
+ snmp_agent_test_get \
snmp_agent_conf_test \
snmp_agent_test_lib \
snmp_manager_config_test \
diff --git a/lib/snmp/test/snmp_agent_test_get.erl b/lib/snmp/test/snmp_agent_test_get.erl
new file mode 100644
index 0000000000..517c71507a
--- /dev/null
+++ b/lib/snmp/test/snmp_agent_test_get.erl
@@ -0,0 +1,58 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_agent_test_get).
+
+-behaviour(snmpa_get_mechanism).
+
+
+%%%-----------------------------------------------------------------
+%%% snmpa_get_mechanism exports
+%%%-----------------------------------------------------------------
+
+-export([
+ do_get/3, do_get/4,
+ do_get_next/3,
+ do_get_bulk/7
+ ]).
+
+
+
+do_get(UnsortedVarbinds, IsNotification, Extra) ->
+ snmpa_get:do_get(UnsortedVarbinds, IsNotification, Extra).
+
+
+
+do_get(MibView, UnsortedVarbinds, IsNotification, Extra) ->
+ snmpa_get:do_get(MibView, UnsortedVarbinds, IsNotification, Extra).
+
+
+
+do_get_next(MibView, UnsortedVBs, Extra) ->
+ snmpa_get:do_get_next(MibView, UnsortedVBs, Extra).
+
+
+
+
+do_get_bulk(MibView, NonRepeaters, MaxRepetitions,
+ PduMS, Varbinds, GbMaxVBs, Extra) ->
+ snmpa_get:do_get_bulk(MibView, NonRepeaters, MaxRepetitions,
+ PduMS, Varbinds, GbMaxVBs,
+ Extra).
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 66211d7105..c19c88528f 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -445,6 +445,7 @@ start_agent(Config, Vsns, Opts) ->
[{versions, Vsns},
{agent_type, master},
{agent_verbosity, trace},
+ {get_mechanism, snmp_agent_test_get},
{db_dir, AgentDbDir},
{audit_trail_log, [{type, read_write},
{dir, AgentLogDir},
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 6ced55f0cc..bb9b05b89f 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -6179,7 +6179,12 @@ start_agent(Node, Vsns, Conf0, _Opts) ->
{mib_server, [{verbosity, MSV}]},
{note_store, [{verbosity, NSV}]},
{stymbolic_store, [{verbosity, SSV}]},
- {net_if, [{verbosity, NIV}]},
+ {net_if, [{verbosity, NIV},
+ %% On some linux "they" add a 127.0.1.1 or somthing
+ %% similar, so if we don't specify bind_to
+ %% we don't know which address will be selected
+ %% (which will cause problems for some test cases).
+ {options, [{bind_to, true}]}]},
{multi_threaded, true}],
?line ok = set_agent_env(Node, Env),
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 9503060140..75cc7d2bfb 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -372,6 +372,366 @@
</section>
</section>
+<section><title>Ssh 4.6.9.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a client was connected to an server on an already open
+ socket, the callback <c>fun(PeerName,FingerPrint)</c> in
+ the <c>accept_callback</c> option passed the local name
+ in the argument PeerName instead of the remote name.</p>
+ <p>
+ Own Id: OTP-15763</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ When an SSH server receives the very first message on a
+ new TCP connection, and that message is not the expected
+ one, the 64 first bytes of the received message are now
+ dumped in the INFO REPORT that reports the Protocol
+ Error.</p>
+ <p>
+ This facilitates the debugging of who sends the bad
+ message or of detecting a possible port scanning.</p>
+ <p>
+ Own Id: OTP-15772</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The callback <c>ssh_channel:init/1</c> was missing in
+ OTP-21</p>
+ <p>
+ Own Id: OTP-15762</p>
+ </item>
+ <item>
+ <p>
+ If a client was connected to an server on an already open
+ socket, the callback <c>fun(PeerName,FingerPrint)</c> in
+ the <c>accept_callback</c> option passed the local name
+ in the argument PeerName instead of the remote name.</p>
+ <p>
+ Own Id: OTP-15763</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH sftp daemon now accepts an SSH_FXP_STAT message
+ encoded according to the wrong sftp version. Some clients
+ sends such messages.</p>
+ <p>
+ Own Id: OTP-15498 Aux Id: ERL-822, PR-2077 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed port leakage if a ssh:daemon call failed.</p>
+ <p>
+ Own Id: OTP-15397 Aux Id: ERL-801 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Incompatibility with newer OpenSSH fixed. Previously
+ versions 7.8 and later could cause Erlang SSH to exit.</p>
+ <p>
+ Own Id: OTP-15413</p>
+ </item>
+ <item>
+ <p>
+ The '<c>exec</c>' option for ssh daemons had wrong format
+ in the documentation.</p>
+ <p>
+ Own Id: OTP-15416</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added public key methods ssh-ed25519 and ssh-ed448.</p>
+ <p>
+ Requires OpenSSL 1.1.1 or higher as cryptolib under the
+ OTP application <c>crypto</c>.</p>
+ <p>
+ Own Id: OTP-15094 Aux Id: OTP-15419 </p>
+ </item>
+ <item>
+ <p>
+ The SSH property tests are now adapted to the PropEr
+ testing tool.</p>
+ <p>
+ Own Id: OTP-15312</p>
+ </item>
+ <item>
+ <p>
+ The term "user" was not documented in the SSH app. A new
+ chapter with terminology is added to the User's Manual
+ where the term "user" is defined.</p>
+ <p>
+ A reference manual page about the module <c>ssh_file</c>
+ is also added. This is the default callback module for
+ user's keys, host keys etc.</p>
+ <p>
+ Own Id: OTP-15314</p>
+ </item>
+ <item>
+ <p>
+ Host and user key checking is made more robust.</p>
+ <p>
+ Own Id: OTP-15424</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Extended the undocumented <c>ssh_dbg</c> debug module
+ with an api for a circular trace buffer. This makes it
+ easy to record the last low-level events before an error
+ is detected. It is intended for solving difficult errors.</p>
+ <p>
+ Own Id: OTP-15020</p>
+ </item>
+ <item>
+ <p>
+ The key exchange methods
+ <c>'[email protected]'</c>,
+ <c>'curve25519-sha256'</c> and <c>'curve448-sha512'</c>
+ are implemented. The last two are defined in
+ https://tools.ietf.org/html/draft-ietf-curdle-ssh-curves</p>
+ <p>
+ They all depends on that OpenSSL 1.1.1 or higher is used
+ as cryptolib.</p>
+ <p>
+ Own Id: OTP-15133 Aux Id: OTP-15240 </p>
+ </item>
+ <item>
+ <p>
+ The cipher '<c>[email protected]</c>' is now
+ supported if OpenSSL 1.1.1 or higher is used as
+ cryptolib.</p>
+ <p>
+ Own Id: OTP-15209 Aux Id: OTP-15164 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.7</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If the daemon port listener is restarted, it could
+ potentially fail with <c>eaddrinuse</c> if the timing is
+ unlucky. It will now retry and exponentially back off the
+ listener restart a few times before failing.</p>
+ <p>
+ Own Id: OTP-14955</p>
+ </item>
+ <item>
+ <p>
+ A channel callback module always got the module name as
+ reason in a call to terminate. Now it will get the proper
+ Reason, usually 'normal'.</p>
+ <p>
+ Own Id: OTP-15084</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The option <c>exec</c> has new option values defined to
+ make it much more easy to implement an own <c>exec</c>
+ server.</p>
+ <p>
+ An option called <c>exec</c> for daemons implementing the
+ handling of 'exec' requests has existed a long time but
+ has been undocumented. The old undocumented value - as
+ well as its behavior - is kept for compatibility EXCEPT
+ that error messages are changed and are sent as
+ "stderror" text.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14851</p>
+ </item>
+ <item>
+ <p>
+ Updated ssh_connection:shell/2 documentation.</p>
+ <p>
+ Own Id: OTP-14880</p>
+ </item>
+ <item>
+ <p>
+ The experimental <c>ssh_dbg</c> module is completely
+ re-written. Its purpose is to make tracing and debugging
+ easier on deployed systems.</p>
+ <p>
+ Own Id: OTP-14896</p>
+ </item>
+ <item>
+ <p>
+ The SSH supervisor structure has been slightly changed.
+ This makes stopping the ssh application considerably
+ faster if there are open connections. This is important
+ in for example restarts.</p>
+ <p>
+ Own Id: OTP-14988</p>
+ </item>
+ <item>
+ <p>
+ The type specifications in SSH are completly reworked and
+ the following types are renamed:</p>
+ <p>
+ <c>ssh:ssh_connection_ref()</c> is changed to
+ <c>ssh:connection_ref()</c>, </p>
+ <p>
+ <c>ssh:ssh_daemon_ref()</c> is changed to
+ <c>ssh:daemon_ref()</c>,</p>
+ <p>
+ <c>ssh:ssh_channel_id()</c> is changed to
+ <c>ssh:channel_id()</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15002 Aux Id: OTP-15030 </p>
+ </item>
+ <item>
+ <p>
+ The internal timer handling in SSH is now based on the
+ gen_statem timers.</p>
+ <p>
+ Own Id: OTP-15019</p>
+ </item>
+ <item>
+ <p>
+ Removed the undocumented and unused modules
+ <c>ssh_client_key.erl</c> and <c>ssh_server_key.erl</c>.</p>
+ <p>
+ Own Id: OTP-15028</p>
+ </item>
+ <item>
+ <p>
+ The Reference Manual pages are partly updated.</p>
+ <p>
+ The ssh page is now generated from specs and types, is
+ restructured and is partly rephrased.</p>
+ <p>
+ The ssh_channel, ssh_connection, ssh_client_key_api,
+ ssh_server_key_api and ssh_sftp pages are updated with
+ links, correct type names and some minor changes.</p>
+ <p>
+ Own Id: OTP-15030 Aux Id: OTP-15002 </p>
+ </item>
+ <item>
+ <p>
+ The behaviors <c>ssh_channel</c> and
+ <c>ssh_daemon_channel</c> are renamed to
+ <c>ssh_client_channel</c> and <c>ssh_server_channel</c>
+ respectively.</p>
+ <p>
+ The old modules are kept for compatibility but should
+ preferably be replaced when updating callback modules
+ referring them.</p>
+ <p>
+ Own Id: OTP-15041</p>
+ </item>
+ <item>
+ <p>
+ New test suite for channels.</p>
+ <p>
+ Own Id: OTP-15051</p>
+ </item>
+ <item>
+ <p>
+ The <c>rekey_limit</c> option could now set the max time
+ as well as the previously max data amount.</p>
+ <p>
+ Own Id: OTP-15069 Aux Id: ERL-617 </p>
+ </item>
+ <item>
+ <p>
+ Changed process exit supervision from links to monitors.</p>
+ <p>
+ Own Id: OTP-15082</p>
+ </item>
+ <item>
+ <p>
+ Better handling of misbehaving channel callback modules.</p>
+ <p>
+ Own Id: OTP-15083</p>
+ </item>
+ <item>
+ <p>
+ A new moduli file is generated. This file is used for the
+ recommended <c>diffie-hellman-group-exchange-sha256</c>
+ key exchange algorithm in SSH.</p>
+ <p>
+ Own Id: OTP-15113</p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>Ssh 4.6.9.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 28f8346a19..2191ebe2df 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -128,6 +128,21 @@
</section>
+<section><title>Tools 2.11.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Minor fixes for <c>make clean</c>.</p>
+ <p>
+ Own Id: OTP-15657</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.11.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -1905,4 +1920,3 @@
</section>
</section>
</chapter>
-
diff --git a/make/otp_patch_solve_forward_merge_version b/make/otp_patch_solve_forward_merge_version
index 7f8f011eb7..ec635144f6 100644
--- a/make/otp_patch_solve_forward_merge_version
+++ b/make/otp_patch_solve_forward_merge_version
@@ -1 +1 @@
-7
+9
diff --git a/make/otp_version_tickets b/make/otp_version_tickets
index bf32816fb2..b8220e1a87 100644
--- a/make/otp_version_tickets
+++ b/make/otp_version_tickets
@@ -1,8 +1 @@
-OTP-14746
-OTP-15295
-OTP-15717
-OTP-15758
-OTP-15781
-OTP-15785
-OTP-15793
-OTP-15802
+DEVELOPMENT
diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge
index ff967634a2..bf32816fb2 100644
--- a/make/otp_version_tickets_in_merge
+++ b/make/otp_version_tickets_in_merge
@@ -1,3 +1,8 @@
-OTP-15551
-OTP-15651
-OTP-15652
+OTP-14746
+OTP-15295
+OTP-15717
+OTP-15758
+OTP-15781
+OTP-15785
+OTP-15793
+OTP-15802
diff --git a/otp_versions.table b/otp_versions.table
index 8f885e13fb..28063bb4dd 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -30,6 +30,7 @@ OTP-21.0.3 : erts-10.0.3 # asn1-5.0.6 common_test-1.16 compiler-7.2.2 crypto-4.3
OTP-21.0.2 : compiler-7.2.2 erts-10.0.2 public_key-1.6.1 stdlib-3.5.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 :
OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 :
OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 :
+OTP-20.3.8.21 : common_test-1.15.4.2 erl_interface-3.10.2.2 erts-9.3.3.10 snmp-5.2.11.1 ssh-4.6.9.4 tools-2.11.2.1 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 wx-1.8.3 xmerl-1.3.16.1 :
OTP-20.3.8.20 : common_test-1.15.4.1 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.9 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 :
OTP-20.3.8.19 : diameter-2.1.4.1 erts-9.3.3.9 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 :
OTP-20.3.8.18 : erts-9.3.3.8 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 :