diff options
167 files changed, 5333 insertions, 1387 deletions
diff --git a/OTP_VERSION b/OTP_VERSION index 6ea9a3bd47..1870facefc 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -19.0 +19.0.1 diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7501ccd9ce..4b5b01d2b1 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,6 +32,760 @@ <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 8.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A memory allocation bug in <c>group_leader/2</c> could + cause an emulator crash when garbage collecting a process + that had been assigned a remote group leader. This bug + was introduced in ERTS version 8.0.</p> + <p> + Own Id: OTP-13716</p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 8.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The handling of <c>on_load</c> functions has been + improved. The major improvement is that if a code upgrade + fails because the <c>on_load</c> function fails, the + previous version of the module will now be retained.</p> + <p> + Own Id: OTP-12593</p> + </item> + <item> + <p><c>is_builtin(erlang, apply, 3)</c> will now return + <c>true</c>.</p> + <p> + Own Id: OTP-13034</p> + </item> + <item> + <p> + Fix <c>enif_get_list_length</c> to return false if list + is improper or have length larger than <c>UINT_MAX</c> + (did return true and an incorrect length value).</p> + <p> + Own Id: OTP-13288 Aux Id: PR913 </p> + </item> + <item> + <p> + Cleanup hipe signal handling code for x86 and make it + more portable.</p> + <p> + Own Id: OTP-13341 Aux Id: PR951 </p> + </item> + <item> + <p> + Make file:datasync use fsync instead of fdatasync on Mac + OSX.</p> + <p> + Own Id: OTP-13411</p> + </item> + <item> + <p> + Make sure to create a crash dump when running out of + memory. This was accidentally removed in the erts-7.3 + release.</p> + <p> + Own Id: OTP-13419</p> + </item> + <item> + <p> + A bug has been fixed where if erlang was started +B on a + unix platform it would be killed by a SIGUSR2 signal when + creating a crash dump.</p> + <p> + Own Id: OTP-13425</p> + </item> + <item> + <p> + Fix race between <c>process_flag(trap_exit,true)</c> and + a received exit signal.</p> + <p> + A process could terminate due to exit signal even though + <c>process_flag(trap_exit,true)</c> had returned. A very + specific timing between call to <c>process_flag/2</c> and + exit signal from another scheduler was required for this + to happen.</p> + <p> + Own Id: OTP-13452</p> + </item> + <item> + <p>Don't search for non-existing Map keys twice</p> + <p>For <c>maps:get/2,3</c> and <c>maps:find/2</c>, + searching for an immediate key, e.g. an atom, in a small + map, the search was performed twice if the key did not + exist.</p> + <p> + Own Id: OTP-13459</p> + </item> + <item> + <p> + When an abnormally large distribution message is about to + be sent, the VM has been changed to create a crash dump + instead of a core dump.</p> + <p> + Own Id: OTP-13474</p> + </item> + <item> + <p> + Fix <c>erlang:process_info/2</c> type specification</p> + <p> + Own Id: OTP-13485 Aux Id: ERL-123 </p> + </item> + <item> + <p> + Fix bug in <c>open_port/2</c> with option <c>{args, + List}</c>. A vm crash could be caused by an improper + <c>List</c>.</p> + <p> + Own Id: OTP-13489 Aux Id: ERL-127 </p> + </item> + <item> + <p> + Fixed a race-condition bug where the emulator could crash + when <c>erlang:system_profile/1,2</c> was enabled and a + process had to be re-scheduled during termination.</p> + <p> + Own Id: OTP-13494 Aux Id: ERL-126 </p> + </item> + <item> + <p> + Fixed bugs where the reduction counter was not handled + correct.</p> + <p> + Own Id: OTP-13512</p> + </item> + <item> + <p> + Fixed typo in description of the <c>EPMD_DUMP_REQ</c> + response.</p> + <p> + Own Id: OTP-13517</p> + </item> + <item> + <p> + Fixed a bug where a process flagged as sensitive would + sometimes record its save_calls when it shouldn't.</p> + <p> + Own Id: OTP-13540</p> + </item> + <item> + <p> + Update configure scripts to not use hard-coded path for + /bin/pwd and /bin/rm.</p> + <p> + Own Id: OTP-13562</p> + </item> + <item> + <p> + When passing a larger binary than the outputv callback of + a linked-in driver can handle in one io vector slot, the + binary is now split into multiple slots in the io vector. + This change only effects system where the max size of an + io vector slot is smaller then the word size of the + system (e.g. Windows).</p> + <p> + This change means that it is now possible on Windows to + send binaries that are larger than 4GB to port_command, + which is what is used for file:write, gen_tcp:send etc.</p> + <p> + Own Id: OTP-13628</p> + </item> + <item> + <p> + Workaround of Maps output in crashdumps. Currently the + atom 'undefined' is generated instead of Map data if a + Map type is encountered during crash.</p> + <p> + Own Id: OTP-13657</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The tracing support has been extended to allow a <seealso + marker="erl_tracer">tracer module</seealso> to be the + trace event handler instead of a process or port. The + <seealso marker="erl_tracer">tracer module</seealso> + makes it possible for trace tools to filter or manipulate + trace event data without the trace event first having to + be copied from the traced process or port.</p> + <p> + With the introduction of this feature, + <c>erlang:trace(all|existing, _, _)</c> now also returns + the tracer process as part of the number of processes on + which tracing is enabled. The is incompatible with the + previous releases.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-10267</p> + </item> + <item> + <p> + Introduce LTTng tracing of Erlang Runtime System</p> + <p> + For LTTng to be enabled OTP needs to be built with + configure option <c>--with-dynamic-trace=lttng</c>.</p> + <p> + This feature introduces tracepoints for schedulers, + drivers, memory carriers, memory and async thread pool.</p> + <p> For a list of all tracepoints, see <seealso + marker="runtime_tools:LTTng">Runtime Tools User's + Guide</seealso> .</p> + <p> + Own Id: OTP-10282</p> + </item> + <item> + <p> + Make it possible to monitor/demonitor ports using the + <seealso + marker="erlang#monitor/2">erlang:monitor/2</seealso> API. + The process and port information functions have also been + updated to include information about monitors from + processes to ports.</p> + <p> + Own Id: OTP-11384</p> + </item> + <item> + <p> + Add microstate accounting</p> + <p> + Microstate accounting is a way to track which state the + different threads within ERTS are in. The main usage area + is to pin point performance bottlenecks by checking which + states the threads are in and then from there figuring + out why and where to optimize.</p> + <p> + Since checking whether microstate accounting is on or off + is relatively expensive only a few of the states are + enabled by default and more states can be enabled through + configure.</p> + <p> + There is a convenience module called msacc that has been + added to runtime_tools that can assist in gathering and + interpreting the data from Microstate accounting.</p> + <p> + For more information see <seealso + marker="erts:erlang#statistics_microstate_accounting">erlang:statistics(microstate_accounting, + _)</seealso> and the <seealso + marker="runtime_tools:msacc">msacc</seealso> module in + runtime_tools.</p> + <p> + Own Id: OTP-12345</p> + </item> + <item> + <p> + The port of Erlang/OTP to the real-time operating system + OSE has been removed.</p> + <p> + Own Id: OTP-12573</p> + </item> + <item> + <p> + Sharing preserved copy for messages and exit signals</p> + <p> + Enable sharing preserved copy with configure option + <c>--enable-sharing-preserving</c>. This will preserve + sharing, within the process, when communication with + other processes in the Erlang node. There is a trade-off, + the copy is more costly but this cost can be reclaimed if + there is a lot of sharing in the message. In addition + literals will not be copied in a send except during a + purge phase of the module where the literals are located. + This feature is considered experimental in 19.0.</p> + <p> + Own Id: OTP-12590 Aux Id: OTP-10251 </p> + </item> + <item> + <p> + Halfword BEAM has been removed.</p> + <p> + Own Id: OTP-12883</p> + </item> + <item> + <p> + Added <seealso + marker="kernel:os#perf_counter/1">os:perf_counter/1</seealso>.</p> + <p> + The perf_counter is a very very cheap and high resolution + timer that can be used to timestamp system events. It + does not have monoticity guarantees, but should on most + OS's expose a monotonous time.</p> + <p> + Own Id: OTP-12908</p> + </item> + <item> + <p> + Support for a fragmented young heap generation. That is, + the young heap generation can consist of multiple non + continuous memory areas. The main reason for this change + is to avoid extra copying of messages that could not be + allocated directly on the receivers heap.</p> + <p> + Own Id: OTP-13047</p> + </item> + <item> + <p> + Erlang linked-in driver can now force the call to + open_port to block until a call to erl_drv_init_ack is + made inside the driver. This is useful when you want to + do some asynchronous initialization, for example getting + configuration from a pipe, and you want the initial + open_port call to fail if the configuration is incomplete + or wrong. See the erl_driver documentation for more + details on the API.</p> + <p> + Own Id: OTP-13086</p> + </item> + <item> + <p> + Erlang linked-in drivers can now set their own pids as + seen in <c>erlang:port_info/1</c> by using the + <c>erl_drv_set_pid</c> function. For more details see the + erl_driver documentation.</p> + <p> + Own Id: OTP-13087</p> + </item> + <item> + <p> + The functionality behind <c>erlang:open_port/2</c> when + called with spawn or spawn_executable has been redone so + that the forking of the new program is done in a separate + process called erl_child_setup. This allows for a much + more robust implementation that uses less memory and does + not block the entire emulator if the program to be + started is on an un-accessible NFS. Benchmarks have shown + this approach to be about 3-5 times as fast as the old + approach where the fork/vfork was done by erts. This is a + pure stability and performance fix, however some error + messages may have changed, which is why it is marked as a + backwards incompatible change.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13088</p> + </item> + <item> + <p>Improved yielding strategy in the implementation of + the following native functions:</p> <list> + <item><c>erlang:binary_to_list/1</c></item> + <item><c>erlang:binary_to_list/3</c></item> + <item><c>erlang:bitstring_to_list/1</c></item> + <item><c>erlang:list_to_binary/1</c></item> + <item><c>erlang:iolist_to_binary/1</c></item> + <item><c>erlang:list_to_bitstring/1</c></item> + <item><c>binary:list_to_bin/1</c></item> </list> <p>This + in order to improve performance of these functions.</p> + <p> + Own Id: OTP-13096</p> + </item> + <item> + <p> + All garbage collections of processes now bump reductions. + Also the amount of reductions bumped when garbage + collecting has been adjusted. It now better corresponds + to the amount of work performed. This in order to improve + the real time characteristics of the system.</p> + <p> + Own Id: OTP-13097</p> + </item> + <item> + <p>New functions that can load multiple modules at once + have been added to the '<c>code</c>' module. The + functions are <c>code:atomic_load/1</c>, + <c>code:prepare_loading/1</c>, + <c>code:finish_loading/1</c>, and + <c>code:ensure_modules_loaded/1</c>.</p> + <p> + Own Id: OTP-13111</p> + </item> + <item> + <p>The <c>-boot_var</c> option for <c>erl</c> now only + supports a single key and single value (as documented). + The option used to allow multiple key/value pairs, but + that behavior was undocumented.</p> + <p>The function <c>erl_prim_loader:start/3</c> has been + removed. Its documentation has also been removed.</p> + <p>The undocumented and unsupported function + <c>erl_prim_loader:get_files/2</c> has been removed.</p> + <p> + Own Id: OTP-13112</p> + </item> + <item> + <p> + Low level BIF <c>erlang:purge_module/1</c> is made more + robust against incorrect use. Lingering processes that + still refer the old code are now killed before the module + is purged to prevent fatal VM behavior.</p> + <p> + Own Id: OTP-13122</p> + </item> + <item> + <p>Improved dirty scheduler implementation. For more + information see the <seealso + marker="erl_nif#dirty_nifs">NIF + documentation</seealso>.</p> <note><list> <item><p>The + dirty scheduler support is still + <em>experimental</em>.</p></item> <item><p>The support + for determining whether dirty NIF support exist or not at + compile time using the C preprocessor macro + <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> has been + removed.</p></item> <item><p>The + <c>enif_is_on_dirty_scheduler()</c> function has been + removed. Use <seealso + marker="erl_nif#enif_thread_type"><c>enif_thread_type()</c></seealso> + instead.</p></item> </list></note> + <p> + Own Id: OTP-13123</p> + </item> + <item> + <p> + Various optimizations done to process dictionary access.</p> + <p> + Own Id: OTP-13167</p> + </item> + <item> + <p> + Added max_heap_size process flag. max_heap_size allows + the user to limit the maximum heap used by a process. See + <seealso + marker="erlang#process_flag/2">erlang:process_flag</seealso> + for more details.</p> + <p> + Own Id: OTP-13174</p> + </item> + <item> + <p> + Allow dynamic drivers and NIF libraries to be built with + gcc option <c>-fvisibility=hidden</c> for faster loading + and more optimized code.</p> + <p> + Own Id: OTP-13227</p> + </item> + <item> + <p> + Add <c>erlang:process_info(Pid, + garbage_collection_info)</c> which returns extended + garbage_collection information. For more details see the + documentation.</p> + <p> + Own Id: OTP-13265</p> + </item> + <item> + <p> + The functions <c>erlang:list_to_integer/1</c> and + <c>string:to_integer/1</c> have been optimized for large + inputs.</p> + <p> + Own Id: OTP-13293</p> + </item> + <item> + <p> + Improved memory allocation strategy for hipe native code + on x86_64 (amd64) architectures by reserving enough low + virtual address space needed for the HiPE/AMD64 small + code model. The default virtual address area for hipe + code is set to 512Mb, but can be changed with emulator + flag <c>+MXscs</c>.</p> + <p> + Own Id: OTP-13359</p> + </item> + <item> + <p> + Introduction of configurable management of data referred + to by the message queue of a process. Each process can be + configured individually.</p> + <p> + It is now possible to configure the message queue of a + process, so that all data referred by it will be kept + outside of the heap, and by this prevent this data from + being part of garbage collections.</p> + <p> + For more information see the documentation of <seealso + marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data, + MQD)</c></seealso>.</p> + <p> + Own Id: OTP-13366 Aux Id: OTP-13047 </p> + </item> + <item> + <p> + Processes now yield when scanning large message queues + and not finding a matching message. This in order to + improve real time characteristics.</p> + <p> + Own Id: OTP-13401</p> + </item> + <item> + <p> + Optimized an erts internal function that is used to + traverse erlang terms. The internal function was mainly + used by term_to_binary and comparison of terms. + Benchmarks have shown up to a 10% performance increase in + those functions after the optimization.</p> + <p> + Own Id: OTP-13440</p> + </item> + <item> + <p> + Add the following NIF API functions:</p> + <p> + <list> <item><seealso + marker="erl_nif#enif_cpu_time"><c>enif_cpu_time</c></seealso></item> + <item><seealso + marker="erl_nif#enif_now_time"><c>enif_now_time</c></seealso></item> + <item><seealso + marker="erl_nif#enif_make_unique_integer"><c>enif_make_unique_integer</c></seealso></item> + <item><seealso + marker="erl_nif#enif_is_process_alive"><c>enif_is_process_alive</c></seealso></item> + <item><seealso + marker="erl_nif#enif_is_port_alive"><c>enif_is_port_alive</c></seealso></item> + <item><seealso + marker="erl_nif#enif_term_to_binary"><c>enif_term_to_binary</c></seealso></item> + <item><seealso + marker="erl_nif#enif_binary_to_term"><c>enif_binary_to_term</c></seealso></item> + <item><seealso + marker="erl_nif#enif_port_command"><c>enif_port_command</c></seealso></item> + </list></p> + <p> + for details of what each function does, see the erl_nif + documentation.</p> + <p> + Own Id: OTP-13442</p> + </item> + <item> + <p> + Optimize <c>'++'</c> operator and <c>lists:append/2</c> + by using a single pass to build a new list while checking + for properness.</p> + <p> + Own Id: OTP-13487</p> + </item> + <item> + <p> + Handle terms (pids,ports and refs) from nodes with a + 'creation' value larger than 3. This is a preparation of + the distribution protocol to allow OTP 19 nodes to + correctly communicate with future nodes (20 or higher). + The 'creation' value differentiates different + incarnations of the same node (name).</p> + <p> + Own Id: OTP-13488</p> + </item> + <item> + <p> + Don't send unasked for systemd notifications in epmd</p> + <p> + Own Id: OTP-13493 Aux Id: PR-999 </p> + </item> + <item> + <p> + The enif_send API has been extended to allow NULL to be + used as the message environment. When used this way, a + message environment is implicitly created and the given + term is copied into that environment before sending. This + can be an optimization if many small messages are being + sent by the nif.</p> + <p> + Own Id: OTP-13495</p> + </item> + <item> + <p> + The tracing support has been extended to allow tracing on + ports. Ports can be traced on using the 'ports', 'send' + and 'receive' trace flags.</p> + <p> + The first argument of <seealso + marker="erts:erlang#trace/3">erlang:trace/3</seealso> has + been extended so that <c>'all'</c>, <c>'existing'</c> and + <c>'new'</c> now include both processes and ports. New + <c>Tracee</c> variants, <c>'all_processes'</c>, + <c>'all_ports'</c>, <c>'existing_processes'</c> etc have + been added to specify only processes or ports.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13496</p> + </item> + <item> + <p> + When the <c>'procs'</c> trace flag is enabled, a + <c>'spawned'</c> trace event is now also generated by a + newly created process. The previous event <c>'spawn'</c> + remains, but as it is generated by the process that did + the spawn, it is not guaranteed that it is ordered with + other trace events from the newly spawned process. So + when tracking the lifetime of a process this new event + should be used as the creation event.</p> + <p> + This new trace event is marked as an incompatibility + because tools that expect certain trace events when + enabling 'procs' will have to updated.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13497</p> + </item> + <item> + <p> + Add the <seealso + marker="erts:erlang#match_spec_test/3">erlang:match_spec_test/3</seealso> + function. The functions allows the testing of match + specifications for both tracing and ets tables. It can be + used to test that a match specification does the expected + filtering on specific data. It also returns more verbose + error reasons for incorrectly constructed match + specifications.</p> + <p> + Own Id: OTP-13501</p> + </item> + <item> + <p> + The erts internal tracing support has been changed to + have much less overhead and be more scalable.</p> + <p> + This rewrite does not break any backwards + incompatibilities, but it does change the ordering of + some trace messages when compared to previous releases. + It should be noted that this only applies to trace + messages sent to processes or ports, it does not apply to + the new tracer module. However in future releases they + may also be effected by this.</p> + <p> + Trace messages are only guaranteed to be ordered from one + traced process or port. In previous releases this was not + visible as a <c>'send'</c> trace message would always + arrive before the corresponding <c>'receive'</c> trace + message that is no longer always the case. This also + means that timestamped trace messages may seem to arrive + out of order as the timestamp is taken when the event is + triggered and not when it is put in the queue of the + tracer.</p> + <p> + Own Id: OTP-13503</p> + </item> + <item> + <p> + Add possibility to filter <c>send</c> and <c>receive</c> + trace with match specifications.</p> + <p> + Own Id: OTP-13507</p> + </item> + <item> + <p> + Add <c>maps:update_with/3,4</c> and <c>maps:take/2</c></p> + <p> + Own Id: OTP-13522 Aux Id: PR-1025 </p> + </item> + <item> + <p> + Introduce LTTng tracing via Erlang tracing.</p> + <p> + For LTTng to be enabled OTP needs to be built with + configure option <c>--with-dynamic-trace=lttng</c>.</p> + <p>The dynamic trace module <c>dyntrace</c> is now + capable to be used as a LTTng sink for Erlang tracing. + For a list of all tracepoints, see <seealso + marker="runtime_tools:LTTng">Runtime Tools User's + Guide</seealso> .</p> + <p>This feature also introduces an incompatible change in + trace tags. The trace tags <c>gc_start</c> and + <c>gc_end</c> has been split into <c>gc_minor_start</c>, + <c>gc_minor_end</c> and <c>gc_major_start</c>, + <c>gc_major_end</c>.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13532</p> + </item> + <item> + <p> + Print heap pointers for garbing processes during + crashdump</p> + <p> + Own Id: OTP-13541 Aux Id: PR-1026 </p> + </item> + <item> + <p> + Changed and improved low level memory statistics returned + by <c>erlang:system_info/1</c>. The info for + <c>erts_mmap</c> has been moved from <c>mseg_alloc</c> to + its own section returned by <c>{allocator, + erts_mmap}</c>.</p> + <p> + Own Id: OTP-13560</p> + </item> + <item> + <p> + Add enif_snprintf to the NIF API</p> + <p> + The function <c>enif_snprintf</c> is similar to + <c>snprintf</c> call but can handle formatting of Erlang + terms via <c>%T</c> format specifier.</p> + <p> + Own Id: OTP-13580</p> + </item> + <item> + <p>The warning in the documentation for + <c>erlang:raise/3</c> has been removed. It is now + officially perfectly fine to use raise/3 in production + code.</p> + <p> + Own Id: OTP-13599</p> + </item> + <item> + <p> + Fix bugs caused by the VM sometimes truncating object + sizes or offsets to 32 bits on 64-bit hosts. These bugs + were mainly found when working with large unicode strings + and nifs environments.</p> + <p> + Own Id: OTP-13606</p> + </item> + <item> + <p> + Add <c>-start_epmd</c> command line option, this lets you + disable automatic starting of epmd when starting a + distributed node.</p> + <p> + Add <c>-epmd_module</c> command line option, this lets + you specify a module to register and look-up node names + in. The default module is <c>erl_epmd</c>.</p> + <p> + Own Id: OTP-13627</p> + </item> + <item> + <p> + <c>erlang:halt</c> now truncates strings longer than 200 + characters instead of failing with <c>badarg</c>.</p> + <p> + Own Id: OTP-13630</p> + </item> + <item> + <p> + Fix possible race in poller wake up on windows</p> + <p> + Own Id: OTP-13634</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 7.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index fc14061a44..d9048065c8 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4305,8 +4305,9 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2) else { locks &= ~ERTS_PROC_LOCK_STATUS; erts_smp_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS); - if (erts_smp_atomic32_read_nob(&new_member->state) - & !(ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + if (new_member == BIF_P + || !(erts_smp_atomic32_read_nob(&new_member->state) + & (ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS))) { new_member->group_leader = STORE_NC_IN_PROC(new_member, BIF_ARG_1); } @@ -4326,6 +4327,7 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2) BIF_ARG_1); bp->next = new_member->mbuf; new_member->mbuf = bp; + new_member->mbuf_sz += bp->used_size; } } } diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4 index 2c0fbbee2d..409fd0ef89 100644 --- a/erts/emulator/hipe/hipe_amd64_asm.m4 +++ b/erts/emulator/hipe/hipe_amd64_asm.m4 @@ -121,6 +121,22 @@ define(NSP,%rsp)dnl /* + * Debugging macros + * + * Keeps track of whether context has been saved in the debug build, allowing us + * to detect when the garbage collector is called when it shouldn't. + */ +`#ifdef DEBUG +# define SET_GC_UNSAFE \ + movq $1, P_GCUNSAFE(P) +# define SET_GC_SAFE \ + movq $0, P_GCUNSAFE(P) +#else +# define SET_GC_UNSAFE +# define SET_GC_SAFE +#endif' + +/* * Context switching macros. */ `#define SWITCH_C_TO_ERLANG_QUICK \ @@ -133,12 +149,14 @@ define(NSP,%rsp)dnl `#define SAVE_CACHED_STATE \ SAVE_HP; \ - SAVE_FCALLS' + SAVE_FCALLS; \ + SET_GC_SAFE' `#define RESTORE_CACHED_STATE \ RESTORE_HP; \ RESTORE_HEAP_LIMIT; \ - RESTORE_FCALLS' + RESTORE_FCALLS; \ + SET_GC_UNSAFE' `#define SWITCH_C_TO_ERLANG \ RESTORE_CACHED_STATE; \ diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4 index 9cf3bf74fd..21739726bb 100644 --- a/erts/emulator/hipe/hipe_amd64_bifs.m4 +++ b/erts/emulator/hipe/hipe_amd64_bifs.m4 @@ -600,10 +600,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu) define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)') /* - * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2). + * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3). */ define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)') define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)') +define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)') /* * Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1. diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index b37ed3c68a..f3404888d5 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -94,6 +94,7 @@ ASYM(nbif_return): .nosave_exit: /* switch to C stack */ SWITCH_ERLANG_TO_C_QUICK + SET_GC_SAFE /* restore C callee-save registers, drop frame, return */ movq (%rsp), %rbp # kills P movq 8(%rsp), %rbx @@ -398,6 +399,7 @@ nbif_4_simple_exception: movl %eax, P_NARITY(P) # Note: narity is a 32-bit field /* find and prepare to invoke the handler */ SWITCH_ERLANG_TO_C_QUICK # The cached state is clean and need not be saved. + SET_GC_SAFE movq P, %rdi call CSYM(hipe_handle_exception) # Note: hipe_handle_exception() conses SWITCH_C_TO_ERLANG # %rsp updated by hipe_find_handler() diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4 index ae9ec752bb..68a6faa70b 100644 --- a/erts/emulator/hipe/hipe_arm_asm.m4 +++ b/erts/emulator/hipe/hipe_arm_asm.m4 @@ -48,6 +48,24 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive `#define TEMP_LR r8' /* + * Debugging macros + * + * Keeps track of whether context has been saved in the debug build, allowing us + * to detect when the garbage collector is called when it shouldn't. + */ +`#ifdef DEBUG +# define SET_GC_UNSAFE(SCRATCH) \ + mov SCRATCH, #1; \ + str SCRATCH, [P, #P_GCUNSAFE] +# define SET_GC_SAFE(SCRATCH) \ + mov SCRATCH, #0; \ + str SCRATCH, [P, #P_GCUNSAFE] +#else +# define SET_GC_UNSAFE(SCRATCH) +# define SET_GC_SAFE(SCRATCH) +#endif' + +/* * Context switching macros. * * RESTORE_CONTEXT and RESTORE_CONTEXT_QUICK do not affect @@ -59,12 +77,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive `#define RESTORE_CONTEXT_QUICK \ mov lr, TEMP_LR' -`#define SAVE_CACHED_STATE \ - str HP, [P, #P_HP]; \ - str NSP, [P, #P_NSP]' +`#define SAVE_CACHED_STATE \ + str HP, [P, #P_HP]; \ + str NSP, [P, #P_NSP]; \ + SET_GC_SAFE(HP)' -`#define RESTORE_CACHED_STATE \ - ldr HP, [P, #P_HP]; \ +`#define RESTORE_CACHED_STATE \ + SET_GC_UNSAFE(HP); \ + ldr HP, [P, #P_HP]; \ ldr NSP, [P, #P_NSP]' `#define SAVE_CONTEXT_BIF \ @@ -75,12 +95,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive ldr HP, [P, #P_HP]' `#define SAVE_CONTEXT_GC \ + SET_GC_SAFE(TEMP_LR); \ mov TEMP_LR, lr; \ str lr, [P, #P_NRA]; \ str NSP, [P, #P_NSP]; \ str HP, [P, #P_HP]' `#define RESTORE_CONTEXT_GC \ + SET_GC_UNSAFE(HP); \ ldr HP, [P, #P_HP]' /* diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index d9c9952dbf..d7a2fec04a 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -198,8 +198,9 @@ $1: * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-2 parameters and + * Generate native interface for a BIF with 0-3 parameters and * standard failure mode. * The BIF may do a GC. */ @@ -279,6 +280,36 @@ $1: .type $1, %function #endif') +define(gc_bif_interface_3, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov r0, P + NBIF_ARG(r1,3,0) + NBIF_ARG(r2,3,1) + NBIF_ARG(r3,3,2) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_GC + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + str r2, [r0, #P_ARG1] + str r3, [r0, #P_ARG2] + add r1, r0, #P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF(3) + + /* Restore registers. Check for exception. */ + cmp r0, #THE_NON_VALUE + RESTORE_CONTEXT_GC + beq nbif_3_simple_exception + NBIF_RET(3) + .size $1, .-$1 + .type $1, %function +#endif') + /* * gc_nofail_primop_interface_1(nbif_name, cbif_name) * diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index 49ffa8b1d8..5b7f8ad52d 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -342,6 +342,7 @@ nbif_4_gc_after_bif: str r1, [P, #P_NARITY] str TEMP_LR, [P, #P_NRA] str NSP, [P, #P_NSP] + SET_GC_SAFE(TEMP_LR) mov TEMP_LR, lr mov r3, #0 /* Pass 0 in arity */ mov r2, #0 /* Pass NULL in regs */ @@ -349,6 +350,7 @@ nbif_4_gc_after_bif: mov r0, P bl erts_gc_after_bif_call mov lr, TEMP_LR + SET_GC_UNSAFE(TEMP_LR) ldr TEMP_LR, [P, #P_NRA] mov r1, #0 str r1, [P, #P_NARITY] @@ -404,6 +406,7 @@ nbif_4_simple_exception: str NSP, [P, #P_NSP] str TEMP_LR, [P, #P_NRA] str r1, [P, #P_NARITY] + SET_GC_SAFE(r0) /* find and prepare to invoke the handler */ mov r0, P bl hipe_handle_exception /* Note: hipe_handle_exception() conses */ @@ -423,6 +426,7 @@ nbif_4_simple_exception: str NSP, [P, #P_NSP] str r1, [P, #P_NARITY] str TEMP_LR, [P, #P_NRA] + SET_GC_SAFE(NSP) b .nosave_exit /* diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 29095a5389..dcf3447af9 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -96,6 +96,7 @@ * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * * A BIF which may do a GC or walk the native stack. * May read NSP, NSP_LIMIT, NRA, HP, HP_LIMIT, and FCALLS. @@ -263,32 +264,34 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc) ',)dnl /* - * Standard BIFs. - * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index) + * BIFs that disable GC while trapping are called via a wrapper + * to reserve stack space for the "trap frame". + * They occasionally need to call the garbage collector in order to make room + * for the trap frame on the BEAM stack. */ +gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1) +gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2) +gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1) +gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2) +gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1) +gc_bif_interface_3(nbif_binary_to_list_3, hipe_wrapper_binary_to_list_3) +gc_bif_interface_1(nbif_bitstring_to_list_1, hipe_wrapper_bitstring_to_list_1) +gc_bif_interface_1(nbif_list_to_binary_1, hipe_wrapper_list_to_binary_1) +gc_bif_interface_1(nbif_iolist_to_binary_1, hipe_wrapper_iolist_to_binary_1) +gc_bif_interface_1(nbif_binary_list_to_bin_1, hipe_wrapper_binary_list_to_bin_1) +gc_bif_interface_1(nbif_list_to_bitstring_1, hipe_wrapper_list_to_bitstring_1) +gc_bif_interface_2(nbif_send_2, hipe_wrapper_send_2) +gc_bif_interface_3(nbif_send_3, hipe_wrapper_send_3) +gc_bif_interface_2(nbif_ebif_bang_2, hipe_wrapper_ebif_bang_2) +gc_bif_interface_2(nbif_maps_merge_2, hipe_wrapper_maps_merge_2) -/* BIFs that disable GC while trapping are called via a wrapper - * to reserve stack space for the "trap frame". + +/* + * Standard BIFs. + * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index) */ -define(CFUN,`ifelse( -$1, term_to_binary_1, hipe_wrapper_$1, -$1, term_to_binary_2, hipe_wrapper_$1, -$1, binary_to_term_1, hipe_wrapper_$1, -$1, binary_to_term_2, hipe_wrapper_$1, -$1, binary_to_list_1, hipe_wrapper_$1, -$1, binary_to_list_3, hipe_wrapper_$1, -$1, bitstring_to_list_1, hipe_wrapper_$1, -$1, list_to_binary_1, hipe_wrapper_$1, -$1, iolist_to_binary_1, hipe_wrapper_$1, -$1, binary_list_to_bin_1, hipe_wrapper_$1, -$1, list_to_bitstring_1, hipe_wrapper_$1, -$1, send_2, hipe_wrapper_$1, -$1, send_3, hipe_wrapper_$1, -$1, ebif_bang_2, hipe_wrapper_$1, -$1, maps_merge_2, hipe_wrapper_$1, -$1)') -define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))') +define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, $4)') include(TARGET/`erl_bif_list.h') /* diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index d0619a0609..566c65882e 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -46,6 +46,8 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop) /* arch-specific nstack walk state */ struct nstack_walk_state walk_state; + ASSERT(!p->hipe.gc_is_unsafe); + if (!p->hipe.nstack) { ASSERT(!p->hipe.nsp && !p->hipe.nstend); return n_htop; @@ -136,6 +138,8 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) char *mature; Uint mature_size; + ASSERT(!p->hipe.gc_is_unsafe); + if (!p->hipe.nstack) { ASSERT(!p->hipe.nsp && !p->hipe.nstend); return; diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 0d3493ec6c..b9d4226705 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -525,6 +525,12 @@ static const struct rts_param rts_params[] = { { 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) }, { 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE }, + + { 53, "P_GCUNSAFE", +#ifdef DEBUG + 1, offsetof(struct process, hipe.gc_is_unsafe) +#endif + }, }; #define NR_PARAMS ARRAY_SIZE(rts_params) diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4 index 57b4208bee..b540562185 100644 --- a/erts/emulator/hipe/hipe_ppc_bifs.m4 +++ b/erts/emulator/hipe/hipe_ppc_bifs.m4 @@ -212,8 +212,9 @@ ASYM($1): * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-2 parameters and + * Generate native interface for a BIF with 0-3 parameters and * standard failure mode. * The BIF may do a GC. */ @@ -300,6 +301,39 @@ ASYM($1): TYPE_FUNCTION(ASYM($1)) #endif') +define(gc_bif_interface_3, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + GLOBAL(ASYM($1)) +ASYM($1): + /* Set up C argument registers. */ + mr r3, P + NBIF_ARG(r4,3,0) + NBIF_ARG(r5,3,1) + NBIF_ARG(r6,3,2) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_GC + STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */ + STORE r5, P_ARG1(r3) + STORE r6, P_ARG2(r3) + addi r4, r3, P_ARG0 + CALL_BIF($2) + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + CMPI r3, THE_NON_VALUE + RESTORE_CONTEXT_GC + beq- 1f + NBIF_RET(3) +1: /* workaround for bc:s small offset operand */ + b CSYM(nbif_3_simple_exception) + HANDLE_GOT_MBUF(3) + SET_SIZE(ASYM($1)) + TYPE_FUNCTION(ASYM($1)) +#endif') + /* * gc_nofail_primop_interface_1(nbif_name, cbif_name) * diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index 21c4239753..a8d5972280 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -52,6 +52,9 @@ struct hipe_process_state { #if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) void (*bif_callee)(void); /* When calling BIF's via debug wrapper */ #endif +#ifdef DEBUG + UWord gc_is_unsafe; /* Nonzero when GC-required state is on stack */ +#endif }; extern void hipe_arch_print_pcb(struct hipe_process_state *p); @@ -68,6 +71,9 @@ static __inline__ void hipe_init_process(struct hipe_process_state *p) p->nra = NULL; #endif p->narity = 0; +#ifdef DEBUG + p->gc_is_unsafe = 0; +#endif } static __inline__ void hipe_delete_process(struct hipe_process_state *p) diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4 index 2e886ec1d1..1389beaa61 100644 --- a/erts/emulator/hipe/hipe_sparc_bifs.m4 +++ b/erts/emulator/hipe/hipe_sparc_bifs.m4 @@ -210,8 +210,9 @@ $1: * gc_bif_interface_0(nbif_name, cbif_name) * gc_bif_interface_1(nbif_name, cbif_name) * gc_bif_interface_2(nbif_name, cbif_name) + * gc_bif_interface_3(nbif_name, cbif_name) * - * Generate native interface for a BIF with 0-2 parameters and + * Generate native interface for a BIF with 0-3 parameters and * standard failure mode. * The BIF may do a GC. */ @@ -295,6 +296,37 @@ $1: .type $1, #function #endif') +define(gc_bif_interface_3, +` +#ifndef HAVE_$1 +#`define' HAVE_$1 + .global $1 +$1: + /* Set up C argument registers. */ + mov P, %o0 + NBIF_ARG(%o1,3,0) + NBIF_ARG(%o2,3,1) + NBIF_ARG(%o3,3,2) + + /* Save caller-save registers and call the C function. */ + SAVE_CONTEXT_GC + st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg + st %o2, [%o0+P_ARG1] + st %o3, [%o0+P_ARG2] + add %o0, P_ARG0, %o1 + CALL_BIF($2) + nop + TEST_GOT_MBUF + + /* Restore registers. Check for exception. */ + TEST_GOT_EXN(3) + RESTORE_CONTEXT_GC + NBIF_RET(3) + HANDLE_GOT_MBUF(3) + .size $1, .-$1 + .type $1, #function +#endif') + /* * gc_nofail_primop_interface_1(nbif_name, cbif_name) * diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4 index b8ac5046d5..c0c149733c 100644 --- a/erts/emulator/hipe/hipe_x86_bifs.m4 +++ b/erts/emulator/hipe/hipe_x86_bifs.m4 @@ -671,10 +671,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu) define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)') /* - * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2). + * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3). */ define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)') define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)') +define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)') /* * Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1. diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index fa4750a6e9..6b9ddd8da4 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -54,6 +54,7 @@ #include <stdlib.h> #include <stdio.h> +#include <stdarg.h> #include <sys/wait.h> #define WANT_NONBLOCKING @@ -79,10 +80,17 @@ #define DEBUG_PRINT(fmt, ...) #endif -#define ABORT(fmt, ...) do { \ - fprintf(stderr, "erl_child_setup: " fmt "\r\n", ##__VA_ARGS__); \ - abort(); \ - } while(0) +static char abort_reason[200]; /* for core dump inspection */ + +static void ABORT(const char* fmt, ...) +{ + va_list arglist; + va_start(arglist, fmt); + vsprintf(abort_reason, fmt, arglist); + fprintf(stderr, "erl_child_setup: %s\r\n", abort_reason); + va_end(arglist); + abort(); +} #ifdef DEBUG void diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 78e7086bd8..400f163652 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -688,6 +688,10 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, return ERL_DRV_ERROR_ERRNO; } + /* + * Whitebox test port_SUITE:pipe_limit_env + * assumes this command payload format. + */ io_vector[i].iov_base = (void*)&buffsz; io_vector[i++].iov_len = sizeof(buffsz); @@ -771,9 +775,14 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, if (res < (buffsz + sizeof(buffsz))) { /* we only wrote part of the command payload. Enqueue the rest. */ for (i = 0; i < iov_len; i++) { - driver_enq(port_num, io_vector[i].iov_base, io_vector[i].iov_len); + if (res >= io_vector[i].iov_len) + res -= io_vector[i].iov_len; + else { + driver_enq(port_num, io_vector[i].iov_base + res, + io_vector[i].iov_len - res); + res = 0; + } } - driver_deq(port_num, res); driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1); } diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index a683307722..5c0bfdffd4 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -92,6 +92,7 @@ exit_status/1, exit_status_multi_scheduling_block/1, huge_env/1, + pipe_limit_env/1, input_only/1, iter_max_ports/1, line/1, @@ -159,7 +160,7 @@ all() -> {group, multiple_packets}, parallell, dying_port, port_program_with_path, open_input_file_port, open_output_file_port, name1, env, huge_env, bad_env, cd, - cd_relative, bad_args, + cd_relative, pipe_limit_env, bad_args, exit_status, iter_max_ports, count_fds, t_exit, {group, tps}, line, stderr_to_stdout, otp_3906, otp_4389, win_massive, mix_up_ports, otp_5112, otp_5119, @@ -1003,6 +1004,55 @@ huge_env(Config) when is_list(Config) -> ct:fail("Open port failed ~p:~p",[E,R]) end. +%% Test to spawn program with command payload buffer +%% just around pipe capacity (9f779819f6bda734c5953468f7798) +pipe_limit_env(Config) when is_list(Config) -> + Cmd = "true", + CmdSize = command_payload_size(Cmd), + Limits = [4096, 16384, 65536], % Try a couple of common pipe buffer sizes + + lists:foreach(fun(Lim) -> + lists:foreach(fun(L) -> pipe_limit_env_do(L, Cmd, CmdSize) + end, lists:seq(Lim-5, Lim+5)) + end, Limits), + ok. + +pipe_limit_env_do(Bytes, Cmd, CmdSize) -> + case env_of_bytes(Bytes-CmdSize) of + [] -> skip; + Env -> + try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of + P -> + receive + {P, {exit_status,N}} = M -> + %% Bug caused exit_status 150 (EINVAL+128) + 0 = N + end + catch E:R -> + %% Have to catch the error here, as printing the stackdump + %% in the ct log is way to heavy for some test machines. + ct:fail("Open port failed ~p:~p",[E,R]) + end + end. + +%% environ format: KEY=VALUE\0 +env_of_bytes(Bytes) when Bytes > 3 -> + Env = [{"X",lists:duplicate(Bytes-3, $x)}]; +env_of_bytes(_) -> []. + +%% White box assumption about payload written to pipe +%% for Cmd and current environment (see spawn_start in sys_driver.c) +command_payload_size(Cmd) -> + EnvSize = lists:foldl(fun(E,Acc) -> length(E) + 1 + Acc end, + 0, os:getenv()), + {ok, PWD} = file:get_cwd(), + (4 % buffsz + + 4 % flags + + 5 + length(Cmd) + 1 % "exec $Cmd" + + length(PWD) + 1 % $PWD + + 1 % nullbuff + + 4 % env_len + + EnvSize). %% Test bad 'args' options. bad_args(Config) when is_list(Config) -> diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 15fb718c47..e2bf302cca 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -3581,9 +3581,24 @@ document etp-block %--------------------------------------------------------------------------- end +define etp-smp-atomic + if (etp_smp_compiled) + set $arg1 = (($arg0).counter) + else + set $arg1 = ($arg0) + end +end + +document etp-smp-atomic +%--------------------------------------------------------------------------- +% Read an erts_smp_atomic_t value from $arg0 into $arg1 +%--------------------------------------------------------------------------- +end + define etp-carrier-blocks set $etp_crr = (Carrier_t*) $arg0 - set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7) + etp-smp-atomic $etp_crr->allctr $etp_alc + set $etp_alc = (Allctr_t*)($etp_alc & ~7) set $etp_crr_end = ((char*)$etp_crr + ($etp_crr->chdr & ~7) - (sizeof(void*) & ~8)) set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size) set $etp_prev_blk = 0 diff --git a/erts/vsn.mk b/erts/vsn.mk index 6ad3680213..924558dab1 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 8.0 +VSN = 8.0.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index 7af6ad72d2..68d335f451 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the asn1 application.</p> +<section><title>Asn1 4.0.3</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + <section><title>Asn1 4.0.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index ab2c127ca2..527af05da1 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1 +1 @@ -ASN1_VSN = 4.0.2 +ASN1_VSN = 4.0.3 diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index ebba864606..32ae699c7a 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -33,6 +33,31 @@ <file>notes.xml</file> </header> +<section><title>Common_Test 1.12.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The following modules were missing in + common_test.app.src: ct_groups, ct_property_test, + ct_release_test, ct_webtool, ct_webtool_sup, + test_server_gl. They have now been added.</p> + <p> + Own Id: OTP-13475</p> + </item> + <item> + <p> + Common Test printed incorrect timestamps for received + error reports.</p> + <p> + Own Id: OTP-13615 Aux Id: seq13124 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.12.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index 2fab4d3883..c68750886a 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1 +1 @@ -COMMON_TEST_VSN = 1.13 +COMMON_TEST_VSN = 1.12.2 diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index ae375c5f58..e25cdc580c 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,100 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>compile:forms/1,2</c> would crash when used in a + working directory that had been deleted by another + process.</p> + <p> + Own Id: OTP-13430 Aux Id: ERL-113 </p> + </item> + <item> + <p>Dialyzer no longer crashes when there is an invalid + function call such as <c>42(7)</c> in a module being + analyzed. The compiler will now warn for invalid function + calls such as <c>X = 42, x(7)</c>.</p> + <p> + Own Id: OTP-13552 Aux Id: ERL-138 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Optimization of tuple matching has been slightly + improved.</p> + <p> + Own Id: OTP-12951</p> + </item> + <item> + <p>Five deprecated and undocumented functions in the + module <c>core_lib</c> have been removed. The functions + are: <c>get_anno/{1,2}</c>, <c>is_literal/1</c>, + <c>is_literal_list/1</c>, and <c>literal_value</c>. Use + the appropriate functions in the <c>cerl</c> module + instead.</p> + <p> + Own Id: OTP-12979</p> + </item> + <item> + <p>The pre-processor can now expand the ?FUNCTION_NAME + and ?FUNCTION_ARITY macros.</p> + <p> + Own Id: OTP-13059</p> + </item> + <item> + <p>The function mapfold/4 has been added to the + <c>cerl_trees</c> module.</p> + <p> + Own Id: OTP-13280</p> + </item> + <item> + <p>Bitstring comprehensions have been generalized to + allow arbitrary expressions in the construction part.</p> + <p> + Own Id: OTP-13289</p> + </item> + <item> + <p>The compiler will now produce warnings for binary + patterns that will never match (example: + <c><<-1/unsigned>> = Bin</c>). </p> + <p> + Own Id: OTP-13374 Aux Id: ERL-44 </p> + </item> + <item> + <p>The compiler will no longer put the compilation date + and time into BEAM files. That means that two BEAM files + compiled on the same computer from the same source code + and compilation options will be identical.</p> + <p>Note: If you want to find out whether a BEAM file on + disk is different from the loaded code, compared the MD5 + value obtained from <c>Mod:module_info(md5)</c> with the + MD5 value obtained from + <c>beam_lib:md5(BeamFileForMod)</c></p>. + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13504</p> + </item> + <item> + <p>The function <c>compile:env_compiler_options/0</c> has + been added to allow tools to pick up the same default + compiler options as the compiler itself.</p> + <p> + Own Id: OTP-13654</p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 6.0.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 82ff8a95f3..e951a25e04 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -43,6 +43,10 @@ -type abstract_code() :: [erl_parse:abstract_form()]. +%% Internal representations used for 'from_asm' and 'from_beam' compilation can +%% also be valid, but have no relevant types defined. +-type forms() :: abstract_code() | cerl:c_module(). + -type option() :: atom() | {atom(), term()} | {'d', atom(), term()}. -type err_info() :: {erl_anno:line() | 'none', @@ -88,7 +92,7 @@ file(File, Opt) -> forms(Forms) -> forms(Forms, ?DEFAULT_OPTIONS). --spec forms(abstract_code(), [option()] | option()) -> comp_ret(). +-spec forms(forms(), [option()] | option()) -> comp_ret(). forms(Forms, Opts) when is_list(Opts) -> do_compile({forms,Forms}, [binary|Opts++env_default_opts()]); @@ -116,7 +120,7 @@ noenv_file(File, Opts) when is_list(Opts) -> noenv_file(File, Opt) -> noenv_file(File, [Opt|?DEFAULT_OPTIONS]). --spec noenv_forms(abstract_code(), [option()] | option()) -> comp_ret(). +-spec noenv_forms(forms(), [option()] | option()) -> comp_ret(). noenv_forms(Forms, Opts) when is_list(Opts) -> do_compile({forms,Forms}, [binary|Opts]); @@ -236,6 +240,8 @@ format_error({epp,E}) -> epp:format_error(E); format_error(write_error) -> "error writing file"; +format_error({write_error, Error}) -> + io_lib:format("error writing file: ~ts", [file:format_error(Error)]); format_error({rename,From,To,Error}) -> io_lib:format("failed to rename ~ts to ~ts: ~ts", [From,To,file:format_error(Error)]); @@ -1206,7 +1212,7 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) -> end, {ok,St} catch - exit:_ -> + error:_ -> %% Couldn't write to output Makefile. Err = {St#compile.ifile,[{none,?MODULE,write_error}]}, {error,St#compile{errors=St#compile.errors++[Err]}} @@ -1479,8 +1485,8 @@ save_binary_1(St) -> end, {error,St#compile{errors=St#compile.errors ++ Es}} end; - {error,_Error} -> - Es = [{Tfile,[{none,compile,write_error}]}], + {error,Error} -> + Es = [{Tfile,[{none,compile,{write_error,Error}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. @@ -1628,8 +1634,8 @@ listing(LFun, Ext, St) -> LFun(Lf, Code), ok = file:close(Lf), {ok,St}; - {error,_Error} -> - Es = [{Lfile,[{none,compile,write_error}]}], + {error,Error} -> + Es = [{Lfile,[{none,compile,{write_error,Error}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index c83455240d..23dd4bd4b1 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 6.0.3 +COMPILER_VSN = 7.0 diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml index 83fa5fa4b7..fe94cb64d3 100644 --- a/lib/cosEvent/doc/src/notes.xml +++ b/lib/cosEvent/doc/src/notes.xml @@ -33,7 +33,22 @@ <file>notes.xml</file> </header> - <section><title>cosEvent 2.2</title> + <section><title>cosEvent 2.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosEvent 2.2</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosEvent/vsn.mk b/lib/cosEvent/vsn.mk index 3149020d7c..c39bed9fe4 100644 --- a/lib/cosEvent/vsn.mk +++ b/lib/cosEvent/vsn.mk @@ -1,2 +1,2 @@ -COSEVENT_VSN = 2.2 +COSEVENT_VSN = 2.2.1 diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml index 5617efe697..5e5bb2c33e 100644 --- a/lib/cosEventDomain/doc/src/notes.xml +++ b/lib/cosEventDomain/doc/src/notes.xml @@ -32,7 +32,22 @@ <file>notes.xml</file> </header> - <section><title>cosEventDomain 1.2</title> + <section><title>cosEventDomain 1.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosEventDomain 1.2</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosEventDomain/vsn.mk b/lib/cosEventDomain/vsn.mk index bdde1f6ab2..4e10d6ac60 100644 --- a/lib/cosEventDomain/vsn.mk +++ b/lib/cosEventDomain/vsn.mk @@ -1,2 +1,2 @@ -COSEVENTDOMAIN_VSN = 1.2 +COSEVENTDOMAIN_VSN = 1.2.1 diff --git a/lib/cosFileTransfer/doc/src/notes.xml b/lib/cosFileTransfer/doc/src/notes.xml index eacc75062b..58ab087014 100644 --- a/lib/cosFileTransfer/doc/src/notes.xml +++ b/lib/cosFileTransfer/doc/src/notes.xml @@ -31,7 +31,22 @@ <file>notes.xml</file> </header> - <section><title>cosFileTransfer 1.2</title> + <section><title>cosFileTransfer 1.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosFileTransfer 1.2</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk index 00bfdb3087..e271c05242 100644 --- a/lib/cosFileTransfer/vsn.mk +++ b/lib/cosFileTransfer/vsn.mk @@ -1 +1 @@ -COSFILETRANSFER_VSN = 1.2 +COSFILETRANSFER_VSN = 1.2.1 diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml index 3f3f0be3e7..1237000153 100644 --- a/lib/cosNotification/doc/src/notes.xml +++ b/lib/cosNotification/doc/src/notes.xml @@ -32,7 +32,22 @@ <file>notes.xml</file> </header> - <section><title>cosNotification 1.2.1</title> + <section><title>cosNotification 1.2.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosNotification 1.2.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk index 07b9bf474b..0d95ab4853 100644 --- a/lib/cosNotification/vsn.mk +++ b/lib/cosNotification/vsn.mk @@ -1,2 +1,2 @@ -COSNOTIFICATION_VSN = 1.2.1 +COSNOTIFICATION_VSN = 1.2.2 diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml index 4ec7eca94a..d5219fc110 100644 --- a/lib/cosProperty/doc/src/notes.xml +++ b/lib/cosProperty/doc/src/notes.xml @@ -32,7 +32,22 @@ <file>notes.xml</file> </header> - <section><title>cosProperty 1.2</title> + <section><title>cosProperty 1.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosProperty 1.2</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosProperty/vsn.mk b/lib/cosProperty/vsn.mk index d96508c2d2..1a8e42ffdb 100644 --- a/lib/cosProperty/vsn.mk +++ b/lib/cosProperty/vsn.mk @@ -1,2 +1,2 @@ -COSPROPERTY_VSN = 1.2 +COSPROPERTY_VSN = 1.2.1 diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml index 62c1aa3c26..686d9e6add 100644 --- a/lib/cosTime/doc/src/notes.xml +++ b/lib/cosTime/doc/src/notes.xml @@ -33,7 +33,22 @@ <file>notes.xml</file> </header> - <section><title>cosTime 1.2.1</title> + <section><title>cosTime 1.2.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosTime 1.2.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosTime/vsn.mk b/lib/cosTime/vsn.mk index 39b457b53b..7c9cae2d2f 100644 --- a/lib/cosTime/vsn.mk +++ b/lib/cosTime/vsn.mk @@ -1,2 +1,2 @@ -COSTIME_VSN = 1.2.1 +COSTIME_VSN = 1.2.2 diff --git a/lib/cosTransactions/doc/src/notes.xml b/lib/cosTransactions/doc/src/notes.xml index b681330391..85ace1208b 100644 --- a/lib/cosTransactions/doc/src/notes.xml +++ b/lib/cosTransactions/doc/src/notes.xml @@ -33,7 +33,22 @@ <file>notes.xml</file> </header> - <section><title>cosTransactions 1.3.1</title> + <section><title>cosTransactions 1.3.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>cosTransactions 1.3.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/cosTransactions/vsn.mk b/lib/cosTransactions/vsn.mk index 3a18cae384..ab163d83c2 100644 --- a/lib/cosTransactions/vsn.mk +++ b/lib/cosTransactions/vsn.mk @@ -1 +1 @@ -COSTRANSACTIONS_VSN = 1.3.1 +COSTRANSACTIONS_VSN = 1.3.2 diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index 6c76a0d7b0..7d3a85326f 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -31,6 +31,52 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 3.7</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Refactor <c>crypto</c> to use the EVP interface of + OpenSSL, which is the recommended interface that also + enables access to hardware acceleration for some + operations.</p> + <p> + Own Id: OTP-12217</p> + </item> + <item> + <p> + Add support for 192-bit keys for the <c>aes_cbc</c> + cipher.</p> + <p> + Own Id: OTP-13206 Aux Id: pr 832 </p> + </item> + <item> + <p> + Add support for 192-bit keys for <c>aes_ecb</c>.</p> + <p> + Own Id: OTP-13207 Aux Id: pr829 </p> + </item> + <item> + <p> + Deprecate the function <c>crypto:rand_bytes</c> and make + sure that <c>crypto:strong_rand_bytes</c> is used in all + places that are cryptographically significant.</p> + <p> + Own Id: OTP-13214</p> + </item> + <item> + <p> + Enable AES-GCM encryption/decryption to change the tag + length between 1 to 16 bytes.</p> + <p> + Own Id: OTP-13483 Aux Id: PR-998 </p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 3.6.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 6dcb28ec8a..96466869d1 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 3.6.3 +CRYPTO_VSN = 3.7 diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml index 3028d8dd41..2e0d834269 100644 --- a/lib/debugger/doc/src/notes.xml +++ b/lib/debugger/doc/src/notes.xml @@ -33,6 +33,22 @@ <p>This document describes the changes made to the Debugger application.</p> +<section><title>Debugger 4.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>When the debugger searches for source files, it will + also use the location of the source in the compilation + information part of the BEAM file.</p> + <p> + Own Id: OTP-13375</p> + </item> + </list> + </section> + +</section> + <section><title>Debugger 4.1.2</title> <section><title>Improvements and New Features</title> diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk index cf8ffd3272..dd496013cd 100644 --- a/lib/debugger/vsn.mk +++ b/lib/debugger/vsn.mk @@ -1 +1 @@ -DEBUGGER_VSN = 4.1.2 +DEBUGGER_VSN = 4.2 diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index d9af2cb4cd..6400072b1f 100644 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -32,6 +32,102 @@ <p>This document describes the changes made to the Dialyzer application.</p> +<section><title>Dialyzer 3.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fix a map related bug.</p> + <p> + Own Id: OTP-13709 Aux Id: ERL-177, PR-1115 </p> + </item> + </list> + </section> + +</section> + +<section><title>Dialyzer 3.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug in the translation of forms to types. </p> + <p> + Own Id: OTP-13520</p> + </item> + <item> + <p>Correct misspelling in Dialyzer's acronym definition. + </p> + <p> + Own Id: OTP-13544 Aux Id: PR-1007 </p> + </item> + <item> + <p>Dialyzer no longer crashes when there is an invalid + function call such as <c>42(7)</c> in a module being + analyzed. The compiler will now warn for invalid function + calls such as <c>X = 42, x(7)</c>.</p> + <p> + Own Id: OTP-13552 Aux Id: ERL-138 </p> + </item> + <item> + <p> Fix a bug that caused Dialyzer to go into an infinite + loop. </p> + <p> + Own Id: OTP-13653 Aux Id: ERL-157 </p> + </item> + <item> + <p>Fix a bug in Dialyzer related to call-site + analysis.</p> + <p> + Own Id: OTP-13655 Aux Id: PR-1092 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> The evaluation of SCCs in <c>dialyzer_typesig</c> is + optimized. </p> <p> Maps are used instead of Dicts to + further optimize the evaluation. </p> + <p> + Own Id: OTP-10349</p> + </item> + <item> + <p> Since Erlang/OTP R14A, when support for parameterized + modules was added, <c>module()</c> has included + <c>tuple()</c>, but that part is removed; the type + <c>module()</c> is now the same as <c>atom()</c>, as + documented in the Reference Manual. </p> + <p> + Own Id: OTP-13244</p> + </item> + <item> + <p> The type specification syntax for Maps is improved: + </p> <list> <item> <p> The association type <c>KeyType := + ValueType</c> denotes an association that must be + present. </p> </item> <item> <p> The shorthand <c>...</c> + stands for the association type <c>any() => any()</c>. + </p> </item> </list> <p> An incompatible change is that + <c>#{}</c> stands for the empty map. The type + <c>map()</c> (a map of any size) can be written as + <c>#{...}</c>. </p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13542 Aux Id: PR-1014 </p> + </item> + <item> + <p>The translation of forms to types is improved. </p> + <p> + Own Id: OTP-13547</p> + </item> + </list> + </section> + +</section> + <section><title>Dialyzer 2.9</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 9399789464..963c953447 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -978,12 +978,21 @@ handle_case(Tree, Map, State) -> false -> State1 end, Map2 = join_maps_begin(Map1), - {MapList, State3, Type} = + {MapList, State3, Type, Warns} = handle_clauses(Clauses, Arg, ArgType, ArgType, State2, - [], Map2, [], []), + [], Map2, [], [], []), + %% Non-Erlang BEAM languages, such as Elixir, expand language constructs + %% into case statements. In that case, we do not want to warn on + %% individual clauses not matching unless none of them can. + SupressForced = is_compiler_generated(cerl:get_ann(Tree)) + andalso not (t_is_none(Type)), + State4 = lists:foldl(fun({T,R,M,F}, S) -> + state__add_warning( + S,T,R,M,F andalso (not SupressForced)) + end, State3, Warns), Map3 = join_maps_end(MapList, Map2), debug_pp_map(Map3), - {State3, Map3, Type} + {State4, Map3, Type} end. %%---------------------------------------- @@ -1082,22 +1091,24 @@ handle_receive(Tree, Map, State) -> RaceListSize + 1, State); false -> State end, - {MapList, State2, ReceiveType} = + {MapList, State2, ReceiveType, Warns} = handle_clauses(Clauses, ?no_arg, t_any(), t_any(), State1, [], Map, - [], []), + [], [], []), + State3 = lists:foldl(fun({T,R,M,F}, S) -> state__add_warning(S,T,R,M,F) end, + State2, Warns), Map1 = join_maps(MapList, Map), - {State3, Map2, TimeoutType} = traverse(Timeout, Map1, State2), - Opaques = State3#state.opaques, + {State4, Map2, TimeoutType} = traverse(Timeout, Map1, State3), + Opaques = State4#state.opaques, case (t_is_atom(TimeoutType, Opaques) andalso (t_atom_vals(TimeoutType, Opaques) =:= ['infinity'])) of true -> - {State3, Map2, ReceiveType}; + {State4, Map2, ReceiveType}; false -> Action = cerl:receive_action(Tree), - {State4, Map3, ActionType} = traverse(Action, Map, State3), + {State5, Map3, ActionType} = traverse(Action, Map, State4), Map4 = join_maps([Map3, Map1], Map), Type = t_sup(ReceiveType, ActionType), - {State4, Map4, Type} + {State5, Map4, Type} end. %%---------------------------------------- @@ -1245,7 +1256,7 @@ handle_tuple(Tree, Map, State) -> %% Clauses %% handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, - Acc, ClauseAcc) -> + Acc, ClauseAcc, WarnAcc0) -> IsRaceAnalysisEnabled = is_race_analysis_enabled(State), State1 = case IsRaceAnalysisEnabled of @@ -1258,8 +1269,8 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, State); false -> State end, - {State2, ClauseMap, BodyType, NewArgType} = - do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1), + {State2, ClauseMap, BodyType, NewArgType, WarnAcc} = + do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1, WarnAcc0), {NewClauseAcc, State3} = case IsRaceAnalysisEnabled of true -> @@ -1277,9 +1288,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, false -> {[BodyType|CaseTypes], [ClauseMap|Acc]} end, handle_clauses(Left, Arg, NewArgType, OrigArgType, State3, - NewCaseTypes, MapIn, NewAcc, NewClauseAcc); + NewCaseTypes, MapIn, NewAcc, NewClauseAcc, WarnAcc); handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, - ClauseAcc) -> + ClauseAcc, WarnAcc) -> State1 = case is_race_analysis_enabled(State) of true -> @@ -1289,9 +1300,9 @@ handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, RaceListSize + 1, State); false -> State end, - {lists:reverse(Acc), State1, t_sup(CaseTypes)}. + {lists:reverse(Acc), State1, t_sup(CaseTypes), WarnAcc}. -do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> +do_clause(C, Arg, ArgType0, OrigArgType, Map, State, Warns) -> Pats = cerl:clause_pats(C), Guard = cerl:clause_guard(C), Body = cerl:clause_body(C), @@ -1323,7 +1334,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> [cerl_prettypr:format(C), format_type(ArgType0, State1)]), case state__warning_mode(State1) of false -> - {State1, Map, t_none(), ArgType0}; + {State1, Map, t_none(), ArgType0, Warns}; true -> {Msg, Force} = case t_is_none(ArgType0) of @@ -1403,8 +1414,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> {record_match, _} -> ?WARN_MATCHING; {pattern_match_cov, _} -> ?WARN_MATCHING end, - {state__add_warning(State1, WarnType, C, Msg, Force), - Map, t_none(), ArgType0} + {State1, Map, t_none(), ArgType0, [{WarnType, C, Msg, Force}|Warns]} end; {Map2, PatTypes} -> Map3 = @@ -1437,9 +1447,9 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> false -> {guard_fail_pat, [PatString, format_type(ArgType0, State1)]} end, - State2 = + Warn = case Reason of - none -> state__add_warning(State1, ?WARN_MATCHING, C, DefaultMsg); + none -> {?WARN_MATCHING, C, DefaultMsg, false}; {FailGuard, Msg} -> case is_compiler_generated(cerl:get_ann(FailGuard)) of false -> @@ -1448,15 +1458,15 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> {neg_guard_fail, _} -> ?WARN_MATCHING; {opaque_guard, _} -> ?WARN_OPAQUE end, - state__add_warning(State1, WarnType, FailGuard, Msg); + {WarnType, FailGuard, Msg, false}; true -> - state__add_warning(State1, ?WARN_MATCHING, C, Msg) + {?WARN_MATCHING, C, Msg, false} end end, - {State2, Map, t_none(), NewArgType}; + {State1, Map, t_none(), NewArgType, [Warn|Warns]}; Map4 -> {RetState, RetMap, BodyType} = traverse(Body, Map4, State1), - {RetState, RetMap, BodyType, NewArgType} + {RetState, RetMap, BodyType, NewArgType, Warns} end end. diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile index f43e04dd59..0d8fba438c 100644 --- a/lib/dialyzer/test/Makefile +++ b/lib/dialyzer/test/Makefile @@ -12,6 +12,7 @@ AUXILIARY_FILES=\ dialyzer_common.erl\ file_utils.erl\ dialyzer_SUITE.erl\ + abstract_SUITE.erl\ plt_SUITE.erl # ---------------------------------------------------- diff --git a/lib/dialyzer/test/abstract_SUITE.erl b/lib/dialyzer/test/abstract_SUITE.erl new file mode 100644 index 0000000000..269db3e836 --- /dev/null +++ b/lib/dialyzer/test/abstract_SUITE.erl @@ -0,0 +1,102 @@ +%% This suite contains cases that cannot be written +%% in Erlang itself and must be done via the abstract +%% format. + +-module(abstract_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include("dialyzer_test_constants.hrl"). + +-export([suite/0, all/0, init_per_suite/0, init_per_suite/1]). +-export([generated_case/1]). + +suite() -> + [{timetrap, {minutes, 1}}]. +all() -> + [generated_case]. + +init_per_suite() -> + [{timetrap, ?plt_timeout}]. +init_per_suite(Config) -> + OutDir = ?config(priv_dir, Config), + case dialyzer_common:check_plt(OutDir) of + fail -> {skip, "Plt creation/check failed."}; + ok -> [{dialyzer_options, []}|Config] + end. + +generated_case(Config) when is_list(Config) -> + %% Equivalent to: + %% + %% -module(foo). + %% -export(bar). + %% bar() -> + %% Arg = sample, + %% case Arg of + %% #{} -> map; + %% _ -> Arg:fn() + %% end. + %% + %% Except the case statement and its clauses are marked as autogenerated. + [] = + test([{attribute,1,module,foo}, + {attribute,2,export,[{bar,0}]}, + {function,3,bar,0, + [{clause,3,[],[], + [{match,4,{var,4,'Arg'},{atom,4,sample}}, + {'case',[{location,5},{generated,true}],{var,5,'Arg'}, + [{clause,[{location,6},{generated,true}],[{map,6,[]}],[], + [{atom,6,map}]}, + {clause,[{location,7},{generated,true}],[{var,7,'_'}],[], + [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}], + Config, [], []), + %% With the first clause not auto-generated + [{warn_matching,{_,6},_}] = + test([{attribute,1,module,foo}, + {attribute,2,export,[{bar,0}]}, + {function,3,bar,0, + [{clause,3,[],[], + [{match,4,{var,4,'Arg'},{atom,4,sample}}, + {'case',[{location,5},{generated,true}],{var,5,'Arg'}, + [{clause,6,[{map,6,[]}],[], + [{atom,6,map}]}, + {clause,[{location,7},{generated,true}],[{var,7,'_'}],[], + [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}], + Config, [], []), + %% With Arg set to [] so neither clause matches + [{warn_return_no_exit,{_,3},_}, + {warn_matching,{_,6},_}, + {warn_failing_call,{_,7},_}] = + test([{attribute,1,module,foo}, + {attribute,2,export,[{bar,0}]}, + {function,3,bar,0, + [{clause,3,[],[], + [{match,4,{var,4,'Arg'},{nil,4}}, + {'case',[{location,5},{generated,true}],{var,5,'Arg'}, + [{clause,[{location,6},{generated,true}],[{map,6,[]}],[], + [{atom,6,map}]}, + {clause,[{location,7},{generated,true}],[{var,7,'_'}],[], + [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}], + Config, [], []), + ok. + +test(Prog, Config, COpts, DOpts) -> + {ok, BeamFile} = compile(Config, Prog, COpts), + run_dialyzer(Config, succ_typings, [BeamFile], DOpts). + +compile(Config, Prog, CompileOpts) -> + OutDir = ?config(priv_dir, Config), + Opts = [{outdir, OutDir}, debug_info, return_errors | CompileOpts], + {ok, Module, Source} = compile:forms(Prog, Opts), + BeamFile = filename:join([OutDir, lists:concat([Module, ".beam"])]), + ok = file:write_file(BeamFile, Source), + {ok, BeamFile}. + +run_dialyzer(Config, Analysis, Files, Opts) -> + OutDir = ?config(priv_dir, Config), + PltFilename = dialyzer_common:plt_file(OutDir), + dialyzer:run([{analysis_type, Analysis}, + {files, Files}, + {init_plt, PltFilename}, + {check_plt, false}, + {from, byte_code} | + Opts]). diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl index d2b1026c06..48083a2731 100644 --- a/lib/dialyzer/test/dialyzer_common.erl +++ b/lib/dialyzer/test/dialyzer_common.erl @@ -7,7 +7,7 @@ -module(dialyzer_common). --export([check_plt/1, check/4, create_all_suites/0, new_tests/2]). +-export([check_plt/1, check/4, create_all_suites/0, new_tests/2, plt_file/1]). -include_lib("kernel/include/file.hrl"). @@ -39,7 +39,7 @@ check_plt(OutDir) -> io:format("Checking plt:"), - PltFilename = filename:join(OutDir, ?plt_filename), + PltFilename = plt_file(OutDir), case file:read_file_info(PltFilename) of {ok, _} -> dialyzer_check_plt(PltFilename); {error, _ } -> @@ -63,6 +63,11 @@ check_plt(OutDir) -> end end. +-spec plt_file(string()) -> string(). + +plt_file(OutDir) -> + filename:join(OutDir, ?plt_filename). + dialyzer_check_plt(PltFilename) -> try dialyzer:run([{analysis_type, plt_check}, {init_plt, PltFilename}]) of @@ -119,7 +124,7 @@ build_plt(PltFilename) -> 'same' | {differ, [term()]}. check(TestCase, Opts, Dir, OutDir) -> - PltFilename = filename:join(OutDir, ?plt_filename), + PltFilename = plt_file(OutDir), SrcDir = filename:join(Dir, ?input_files_directory), ResDir = filename:join(Dir, ?result_files_directory), Filename = filename:join(SrcDir, atom_to_list(TestCase)), diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl new file mode 100644 index 0000000000..827984b20b --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl @@ -0,0 +1,9 @@ +-module(a). +-export([to_map/1, to_map/2]). +-type t() :: #{type := b:t()}. + +-spec to_map(t()) -> map(). +to_map(Resource) -> to_map(Resource, #{}). + +-spec to_map(t(), map()) -> map(). +to_map(_, Map) when is_map(Map) -> #{}. diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl new file mode 100644 index 0000000000..31f9bb6b3e --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl @@ -0,0 +1,3 @@ +-module(b). +-export_type([t/0]). +-type t() :: binary(). diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 077fe01e85..b9a28afdd9 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 3.0 +DIALYZER_VSN = 3.0.1 diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index 82448e7f51..b1be7bdcf7 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -43,6 +43,41 @@ first.</p> <!-- ===================================================================== --> +<section><title>diameter 1.12</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Ensure listening socket is closed at transport removal.</p> + <p> + Transport removal did not immediately close a + <c>diameter_tcp/sctp</c> listening socket, and a + subsequent peer connection caused it to remain open.</p> + <p> + Own Id: OTP-13611</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add <c>diameter:peer_info/1</c>.</p> + <p> + That retrieves information in the style of + <c>diameter:service_info/2</c>, but for a single peer + connection.</p> + <p> + Own Id: OTP-13508</p> + </item> + </list> + </section> + +</section> + <section><title>diameter 1.11.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml index 130a5a850e..ae8147c564 100644 --- a/lib/edoc/doc/src/notes.xml +++ b/lib/edoc/doc/src/notes.xml @@ -32,6 +32,20 @@ <p>This document describes the changes made to the EDoc application.</p> +<section><title>Edoc 0.7.19</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Handle typed record fields. </p> + <p> + Own Id: OTP-13558</p> + </item> + </list> + </section> + +</section> + <section><title>Edoc 0.7.18</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl index e7a4c36ca4..2d6cb04a6d 100644 --- a/lib/edoc/src/edoc_extract.erl +++ b/lib/edoc/src/edoc_extract.erl @@ -32,9 +32,9 @@ %% %% @headerfile "edoc.hrl" (disabled until it can be made private) -include("edoc.hrl"). -%% @type filename() = file:filename(). -%% @type proplist() = proplists:property(). -%% @type syntaxTree() = erl_syntax:syntaxTree(). +%% @type filename() = //kernel/file:filename(). +%% @type proplist() = //stdlib/proplists:property(). +%% @type syntaxTree() = //syntax_tools/erl_syntax:syntaxTree(). %% @spec source(File::filename(), Env::edoc_env(), Options::proplist()) %% -> {ModuleName, edoc:edoc_module()} @@ -639,11 +639,11 @@ file_macros(_Context, Env) -> %% %% The idea is to mimic how the @type tag works. %% Using @type: -%% @type t() = t1(). Some docs of t/0; -%% Further docs of t/0. +%%```@type t() = t1(). Some docs of t/0; +%% Further docs of t/0.''' %% The same thing using -type: -%% -type t() :: t1(). % Some docs of t/0; -%% Further docs of t/0. +%%```-type t() :: t1(). % Some docs of t/0; +%% Further docs of t/0.''' find_type_docs(Forms0, Comments, Env, File) -> Tree = erl_recomment:recomment_forms(Forms0, Comments), Forms = preprocess_forms(Tree), diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index dcc239f6b4..cc0a8d0b94 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -935,7 +935,7 @@ get_doc_env(Opts) -> %% Modules = [atom()] %% proplist() = [term()] %% -%% @type proplist() = proplists:property(). +%% @type proplist() = //stdlib/proplists:property(). %% @type edoc_env(). Environment information needed by EDoc for %% generating references. The data representation is not documented. %% diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl index 9e2e41e902..93f423b906 100644 --- a/lib/edoc/src/edoc_tags.erl +++ b/lib/edoc/src/edoc_tags.erl @@ -213,8 +213,10 @@ filter_tags([#tag{name = N, line = L} = T | Ts], Tags, Where, Ts1) -> true -> filter_tags(Ts, Tags, Where, [T | Ts1]); false -> - [warning(L, Where, "tag @~s not recognized.", [N]) || - Where =/= no], + case Where of + no -> ok; + _ -> warning(L, Where, "tag @~s not recognized.", [N]) + end, filter_tags(Ts, Tags, Where, Ts1) end; filter_tags([], _, _, Ts) -> @@ -451,7 +453,7 @@ check_type(#tag{line = L, data = Data}, P0, Ls, Ts) -> check_type(#t_def{type = Type}, P, Ls, Ts) -> check_type(Type, P, Ls, Ts); check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) -> - _ = check_used_type(Name, Args, P, Ls), + check_used_type(Name, Args, P, Ls), check_types3(Args++Ts, P, Ls); check_type(#t_var{}, P, Ls, Ts) -> check_types3(Ts, P, Ls); @@ -503,7 +505,8 @@ check_used_type(#t_name{name = N, module = Mod}=Name, Args, P, LocalTypes) -> false -> #parms{warn = W, line = L, file = File} = P, %% true = ets:insert(DT, TypeName), - [type_warning(L, File, "missing type", N, NArgs) || W] + _ = [type_warning(L, File, "missing type", N, NArgs) || W], + ok end. type_warning(Line, File, S, N, NArgs) -> diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk index 83514ac94f..f38800b3e0 100644 --- a/lib/edoc/vsn.mk +++ b/lib/edoc/vsn.mk @@ -1 +1 @@ -EDOC_VSN = 0.7.18 +EDOC_VSN = 0.7.19 diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml index aa3e3137ae..7aad745f67 100644 --- a/lib/eldap/doc/src/notes.xml +++ b/lib/eldap/doc/src/notes.xml @@ -31,6 +31,39 @@ </header> <p>This document describes the changes made to the Eldap application.</p> +<section><title>Eldap 1.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If the underlying tcp connection is closed and an LDAP + operation returned tcp_error, the client applications + tend to close the ldap handle with eldap:close. This will + cause a <c>{nocatch, {gen_tcp_error, ...}}</c> exception.</p> + <p> + Such errors are now ignored during close, because the + socket will be closed anyway.</p> + <p> + Own Id: OTP-13590 Aux Id: PR-1048 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Modernize test suites</p> + <p> + Own Id: OTP-13566</p> + </item> + </list> + </section> + +</section> + <section><title>Eldap 1.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml index be94c0a7a0..21dc617d55 100644 --- a/lib/erl_docgen/doc/src/notes.xml +++ b/lib/erl_docgen/doc/src/notes.xml @@ -31,7 +31,52 @@ </header> <p>This document describes the changes made to the <em>erl_docgen</em> application.</p> - <section><title>Erl_Docgen 0.4.2</title> + <section><title>Erl_Docgen 0.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Generate HTML anchors for data types without + <c>name</c> attribute. </p> + <p> + Own Id: OTP-13600 Aux Id: Jira: ERL-141 </p> + </item> + <item> + <p> Updated make rules so it's possible to use the + xmllint target for checking the system + documentation.<br/> Removed usage of non defined DTD tag + (output) from the system documentation and corrected a + number of xml faults. </p> <p> Added support for quote + tag and a new level of header formatting in erl_docgen. + </p> <p> A fault when generating html for manual set + markers for section headings is corrected so now is the + title visible after hyperlink jump. </p> + <p> + Own Id: OTP-13638</p> + </item> + <item> + <p> Corrected the space handling for the seealso tag. + </p> + <p> + Own Id: OTP-13639</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Sort the modules function index alphabetically. </p> + <p> + Own Id: OTP-13668 Aux Id: PR-543 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erl_Docgen 0.4.2</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript index ac4278bf22..4a418fe144 100755 --- a/lib/erl_docgen/priv/bin/xref_mod_app.escript +++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript @@ -84,7 +84,7 @@ preloaded(TopDir) -> %% It's OK if too much data is generated as long as all applications %% and all modules are mentioned. appmods(D) -> - ErlFiles = filelib:wildcard(filename:join([D,"src","*.erl"])), + ErlFiles = filelib:wildcard(filename:join([D,"src","**","*.erl"])), AppV = filename:basename(D), App = case string:rstr(AppV, "-") of 0 -> AppV; diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd index 835407520a..615b88b61a 100644 --- a/lib/erl_docgen/priv/dtd/erlref.dtd +++ b/lib/erl_docgen/priv/dtd/erlref.dtd @@ -29,7 +29,7 @@ <!-- `name' is used in common.refs.dtd and must therefore be defined in each *ref. dtd --> -<!ELEMENT name (#PCDATA) > +<!ELEMENT name (#PCDATA|seealso)* > <!ATTLIST name name CDATA #IMPLIED arity CDATA #IMPLIED clause_i CDATA #IMPLIED diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index fe6c7d3c28..edab8e1c7e 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -2007,10 +2007,10 @@ </xsl:variable> <xsl:choose> <xsl:when test="ancestor::datatype"> - <a name="type-{$fname}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/> + <a name="type-{$fname}"></a><span class="bold_code"><xsl:apply-templates/></span><br/> </xsl:when> <xsl:otherwise> - <a name="{$fname}-{$arity}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/> + <a name="{$fname}-{$arity}"></a><span class="bold_code"><xsl:apply-templates/></span><br/> </xsl:otherwise> </xsl:choose> </xsl:when> diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl index 0ac7985a48..4f3af1f767 100644 --- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl +++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2015. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. 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. @@ -563,7 +563,14 @@ otp_xmlify_a_fileref(FileRef1, AppS) -> true -> case split(Marker0, "-") of [Func,Arity] -> - Func++"/"++Arity; + try list_to_integer(Arity) of + _ -> + Func++"/"++Arity + catch + _:_ -> + %% This is "type-"<a-type>. + Marker0 + end; _ -> Marker0 end @@ -831,19 +838,49 @@ local_types([]) -> []; local_types(Es) -> local_defs2(get_elem(localdef, Es)). +-define(LOCAL_TYPES, edoc_local_defs). + local_defs2([]) -> []; local_defs2(Es) -> + case collect_local_types(Es) of + [] -> local_defs3(Es); + LocalTypes -> + ?LOCAL_TYPES = ets:new(?LOCAL_TYPES, [named_table]), + true = ets:insert(?LOCAL_TYPES, LocalTypes), + try + local_defs3(Es) + after + ets:delete(?LOCAL_TYPES) + end + end. + +local_defs3(Es) -> {type,[?NL | [{v, localdef2(E)} || E <- Es]]}. +%% Does not work well for parametrized types. +collect_local_types(Es) -> + lists:append([collect_local_type(E) || E <- Es]). + +collect_local_type(#xmlElement{content = Es}) -> + case get_elem(typevar, Es) of + [] -> + [{t_abstype(get_content(abstype, Es))}]; + [_] -> + [] + end. + %% Like localdef/1, but does not use label_anchor/2 -- we don't want any %% markers or em tags in <v> tag, plain text only! +%% When used stand-alone, EDoc generates links to local types. An ETS +%% table holds local types, to avoid generating links to them. localdef2(#xmlElement{content = Es}) -> - case get_elem(typevar, Es) of - [] -> - t_utype(get_elem(type, Es)); - [V] -> - t_var(V) ++ [" = "] ++ t_utype(get_elem(type, Es)) - end. + Var = case get_elem(typevar, Es) of + [] -> + [t_abstype(get_content(abstype, Es))]; + [V] -> + t_var(V) + end, + Var ++ [" = "] ++ t_utype(get_elem(type, Es)). type_name(#xmlElement{content = Es}) -> t_name(get_elem(erlangName, get_content(typedef, Es))). @@ -855,10 +892,9 @@ types(Ts) -> typedecl(Name, #xmlElement{content = Es}) -> TypedefEs = get_content(typedef, Es), Id = "type-"++Name, - [{tag, typedef(TypedefEs)}, + [{tag, [{marker,[{id,Id}],[]}] ++ typedef(TypedefEs)}, ?NL, - {item, [{marker,[{id,Id}],[]} | - local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)]}, + {item, local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)}, ?NL]. typedef(Es) -> @@ -866,14 +902,14 @@ typedef(Es) -> ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), case get_elem(type, Es) of [] -> - [{tt, Name}]; + Name; Type -> - [{tt, Name ++ [" = "] ++ t_utype(Type)}] + Name ++ [" = "] ++ t_utype(Type) end. -local_defs([]) -> []; +local_defs([]) -> [{p,[]}]; local_defs(Es) -> - [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}]. + [?NL, {ul, [{li, [{p, localdef(E)}]} || E <- Es]}]. localdef(E = #xmlElement{content = Es}) -> Var = case get_elem(typevar, Es) of @@ -917,6 +953,7 @@ seealso_module(Es) -> Es1 -> {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]} end. + seealso_function(Es) -> case get_elem(see, Es) of [] -> []; @@ -988,7 +1025,14 @@ t_name([E]) -> end. t_utype([E]) -> - t_utype_elem(E). + flatten_type(t_utype_elem(E)). + +%% Make sure see also are top elements of lists. +flatten_type(T) -> + [case is_integer(E) of + true -> [E]; + false -> E + end || E <- lists:flatten(T)]. t_utype_elem(E=#xmlElement{content = Es}) -> case get_attrval(name, E) of @@ -1021,16 +1065,14 @@ t_type([#xmlElement{name = tuple, content = Es}]) -> t_tuple(Es); t_type([#xmlElement{name = 'fun', content = Es}]) -> t_fun(Es); -t_type([#xmlElement{name = abstype, content = Es}]) -> - t_abstype(Es); +t_type([E = #xmlElement{name = abstype, content = Es}]) -> + t_abstype(E, Es); t_type([#xmlElement{name = union, content = Es}]) -> t_union(Es); t_type([#xmlElement{name = record, content = Es}]) -> t_record(Es); t_type([#xmlElement{name = map, content = Es}]) -> - t_map(Es); -t_type([#xmlElement{name = map_field, content = Es}]) -> - t_map_field(Es). + t_map(Es). t_var(E) -> [get_attrval(name, E)]. @@ -1065,35 +1107,53 @@ t_fun(Es) -> t_record([E|Es]) -> ["#", get_attrval(value, E), "{"++ seq(fun t_field/1, Es) ++"}"]. + t_field(#xmlElement{name=field, content=[Atom,Type]}) -> [get_attrval(value, Atom), "="] ++ t_utype_elem(Type). t_map(Es) -> - ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). + ["#{"] ++ seq(fun t_map_field/1, Es, ["}"]). + +t_map_field(E = #xmlElement{name = map_field, content = [K,V]}) -> + KElem = t_utype_elem(K), + VElem = t_utype_elem(V), + AS = case get_attrval(assoc_type, E) of + "assoc" -> " => "; + "exact" -> " := " + end, + [KElem ++ AS ++ VElem]. -t_map_field([K,V]) -> - [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)]. +t_abstype(E, Es) -> + see_type(E, t_abstype(Es)). t_abstype(Es) -> - case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of - {Mod,Type} -> - [Type, "("] ++ - seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++ - [" (see module ", Mod, ")"]; - Type -> - [Type, "("] ++ - seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) + Name = t_name(get_elem(erlangName, Es)), + [Name, "("] ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"]). + +see_type(E, Es0) -> + case get_attrval(href, E) of + [] -> Es0; + Href0 -> + try + false = is_local_type(Es0), + %% Fails for parametrized types: + Text = #xmlText{value = lists:append(Es0)}, + {Href, Es} = otp_xmlify_a_href(Href0, [Text]), + [{seealso, [{marker, Href}], Es}] + catch + _:_ -> + Es0 + end end. -%% Split at one colon, but not at two (or more) -split_at_colon([$:,$:|_]=Rest,Acc) -> - lists:reverse(Acc)++Rest; -split_at_colon([$:|Type],Acc) -> - {lists:reverse(Acc),Type}; -split_at_colon([Char|Rest],Acc) -> - split_at_colon(Rest,[Char|Acc]); -split_at_colon([],Acc) -> - lists:reverse(Acc). +is_local_type(Es) -> + try + [_] = ets:lookup(?LOCAL_TYPES, Es), + true + catch + _:_-> + false + end. t_union(Es) -> seq(fun t_utype_elem/1, Es, " | ", []). diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk index 3188b926ff..98d1c3f7be 100644 --- a/lib/erl_docgen/vsn.mk +++ b/lib/erl_docgen/vsn.mk @@ -1 +1 @@ -ERL_DOCGEN_VSN = 0.4.2 +ERL_DOCGEN_VSN = 0.5 diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml index 9420beaf43..a69c5aac11 100644 --- a/lib/erl_interface/doc/src/notes.xml +++ b/lib/erl_interface/doc/src/notes.xml @@ -31,6 +31,48 @@ </header> <p>This document describes the changes made to the Erl_interface application.</p> +<section><title>Erl_Interface 3.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix decoding of LLONG_MIN in erl_decode</p> + <p> + Own Id: OTP-13666 Aux Id: ERL-158 </p> + </item> + <item> + <p> + On windows <c>ei_decode_ulong</c> and + <c>ei_decode_long</c> now correctly returns an error when + trying to decode a number that does not fit in a long. + Fixed a bug on windows where enabling ei tracing would + cause a segmentation fault.</p> + <p> + Own Id: OTP-13673</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Handle terms (pids,ports and refs) from nodes with a + 'creation' value larger than 3. This is a preparation of + the distribution protocol to allow OTP 19 nodes to + correctly communicate with future nodes (20 or higher). + The 'creation' value differentiates different + incarnations of the same node (name).</p> + <p> + Own Id: OTP-13488</p> + </item> + </list> + </section> + +</section> + <section><title>Erl_Interface 3.8.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk index 56dbdbac9f..33705d1e8b 100644 --- a/lib/erl_interface/vsn.mk +++ b/lib/erl_interface/vsn.mk @@ -1,2 +1,2 @@ -EI_VSN = 3.8.2 +EI_VSN = 3.9 ERL_INTERFACE_VSN = $(EI_VSN) diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml index ee9e34d14d..5300d2e4ef 100644 --- a/lib/et/doc/src/notes.xml +++ b/lib/et/doc/src/notes.xml @@ -37,6 +37,22 @@ one section in this document. The title of each section is the version number of <c>Event Tracer (ET)</c>.</p> +<section><title>ET 1.6</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Update selector to utilize new garbage collection trace + tags.</p> + <p> + Own Id: OTP-13545</p> + </item> + </list> + </section> + +</section> + <section><title>ET 1.5.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk index 0af7bf75e1..a37fec083b 100644 --- a/lib/et/vsn.mk +++ b/lib/et/vsn.mk @@ -1 +1 @@ -ET_VSN = 1.5.1 +ET_VSN = 1.6 diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml index b513caf95b..88602e8222 100644 --- a/lib/eunit/doc/src/notes.xml +++ b/lib/eunit/doc/src/notes.xml @@ -33,6 +33,21 @@ </header> <p>This document describes the changes made to the EUnit application.</p> +<section><title>Eunit 2.3</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>There is a new <c>debugVal/2</c> that gives control + over the truncation depth.</p> + <p> + Own Id: OTP-13612</p> + </item> + </list> + </section> + +</section> + <section><title>Eunit 2.2.13</title> <section><title>Improvements and New Features</title> diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index dcb7fad699..b551ee6eb6 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.2.13 +EUNIT_VSN = 2.3 diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml index 3ceae98bc5..20188c75e2 100644 --- a/lib/gs/doc/src/notes.xml +++ b/lib/gs/doc/src/notes.xml @@ -31,7 +31,22 @@ </header> <p>This document describes the changes made to the GS application.</p> - <section><title>GS 1.6</title> + <section><title>GS 1.6.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>GS 1.6</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk index 345f0f37f2..c762507bab 100644 --- a/lib/gs/vsn.mk +++ b/lib/gs/vsn.mk @@ -1,2 +1,2 @@ -GS_VSN = 1.6 +GS_VSN = 1.6.1 diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 7826dada9d..243bb6e25d 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -1664,10 +1664,12 @@ t_map(Pairs0, DefK0, DefV0) -> %% define(DEBUG, true). try validate_map_elements(Pairs) - catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]); - error:{badarg, E} -> error({badarg, E}, [Pairs0,DefK0,DefV0]) + catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]) end, - ?map(Pairs, DefK, DefV). + case map_pairs_are_none(Pairs) of + true -> ?none; + false -> ?map(Pairs, DefK, DefV) + end. normalise_map_optionals([], _, _) -> []; normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) -> @@ -1684,7 +1686,6 @@ normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) -> normalise_map_optionals([E|T], DefK, DefV) -> [E|normalise_map_optionals(T, DefK, DefV)]. -validate_map_elements([{_,?mand,?none}|_]) -> error({badarg, none_in_mand}); validate_map_elements([{K1,_,_}|Rest=[{K2,_,_}|_]]) -> case is_singleton_type(K1) andalso K1 < K2 of false -> error(badarg); @@ -1697,6 +1698,10 @@ validate_map_elements([{K,_,_}]) -> end; validate_map_elements([]) -> true. +map_pairs_are_none([]) -> false; +map_pairs_are_none([{_,?mand,?none}|_]) -> true; +map_pairs_are_none([_|Ps]) -> map_pairs_are_none(Ps). + -spec t_is_map(erl_type()) -> boolean(). t_is_map(Type) -> @@ -2833,12 +2838,7 @@ t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) -> %% becomes mandatory in the infinumum (K, _, V1, _, V2) -> {K, ?mand, t_inf(V1, V2)} end, A, B), - %% If the infinimum of any mandatory values is ?none, the entire map infinimum - %% is ?none. - case lists:any(fun({_,?mand,?none})->true; ({_,_,_}) -> false end, Pairs) of - true -> t_none(); - false -> t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV)) - end; + t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV)); t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) -> ?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2)); t_inf(?nil, ?nil, _Opaques) -> ?nil; diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index 4ebd4b817c..e2a1524be6 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -31,6 +31,46 @@ </header> <p>This document describes the changes made to HiPE.</p> +<section><title>Hipe 3.15.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + HiPE compiler crashed, during compilation, in some cases + that involved inlining of float operations on complicated + control flow graphs.</p> + <p> + Own Id: OTP-13407 Aux Id: PR-984 </p> + </item> + <item> + <p> + Various fixes and improvements to the HiPE LLVM backend. + <list> <item>Add support for LLVM 3.7 and 3.8 in the + HiPE/LLVM x86_64 backend</item> <item>Reinstate support + for the LLVM backend on x86 (works OK for LLVM 3.5 to 3.7 + -- LLVM 3.8 has a bug that prevents it from generating + correct native code on x86)</item> </list></p> + <p> + Own Id: OTP-13626</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Elimination of <c>maps:is_key/2</c> calls to HiPE</p> + <p> + Own Id: OTP-13625 Aux Id: PR-1069 </p> + </item> + </list> + </section> + +</section> + <section><title>Hipe 3.15</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index 2edfd790ed..e61c1a042c 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.15 +HIPE_VSN = 3.15.1 diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml index 4b73aa5509..08b02bc4a4 100644 --- a/lib/ic/doc/src/notes.xml +++ b/lib/ic/doc/src/notes.xml @@ -31,7 +31,22 @@ <file>notes.xml</file> </header> - <section><title>IC 4.4</title> + <section><title>IC 4.4.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>IC 4.4</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk index 272c306799..7d00ae0170 100644 --- a/lib/ic/vsn.mk +++ b/lib/ic/vsn.mk @@ -1 +1 @@ -IC_VSN = 4.4 +IC_VSN = 4.4.1 diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index ab2cc5ab21..2f071f049f 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,7 +33,75 @@ <file>notes.xml</file> </header> - <section><title>Inets 6.2.4</title> + <section><title>Inets 6.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A debug message was accidently left enabled in the ftp + client.</p> + <p> + Own Id: OTP-13712 Aux Id: seq13143 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 6.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Ftp client fixes: 1) Corrected a bug that the ftp client + gen_server crashed if the listening data socket was + closed.</p> + <p> + 2) Corrections of ftp client error codes so they are as + defined in the reference manual</p> + <p> + Own Id: OTP-13644</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Remove usage of erlang:now(). </p> + <p> + Own Id: OTP-12441</p> + </item> + <item> + <p> Add handling of DELETE Body to http client. </p> + <p> + Own Id: OTP-13383 Aux Id: PR-972 </p> + </item> + <item> + <p> + Removed references to mod_include and webtool from + examples and tests.</p> + <p> + Own Id: OTP-13445 Aux Id: PR-988 </p> + </item> + <item> + <p> + Remove module inets_regexp. Module re should be used + instead.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13561</p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 6.2.4</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index bbf25f8e90..8bad91bf98 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -106,8 +106,8 @@ -type common_reason() :: 'econn' | 'eclosed' | term(). -type file_write_error_reason() :: term(). % See file:write for more info -%%-define(DBG(F,A), 'n/a'). --define(DBG(F,A), io:format(F,A)). +-define(DBG(F,A), 'n/a'). +%%-define(DBG(F,A), io:format(F,A)). %%%========================================================================= %%% API - CLIENT FUNCTIONS @@ -2099,7 +2099,7 @@ handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}} %%-------------------------------------------------------------------------- %% Default -handle_ctrl_result({Status, Lines}, #state{client = From} = State) +handle_ctrl_result({Status, _Lines}, #state{client = From} = State) when From =/= undefined -> ctrl_result_response(Status, State, {error, Status}). diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 543e0d44fd..3408c3b128 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 6.3 +INETS_VSN = 6.3.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml index c1b7c027ed..3151fc4b5e 100644 --- a/lib/jinterface/doc/src/notes.xml +++ b/lib/jinterface/doc/src/notes.xml @@ -31,6 +31,38 @@ </header> <p>This document describes the changes made to the Jinterface application.</p> +<section><title>Jinterface 1.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix Jinterface build on Maven</p> + <p> + Own Id: OTP-13482</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Handle terms (pids,ports and refs) from nodes with a + 'creation' value larger than 3. This is a preparation of + the distribution protocol to allow OTP 19 nodes to + correctly communicate with future nodes (20 or higher). + The 'creation' value differentiates different + incarnations of the same node (name).</p> + <p> + Own Id: OTP-13488</p> + </item> + </list> + </section> + +</section> + <section><title>Jinterface 1.6.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk index 41e670528a..752b34e78c 100644 --- a/lib/jinterface/vsn.mk +++ b/lib/jinterface/vsn.mk @@ -1 +1 @@ -JINTERFACE_VSN = 1.6.1 +JINTERFACE_VSN = 1.7 diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index d0540768de..f37433110c 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,203 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 5.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The handling of <c>on_load</c> functions has been + improved. The major improvement is that if a code upgrade + fails because the <c>on_load</c> function fails, the + previous version of the module will now be retained.</p> + <p> + Own Id: OTP-12593</p> + </item> + <item> + <p><c>rpc:call()</c> and <c>rpc:block_call()</c> would + sometimes cause an exception (which was not mentioned in + the documentation). This has been corrected so that + <c>{badrpc,Reason}</c> will be returned instead.</p> + <p> + Own Id: OTP-13409</p> + </item> + <item> + <p>On Windows, for modules that were loaded early (such + as the <c>lists</c> module), <c>code:which/1</c> would + return the path with mixed slashes and backslashes, for + example: <c>"C:\\Program + Files\\erl8.0/lib/stdlib-2.7/ebin/lists.beam"</c>. This + has been corrected.</p> + <p> + Own Id: OTP-13410</p> + </item> + <item> + <p> + Make file:datasync use fsync instead of fdatasync on Mac + OSX.</p> + <p> + Own Id: OTP-13411</p> + </item> + <item> + <p> + The default chunk size for the fallback sendfile + implementation, used on platforms that do not have a + native sendfile, has been decreased in order to reduce + connectivity issues.</p> + <p> + Own Id: OTP-13444</p> + </item> + <item> + <p> + Large file writes (2Gb or more) could fail on some Unix + platforms (for example, OS X and FreeBSD).</p> + <p> + Own Id: OTP-13461</p> + </item> + <item> + <p> + A bug has been fixed where the DNS resolver inet_res did + not refresh its view of the contents of for example + resolv.conf immediately after start and hence then failed + name resolution. Reported and fix suggested by Michal + Ptaszek in GitHUB pull req #949.</p> + <p> + Own Id: OTP-13470 Aux Id: Pull #969 </p> + </item> + <item> + <p> + Fix process leak from global_group.</p> + <p> + Own Id: OTP-13516 Aux Id: PR-1008 </p> + </item> + <item> + <p> + The function <c>inet:gethostbyname/1</c> now honors the + resolver option <c>inet6</c> instead of always looking up + IPv4 addresses.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13622 Aux Id: PR-1065 </p> + </item> + <item> + <p> + The <c>Status</c> argument to <c>init:stop/1</c> is now + sanity checked to make sure <c>erlang:halt</c> does not + fail.</p> + <p> + Own Id: OTP-13631 Aux Id: PR-911 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add {line_delim, byte()} option to inet:setopts/2 and + decode_packet/3</p> + <p> + Own Id: OTP-12837</p> + </item> + <item> + <p> + Added <seealso + marker="kernel:os#perf_counter/1">os:perf_counter/1</seealso>.</p> + <p> + The perf_counter is a very very cheap and high resolution + timer that can be used to timestamp system events. It + does not have monoticity guarantees, but should on most + OS's expose a monotonous time.</p> + <p> + Own Id: OTP-12908</p> + </item> + <item> + <p> + The os:cmd call has been optimized on unix platforms to + be scale better with the number of schedulers.</p> + <p> + Own Id: OTP-13089</p> + </item> + <item> + <p>New functions that can load multiple modules at once + have been added to the '<c>code</c>' module. The + functions are <c>code:atomic_load/1</c>, + <c>code:prepare_loading/1</c>, + <c>code:finish_loading/1</c>, and + <c>code:ensure_modules_loaded/1</c>.</p> + <p> + Own Id: OTP-13111</p> + </item> + <item> + <p> + The code path cache feature turned out not to be very + useful in practice and has been removed. If an attempt is + made to enable the code path cache, there will be a + warning report informing the user that the feature has + been removed.</p> + <p> + Own Id: OTP-13191</p> + </item> + <item> + <p>When an attempt is made to start a distributed Erlang + node with the same name as an existing node, the error + message will be much shorter and easier to read than + before. Example:</p> + <p><c>Protocol 'inet_tcp': the name somename@somehost + seems to be in use by another Erlang node</c></p> + <p> + Own Id: OTP-13294</p> + </item> + <item> + <p> + The output of the default error logger is somewhat + prettier and easier to read. The default error logger is + used during start-up of the OTP system. If the start-up + fails, the output will be easier to read.</p> + <p> + Own Id: OTP-13325</p> + </item> + <item> + <p>The functions <c>rpc:safe_multi_server_call/2,3</c> + that were deprecated in R12B have been removed.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13449</p> + </item> + <item> + <p> + Update the error reasons in dist_util, and show them in + the logs if net_kernel:verbose(1) has been called.</p> + <p> + Own Id: OTP-13458</p> + </item> + <item> + <p> + Experimental support for Unix Domain Sockets has been + implemented. Read the sources if you want to try it out. + Example: <c>gen_udp:open(0, + [{ifaddr,{local,"/tmp/socket"}}])</c>. Documentation will + be written after user feedback on the experimental API.</p> + <p> + Own Id: OTP-13572 Aux Id: PR-612 </p> + </item> + <item> + <p> + Allow heart to be configured to not kill the previous + emulator before calling the HEART_COMMAND. This is done + by setting the environment variable HEART_NO_KILL to + TRUE.</p> + <p> + Own Id: OTP-13650</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 4.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml index 7deafc79e9..a05a339003 100644 --- a/lib/megaco/doc/src/notes.xml +++ b/lib/megaco/doc/src/notes.xml @@ -37,7 +37,22 @@ section is the version number of Megaco.</p> - <section><title>Megaco 3.18</title> + <section><title>Megaco 3.18.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>Megaco 3.18</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk index 2e850f2917..b95cd66a81 100644 --- a/lib/megaco/vsn.mk +++ b/lib/megaco/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = megaco -MEGACO_VSN = 3.18 +MEGACO_VSN = 3.18.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)" diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 4a68e76d50..7d8e8d0c44 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -39,7 +39,28 @@ thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> - <section><title>Mnesia 4.13.4</title> + <section><title>Mnesia 4.14</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added experimental external backend plugin api. This adds + the possibility for the user to write other storage + backends for data, for example by using shared memory or + ram-cached disk storage.</p> + <p> + The plugin api may change in future versions after being + battle tested.</p> + <p> + Own Id: OTP-13058</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.13.4</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index 194bc439a0..fb4200f62d 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.13.4 +MNESIA_VSN = 4.14 diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml index c3bd0d33b9..f79f75fead 100644 --- a/lib/observer/doc/src/notes.xml +++ b/lib/observer/doc/src/notes.xml @@ -32,6 +32,68 @@ <p>This document describes the changes made to the Observer application.</p> +<section><title>Observer 2.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed a crash happening when observing another node, who + have a different number of schedulers than the current + one.</p> + <p> + Own Id: OTP-13702 Aux Id: ERL-171 </p> + </item> + </list> + </section> + +</section> + +<section><title>Observer 2.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Update observer GUI to support tracing on ports, and to + set matchspecs for send/receive. This required some minor + bugfixes in runtime_tools/dbg.</p> + <p> + Own Id: OTP-13481</p> + </item> + <item> + <p> + Update dbg and ttb to work with a tracer module as tracer + and tracing on ports.</p> + <p> + Own Id: OTP-13500</p> + </item> + <item> + <p> + Added possibility to change update frequency and length + of the graph windows.</p> + <p> + Own Id: OTP-13555</p> + </item> + <item> + <p> + Improved background coloring to work with dark themes and + other visual improvements.</p> + <p> + Own Id: OTP-13556</p> + </item> + <item> + <p> + Crashdump viewer now allows port info "Port controls + forker process..."</p> + <p> + Own Id: OTP-13647</p> + </item> + </list> + </section> + +</section> + <section><title>Observer 2.1.2</title> <section><title>Improvements and New Features</title> diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 1010a6af4c..6a1aac92c7 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -235,12 +235,14 @@ terminate(_Event, #state{appmon=Pid}) -> code_change(_, _, State) -> State. -restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti}=State) -> +restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti, wins=Wins0}=State) -> catch Old ! exit, Me = self(), Pid = spawn_link(Node, observer_backend, fetch_stats, [Me, round(1000/Freq)]), wxWindow:refresh(Panel), - precalc(State#state{active=true, appmon=Pid, samples=reset_data(), time=Ti#ti{tick=0}}). + Wins = [W#win{state=undefined} || W <- Wins0], + precalc(State#state{active=true, appmon=Pid, samples=reset_data(), + wins=Wins, time=Ti#ti{tick=0}}). reset_data() -> {0, queue:new()}. @@ -253,18 +255,25 @@ add_data(Stats, {N, Q}, Wins, _, Active) -> add_data_1([#win{state={_,St}}|_]=Wins0, Last, N, {Drop, Q}, Active) when St /= undefined -> - {Wins, Stat} = - lists:mapfoldl(fun(Win0, Entry) -> - {Win1,Stat} = add_data_2(Win0, Last, Entry), - case Active of - true -> - Win = add_data_3(Win1, N, Drop, Stat, Q), - {Win, Stat}; - false -> - {Win1, Stat} - end - end, #{}, Wins0), - {Wins, {N,queue:in(Stat#{}, Q)}}; + try + {Wins, Stat} = + lists:mapfoldl(fun(Win0, Entry) -> + {Win1,Stat} = add_data_2(Win0, Last, Entry), + case Active of + true -> + Win = add_data_3(Win1, N, Drop, Stat, Q), + {Win, Stat}; + false -> + {Win1, Stat} + end + end, #{}, Wins0), + {Wins, {N,queue:in(Stat#{}, Q)}} + catch no_scheduler_change -> + {[Win#win{state=init_data(Id, Last), + info = info(Id, Last)} + || #win{name=Id}=Win <- Wins0], {0,queue:new()}} + end; + add_data_1(Wins, Stats, 1, {_, Q}, _) -> {[Win#win{state=init_data(Id, Stats), info = info(Id, Stats)} @@ -409,7 +418,8 @@ collect_data(utilz, MemInfo, Max) -> calc_delta([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) -> [100*(WN-WP) div (TN-TP)|calc_delta(Ss, Ps)]; -calc_delta([], []) -> []. +calc_delta([], []) -> []; +calc_delta(_, _) -> throw(no_scheduler_change). precalc(#state{samples=Data0, paint=Paint, time=Ti, wins=Wins0}=State) -> Wins = [precalc(Ti, Data0, Paint, Win) || Win <- Wins0], diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk index aede0858d6..9a7c6a546f 100644 --- a/lib/observer/vsn.mk +++ b/lib/observer/vsn.mk @@ -1 +1 @@ -OBSERVER_VSN = 2.1.2 +OBSERVER_VSN = 2.2.1 diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml index ac3c99badc..d3dd39616f 100644 --- a/lib/odbc/doc/src/notes.xml +++ b/lib/odbc/doc/src/notes.xml @@ -32,7 +32,23 @@ <p>This document describes the changes made to the odbc application. </p> - <section><title>ODBC 2.11.1</title> + <section><title>ODBC 2.11.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Configure enhancement for better handling program paths + used in the build process</p> + <p> + Own Id: OTP-13559</p> + </item> + </list> + </section> + +</section> + +<section><title>ODBC 2.11.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl index 5727c1ca50..261dfc6f20 100644 --- a/lib/odbc/test/odbc_connect_SUITE.erl +++ b/lib/odbc/test/odbc_connect_SUITE.erl @@ -89,6 +89,7 @@ init_per_suite(Config) when is_list(Config) -> [{auto_commit, off}] ++ odbc_test_lib:platform_options()) of {ok, Ref} -> odbc:disconnect(Ref), + ct:timetrap(?default_timeout), [{tableName, odbc_test_lib:unique_table_name()} | Config]; _ -> {skip, "ODBC is not properly setup"} @@ -129,11 +130,8 @@ init_per_testcase(_TestCase, Config) -> init_per_testcase_common(Config). init_per_testcase_common(Config) -> - test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), - Dog = test_server:timetrap(?default_timeout), - Temp = lists:keydelete(connection_ref, 1, Config), - NewConfig = lists:keydelete(watchdog, 1, Temp), - [{watchdog, Dog} | NewConfig]. + ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), + lists:keydelete(connection_ref, 1, Config). %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ @@ -153,25 +151,22 @@ end_per_testcase(_TestCase, Config) -> end_per_testcase_common(Config). end_per_testcase_common(Config) -> - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table), io:format("Drop table: ~p ~p~n", [Table, Result]), - odbc:disconnect(Ref), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + odbc:disconnect(Ref). %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -commit(doc)-> - ["Test the use of explicit commit"]; -commit(suite) -> []; +commit()-> + [{doc,"Test the use of explicit commit"}]. commit(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -205,14 +200,13 @@ commit(Config) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -rollback(doc)-> - ["Test the use of explicit rollback"]; -rollback(suite) -> []; +rollback()-> + [{doc,"Test the use of explicit rollback"}]. rollback(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), @@ -245,9 +239,8 @@ rollback(Config) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -not_explicit_commit(doc) -> - ["Test what happens if you try using commit on a auto_commit connection."]; -not_explicit_commit(suite) -> []; +not_explicit_commit() -> + [{doc,"Test what happens if you try using commit on a auto_commit connection."}]. not_explicit_commit(_Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}] ++ @@ -256,19 +249,17 @@ not_explicit_commit(_Config) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -not_exist_db(doc) -> - ["Tests valid data format but invalid data in the connection parameters."]; -not_exist_db(suite) -> []; +not_exist_db() -> + [{doc,"Tests valid data format but invalid data in the connection parameters."}]. not_exist_db(_Config) -> {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", odbc_test_lib:platform_options()), %% So that the odbc control server can be stoped "in the correct way" - test_server:sleep(100). + ct:sleep(100). %%------------------------------------------------------------------------- -no_c_executable(doc) -> - "Test what happens if the port-program can not be found"; -no_c_executable(suite) -> []; +no_c_executable() -> + [{doc,"Test what happens if the port-program can not be found"}]. no_c_executable(_Config) -> process_flag(trap_exit, true), Dir = filename:nativename(filename:join(code:priv_dir(odbc), @@ -293,9 +284,8 @@ no_c_executable(_Config) -> end. %%------------------------------------------------------------------------ -port_dies(doc) -> - "Tests what happens if the port program dies"; -port_dies(suite) -> []; +port_dies() -> + [{doc,"Tests what happens if the port program dies"}]. port_dies(_Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), {status, _} = process_info(Ref, status), @@ -307,7 +297,7 @@ port_dies(_Config) -> %% Wait for exit_status from port 5000 ms (will not get a exit %% status in this case), then wait a little longer to make sure %% the port and the controlprocess has had time to terminate. - test_server:sleep(10000), + ct:sleep(10000), undefined = process_info(Ref, status); [] -> ct:fail([erlang:port_info(P, name) || P <- erlang:ports()]) @@ -315,9 +305,8 @@ port_dies(_Config) -> %%------------------------------------------------------------------------- -control_process_dies(doc) -> - "Tests what happens if the Erlang control process dies"; -control_process_dies(suite) -> []; +control_process_dies() -> + [{doc,"Tests what happens if the Erlang control process dies"}]. control_process_dies(_Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), process_flag(trap_exit, true), @@ -326,7 +315,7 @@ control_process_dies(_Config) -> [Port] -> {connected, Ref} = erlang:port_info(Port, connected), exit(Ref, kill), - test_server:sleep(500), + ct:sleep(500), undefined = erlang:port_info(Port, connected); %% Check for c-program still running, how? [] -> @@ -334,9 +323,8 @@ control_process_dies(_Config) -> end. %%------------------------------------------------------------------------- -client_dies_normal(doc) -> - ["Client dies with reason normal."]; -client_dies_normal(suite) -> []; +client_dies_normal() -> + [{doc,"Client dies with reason normal."}]. client_dies_normal(Config) when is_list(Config) -> Pid = spawn(?MODULE, client_normal, [self()]), @@ -352,7 +340,7 @@ client_dies_normal(Config) when is_list(Config) -> {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> - test_server:fail(control_process_not_stopped) + ct:fail(control_process_not_stopped) end. client_normal(Pid) -> @@ -366,9 +354,8 @@ client_normal(Pid) -> %%------------------------------------------------------------------------- -client_dies_timeout(doc) -> - ["Client dies with reason timeout."]; -client_dies_timeout(suite) -> []; +client_dies_timeout() -> + [{doc,"Client dies with reason timeout."}]. client_dies_timeout(Config) when is_list(Config) -> Pid = spawn(?MODULE, client_timeout, [self()]), @@ -384,7 +371,7 @@ client_dies_timeout(Config) when is_list(Config) -> {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> - test_server:fail(control_process_not_stopped) + ct:fail(control_process_not_stopped) end. client_timeout(Pid) -> @@ -398,9 +385,8 @@ client_timeout(Pid) -> %%------------------------------------------------------------------------- -client_dies_error(doc) -> - ["Client dies with reason error."]; -client_dies_error(suite) -> []; +client_dies_error() -> + [{doc,"Client dies with reason error."}]. client_dies_error(Config) when is_list(Config) -> Pid = spawn(?MODULE, client_error, [self()]), @@ -416,7 +402,7 @@ client_dies_error(Config) when is_list(Config) -> {'DOWN', MonitorReference, _Type, _Object, _Info} -> ok after 5000 -> - test_server:fail(control_process_not_stopped) + ct:fail(control_process_not_stopped) end. client_error(Pid) -> @@ -430,9 +416,8 @@ client_error(Pid) -> %%------------------------------------------------------------------------- -connect_timeout(doc) -> - ["Test the timeout for the connect function."]; -connect_timeout(suite) -> []; +connect_timeout() -> + [{doc,"Test the timeout for the connect function."}]. connect_timeout(Config) when is_list(Config) -> {'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(), [{timeout, 0}] ++ @@ -442,10 +427,9 @@ connect_timeout(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -connect_port_timeout(doc) -> - ["Test the timeout for the port program to connect back to the odbc " - "application within the connect function."]; -connect_port_timeout(suite) -> []; +connect_port_timeout() -> + [{"Test the timeout for the port program to connect back to the odbc " + "application within the connect function."}]. connect_port_timeout(Config) when is_list(Config) -> %% Application environment var 'port_timeout' has been set to 0 by %% init_per_testcase/2. @@ -453,15 +437,14 @@ connect_port_timeout(Config) when is_list(Config) -> odbc_test_lib:platform_options()). %%------------------------------------------------------------------------- -timeout(doc) -> - ["Test that timeouts don't cause unwanted behavior sush as receiving" - " an anwser to a previously tiemed out query."]; -timeout(suite) -> []; +timeout() -> + [{"Test that timeouts don't cause unwanted behavior sush as receiving" + " an anwser to a previously tiemed out query."}]. timeout(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}]), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), @@ -512,7 +495,7 @@ update_table_timeout(Table, TimeOut, Pid) -> {'EXIT', timeout} -> Pid ! timout_occurred; {updated, 1} -> - test_server:fail(database_locker_failed) + ct:fail(database_locker_failed) end, receive @@ -537,15 +520,14 @@ update_table_timeout(Table, TimeOut, Pid) -> ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- -many_timeouts(doc) -> - ["Tests that many consecutive timeouts lead to that the connection " - "is shutdown."]; -many_timeouts(suite) -> []; +many_timeouts() -> + [{doc, "Tests that many consecutive timeouts lead to that the connection " + "is shutdown."}]. many_timeouts(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -592,19 +574,18 @@ loop_many_timouts(Ref, UpdateQuery, TimeOut) -> {'EXIT',timeout} -> loop_many_timouts(Ref, UpdateQuery, TimeOut); {updated, 1} -> - test_server:fail(database_locker_failed); + ct:fail(database_locker_failed); {error, connection_closed} -> ok end. %%------------------------------------------------------------------------- -timeout_reset(doc) -> - ["Check that the number of consecutive timouts is reset to 0 when " - "a successful call to the database is made."]; -timeout_reset(suite) -> []; +timeout_reset() -> + [{doc, "Check that the number of consecutive timouts is reset to 0 when " + "a successful call to the database is made."}]. timeout_reset(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -688,21 +669,20 @@ loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) -> loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts - 1); {updated, 1} -> - test_server:fail(database_locker_failed); + ct:fail(database_locker_failed); {error, connection_closed} -> - test_server:fail(connection_closed_premature) + ct:fail(connection_closed_premature) end. %%------------------------------------------------------------------------- -disconnect_on_timeout(doc) -> - ["Check that disconnect after a time out works properly"]; -disconnect_on_timeout(suite) -> []; +disconnect_on_timeout() -> + [{doc,"Check that disconnect after a time out works properly"}]. disconnect_on_timeout(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{auto_commit, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), TransStr = transaction_support_str(?RDBMS), {updated, _} = @@ -726,7 +706,7 @@ disconnect_on_timeout(Config) when is_list(Config) -> ok -> ok = odbc:commit(Ref, commit); nok -> - test_server:fail(database_locker_failed) + ct:fail(database_locker_failed) end. update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> @@ -744,14 +724,13 @@ update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> end. %%------------------------------------------------------------------------- -connection_closed(doc) -> - ["Checks that you get an appropriate error message if you try to" - " use a connection that has been closed"]; -connection_closed(suite) -> []; +connection_closed() -> + [{doc, "Checks that you get an appropriate error message if you try to" + " use a connection that has been closed"}]. connection_closed(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -771,14 +750,13 @@ connection_closed(Config) when is_list(Config) -> {error, connection_closed} = odbc:commit(Ref, commit). %%------------------------------------------------------------------------- -disable_scrollable_cursors(doc) -> - ["Test disabling of scrollable cursors."]; -disable_scrollable_cursors(suite) -> []; +disable_scrollable_cursors() -> + [{doc,"Test disabling of scrollable cursors."}]. disable_scrollable_cursors(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{scrollable_cursors, off}]), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -792,10 +770,10 @@ disable_scrollable_cursors(Config) when is_list(Config) -> NextResult = ?RDBMS:selected_ID(1, next), - test_server:format("Expected: ~p~n", [NextResult]), + ct:pal("Expected: ~p~n", [NextResult]), Result = odbc:next(Ref), - test_server:format("Got: ~p~n", [Result]), + ct:pal("Got: ~p~n", [Result]), NextResult = Result, {error, scrollable_cursors_disabled} = odbc:first(Ref), @@ -809,15 +787,14 @@ disable_scrollable_cursors(Config) when is_list(Config) -> {selected, _ColNames,[]} = odbc:select(Ref, next, 1). %%------------------------------------------------------------------------- -return_rows_as_lists(doc)-> - ["Test the option that a row may be returned as a list instead " - "of a tuple. Too be somewhat backward compatible."]; -return_rows_as_lists(suite) -> []; +return_rows_as_lists()-> + [{doc,"Test the option that a row may be returned as a list instead " + "of a tuple. Too be somewhat backward compatible."}]. return_rows_as_lists(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{tuple_row, off}] ++ odbc_test_lib:platform_options()), - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -854,29 +831,28 @@ return_rows_as_lists(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -api_missuse(doc)-> - ["Test that behaviour of the control process if the api is abused"]; -api_missuse(suite) -> []; +api_missuse()-> + [{doc,"Test that behaviour of the control process if the api is abused"}]. api_missuse(Config) when is_list(Config)-> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Serious programming fault, connetion will be shut down gen_server:call(Ref, {self(), foobar, 10}, infinity), - test_server:sleep(10), + ct:sleep(10), undefined = process_info(Ref, status), {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Serious programming fault, connetion will be shut down gen_server:cast(Ref2, {self(), foobar, 10}), - test_server:sleep(10), + ct:sleep(10), undefined = process_info(Ref2, status), {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Could be an innocent misstake the connection lives. Ref3 ! foobar, - test_server:sleep(10), + ct:sleep(10), {status, _} = process_info(Ref3, status). transaction_support_str(mysql) -> @@ -886,13 +862,13 @@ transaction_support_str(_) -> %%------------------------------------------------------------------------- -extended_errors(doc)-> - ["Test the extended errors connection option: When off; the old behaviour of just an error " - "string is returned on error. When on, the error string is replaced by a 3 element tuple " - "that also exposes underlying ODBC provider error codes."]; -extended_errors(suite) -> []; +extended_errors()-> + [{doc, + "Test the extended errors connection option: When off; the old behaviour of just an error " + "string is returned on error. When on, the error string is replaced by a 3 element tuple " + "that also exposes underlying ODBC provider error codes."}]. extended_errors(Config) when is_list(Config)-> - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), {updated, _} = odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))"), diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl index c88c00725e..a3a4bc78eb 100644 --- a/lib/odbc/test/odbc_data_type_SUITE.erl +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -120,6 +120,7 @@ init_per_suite(Config) when is_list(Config) -> false -> case (catch odbc:start()) of ok -> + ct:timetrap(?default_timeout), [{tableName, odbc_test_lib:unique_table_name()}| Config]; _ -> {skip, "ODBC not startable"} @@ -191,23 +192,22 @@ init_per_testcase(Case, Config) -> common_init_per_testcase(Case, Config) -> PlatformOptions = odbc_test_lib:platform_options(), - case atom_to_list(Case) of - "binary" ++ _ -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{binary_strings, on}] ++ PlatformOptions); - LCase when LCase == "utf8"; - LCase == "nchar"; - LCase == "nvarchar" -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{binary_strings, on}] ++ PlatformOptions); - _ -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), PlatformOptions) - end, + {ok, Ref} = + case atom_to_list(Case) of + "binary" ++ _ -> + odbc:connect(?RDBMS:connection_string(), + [{binary_strings, on}] ++ PlatformOptions); + LCase when LCase == "utf8"; + LCase == "nchar"; + LCase == "nvarchar" -> + odbc:connect(?RDBMS:connection_string(), + [{binary_strings, on}] ++ PlatformOptions); + _ -> + odbc:connect(?RDBMS:connection_string(), PlatformOptions) + end, odbc_test_lib:strict(Ref, ?RDBMS), - Dog = test_server:timetrap(?default_timeout), - Temp = lists:keydelete(connection_ref, 1, Config), - NewConfig = lists:keydelete(watchdog, 1, Temp), - [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. + NewConfig = lists:keydelete(connection_ref, 1, Config), + [{connection_ref, Ref} | NewConfig]. is_fixed_upper_limit(mysql) -> false; @@ -231,28 +231,23 @@ is_supported_bit(_) -> %% Description: Cleanup after each test case %%-------------------------------------------------------------------- end_per_testcase(_TestCase, Config) -> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), ok = odbc:disconnect(Ref), %% Clean up if needed - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc:sql_query(NewRef, "DROP TABLE " ++ Table), - odbc:disconnect(NewRef), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. + odbc:disconnect(NewRef). %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -char_fixed_lower_limit(doc) -> - ["Tests fixed length char data type lower boundaries."]; -char_fixed_lower_limit(suite) -> - []; +char_fixed_lower_limit() -> + [{doc,"Tests fixed length char data type lower boundaries."}]. char_fixed_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -287,18 +282,16 @@ char_fixed_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -char_fixed_upper_limit(doc) -> - ["Tests fixed length char data type upper boundaries."]; -char_fixed_upper_limit(suite) -> - []; +char_fixed_upper_limit() -> + [{doc,"Tests fixed length char data type upper boundaries."}]. char_fixed_upper_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Limit unknown"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! @@ -337,14 +330,12 @@ char_fixed_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -char_fixed_padding(doc) -> - ["Tests that data that is shorter than the given size is padded " - "with blanks."]; -char_fixed_padding(suite) -> - []; +char_fixed_padding() -> + [{doc, "Tests that data that is shorter than the given size is padded " + "with blanks."}]. char_fixed_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -365,13 +356,11 @@ char_fixed_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -varchar_lower_limit(doc) -> - ["Tests variable length char data type lower boundaries."]; -varchar_lower_limit(suite) -> - []; +varchar_lower_limit() -> + [{doc,"Tests variable length char data type lower boundaries."}]. varchar_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -405,13 +394,11 @@ varchar_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -varchar_upper_limit(doc) -> - ["Tests variable length char data type upper boundaries."]; -varchar_upper_limit(suite) -> - []; +varchar_upper_limit() -> + [{doc,"Tests variable length char data type upper boundaries."}]. varchar_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> @@ -455,14 +442,12 @@ varchar_upper_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -varchar_no_padding(doc) -> - ["Tests that data that is shorter than the given max size is not padded " - "with blanks."]; -varchar_no_padding(suite) -> - []; +varchar_no_padding() -> + [{doc, "Tests that data that is shorter than the given max size is not padded " + "with blanks."}]. varchar_no_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should NOT be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -481,13 +466,11 @@ varchar_no_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -text_lower_limit(doc) -> - ["Tests 'long' char data type lower boundaries."]; -text_lower_limit(suite) -> - []; +text_lower_limit() -> + [{doc,"Tests 'long' char data type lower boundaries."}]. text_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -504,15 +487,13 @@ text_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -text_upper_limit(doc) -> - []; -text_upper_limit(suite) -> - []; +text_upper_limit() -> + [{doc,"Tests 'text' char data type upper boundaries."}]. text_upper_limit(Config) when is_list(Config) -> {skip,"Consumes too much resources" }. -%% Ref = ?config(connection_ref, Config), -%% Table = ?config(tableName, Config), +%% Ref = proplists:get_value(connection_ref, Config), +%% Table = proplists:get_value(tableName, Config), %% {updated, _} = % Value == 0 || -1 driver dependent! %% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -534,13 +515,11 @@ text_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_char_fixed_lower_limit(doc) -> - ["Tests fixed length char data type lower boundaries."]; -binary_char_fixed_lower_limit(suite) -> - []; +binary_char_fixed_lower_limit() -> + [{doc,"Tests fixed length char data type lower boundaries."}]. binary_char_fixed_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -579,18 +558,16 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) -> ++ "')"). %%------------------------------------------------------------------------- -binary_char_fixed_upper_limit(doc) -> - ["Tests fixed length char data type upper boundaries."]; -binary_char_fixed_upper_limit(suite) -> - []; +binary_char_fixed_upper_limit() -> + [{doc,"Tests fixed length char data type upper boundaries."}]. binary_char_fixed_upper_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Limit unknown"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! @@ -630,14 +607,12 @@ binary_char_fixed_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_char_fixed_padding(doc) -> - ["Tests that data that is shorter than the given size is padded " - "with blanks."]; -binary_char_fixed_padding(suite) -> - []; +binary_char_fixed_padding() -> + [{doc, "Tests that data that is shorter than the given size is padded " + "with blanks."}]. binary_char_fixed_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -658,13 +633,11 @@ binary_char_fixed_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_varchar_lower_limit(doc) -> - ["Tests variable length char data type lower boundaries."]; -binary_varchar_lower_limit(suite) -> - []; +binary_varchar_lower_limit() -> + [{doc,"Tests variable length char data type lower boundaries."}]. binary_varchar_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Below limit {error, _} = @@ -701,13 +674,11 @@ binary_varchar_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_varchar_upper_limit(doc) -> - ["Tests variable length char data type upper boundaries."]; -binary_varchar_upper_limit(suite) -> - []; +binary_varchar_upper_limit() -> + [{doc,"Tests variable length char data type upper boundaries."}]. binary_varchar_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> @@ -750,14 +721,12 @@ binary_varchar_upper_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -binary_varchar_no_padding(doc) -> - ["Tests that data that is shorter than the given max size is not padded " - "with blanks."]; -binary_varchar_no_padding(suite) -> - []; +binary_varchar_no_padding() -> + [{doc,"Tests that data that is shorter than the given max size is not padded " + "with blanks."}]. binary_varchar_no_padding(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), %% Data should NOT be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! @@ -776,13 +745,11 @@ binary_varchar_no_padding(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_text_lower_limit(doc) -> - ["Tests 'long' char data type lower boundaries."]; -binary_text_lower_limit(suite) -> - []; +binary_text_lower_limit() -> + [{doc,"Tests 'long' char data type lower boundaries."}]. binary_text_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -799,15 +766,13 @@ binary_text_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -binary_text_upper_limit(doc) -> - []; -binary_text_upper_limit(suite) -> - []; +binary_text_upper_limit() -> + [{doc,"Tests text char data type upper boundaries."}]. binary_text_upper_limit(Config) when is_list(Config) -> {skip,"Consumes too much resources" }. -%% Ref = ?config(connection_ref, Config), -%% Table = ?config(tableName, Config), +%% Ref = proplists:get_value(connection_ref, Config), +%% Table = proplists:get_value(tableName, Config), %% {updated, _} = % Value == 0 || -1 driver dependent! %% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -830,17 +795,15 @@ binary_text_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -tiny_int_lower_limit(doc) -> - ["Tests integer of type tinyint."]; -tiny_int_lower_limit(suite) -> - []; +tiny_int_lower_limit() -> + [{doc,"Tests integer of type tinyint."}]. tiny_int_lower_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Type tiniyint not supported"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -864,17 +827,15 @@ tiny_int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -tiny_int_upper_limit(doc) -> - ["Tests integer of type tinyint."]; -tiny_int_upper_limit(suite) -> - []; +tiny_int_upper_limit() -> + [{doc,"Tests integer of type tinyint."}]. tiny_int_upper_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Type tiniyint not supported"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -898,13 +859,11 @@ tiny_int_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -small_int_lower_limit(doc) -> - ["Tests integer of type smallint."]; -small_int_lower_limit(suite) -> - []; +small_int_lower_limit() -> + [{doc,"Tests integer of type smallint."}]. small_int_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -927,13 +886,11 @@ small_int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -small_int_upper_limit(doc) -> - ["Tests integer of type smallint."]; -small_int_upper_limit(suite) -> - []; +small_int_upper_limit() -> + [{doc,"Tests integer of type smallint."}]. small_int_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -955,13 +912,11 @@ small_int_upper_limit(Config) when is_list(Config) -> ++ "')"). %%------------------------------------------------------------------------- -int_lower_limit(doc) -> - ["Tests integer of type int."]; -int_lower_limit(suite) -> - []; +int_lower_limit() -> + [{doc,"Tests integer of type int."}]. int_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -983,13 +938,11 @@ int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -int_upper_limit(doc) -> - ["Tests integer of type int."]; -int_upper_limit(suite) -> - []; +int_upper_limit() -> + [{doc,"Tests integer of type int."}]. int_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1011,13 +964,11 @@ int_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -big_int_lower_limit(doc) -> - ["Tests integer of type bigint"]; -big_int_lower_limit(suite) -> - []; +big_int_lower_limit() -> + [{doc,"Tests integer of type bigint"}]. big_int_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1040,13 +991,11 @@ big_int_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -big_int_upper_limit(doc) -> - ["Tests integer of type bigint."]; -big_int_upper_limit(suite) -> - []; +big_int_upper_limit() -> + [{doc,"Tests integer of type bigint."}]. big_int_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1068,17 +1017,13 @@ big_int_upper_limit(Config) when is_list(Config) -> ++ "')"). %%------------------------------------------------------------------------- -bit_false(doc) -> - [""]; -bit_false(suite) -> - []; bit_false(Config) when is_list(Config) -> case ?RDBMS of oracle -> {skip, "Not supported by driver"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1102,17 +1047,13 @@ bit_false(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -bit_true(doc) -> - [""]; -bit_true(suite) -> - []; bit_true(Config) when is_list(Config) -> case ?RDBMS of oracle -> {skip, "Not supported by driver"}; _ -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! @@ -1136,14 +1077,11 @@ bit_true(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -float_lower_limit(doc) -> - [""]; -float_lower_limit(suite) -> - []; + float_lower_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of mysql -> @@ -1186,13 +1124,10 @@ float_lower_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -float_upper_limit(doc) -> - [""]; -float_upper_limit(suite) -> - []; + float_upper_limit(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of mysql -> @@ -1218,13 +1153,11 @@ float_upper_limit(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -float_zero(doc) -> - ["Test the float value zero."]; -float_zero(suite) -> - []; +float_zero() -> + [{doc,"Test the float value zero."}]. float_zero(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1237,13 +1170,11 @@ float_zero(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table). %%------------------------------------------------------------------------- -real_zero(doc) -> - ["Test the real value zero."]; -real_zero(suite) -> - []; +real_zero() -> + [{doc,"Test the real value zero."}]. real_zero(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> @@ -1262,13 +1193,11 @@ real_zero(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table) end. %%------------------------------------------------------------------------ -dec_long(doc) -> - [""]; dec_long(suit) -> []; dec_long(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1281,13 +1210,11 @@ dec_long(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------ -dec_double(doc) -> - [""]; dec_double(suit) -> []; dec_double(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1329,13 +1256,11 @@ dec_double(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ -dec_bignum(doc) -> - [""]; dec_bignum(suit) -> []; dec_bignum(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1361,13 +1286,11 @@ dec_bignum(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ -num_long(doc) -> - [""]; num_long(suit) -> []; num_long(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1380,13 +1303,11 @@ num_long(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------ -num_double(doc) -> - [""]; num_double(suit) -> []; num_double(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1426,13 +1347,11 @@ num_double(Config) when is_list(Config) -> odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ -num_bignum(doc) -> - [""]; num_bignum(suit) -> []; num_bignum(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1459,13 +1378,13 @@ num_bignum(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ -utf8(doc) -> - ["Test unicode support"]; +utf8() -> + [{doc,"Test unicode support"}]. utf8(suit) -> []; utf8(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"), @@ -1487,30 +1406,30 @@ utf8(Config) when is_list(Config) -> end, Latin1Data), - test_server:format("UnicodeIn: ~p ~n",[UnicodeIn]), + ct:pal("UnicodeIn: ~p ~n",[UnicodeIn]), {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)", [{{sql_varchar,50}, UnicodeIn}]), {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), - test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + ct:pal("UnicodeOut: ~p~n", [UnicodeOut]), Result = lists:map(fun({Char}) -> unicode:characters_to_list(Char,utf8) end, UnicodeOut), - test_server:format("Result: ~p ~n", [Result]), + ct:pal("Result: ~p ~n", [Result]), Latin1Data = Result. %%------------------------------------------------------------------------ -nchar(doc) -> - ["Test unicode nchar support in sqlserver"]; +nchar() -> + [{doc,"Test unicode nchar support in sqlserver"}]. nchar(suit) -> []; nchar(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1520,13 +1439,13 @@ nchar(Config) when is_list(Config) -> %%------------------------------------------------------------------------ -nvarchar(doc) -> - ["Test 'unicode' nvarchar support"]; +nvarchar() -> + [{doc,"Test 'unicode' nvarchar support"}]. nvarchar(suit) -> []; nvarchar(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1535,13 +1454,11 @@ nvarchar(Config) when is_list(Config) -> w_char_support(Ref, Table, sql_wlongvarchar, 50). %%------------------------------------------------------------------------ -timestamp(doc) -> - [""]; timestamp(suit) -> []; timestamp(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1582,21 +1499,21 @@ w_char_support(Ref, Table, CharType, Size) -> end, Latin1Data), - test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), + ct:pal("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", [{{CharType, Size},UnicodeIn}]), {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), - test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + ct:pal("UnicodeOut: ~p~n", [UnicodeOut]), PadResult = lists:map(fun({Unicode}) -> unicode:characters_to_list(Unicode,{utf16,little}) end, UnicodeOut), - test_server:format("Result: ~p~n", [PadResult]), + ct:pal("Result: ~p~n", [PadResult]), Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult), diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl index 5f719b7287..c283872965 100644 --- a/lib/odbc/test/odbc_query_SUITE.erl +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -113,6 +113,7 @@ init_per_suite(Config) when is_list(Config) -> false -> case (catch odbc:start()) of ok -> + ct:timetrap(?default_timeout), [{tableName, odbc_test_lib:unique_table_name()}| Config]; _ -> {skip, "ODBC not startable"} @@ -144,10 +145,10 @@ end_per_suite(_Config) -> init_per_testcase(_Case, Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc_test_lib:strict(Ref, ?RDBMS), - Dog = test_server:timetrap(?default_timeout), - Temp = lists:keydelete(connection_ref, 1, Config), - NewConfig = lists:keydelete(watchdog, 1, Temp), - [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. + + NewConfig = lists:keydelete(connection_ref, 1, Config), + + [{connection_ref, Ref} | NewConfig]. %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ @@ -158,27 +159,23 @@ init_per_testcase(_Case, Config) -> %% Description: Cleanup after each test case %%-------------------------------------------------------------------- end_per_testcase(_Case, Config) -> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), ok = odbc:disconnect(Ref), %% Clean up if needed - Table = ?config(tableName, Config), + Table = proplists:get_value(tableName, Config), {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc:sql_query(NewRef, "DROP TABLE " ++ Table), - odbc:disconnect(NewRef), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. + odbc:disconnect(NewRef). %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -stored_proc(doc)-> - ["Test stored proc with OUT param"]; -stored_proc(suite) -> []; +stored_proc()-> + [{doc, "Test stored proc with OUT param"}]. stored_proc(Config) when is_list(Config) -> case ?RDBMS of X when X == oracle; X == postgres-> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), {updated, _} = odbc:sql_query(Ref, ?RDBMS:stored_proc_integer_out()), @@ -192,12 +189,11 @@ stored_proc(Config) when is_list(Config) -> {skip, "stored proc not yet supported"} end. -sql_query(doc)-> - ["Test the common cases"]; -sql_query(suite) -> []; +sql_query()-> + [{doc, "Test the common cases"}]. sql_query(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -235,14 +231,14 @@ sql_query(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_count(doc) -> - ["Tests select_count/[2,3]'s timeout, " - " select_count's functionality will be better tested by other tests " - " such as first."]; +select_count() -> + [{doc, "Tests select_count/[2,3]'s timeout, " + " select_count's functionality will be better tested by other tests " + " such as first."}]. select_count(sute) -> []; select_count(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -257,12 +253,11 @@ select_count(Config) when is_list(Config) -> (catch odbc:select_count(Ref, "SELECT * FROM ", -1)), ok. %%------------------------------------------------------------------------- -first(doc) -> - ["Tests first/[1,2]"]; -first(suite) -> []; +first() -> + [doc, {"Tests first/[1,2]"}]. first(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -284,12 +279,11 @@ first(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -last(doc) -> - ["Tests last/[1,2]"]; -last(suite) -> []; +last() -> + [{doc, "Tests last/[1,2]"}]. last(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -311,12 +305,11 @@ last(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -next(doc) -> - ["Tests next/[1,2]"]; -next(suite) -> []; +next() -> + [{doc, "Tests next/[1,2]"}]. next(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -337,12 +330,11 @@ next(Config) when is_list(Config) -> {'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)), ok. %%------------------------------------------------------------------------- -prev(doc) -> - ["Tests prev/[1,2]"]; -prev(suite) -> []; +prev() -> + [{doc, "Tests prev/[1,2]"}]. prev(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -366,12 +358,12 @@ prev(Config) when is_list(Config) -> {'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)), ok. %%------------------------------------------------------------------------- -select_next(doc) -> - ["Tests select/[4,5] with CursorRelation = next "]; +select_next() -> + [{doc, "Tests select/[4,5] with CursorRelation = next "}]. select_next(suit) -> []; select_next(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -407,12 +399,12 @@ select_next(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_relative(doc) -> - ["Tests select/[4,5] with CursorRelation = relative "]; +select_relative() -> + [{doc, "Tests select/[4,5] with CursorRelation = relative "}]. select_relative(suit) -> []; select_relative(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -448,12 +440,12 @@ select_relative(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_absolute(doc) -> - ["Tests select/[4,5] with CursorRelation = absolute "]; +select_absolute() -> + [{doc, "Tests select/[4,5] with CursorRelation = absolute "}]. select_absolute(suit) -> []; select_absolute(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -482,12 +474,11 @@ select_absolute(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -create_table_twice(doc) -> - ["Test what happens if you try to create the same table twice."]; -create_table_twice(suite) -> []; +create_table_twice() -> + [{doc, "Test what happens if you try to create the same table twice."}]. create_table_twice(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -501,12 +492,11 @@ create_table_twice(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -delete_table_twice(doc) -> - ["Test what happens if you try to delete the same table twice."]; -delete_table_twice(suite) -> []; +delete_table_twice() -> + [{doc, "Test what happens if you try to delete the same table twice."}]. delete_table_twice(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -518,12 +508,12 @@ delete_table_twice(Config) when is_list(Config) -> ok. %------------------------------------------------------------------------- -duplicate_key(doc) -> - ["Test what happens if you try to use the same key twice"]; +duplicate_key() -> + [{doc, "Test what happens if you try to use the same key twice"}]. duplicate_key(suit) -> []; duplicate_key(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -539,13 +529,12 @@ duplicate_key(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -not_connection_owner(doc) -> - ["Test what happens if a process that did not start the connection" - " tries to acess it."]; -not_connection_owner(suite) -> []; +not_connection_owner() -> + [{doc, "Test what happens if a process that did not start the connection" + " tries to acess it."}]. not_connection_owner(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), spawn_link(?MODULE, not_owner, [self(), Ref, Table]), @@ -564,12 +553,11 @@ not_owner(Pid, Ref, Table) -> Pid ! continue. %%------------------------------------------------------------------------- -no_result_set(doc) -> - ["Tests what happens if you try to use a function that needs an " - "associated result set when there is none."]; -no_result_set(suite) -> []; +no_result_set() -> + [{doc, "Tests what happens if you try to use a function that needs an " + "associated result set when there is none."}]. no_result_set(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), {error, result_set_does_not_exist} = odbc:first(Ref), {error, result_set_does_not_exist} = odbc:last(Ref), @@ -582,13 +570,11 @@ no_result_set(Config) when is_list(Config) -> odbc:select(Ref, {relative, 2}, 1), ok. %%------------------------------------------------------------------------- -query_error(doc) -> - ["Test what happens if there is an error in the query."]; -query_error(suite) -> - []; +query_error() -> + [{doc, "Test what happens if there is an error in the query."}]. query_error(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -605,15 +591,13 @@ query_error(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -multiple_select_result_sets(doc) -> - ["Test what happens if you have a batch of select queries."]; -multiple_select_result_sets(suite) -> - []; +multiple_select_result_sets() -> + [{doc, "Test what happens if you have a batch of select queries."}]. multiple_select_result_sets(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -640,16 +624,14 @@ multiple_select_result_sets(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -multiple_mix_result_sets(doc) -> - ["Test what happens if you have a batch of select and other type of" - " queries."]; -multiple_mix_result_sets(suite) -> - []; +multiple_mix_result_sets() -> + [{doc, "Test what happens if you have a batch of select and other type of" + " queries."}]. multiple_mix_result_sets(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -674,15 +656,13 @@ multiple_mix_result_sets(Config) when is_list(Config) -> {skip, "multiple result_set not supported"} end. %%------------------------------------------------------------------------- -multiple_result_sets_error(doc) -> - ["Test what happens if one of the batched queries fails."]; -multiple_result_sets_error(suite) -> - []; +multiple_result_sets_error() -> + [{doc, "Test what happens if one of the batched queries fails."}]. multiple_result_sets_error(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -709,15 +689,13 @@ multiple_result_sets_error(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- -param_insert_tiny_int(doc)-> - ["Test insertion of tiny ints by parameterized queries."]; -param_insert_tiny_int(suite) -> - []; +param_insert_tiny_int()-> + [{doc,"Test insertion of tiny ints by parameterized queries."}]. param_insert_tiny_int(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -746,13 +724,11 @@ param_insert_tiny_int(Config) when is_list(Config) -> {skip, "Type tiniyint not supported"} end. %%------------------------------------------------------------------------- -param_insert_small_int(doc)-> - ["Test insertion of small ints by parameterized queries."]; -param_insert_small_int(suite) -> - []; +param_insert_small_int()-> + [{doc,"Test insertion of small ints by parameterized queries."}]. param_insert_small_int(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -778,13 +754,11 @@ param_insert_small_int(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_int(doc)-> - ["Test insertion of ints by parameterized queries."]; -param_insert_int(suite) -> - []; +param_insert_int()-> + [{doc,"Test insertion of ints by parameterized queries."}]. param_insert_int(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -810,13 +784,11 @@ param_insert_int(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_integer(doc)-> - ["Test insertion of integers by parameterized queries."]; -param_insert_integer(suite) -> - []; +param_insert_integer()-> + [{doc,"Test insertion of integers by parameterized queries."}]. param_insert_integer(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -842,13 +814,11 @@ param_insert_integer(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_decimal(doc)-> - ["Test insertion of decimal numbers by parameterized queries."]; -param_insert_decimal(suite) -> - []; +param_insert_decimal()-> + [{doc,"Test insertion of decimal numbers by parameterized queries."}]. param_insert_decimal(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -893,13 +863,11 @@ param_insert_decimal(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_numeric(doc)-> - ["Test insertion of numeric numbers by parameterized queries."]; -param_insert_numeric(suite) -> - []; +param_insert_numeric()-> + [{doc,"Test insertion of numeric numbers by parameterized queries."}]. param_insert_numeric(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -944,13 +912,11 @@ param_insert_numeric(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_char(doc)-> - ["Test insertion of fixed length string by parameterized queries."]; -param_insert_char(suite) -> - []; +param_insert_char()-> + [{doc,"Test insertion of fixed length string by parameterized queries."}]. param_insert_char(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -980,13 +946,11 @@ param_insert_char(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_character(doc)-> - ["Test insertion of fixed length string by parameterized queries."]; -param_insert_character(suite) -> - []; +param_insert_character()-> + [{doc,"Test insertion of fixed length string by parameterized queries."}]. param_insert_character(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1017,13 +981,11 @@ param_insert_character(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------ -param_insert_char_varying(doc)-> - ["Test insertion of variable length strings by parameterized queries."]; -param_insert_char_varying(suite) -> - []; +param_insert_char_varying()-> + [{doc,"Test insertion of variable length strings by parameterized queries."}]. param_insert_char_varying(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1054,13 +1016,11 @@ param_insert_char_varying(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_character_varying(doc)-> - ["Test insertion of variable length strings by parameterized queries."]; -param_insert_character_varying(suite) -> - []; +param_insert_character_varying()-> + [{doc,"Test insertion of variable length strings by parameterized queries."}]. param_insert_character_varying(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1091,13 +1051,11 @@ param_insert_character_varying(Config) when is_list(Config) -> [{{sql_varchar, 10}, ["1", 2]}])), ok. %%------------------------------------------------------------------------- -param_insert_float(doc)-> - ["Test insertion of floats by parameterized queries."]; -param_insert_float(suite) -> - []; +param_insert_float()-> + [{doc,"Test insertion of floats by parameterized queries."}]. param_insert_float(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1120,7 +1078,7 @@ param_insert_float(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail(float_numbers_do_not_match) + ct:fail(float_numbers_do_not_match) end, {'EXIT',{badarg,odbc,param_query,'Params'}} = @@ -1130,13 +1088,11 @@ param_insert_float(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_real(doc)-> - ["Test insertion of real numbers by parameterized queries."]; -param_insert_real(suite) -> - []; +param_insert_real()-> + [{doc,"Test insertion of real numbers by parameterized queries."}]. param_insert_real(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1161,7 +1117,7 @@ param_insert_real(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail(real_numbers_do_not_match) + ct:fail(real_numbers_do_not_match) end, {'EXIT',{badarg,odbc,param_query,'Params'}} = @@ -1171,13 +1127,11 @@ param_insert_real(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_double(doc)-> - ["Test insertion of doubles by parameterized queries."]; -param_insert_double(suite) -> - []; +param_insert_double()-> + [{doc,"Test insertion of doubles by parameterized queries."}]. param_insert_double(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1200,7 +1154,7 @@ param_insert_double(Config) when is_list(Config) -> true -> ok; false -> - test_server:fail(double_numbers_do_not_match) + ct:fail(double_numbers_do_not_match) end, {'EXIT',{badarg,odbc,param_query,'Params'}} = @@ -1210,13 +1164,11 @@ param_insert_double(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_insert_mix(doc)-> - ["Test insertion of a mixture of datatypes by parameterized queries."]; -param_insert_mix(suite) -> - []; +param_insert_mix()-> + [{doc,"Test insertion of a mixture of datatypes by parameterized queries."}]. param_insert_mix(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1237,13 +1189,11 @@ param_insert_mix(Config) when is_list(Config) -> odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ok. %%------------------------------------------------------------------------- -param_update(doc)-> - ["Test parameterized update query."]; -param_update(suite) -> - []; +param_update()-> + [{doc,"Test parameterized update query."}]. param_update(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1272,12 +1222,12 @@ param_update(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -delete_nonexisting_row(doc) -> % OTP-5759 - ["Make a delete...where with false conditions (0 rows deleted). ", - "This used to give an error message (see ticket OTP-5759)."]; +delete_nonexisting_row() -> % OTP-5759 + [{doc, "Make a delete...where with false conditions (0 rows deleted). ", + "This used to give an error message (see ticket OTP-5759)."}]. delete_nonexisting_row(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table @@ -1301,13 +1251,11 @@ delete_nonexisting_row(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_delete(doc) -> - ["Test parameterized delete query."]; -param_delete(suite) -> - []; +param_delete() -> + [{doc,"Test parameterized delete query."}]. param_delete(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1336,13 +1284,11 @@ param_delete(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -param_select(doc) -> - ["Test parameterized select query."]; -param_select(suite) -> - []; +param_select() -> + [{doc,"Test parameterized select query."}]. param_select(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1366,13 +1312,11 @@ param_select(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_select_empty_params(doc) -> - ["Test parameterized select query with no parameters."]; -param_select_empty_params(suite) -> - []; +param_select_empty_params() -> + [{doc,"Test parameterized select query with no parameters."}]. param_select_empty_params(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1396,13 +1340,11 @@ param_select_empty_params(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -param_delete_empty_params(doc) -> - ["Test parameterized delete query with no parameters."]; -param_delete_empty_params(suite) -> - []; +param_delete_empty_params() -> + [{doc,"Test parameterized delete query with no parameters."}]. param_delete_empty_params(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1430,13 +1372,11 @@ param_delete_empty_params(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_integer(doc) -> - ["Test describe_table/[2,3] for integer columns."]; -describe_integer(suite) -> - []; +describe_integer() -> + [{doc,"Test describe_table/[2,3] for integer columns."}]. describe_integer(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1449,13 +1389,11 @@ describe_integer(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_string(doc) -> - ["Test describe_table/[2,3] for string columns."]; -describe_string(suite) -> - []; +describe_string() -> + [{doc,"Test describe_table/[2,3] for string columns."}]. describe_string(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1470,13 +1408,11 @@ describe_string(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_floating(doc) -> - ["Test describe_table/[2,3] for floting columns."]; -describe_floating(suite) -> - []; +describe_floating() -> + [{doc,"Test describe_table/[2,3] for floting columns."}]. describe_floating(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1490,14 +1426,12 @@ describe_floating(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_dec_num(doc) -> - ["Test describe_table/[2,3] for decimal and numerical columns"]; -describe_dec_num(suite) -> - []; +describe_dec_num() -> + [{doc,"Test describe_table/[2,3] for decimal and numerical columns"}]. describe_dec_num(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, @@ -1511,14 +1445,12 @@ describe_dec_num(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -describe_timestamp(doc) -> - ["Test describe_table/[2,3] for tinmestap columns"]; -describe_timestamp(suite) -> - []; +describe_timestamp() -> + [{doc,"Test describe_table/[2,3] for tinmestap columns"}]. describe_timestamp(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ @@ -1530,14 +1462,12 @@ describe_timestamp(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -describe_no_such_table(doc) -> - ["Test what happens if you try to describe a table that does not exist."]; -describe_no_such_table(suite) -> - []; +describe_no_such_table() -> + [{doc,"Test what happens if you try to describe a table that does not exist."}]. describe_no_such_table(Config) when is_list(Config) -> - Ref = ?config(connection_ref, Config), - Table = ?config(tableName, Config), + Ref = proplists:get_value(connection_ref, Config), + Table = proplists:get_value(tableName, Config), {error, _ } = odbc:describe_table(Ref, Table), ok. @@ -1549,10 +1479,10 @@ describe_no_such_table(Config) when is_list(Config) -> is_driver_error(Error) -> case is_list(Error) of true -> - test_server:format("Driver error ~p~n", [Error]), + ct:pal("Driver error ~p~n", [Error]), ok; false -> - test_server:fail(Error) + ct:fail(Error) end. is_supported_multiple_resultsets(sqlserver) -> true; diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl index f055eeb60e..310b92ca29 100644 --- a/lib/odbc/test/odbc_start_SUITE.erl +++ b/lib/odbc/test/odbc_start_SUITE.erl @@ -49,6 +49,7 @@ init_per_suite(Config) -> _ -> %% Make sure odbc is not already started odbc:stop(), + ct:timetrap(?TIMEOUT), [{tableName, odbc_test_lib:unique_table_name()} | Config] end end. @@ -74,11 +75,9 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. +init_per_testcase(_TestCase, Config) -> + ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), + Config. %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ @@ -88,15 +87,8 @@ init_per_testcase(_TestCase, Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - +end_per_testcase(_TestCase, _Config) -> + ok. %%-------------------------------------------------------------------- %% Function: all(Clause) -> TestCases %% Clause - atom() - suite | doc @@ -135,10 +127,8 @@ app(Config) when is_list(Config) -> appup(Config) when is_list(Config) -> ok = ?t:appup_test(odbc). -start(doc) -> - ["Test start/stop of odbc"]; -start(suite) -> - []; +start() -> + [{doc,"Test start/stop of odbc"}]. start(Config) when is_list(Config) -> PlatformOptions = odbc_test_lib:platform_options(), {error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(), @@ -153,9 +143,9 @@ start(Config) when is_list(Config) -> start_odbc(transient), start_odbc(permanent); {error, odbc_not_started} -> - test_server:fail(start_failed); + ct:fail(start_failed); Error -> - test_server:format("Connection failed: ~p~n", [Error]), + ct:pal("Connection failed: ~p~n", [Error]), {skip, "ODBC is not properly setup"} end. @@ -166,13 +156,12 @@ start_odbc(Type) -> ok = odbc:disconnect(Ref), odbc:stop(); {error, odbc_not_started} -> - test_server:fail(start_failed) + ct:fail(start_failed) end. -long_connection_line(doc)-> - ["Test a connection line longer than 127 characters"]; -long_connection_line(suite) -> []; +long_connection_line()-> + [{doc,"Test a connection line longer than 127 characters"}]. long_connection_line(_Config) -> odbc:start(), String133 = "unknown_odbc_parameter=01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl index 37c2249303..cf82d4d32a 100644 --- a/lib/odbc/test/odbc_test_lib.erl +++ b/lib/odbc/test/odbc_test_lib.erl @@ -61,13 +61,13 @@ odbc_check() -> end. check_row_count(Count, Count) -> - test_server:format("Correct row count Count: ~p~n", [Count]), + ct:pal("Correct row count Count: ~p~n", [Count]), true; check_row_count(_, undefined) -> - test_server:format("Undefined row count ~n", []), + ct:pal("Undefined row count ~n", []), true; check_row_count(Expected, Count) -> - test_server:format("Incorrect row count Expected ~p Got ~p~n", + ct:pal("Incorrect row count Expected ~p Got ~p~n", [Expected, Count]), false. diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk index c7c84560d1..957c6b42eb 100644 --- a/lib/odbc/vsn.mk +++ b/lib/odbc/vsn.mk @@ -1 +1 @@ -ODBC_VSN = 2.11.1 +ODBC_VSN = 2.11.2 diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml index 74d9d7a98c..89f258e5e9 100644 --- a/lib/orber/doc/src/notes.xml +++ b/lib/orber/doc/src/notes.xml @@ -34,7 +34,22 @@ </header> - <section><title>Orber 3.8.1</title> + <section><title>Orber 3.8.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>Orber 3.8.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk index 4947315ad0..dcb2c985a3 100644 --- a/lib/orber/vsn.mk +++ b/lib/orber/vsn.mk @@ -1 +1 @@ -ORBER_VSN = 3.8.1 +ORBER_VSN = 3.8.2 diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml index c565df7f3b..e6e80b046d 100644 --- a/lib/os_mon/doc/src/notes.xml +++ b/lib/os_mon/doc/src/notes.xml @@ -31,6 +31,40 @@ </header> <p>This document describes the changes made to the OS_Mon application.</p> +<section><title>Os_Mon 2.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix type specification for cpu_sup:util/1</p> + <p> + Own Id: OTP-13526 Aux Id: PR-1029 </p> + </item> + <item> + <p> + Fix strict compilation on SUN/SPARC</p> + <p> + Own Id: OTP-13548 Aux Id: PR-1046 </p> + </item> + <item> + <p> + Implement cpu_sup:util/0,1 on Mac OSX</p> + <p> + Own Id: OTP-13597 Aux Id: PR-1049 </p> + </item> + <item> + <p> + Fix memsup:get_os_wordsize() on 64-bit FreeBSD and 64-bit + Linux PPC</p> + <p> + Own Id: OTP-13601 Aux Id: PR-1039 </p> + </item> + </list> + </section> + +</section> + <section><title>Os_Mon 2.4</title> <section><title>Improvements and New Features</title> diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk index 7f2667e40a..1ac0fb1d27 100644 --- a/lib/os_mon/vsn.mk +++ b/lib/os_mon/vsn.mk @@ -1 +1 @@ -OS_MON_VSN = 2.4 +OS_MON_VSN = 2.4.1 diff --git a/lib/otp_mibs/doc/src/notes.xml b/lib/otp_mibs/doc/src/notes.xml index 7beac5ffcb..dbd2f47ffb 100644 --- a/lib/otp_mibs/doc/src/notes.xml +++ b/lib/otp_mibs/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the OTP_Mibs application.</p> +<section><title>Otp_Mibs 1.1.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + <section><title>Otp_Mibs 1.1</title> <section><title>Improvements and New Features</title> diff --git a/lib/otp_mibs/vsn.mk b/lib/otp_mibs/vsn.mk index 38436d363e..7a793007ee 100644 --- a/lib/otp_mibs/vsn.mk +++ b/lib/otp_mibs/vsn.mk @@ -1,4 +1,4 @@ -OTP_MIBS_VSN = 1.1 +OTP_MIBS_VSN = 1.1.1 # Note: The branch 'otp_mibs' is defunct as of otp_mibs-1.0.4 and # should NOT be used again. diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml index 06d66e28c3..b826b4d03a 100644 --- a/lib/parsetools/doc/src/notes.xml +++ b/lib/parsetools/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the Parsetools application.</p> +<section><title>Parsetools 2.1.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + <section><title>Parsetools 2.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk index de3da23c8a..befdd82d6e 100644 --- a/lib/parsetools/vsn.mk +++ b/lib/parsetools/vsn.mk @@ -1 +1 @@ -PARSETOOLS_VSN = 2.1.1 +PARSETOOLS_VSN = 2.1.2 diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml index 750dcb6cf5..c9d5d3ae29 100644 --- a/lib/percept/doc/src/notes.xml +++ b/lib/percept/doc/src/notes.xml @@ -33,6 +33,35 @@ </header> <p>This document describes the changes made to the Percept application.</p> +<section><title>Percept 0.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Remove deprecated <c>erlang:now/0</c> calls</p> + <p> + Own Id: OTP-13422</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Improve line implementation</p> + <p> + Add capabilities for line thickness and anti-aliasing.</p> + <p> + Own Id: OTP-13598</p> + </item> + </list> + </section> + +</section> + <section><title>Percept 0.8.11</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk index 833ab35aa5..614cee8645 100644 --- a/lib/percept/vsn.mk +++ b/lib/percept/vsn.mk @@ -1 +1 @@ -PERCEPT_VSN = 0.8.11 +PERCEPT_VSN = 0.9 diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index 49b2ba0326..c4d930c01f 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -35,6 +35,34 @@ <file>notes.xml</file> </header> +<section><title>Public_Key 1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The ASN-1 type GeneralName can have more values, then the + most common directory name, the code now handles this.</p> + <p> + Own Id: OTP-13554</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Handle PEM encoded EC public keys</p> + <p> + Own Id: OTP-13408</p> + </item> + </list> + </section> + +</section> + <section><title>Public_Key 1.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index f801f55073..84f6a659b5 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1 +1 @@ -PUBLIC_KEY_VSN = 1.1.1 +PUBLIC_KEY_VSN = 1.2 diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml index 0a83954865..6df4924d0a 100644 --- a/lib/reltool/doc/src/notes.xml +++ b/lib/reltool/doc/src/notes.xml @@ -38,7 +38,24 @@ thus constitutes one section in this document. The title of each section is the version number of Reltool.</p> - <section><title>Reltool 0.7</title> + <section><title>Reltool 0.7.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Modify the code as motivated by a change of the + Erlang Parser (<c>undefined</c> is no longer + automatically inserted to the type of record fields + without an initializer). </p> + <p> + Own Id: OTP-13033 Aux Id: OTP-12719 </p> + </item> + </list> + </section> + +</section> + +<section><title>Reltool 0.7</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk index 733c41bc02..76f69fd294 100644 --- a/lib/reltool/vsn.mk +++ b/lib/reltool/vsn.mk @@ -1 +1 @@ -RELTOOL_VSN = 0.7 +RELTOOL_VSN = 0.7.1 diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml index 57241edbdc..6bec7cb9ca 100644 --- a/lib/runtime_tools/doc/src/notes.xml +++ b/lib/runtime_tools/doc/src/notes.xml @@ -32,6 +32,100 @@ <p>This document describes the changes made to the Runtime_Tools application.</p> +<section><title>Runtime_Tools 1.10</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix bug in dbg:trace_port/2 that could cause the trace ip + driver to produce faulty error reports "...(re)selected + before stop_select was called for driver trace_ip_drv".</p> + <p> + Own Id: OTP-13576 Aux Id: ERL-119 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add microstate accounting</p> + <p> + Microstate accounting is a way to track which state the + different threads within ERTS are in. The main usage area + is to pin point performance bottlenecks by checking which + states the threads are in and then from there figuring + out why and where to optimize.</p> + <p> + Since checking whether microstate accounting is on or off + is relatively expensive only a few of the states are + enabled by default and more states can be enabled through + configure.</p> + <p> + There is a convenience module called msacc that has been + added to runtime_tools that can assist in gathering and + interpreting the data from Microstate accounting.</p> + <p> + For more information see <seealso + marker="erts:erlang#statistics_microstate_accounting">erlang:statistics(microstate_accounting, + _)</seealso> and the <seealso + marker="runtime_tools:msacc">msacc</seealso> module in + runtime_tools.</p> + <p> + Own Id: OTP-12345</p> + </item> + <item> + <p> + Update observer GUI to support tracing on ports, and to + set matchspecs for send/receive. This required some minor + bugfixes in runtime_tools/dbg.</p> + <p> + Own Id: OTP-13481</p> + </item> + <item> + <p> + Update dbg and ttb to work with a tracer module as tracer + and tracing on ports.</p> + <p> + Own Id: OTP-13500</p> + </item> + <item> + <p> + Updated dbg to accept the new trace options + <c>monotonic_timestamp</c> and + <c>strict_monotonic_timestamp</c>.</p> + <p> + Own Id: OTP-13502</p> + </item> + <item> + <p> + Introduce LTTng tracing via Erlang tracing.</p> + <p> + For LTTng to be enabled OTP needs to be built with + configure option <c>--with-dynamic-trace=lttng</c>.</p> + <p>The dynamic trace module <c>dyntrace</c> is now + capable to be used as a LTTng sink for Erlang tracing. + For a list of all tracepoints, see <seealso + marker="runtime_tools:LTTng">Runtime Tools User's + Guide</seealso> .</p> + <p>This feature also introduces an incompatible change in + trace tags. The trace tags <c>gc_start</c> and + <c>gc_end</c> has been split into <c>gc_minor_start</c>, + <c>gc_minor_end</c> and <c>gc_major_start</c>, + <c>gc_major_end</c>.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13532</p> + </item> + </list> + </section> + +</section> + <section><title>Runtime_Tools 1.9.3</title> <section><title>Improvements and New Features</title> diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk index bfc8b84b91..b33f6f4721 100644 --- a/lib/runtime_tools/vsn.mk +++ b/lib/runtime_tools/vsn.mk @@ -1 +1 @@ -RUNTIME_TOOLS_VSN = 1.9.3 +RUNTIME_TOOLS_VSN = 1.10 diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml index f07938220c..dae73f8b23 100644 --- a/lib/sasl/doc/src/notes.xml +++ b/lib/sasl/doc/src/notes.xml @@ -31,6 +31,23 @@ </header> <p>This document describes the changes made to the SASL application.</p> +<section><title>SASL 3.0</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The module 'overload' is removed.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13184</p> + </item> + </list> + </section> + +</section> + <section><title>SASL 2.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index b9dc5e4117..0f5c35b300 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -34,7 +34,22 @@ </header> - <section><title>SNMP 5.2.2</title> + <section><title>SNMP 5.2.3</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + +<section><title>SNMP 5.2.2</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index 94325a8ed6..fc5116dac9 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -154,7 +154,7 @@ do_write_agent_conf(Fd, {intAgentMaxPacketSize = Tag, Val} ) -> do_write_agent_conf(Fd, {snmpEngineMaxMessageSize = Tag, Val} ) -> io:format(Fd, "{~w, ~w}.~n", [Tag, Val]); do_write_agent_conf(Fd, {snmpEngineID = Tag, Val} ) -> - io:format(Fd, "{~w, \"~s\"}.~n", [Tag, Val]); + io:format(Fd, "{~w, ~p}.~n", [Tag, Val]); do_write_agent_conf(_Fd, Crap) -> error({bad_agent_config, Crap}). @@ -758,9 +758,9 @@ do_write_usm_conf( PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}) -> io:format(Fd, "{", []), - io:format(Fd, "\"~s\", ", [EngineID]), - io:format(Fd, "\"~s\", ", [UserName]), - io:format(Fd, "\"~s\", ", [SecName]), + io:format(Fd, "~p, ", [EngineID]), + io:format(Fd, "~p, ", [UserName]), + io:format(Fd, "~p, ", [SecName]), io:format(Fd, "~w, ", [Clone]), io:format(Fd, "~w, ", [AuthP]), do_write_usm2(Fd, AuthKeyC, ", "), @@ -859,15 +859,15 @@ do_write_vacm_conf( {vacmSecurityToGroup, SecModel, SecName, GroupName}) -> io:format( - Fd, "{vacmSecurityToGroup, ~w, \"~s\", \"~s\"}.~n", + Fd, "{vacmSecurityToGroup, ~w, ~p, ~p}.~n", [SecModel, SecName, GroupName]); do_write_vacm_conf( Fd, {vacmAccess, GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}) -> io:format( - Fd, "{vacmAccess, \"~s\", \"~s\", ~w, ~w, ~w, " - "\"~s\", \"~s\", \"~s\"}.~n", + Fd, "{vacmAccess, ~p, ~p, ~w, ~w, ~w, " + "~p, ~p, ~p}.~n", [GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV]); do_write_vacm_conf( @@ -875,7 +875,7 @@ do_write_vacm_conf( {vacmViewTreeFamily, ViewIndex, ViewSubtree, ViewStatus, ViewMask}) -> io:format( - Fd, "{vacmViewTreeFamily, \"~s\", ~w, ~w, ~w}.~n", + Fd, "{vacmViewTreeFamily, ~p, ~w, ~w, ~w}.~n", [ViewIndex, ViewSubtree, ViewStatus, ViewMask]); do_write_vacm_conf(_Fd, Crap) -> error({bad_vacm_config, Crap}). diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk index 87539f88f7..0f54e67c65 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_conf_test \ snmp_agent_test_lib \ snmp_manager_config_test \ snmp_manager_user \ diff --git a/lib/snmp/test/snmp_SUITE.erl b/lib/snmp/test/snmp_SUITE.erl index 3b9219739b..05bd86253b 100644 --- a/lib/snmp/test/snmp_SUITE.erl +++ b/lib/snmp/test/snmp_SUITE.erl @@ -81,7 +81,8 @@ groups() -> {group, note_store_test}]}, {agent, [], [{group, mibs_test}, {group, nfilter_test}, - {group, agent_test}, + {group, agent_test}, + {group, agent_conf_test}, {group, snmpnet_test}]}, {manager, [], [{group, manager_config_test}, {group, manager_user_test}, @@ -97,6 +98,7 @@ groups() -> {mibs_test, [], [{snmp_agent_mibs_test, all}]}, {nfilter_test, [], [{snmp_agent_nfilter_test, all}]}, {agent_test, [], [{snmp_agent_test, all}]}, + {agent_conf_test, [], [{snmp_agent_conf_test, all}]}, {snmpnet_test, [], [{snmp_to_snmpnet_SUITE, all}]}, {manager_config_test, [], [{snmp_manager_config_test, all}]}, {manager_user_test, [], [{snmp_manager_user_test, all}]}, diff --git a/lib/snmp/test/snmp_agent_conf_test.erl b/lib/snmp/test/snmp_agent_conf_test.erl new file mode 100644 index 0000000000..0a22bd47d1 --- /dev/null +++ b/lib/snmp/test/snmp_agent_conf_test.erl @@ -0,0 +1,210 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2003-2016. 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_conf_test). + +%%---------------------------------------------------------------------- +%% Include files +%%---------------------------------------------------------------------- + +%-include_lib("test_server/include/test_server.hrl"). +%-include("snmp_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-export([ + all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + + check_agent/1, + check_usm/1, + check_vacm/1 + ]). + + +all() -> [ + check_agent, + check_usm, + check_vacm + ]. + + +groups() -> + []. + + +init_per_suite(Config) -> + PrivDir = ?config(priv_dir, Config), + PrivSubdir = filename:join(PrivDir, "snmp_agent_conf_test"), + ok = filelib:ensure_dir(filename:join(PrivSubdir, "dummy")), + [{priv_subdir, PrivSubdir} | Config]. + +end_per_suite(_Config) -> + ok. + +%%====================================================================== +%% Test data +%%====================================================================== + +engine_ids() -> [ + "plain eid", + "here\"eid", + "comes\neid", + "trouble\0eid", + binary_to_list(<<"ä¸å›½å¼•æ“Žæ ‡è¯†ç¬¦"/utf8>>) +]. + +snmp_admin_strings() -> [ + "plain string", + "heres\"eid", + "trouble\neid", + binary_to_list(<<"ä¸å›½å¼•æ“Žæ ‡è¯†ç¬¦"/utf8>>) +]. + + +%%====================================================================== +%% Test functions +%%====================================================================== + + +check_agent(Config) -> + Dir = ?config(priv_subdir, Config), + lists:foreach( + fun(EngineId) -> check_agent_by_engineid(Dir, EngineId) end, + engine_ids() + ), + ok. + +check_agent_by_engineid(Dir, EngineId) -> + WEntries = [ + snmpa_conf:agent_entry(intAgentIpAddress, {0,0,0,0}), + snmpa_conf:agent_entry(intAgentUDPPort, 161), + snmpa_conf:agent_entry(snmpEngineMaxMessageSize, 484), + snmpa_conf:agent_entry(snmpEngineID, EngineId) + ], + + ok = snmpa_conf:write_agent_config(Dir, WEntries), + {ok, REntries} = snmpa_conf:read_agent_config(Dir), + + true = is_subset(WEntries, REntries), + ok. + +%%====================================================================== + +check_usm(Config) -> + Dir = ?config(priv_subdir, Config), + EngineId = hd(engine_ids()), + UserName = hd(snmp_admin_strings()), + SecName = hd(snmp_admin_strings()), + + %% vary engine id + lists:foreach( + fun(EngineId_) -> check_usm_by_params(Dir, EngineId_, UserName, SecName) end, + engine_ids() + ), + + %% vary user name + lists:foreach( + fun(UserName_) -> check_usm_by_params(Dir, EngineId, UserName_, SecName) end, + snmp_admin_strings() + ), + + %% vary sec name + lists:foreach( + fun(SecName_) -> check_usm_by_params(Dir, EngineId, UserName, SecName_) end, + snmp_admin_strings() + ), + + ok. + +check_usm_by_params(Dir, EngineId, UserName, SecName) -> + WEntries = [ + snmpa_conf:usm_entry( + EngineId, + UserName, + SecName, + zeroDotZero, + usmNoAuthProtocol, % authproto + "", "", + usmNoPrivProtocol, % privproto + "", "", "", + [], %AuthKey + []) %PrivKey + ], + + ok = snmpa_conf:write_usm_config(Dir, WEntries), + {ok, REntries} = snmpa_conf:read_usm_config(Dir), + + true = is_subset(WEntries, REntries), + ok. + +%%====================================================================== + +check_vacm(Config) -> + Dir = ?config(priv_subdir, Config), + + %% vary sec name + lists:foreach( + fun(SecName_) -> check_vacm_by_params(Dir, SecName_) end, + snmp_admin_strings() + ), + + ok. + + +check_vacm_by_params(Dir, SecName) -> + WEntries = [ + %% SecModel, SecName, GroupName + snmpa_conf:vacm_s2g_entry(usm, SecName, SecName), + %% GroupName,Prefix,SecModel,SecLevel,Match,ReadView,WriteView,NotifyView + snmpa_conf:vacm_acc_entry(SecName, "", any, noAuthNoPriv, exact, "all", "all", "all") + ], + + ok = snmpa_conf:write_vacm_config(Dir, WEntries), + {ok, REntries} = snmpa_conf:read_vacm_config(Dir), + + true = is_subset(WEntries, REntries), + ok. + + + +%%====================================================================== + + +%% additional tests needed: +% check_context() +% check_community() +% check_standard() +% check_target_addr() +% check_target_params() +% check_notify() + + +%%====================================================================== +%% Local utility functions +%%====================================================================== + +is_subset(List1, List2) -> + io:format("Check ~p is subset of ~p\n", [List1, List2]), + sets:is_subset( + sets:from_list(List1), + sets:from_list(List2) + ). diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index f58f6b6162..f95b428290 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 5.2.2 +SNMP_VSN = 5.2.3 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 96bc50c689..f9d11b2a60 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,124 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SSH client does not any longer retry a bad password given + as option to ssh:connect et al.</p> + <p> + Own Id: OTP-13674 Aux Id: TR-HU92273 </p> + </item> + <item> + <p> + Removed possible hanging risk for a certain timing + sequence when communicating client and server executes on + the same node.</p> + <p> + Own Id: OTP-13715</p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.3</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + A socket created and connected by gen_tcp could now be + used as input to ssh:connect, ssh:shell, + ssh_sftp:start_channel and ssh:daemon.</p> + <p> + Own Id: OTP-12860</p> + </item> + <item> + <p> + Some time optimization mainly in message encoding.</p> + <p> + Own Id: OTP-13131</p> + </item> + <item> + <p> + Optimized the sftp client time by setting new packet and + window sizes.</p> + <p> + Own Id: OTP-13175</p> + </item> + <item> + <p> + The <c>ssh_connection_handler</c> module in SSH is + changed and now uses the new behaviour <c>gen_statem</c>. </p> + <p> + The module can be used as an example of a + <c>gen_statem</c> callback module but with a warning: + This commit of ssh is just a straightforward port from + gen_fsm to gen_statem with some code cleaning. Since the + state machine and the state callbacks are almost + unchanged the ssh module does not demonstrate the full + potential of the new behaviour.</p> + <p> + The "new" state machine uses compound states. The ssh + server and client state machines are quite similar but + differences exist. With <c>gen_fsm</c> there were flags + in the user data which in fact implemented "substates". + Now with <c>gen_statem</c> those are made explicit in the + state names, eg. the state <c>userauth</c> and the binary + <c>role</c>-flag becomes the two state names + <c>{userauth, server}</c> and <c>{userauth, client}</c>.</p> + <p> + Own Id: OTP-13267</p> + </item> + <item> + <p> + The <c>{error, Reason}</c> tuples returned from + <c>ssh_sftp</c> api functions are described.</p> + <p> + Own Id: OTP-13347 Aux Id: ERL-86 </p> + </item> + <item> + <p> + Added -spec in ssh</p> + <p> + Own Id: OTP-13479</p> + </item> + <item> + <p> + It is now possible to call <c>ssh:daemon/{1,2,3}</c> with + <c>Port=0</c>. This makes the daemon select a free + listening tcp port before opening it. To find this port + number after the call, use the new function + <c>ssh:daemon_info/1</c>. See the reference manual for + details.</p> + <p> + Own Id: OTP-13527</p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.2.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SSH client does not any longer retry a bad password given + as option to ssh:connect et al.</p> + <p> + Own Id: OTP-13674 Aux Id: TR-HU92273 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.2.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 49eec8072f..fb5e086656 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -31,12 +31,111 @@ -export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1, service_request_msg/1, init_userauth_request_msg/1, userauth_request_msg/1, handle_userauth_request/3, - handle_userauth_info_request/3, handle_userauth_info_response/2 + handle_userauth_info_request/2, handle_userauth_info_response/2 ]). %%-------------------------------------------------------------------- %%% Internal application API %%-------------------------------------------------------------------- +%%%---------------------------------------------------------------- +userauth_request_msg(#ssh{userauth_methods = ServerMethods, + userauth_supported_methods = UserPrefMethods, % Note: this is not documented as supported for clients + userauth_preference = ClientMethods0 + } = Ssh0) -> + case sort_select_mthds(ClientMethods0, UserPrefMethods, ServerMethods) of + [] -> + Msg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, + description = "Unable to connect using the available authentication methods", + language = "en"}, + {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh0)}; + + [{Pref,Module,Function,Args} | Prefs] -> + Ssh = case Pref of + "keyboard-interactive" -> Ssh0; + _ -> Ssh0#ssh{userauth_preference = Prefs} + end, + case Module:Function(Args ++ [Ssh]) of + {not_ok, Ssh1} -> + userauth_request_msg(Ssh1#ssh{userauth_preference = Prefs}); + Result -> + {Pref,Result} + end + end. + + + +sort_select_mthds(Clients, undefined, Servers) -> + %% User has not expressed an opinion via option "auth_methods", use the server's prefs + sort_select_mthds1(Clients, Servers, string:tokens(?SUPPORTED_AUTH_METHODS,",")); + +sort_select_mthds(Clients, Users0, Servers0) -> + %% The User has an opinion, use the intersection of that and the Servers whishes but + %% in the Users order + sort_select_mthds1(Clients, string:tokens(Users0,","), Servers0). + + +sort_select_mthds1(Clients, Users0, Servers0) -> + Servers = unique(Servers0), + Users = unique(Users0), + [C || Key <- Users, + lists:member(Key, Servers), + C <- Clients, + element(1,C) == Key]. + +unique(L) -> + lists:reverse( + lists:foldl(fun(E,Acc) -> + case lists:member(E,Acc) of + true -> Acc; + false -> [E|Acc] + end + end, [], L)). + + +%%%---- userauth_request_msg "callbacks" +password_msg([#ssh{opts = Opts, io_cb = IoCb, + user = User, service = Service} = Ssh0]) -> + {Password,Ssh} = + case proplists:get_value(password, Opts) of + undefined when IoCb == ssh_no_io -> + {not_ok, Ssh0}; + undefined -> + {IoCb:read_password("ssh password: ",Ssh0), Ssh0}; + PW -> + %% If "password" option is given it should not be tried again + {PW, Ssh0#ssh{opts = lists:keyreplace(password,1,Opts,{password,not_ok})}} + end, + case Password of + not_ok -> + {not_ok, Ssh}; + _ -> + ssh_transport:ssh_packet( + #ssh_msg_userauth_request{user = User, + service = Service, + method = "password", + data = + <<?BOOLEAN(?FALSE), + ?STRING(unicode:characters_to_binary(Password))>>}, + Ssh) + end. + +%% See RFC 4256 for info on keyboard-interactive +keyboard_interactive_msg([#ssh{user = User, + opts = Opts, + service = Service} = Ssh]) -> + case proplists:get_value(password, Opts) of + not_ok -> + {not_ok,Ssh}; % No need to use a failed pwd once more + _ -> + ssh_transport:ssh_packet( + #ssh_msg_userauth_request{user = User, + service = Service, + method = "keyboard-interactive", + data = << ?STRING(<<"">>), + ?STRING(<<>>) >> }, + Ssh) + end. + publickey_msg([Alg, #ssh{user = User, session_id = SessionId, service = Service, @@ -48,7 +147,7 @@ publickey_msg([Alg, #ssh{user = User, StrAlgo = atom_to_list(Alg), case encode_public_key(StrAlgo, ssh_transport:extract_public_key(PrivKey)) of not_ok -> - not_ok; + {not_ok, Ssh}; PubKeyBlob -> SigData = build_sig_data(SessionId, User, Service, PubKeyBlob, StrAlgo), @@ -65,52 +164,15 @@ publickey_msg([Alg, #ssh{user = User, Ssh) end; _Error -> - not_ok - end. - -password_msg([#ssh{opts = Opts, io_cb = IoCb, - user = User, service = Service} = Ssh]) -> - Password = case proplists:get_value(password, Opts) of - undefined -> - user_interaction(IoCb, Ssh); - PW -> - PW - end, - case Password of - not_ok -> - not_ok; - _ -> - ssh_transport:ssh_packet( - #ssh_msg_userauth_request{user = User, - service = Service, - method = "password", - data = - <<?BOOLEAN(?FALSE), - ?STRING(unicode:characters_to_binary(Password))>>}, - Ssh) + {not_ok, Ssh} end. -user_interaction(ssh_no_io, _) -> - not_ok; -user_interaction(IoCb, Ssh) -> - IoCb:read_password("ssh password: ", Ssh). - - -%% See RFC 4256 for info on keyboard-interactive -keyboard_interactive_msg([#ssh{user = User, - service = Service} = Ssh]) -> - ssh_transport:ssh_packet( - #ssh_msg_userauth_request{user = User, - service = Service, - method = "keyboard-interactive", - data = << ?STRING(<<"">>), - ?STRING(<<>>) >> }, - Ssh). - +%%%---------------------------------------------------------------- service_request_msg(Ssh) -> ssh_transport:ssh_packet(#ssh_msg_service_request{name = "ssh-userauth"}, Ssh#ssh{service = "ssh-userauth"}). +%%%---------------------------------------------------------------- init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> case user_name(Opts) of {ok, User} -> @@ -140,34 +202,9 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> description = ErrStr}) end. -userauth_request_msg(#ssh{userauth_preference = []} = Ssh) -> - Msg = #ssh_msg_disconnect{code = - ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, - description = "Unable to connect using the available" - " authentication methods", - language = "en"}, - {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh)}; - -userauth_request_msg(#ssh{userauth_methods = Methods, - userauth_preference = [{Pref, Module, - Function, Args} | Prefs]} - = Ssh0) -> - Ssh = Ssh0#ssh{userauth_preference = Prefs}, - case lists:member(Pref, Methods) of - true -> - case Module:Function(Args ++ [Ssh]) of - not_ok -> - userauth_request_msg(Ssh); - Result -> - {Pref,Result} - end; - false -> - userauth_request_msg(Ssh) - end. - - -handle_userauth_request(#ssh_msg_service_request{name = - Name = "ssh-userauth"}, +%%%---------------------------------------------------------------- +%%% called by server +handle_userauth_request(#ssh_msg_service_request{name = Name = "ssh-userauth"}, _, Ssh) -> {ok, ssh_transport:ssh_packet(#ssh_msg_service_accept{name = Name}, Ssh#ssh{service = "ssh-connection"})}; @@ -319,21 +356,28 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, partial_success = false}, Ssh)}. - -handle_userauth_info_request( - #ssh_msg_userauth_info_request{name = Name, - instruction = Instr, - num_prompts = NumPrompts, - data = Data}, IoCb, - #ssh{opts = Opts} = Ssh) -> +%%%---------------------------------------------------------------- +%%% keyboard-interactive client +handle_userauth_info_request(#ssh_msg_userauth_info_request{name = Name, + instruction = Instr, + num_prompts = NumPrompts, + data = Data}, + #ssh{opts = Opts, + io_cb = IoCb + } = Ssh) -> PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data), - Responses = keyboard_interact_get_responses(IoCb, Opts, - Name, Instr, PromptInfos), - {ok, - ssh_transport:ssh_packet( - #ssh_msg_userauth_info_response{num_responses = NumPrompts, - data = Responses}, Ssh)}. + case keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) of + not_ok -> + not_ok; + Responses -> + {ok, + ssh_transport:ssh_packet( + #ssh_msg_userauth_info_response{num_responses = NumPrompts, + data = Responses}, Ssh)} + end. +%%%---------------------------------------------------------------- +%%% keyboard-interactive server handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, data = <<?UINT32(Sz), Password:Sz/binary>>}, #ssh{opts = Opts, @@ -369,11 +413,6 @@ method_preference(Algs) -> [{"publickey", ?MODULE, publickey_msg, [A]} | Acc] end, [{"password", ?MODULE, password_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}, {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} ], Algs). @@ -473,6 +512,9 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) -> proplists:get_value(password, Opts, undefined), IoCb, Name, Instr, PromptInfos, Opts, NumPrompts). + +keyboard_interact_get_responses(_, _, not_ok, _, _, _, _, _, _) -> + not_ok; keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _, 1) when Password =/= undefined -> [Password]; %% Password auth implemented with keyboard-interaction and passwd is known @@ -486,17 +528,18 @@ keyboard_interact_get_responses(true, Fun, _Pwd, _IoCb, Name, Instr, PromptInfos keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts). keyboard_interact(IoCb, Name, Instr, Prompts, Opts) -> - if Name /= "" -> IoCb:format("~s~n", [Name]); - true -> ok - end, - if Instr /= "" -> IoCb:format("~s~n", [Instr]); - true -> ok - end, + write_if_nonempty(IoCb, Name), + write_if_nonempty(IoCb, Instr), lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts); ({Prompt, false}) -> IoCb:read_password(Prompt, Opts) end, Prompts). +write_if_nonempty(_, "") -> ok; +write_if_nonempty(_, <<>>) -> ok; +write_if_nonempty(IoCb, Text) -> IoCb:format("~s~n",[Text]). + + keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) -> Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end, PromptInfos), diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index e952a333ff..f9f4c82351 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -428,7 +428,12 @@ init_connection(server, C = #connection{}, Opts) -> init_ssh_record(Role, Socket, Opts) -> {ok, PeerAddr} = inet:peername(Socket), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), - AuthMethods = proplists:get_value(auth_methods, Opts, ?SUPPORTED_AUTH_METHODS), + AuthMethods = proplists:get_value(auth_methods, + Opts, + case Role of + server -> ?SUPPORTED_AUTH_METHODS; + client -> undefined + end), S0 = #ssh{role = Role, key_cb = KeyCb, opts = Opts, @@ -794,9 +799,13 @@ handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) - handle_event(_, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client}, #data{ssh_params = Ssh0} = D) -> - {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_request(Msg, Ssh0#ssh.io_cb, Ssh0), - send_bytes(Reply, D), - {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}}; + case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of + {ok, {Reply, Ssh}} -> + send_bytes(Reply, D), + {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}}; + not_ok -> + {next_state, {userauth,client}, D, [{next_event, internal, Msg}]} + end; handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D) -> case ssh_auth:handle_userauth_info_response(Msg, D#data.ssh_params) of @@ -819,7 +828,18 @@ handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactiv D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}}, {next_state, {userauth,client}, D, [{next_event, internal, Msg}]}; -handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, D) -> +handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, + #data{ssh_params = Ssh0} = D0) -> + Opts = Ssh0#ssh.opts, + D = case proplists:get_value(password, Opts) of + undefined -> + D0; + _ -> + D0#data{ssh_params = + Ssh0#ssh{opts = + lists:keyreplace(password,1,Opts, + {password,not_ok})}} % FIXME:intermodule dependency + end, {next_state, {userauth,client}, D, [{next_event, internal, Msg}]}; handle_event(_, Msg=#ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) -> @@ -1006,13 +1026,13 @@ handle_event({call,From}, get_print_info, StateName, D) -> {keep_state_and_data, [{reply,From,Reply}]}; handle_event({call,From}, {connection_info, Options}, _, D) -> - Info = ssh_info(Options, D, []), + Info = fold_keys(Options, fun conn_info/2, D), {keep_state_and_data, [{reply,From,Info}]}; handle_event({call,From}, {channel_info,ChannelId,Options}, _, D) -> case ssh_channel:cache_lookup(cache(D), ChannelId) of #channel{} = Channel -> - Info = ssh_channel_info(Options, Channel, []), + Info = fold_keys(Options, fun chann_info/2, Channel), {keep_state_and_data, [{reply,From,Info}]}; undefined -> {keep_state_and_data, [{reply,From,[]}]} @@ -1206,8 +1226,9 @@ handle_event(internal, prepare_next_packet, _, D) -> Sz when Sz >= Enough -> self() ! {D#data.transport_protocol, D#data.socket, <<>>}; _ -> - inet:setopts(D#data.socket, [{active, once}]) + ok end, + inet:setopts(D#data.socket, [{active, once}]), keep_state_and_data; handle_event(info, {CloseTag,Socket}, StateName, @@ -1315,12 +1336,10 @@ terminate(shutdown, StateName, State0) -> State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, description = "Application shutdown"}, State0), -timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead finalize_termination(StateName, State); %% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)-> %% State = send_msg(Msg, State0), -%% timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead %% finalize_termination(StateName, Msg, State); terminate({shutdown,_R}, StateName, State) -> @@ -1635,7 +1654,6 @@ new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} = disconnect(Msg=#ssh_msg_disconnect{description=Description}, _StateName, State0) -> State = send_msg(Msg, State0), disconnect_fun(Description, State), -timer:sleep(400), {stop, {shutdown,Description}, State}. %%%---------------------------------------------------------------- @@ -1644,43 +1662,43 @@ counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) -> counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) -> Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}. -ssh_info([], _State, Acc) -> - Acc; -ssh_info([client_version | Rest], #data{ssh_params = #ssh{c_vsn = IntVsn, - c_version = StringVsn}} = State, Acc) -> - ssh_info(Rest, State, [{client_version, {IntVsn, StringVsn}} | Acc]); - -ssh_info([server_version | Rest], #data{ssh_params =#ssh{s_vsn = IntVsn, - s_version = StringVsn}} = State, Acc) -> - ssh_info(Rest, State, [{server_version, {IntVsn, StringVsn}} | Acc]); -ssh_info([peer | Rest], #data{ssh_params = #ssh{peer = Peer}} = State, Acc) -> - ssh_info(Rest, State, [{peer, Peer} | Acc]); -ssh_info([sockname | Rest], #data{socket = Socket} = State, Acc) -> - {ok, SockName} = inet:sockname(Socket), - ssh_info(Rest, State, [{sockname, SockName}|Acc]); -ssh_info([user | Rest], #data{auth_user = User} = State, Acc) -> - ssh_info(Rest, State, [{user, User}|Acc]); -ssh_info([ _ | Rest], State, Acc) -> - ssh_info(Rest, State, Acc). - - -ssh_channel_info([], _, Acc) -> - Acc; +%%%---------------------------------------------------------------- +conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version}; +conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version}; +conn_info(peer, #data{ssh_params=S}) -> S#ssh.peer; +conn_info(user, D) -> D#data.auth_user; +conn_info(sockname, D) -> {ok, SockName} = inet:sockname(D#data.socket), + SockName; +%% dbg options ( = not documented): +conn_info(socket, D) -> D#data.socket; +conn_info(chan_ids, D) -> + ssh_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) -> + [Id | Acc] + end, [], cache(D)). -ssh_channel_info([recv_window | Rest], #channel{recv_window_size = WinSize, - recv_packet_size = Packsize - } = Channel, Acc) -> - ssh_channel_info(Rest, Channel, [{recv_window, {{win_size, WinSize}, - {packet_size, Packsize}}} | Acc]); -ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize, - send_packet_size = Packsize - } = Channel, Acc) -> - ssh_channel_info(Rest, Channel, [{send_window, {{win_size, WinSize}, - {packet_size, Packsize}}} | Acc]); -ssh_channel_info([ _ | Rest], Channel, Acc) -> - ssh_channel_info(Rest, Channel, Acc). +%%%---------------------------------------------------------------- +chann_info(recv_window, C) -> + {{win_size, C#channel.recv_window_size}, + {packet_size, C#channel.recv_packet_size}}; +chann_info(send_window, C) -> + {{win_size, C#channel.send_window_size}, + {packet_size, C#channel.send_packet_size}}; +%% dbg options ( = not documented): +chann_info(pid, C) -> + C#channel.user. +%%%---------------------------------------------------------------- +%% Assisting meta function for the *_info functions +fold_keys(Keys, Fun, Extra) -> + lists:foldr(fun(Key, Acc) -> + try Fun(Key, Extra) of + Value -> [{Key,Value}|Acc] + catch + _:_ -> Acc + end + end, [], Keys). +%%%---------------------------------------------------------------- log_error(Reason) -> Report = io_lib:format("Erlang ssh connection handler failed with reason:~n" " ~p~n" @@ -1689,7 +1707,6 @@ log_error(Reason) -> [Reason, erlang:get_stacktrace()]), error_logger:error_report(Report). - %%%---------------------------------------------------------------- not_connected_filter({connection_reply, _Data}) -> true; not_connected_filter(_) -> false. diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl index 480795cfc7..bd6bc0335b 100644 --- a/lib/ssh/src/ssh_dbg.erl +++ b/lib/ssh/src/ssh_dbg.erl @@ -24,6 +24,7 @@ -export([messages/0, messages/1, + messages/2, stop/0 ]). @@ -36,12 +37,16 @@ writer, acc = []}). %%%================================================================ -messages() -> messages(fun(String,_D) -> io:format(String) end). -%% messages() -> messages(fun(String,Acc) -> [String|Acc] end) +messages() -> + messages(fun(String,_D) -> io:format(String) end). messages(Write) when is_function(Write,2) -> + messages(Write, fun(X) -> X end). + +messages(Write, MangleArg) when is_function(Write,2), + is_function(MangleArg,1) -> catch dbg:start(), - setup_tracer(Write), + setup_tracer(Write, MangleArg), dbg:p(new,c), dbg_ssh_messages(). @@ -63,18 +68,30 @@ msg_formater({trace,_Pid,return_from,{ssh_message,encode,1},_Res}, D) -> msg_formater({trace,_Pid,call,{ssh_message,decode,_}}, D) -> D; msg_formater({trace,Pid,return_from,{ssh_message,decode,1},Msg}, D) -> - fmt("~nRECV ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D); + fmt("~n~p RECV ~s~n", [Pid,wr_record(shrink_bin(Msg))], D); msg_formater({trace,_Pid,call,{ssh_transport,select_algorithm,_}}, D) -> D; msg_formater({trace,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg}}, D) -> - fmt("~nALGORITHMS ~p~n~s~n", [Pid, wr_record(Alg)], D); + fmt("~n~p ALGORITHMS~n~s~n", [Pid, wr_record(Alg)], D); + + +msg_formater({trace,Pid,send,{tcp,Sock,Bytes},Pid}, D) -> + fmt("~n~p TCP SEND on ~p~n ~p~n", [Pid,Sock, shrink_bin(Bytes)], D); + +msg_formater({trace,Pid,send,{tcp,Sock,Bytes},Dest}, D) -> + fmt("~n~p TCP SEND from ~p TO ~p~n ~p~n", [Pid,Sock,Dest, shrink_bin(Bytes)], D); msg_formater({trace,Pid,send,ErlangMsg,Dest}, D) -> - fmt("~nERL MSG ~p SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D); + fmt("~n~p ERL MSG SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D); + + +msg_formater({trace,Pid,'receive',{tcp,Sock,Bytes}}, D) -> + fmt("~n~p TCP RECEIVE on ~p~n ~p~n", [Pid,Sock,shrink_bin(Bytes)], D); msg_formater({trace,Pid,'receive',ErlangMsg}, D) -> - fmt("~nERL MSG ~p RECIEVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D); + fmt("~n~p ERL MSG RECEIVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D); + msg_formater(M, D) -> fmt("~nDBG ~n~p~n", [shrink_bin(M)], D). @@ -87,8 +104,10 @@ fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) -> D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}. %%%---------------------------------------------------------------- -setup_tracer(Write) -> - Handler = fun msg_formater/2, +setup_tracer(Write, MangleArg) -> + Handler = fun(Arg, D) -> + msg_formater(MangleArg(Arg), D) + end, InitialData = #data{writer = Write}, {ok,_} = dbg:tracer(process, {Handler, InitialData}), ok. diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl index 026d0f6151..1d8f370884 100644 --- a/lib/ssh/src/ssh_io.erl +++ b/lib/ssh/src/ssh_io.erl @@ -31,56 +31,55 @@ read_line(Prompt, Ssh) -> format("~s", [listify(Prompt)]), proplists:get_value(user_pid, Ssh) ! {self(), question}, receive - Answer -> + Answer when is_list(Answer) -> Answer end. yes_no(Prompt, Ssh) -> - io:format("~s [y/n]?", [Prompt]), + format("~s [y/n]?", [Prompt]), proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), question}, receive - Answer -> + %% I can't see that the atoms y and n are ever received, but it must + %% be investigated before removing + y -> yes; + n -> no; + + Answer when is_list(Answer) -> case trim(Answer) of "y" -> yes; "n" -> no; "Y" -> yes; "N" -> no; - y -> yes; - n -> no; _ -> - io:format("please answer y or n\n"), + format("please answer y or n\n",[]), yes_no(Prompt, Ssh) end end. -read_password(Prompt, Ssh) -> +read_password(Prompt, #ssh{opts=Opts}) -> read_password(Prompt, Opts); +read_password(Prompt, Opts) when is_list(Opts) -> format("~s", [listify(Prompt)]), - case is_list(Ssh) of - false -> - proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), user_password}; - _ -> - proplists:get_value(user_pid, Ssh) ! {self(), user_password} - end, + proplists:get_value(user_pid, Opts) ! {self(), user_password}, receive - Answer -> - case Answer of - "" -> - read_password(Prompt, Ssh); - Pass -> Pass - end + Answer when is_list(Answer) -> + case trim(Answer) of + "" -> + read_password(Prompt, Opts); + Pwd -> + Pwd + end end. -listify(A) when is_atom(A) -> - atom_to_list(A); -listify(L) when is_list(L) -> - L; -listify(B) when is_binary(B) -> - binary_to_list(B). format(Fmt, Args) -> io:format(Fmt, Args). +%%%================================================================ +listify(A) when is_atom(A) -> atom_to_list(A); +listify(L) when is_list(L) -> L; +listify(B) when is_binary(B) -> binary_to_list(B). + trim(Line) when is_list(Line) -> lists:reverse(trim1(lists:reverse(trim1(Line)))); @@ -93,6 +92,3 @@ trim1([$\r|Cs]) -> trim(Cs); trim1([$\n|Cs]) -> trim(Cs); trim1([$\t|Cs]) -> trim(Cs); trim1(Cs) -> Cs. - - - diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 733414e23a..d52d453007 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -50,7 +50,12 @@ inet6_option/1, inet_option/1, internal_error/1, - known_hosts/1, + known_hosts/1, + login_bad_pwd_no_retry1/1, + login_bad_pwd_no_retry2/1, + login_bad_pwd_no_retry3/1, + login_bad_pwd_no_retry4/1, + login_bad_pwd_no_retry5/1, misc_ssh_options/1, openssh_zlib_basic_test/1, packet_size_zero/1, @@ -100,7 +105,8 @@ all() -> daemon_opt_fd, multi_daemon_opt_fd, packet_size_zero, - ssh_info_print + ssh_info_print, + {group, login_bad_pwd_no_retry} ]. groups() -> @@ -116,7 +122,13 @@ groups() -> {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {key_cb, [], [key_callback, key_callback_options]}, - {internal_error, [], [internal_error]} + {internal_error, [], [internal_error]}, + {login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1, + login_bad_pwd_no_retry2, + login_bad_pwd_no_retry3, + login_bad_pwd_no_retry4, + login_bad_pwd_no_retry5 + ]} ]. @@ -1090,6 +1102,72 @@ ssh_info_print(Config) -> %%-------------------------------------------------------------------- +%% Check that a basd pwd is not tried more times. Could cause lock-out +%% on server + +login_bad_pwd_no_retry1(Config) -> + login_bad_pwd_no_retry(Config, "keyboard-interactive,password"). + +login_bad_pwd_no_retry2(Config) -> + login_bad_pwd_no_retry(Config, "password,keyboard-interactive"). + +login_bad_pwd_no_retry3(Config) -> + login_bad_pwd_no_retry(Config, "password,publickey,keyboard-interactive"). + +login_bad_pwd_no_retry4(Config) -> + login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive"). + +login_bad_pwd_no_retry5(Config) -> + login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive,password,password"). + + + + + +login_bad_pwd_no_retry(Config, AuthMethods) -> + PrivDir = proplists:get_value(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = proplists:get_value(data_dir, Config), + + Parent = self(), + PwdFun = fun(_, _, _, undefined) -> {false, 1}; + (_, _, _, _) -> Parent ! retry_bad_pwd, + false + end, + + {DaemonRef, _Host, Port} = + ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {auth_methods, AuthMethods}, + {user_passwords, [{"foo","somepwd"}]}, + {pwdfun, PwdFun} + ]), + + ConnRes = ssh:connect("localhost", Port, + [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "badpwd"}, + {user_dir, UserDir}, + {user_interaction, false}]), + + receive + retry_bad_pwd -> + ssh:stop_daemon(DaemonRef), + {fail, "Retry bad password"} + after 0 -> + case ConnRes of + {error,"Unable to connect using the available authentication methods"} -> + ssh:stop_daemon(DaemonRef), + ok; + {ok,Conn} -> + ssh:close(Conn), + ssh:stop_daemon(DaemonRef), + {fail, "Connect erroneosly succeded"} + end + end. + +%%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- %% Due to timing the error message may or may not be delivered to diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index b165928877..575c1af3a9 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.3 +SSH_VSN = 4.3.1 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index c427db24a4..3b6f988a2d 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -28,6 +28,160 @@ <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 8.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Server now rejects, a not requested client cert, as an + incorrect handshake message and ends the connection.</p> + <p> + Own Id: OTP-13651</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Remove default support for DES cipher suites</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13195</p> + </item> + <item> + <p> + Deprecate the function <c>crypto:rand_bytes</c> and make + sure that <c>crypto:strong_rand_bytes</c> is used in all + places that are cryptographically significant.</p> + <p> + Own Id: OTP-13214</p> + </item> + <item> + <p> + Better error handling of user error during TLS upgrade. + ERL-69 is solved by gen_statem rewrite of ssl + application.</p> + <p> + Own Id: OTP-13255</p> + </item> + <item> + <p> + Provide user friendly error message when crypto rejects a + key</p> + <p> + Own Id: OTP-13256</p> + </item> + <item> + <p> + Add ssl:getstat/1 and ssl:getstat/2</p> + <p> + Own Id: OTP-13415</p> + </item> + <item> + <p> + TLS distribution connections now allow specifying the + options <c>verify_fun</c>, <c>crl_check</c> and + <c>crl_cache</c>. See the documentation. GitHub pull req + #956 contributed by Magnus Henoch.</p> + <p> + Own Id: OTP-13429 Aux Id: Pull#956 </p> + </item> + <item> + <p> + Remove confusing error message when closing a distributed + erlang node running over TLS</p> + <p> + Own Id: OTP-13431</p> + </item> + <item> + <p> + Remove default support for use of md5 in TLS 1.2 + signature algorithms</p> + <p> + Own Id: OTP-13463</p> + </item> + <item> + <p> + ssl now uses gen_statem instead of gen_fsm to implement + the ssl connection process, this solves some timing + issues in addition to making the code more intuitive as + the behaviour can be used cleanly instead of having a lot + of workaround for shortcomings of the behaviour.</p> + <p> + Own Id: OTP-13464</p> + </item> + <item> + <p> + Phase out interoperability with clients that offer SSLv2. + By default they are no longer supported, but an option to + provide interoperability is offered.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13465</p> + </item> + <item> + <p> + OpenSSL has functions to generate short (eight hex + digits) hashes of issuers of certificates and CRLs. These + hashes are used by the "c_rehash" script to populate + directories of CA certificates and CRLs, e.g. in the + Apache web server. Add functionality to let an Erlang + program find the right CRL for a given certificate in + such a directory.</p> + <p> + Own Id: OTP-13530</p> + </item> + <item> + <p> + Some legacy TLS 1.0 software does not tolerate the 1/n-1 + content split BEAST mitigation technique. Add a + beast_mitigation SSL option (defaulting to + one_n_minus_one) to select or disable the BEAST + mitigation technique.</p> + <p> + Own Id: OTP-13629</p> + </item> + <item> + <p> + Enhance error log messages to facilitate for users to + understand the error</p> + <p> + Own Id: OTP-13632</p> + </item> + <item> + <p> + Increased default DH params to 2048-bit</p> + <p> + Own Id: OTP-13636</p> + </item> + <item> + <p> + Propagate CRL unknown CA error so that public_key + validation process continues correctly and determines + what should happen.</p> + <p> + Own Id: OTP-13656</p> + </item> + <item> + <p> + Introduce a flight concept for handshake packages. This + is a preparation for enabling DTLS, however it can also + have a positive effects for TLS on slow and unreliable + networks.</p> + <p> + Own Id: OTP-13678</p> + </item> + </list> + </section> + +</section> + <section><title>SSL 7.3.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 53282998d0..adee59393e 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -528,13 +528,12 @@ certify(internal, #server_key_exchange{exchange_keys = Keys}, end end; -certify(internal, #certificate_request{hashsign_algorithms = HashSigns}, +certify(internal, #certificate_request{} = CertRequest, #state{session = #session{own_certificate = Cert}, - key_algorithm = KeyExAlg, + role = client, ssl_options = #ssl_options{signature_algs = SupportedHashSigns}, negotiated_version = Version} = State0, Connection) -> - - case ssl_handshake:select_hashsign(HashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of + case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, Version) of #alert {} = Alert -> Connection:handle_own_alert(Alert, Version, certify, State0); NegotiatedHashSign -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 9c3fe9d73b..bca341c8bc 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -74,7 +74,7 @@ ]). %% MISC --export([select_version/3, prf/6, select_hashsign/5, +-export([select_version/3, prf/6, select_hashsign/4, select_hashsign/5, select_hashsign_algs/3, premaster_secret/2, premaster_secret/3, premaster_secret/4]). @@ -581,7 +581,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) -> {atom(), atom()} | undefined | #alert{}. %% -%% Description: Handles signature_algorithms extension +%% Description: Handles signature_algorithms hello extension (server) %%-------------------------------------------------------------------- select_hashsign(_, undefined, _, _, _Version) -> {null, anon}; @@ -593,14 +593,17 @@ select_hashsign(HashSigns, Cert, KeyExAlgo, select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns, {Major, Minor}) when Major >= 3 andalso Minor >= 3 -> #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), - #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, - Sign = cert_sign(Algo), - case lists:filter(fun({sha, dsa = S}) when S == Sign -> - true; - ({_, dsa}) -> - false; - ({_, _} = Algos) -> - is_acceptable_hash_sign(Algos, Sign, KeyExAlgo, SupportedHashSigns); + #'OTPCertificate'{tbsCertificate = TBSCert, + signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp), + #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} = + TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + + Sign = sign_algo(SignAlgo), + SubSing = sign_algo(SubjAlgo), + + case lists:filter(fun({_, S} = Algos) when S == Sign -> + is_acceptable_hash_sign(Algos, Sign, + SubSing, KeyExAlgo, SupportedHashSigns); (_) -> false end, HashSigns) of @@ -613,6 +616,49 @@ select_hashsign(_, Cert, _, _, Version) -> #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, select_hashsign_algs(undefined, Algo, Version). +%%-------------------------------------------------------------------- +-spec select_hashsign(#certificate_request{}, binary(), + [atom()], ssl_record:ssl_version()) -> + {atom(), atom()} | #alert{}. + +%% +%% Description: Handles signature algorithms selection for certificate requests (client) +%%-------------------------------------------------------------------- +select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3-> + %% There client does not have a certificate and will send an empty reply, the server may fail + %% or accept the connection by its own preference. No signature algorihms needed as there is + %% no certificate to verify. + {undefined, undefined}; + +select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns}, + certificate_types = Types}, Cert, SupportedHashSigns, + {Major, Minor}) when Major >= 3 andalso Minor >= 3-> + #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), + #'OTPCertificate'{tbsCertificate = TBSCert, + signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp), + #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} = + TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + + Sign = sign_algo(SignAlgo), + SubSign = sign_algo(SubjAlgo), + + case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of + true -> + case lists:filter(fun({_, S} = Algos) when S == SubSign -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); + (_) -> + false + end, HashSigns) of + [] -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm); + [HashSign | _] -> + HashSign + end; + false -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm) + end; +select_hashsign(#certificate_request{}, Cert, _, Version) -> + select_hashsign(undefined, Cert, undefined, undefined, Version). %%-------------------------------------------------------------------- -spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) -> @@ -648,6 +694,7 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) -> select_hashsign_algs(undefined, ?'id-dsa', _) -> {sha, dsa}. + %%-------------------------------------------------------------------- -spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{}, client | server) -> {binary(), #connection_states{}} | #alert{}. @@ -1143,11 +1190,13 @@ certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 -> end; certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == rsa; + KeyExchange == dh_rsa; KeyExchange == dhe_rsa; KeyExchange == ecdhe_rsa -> <<?BYTE(?RSA_SIGN)>>; -certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dhe_dss; +certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_dss; + KeyExchange == dhe_dss; KeyExchange == srp_dss -> <<?BYTE(?DSS_SIGN)>>; @@ -2164,27 +2213,73 @@ distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) -> [{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs] end. -cert_sign(?rsaEncryption) -> +sign_algo(?rsaEncryption) -> rsa; -cert_sign(?'id-ecPublicKey') -> +sign_algo(?'id-ecPublicKey') -> ecdsa; -cert_sign(?'id-dsa') -> +sign_algo(?'id-dsa') -> dsa; -cert_sign(Alg) -> +sign_algo(Alg) -> {_, Sign} =public_key:pkix_sign_types(Alg), Sign. -is_acceptable_hash_sign({_, Sign} = Algos, Sign, _, SupportedHashSigns) -> - is_acceptable_hash_sign(Algos, SupportedHashSigns); -is_acceptable_hash_sign(Algos,_, KeyExAlgo, SupportedHashSigns) when KeyExAlgo == dh_ecdsa; - KeyExAlgo == ecdh_rsa; - KeyExAlgo == ecdh_ecdsa -> +is_acceptable_hash_sign(Algos, _, _, KeyExAlgo, SupportedHashSigns) when + KeyExAlgo == dh_dss; + KeyExAlgo == dh_rsa; + KeyExAlgo == dh_ecdsa -> + %% dh_* could be called only dh in TLS-1.2 is_acceptable_hash_sign(Algos, SupportedHashSigns); -is_acceptable_hash_sign(_,_,_,_) -> - false. +is_acceptable_hash_sign(Algos, rsa, ecdsa, ecdh_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, dhe_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, ecdhe_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, srp_rsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, rsa_psk, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, dhe_dss, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, srp_dss, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, _, dhe_ecdsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, ecdsa, ecdhe_ecdsa, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign(_, _, _, KeyExAlgo, _) when + KeyExAlgo == psk; + KeyExAlgo == dhe_psk; + KeyExAlgo == srp_anon; + KeyExAlgo == dh_anon; + KeyExAlgo == ecdhe_anon + -> + true; +is_acceptable_hash_sign(_,_, _,_,_) -> + false. + is_acceptable_hash_sign(Algos, SupportedHashSigns) -> lists:member(Algos, SupportedHashSigns). +is_acceptable_cert_type(Sign, _HashSigns, Types) -> + lists:member(sign_type(Sign), binary_to_list(Types)). + +is_supported_sign(Sign, HashSigns) -> + [] =/= lists:dropwhile(fun({_, S}) when S =/= Sign -> + true; + (_)-> + false + end, HashSigns). +sign_type(rsa) -> + ?RSA_SIGN; +sign_type(dsa) -> + ?DSS_SIGN; +sign_type(ecdsa) -> + ?ECDSA_SIGN. + + bad_key(#'DSAPrivateKey'{}) -> unacceptable_dsa_key; bad_key(#'RSAPrivateKey'{}) -> diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl index b8a03f578d..69ac9908fa 100644 --- a/lib/ssl/test/ssl_ECC_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_SUITE.erl @@ -159,42 +159,42 @@ end_per_testcase(_TestCase, Config) -> client_ecdh_server_ecdh(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdh_rsa_opts, Config), - SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config), + SOpts = proplists:get_value(server_ecdh_rsa_opts, Config), basic_test(COpts, SOpts, Config). client_ecdh_server_rsa(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdh_rsa_opts, Config), - SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config), + SOpts = proplists:get_value(server_opts, Config), basic_test(COpts, SOpts, Config). client_rsa_server_ecdh(Config) when is_list(Config) -> - COpts = proplists:get_value(client_ecdh_rsa_opts, Config), - SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config), + COpts = proplists:get_value(client_opts, Config), + SOpts = proplists:get_value(server_ecdh_rsa_opts, Config), basic_test(COpts, SOpts, Config). client_rsa_server_rsa(Config) when is_list(Config) -> - COpts = proplists:get_value(client_verification_opts, Config), - SOpts = proplists:get_value(server_verification_opts, Config), + COpts = proplists:get_value(client_opts, Config), + SOpts = proplists:get_value(server_opts, Config), basic_test(COpts, SOpts, Config). client_ecdsa_server_ecdsa(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), basic_test(COpts, SOpts, Config). client_ecdsa_server_rsa(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + SOpts = proplists:get_value(server_opts, Config), basic_test(COpts, SOpts, Config). client_rsa_server_ecdsa(Config) when is_list(Config) -> - COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + COpts = proplists:get_value(client_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), basic_test(COpts, SOpts, Config). client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) -> COpts = proplists:get_value(client_ecdsa_opts, Config), - SOpts = proplists:get_value(server_ecdsa_verify_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), ServerCert = proplists:get_value(certfile, SOpts), ServerKeyFile = proplists:get_value(keyfile, SOpts), {ok, PemBin} = file:read_file(ServerKeyFile), @@ -244,20 +244,20 @@ basic_test(ClientCert, ClientKey, ClientCA, ServerCert, ServerKey, ServerCA, Con check_result(Server, SType, Client, CType), close(Server, Client). -start_client(openssl, Port, CA, OwnCa, Cert, Key, Config) -> - PrivDir = proplists:get_value(priv_dir, Config), - NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa), +start_client(openssl, Port, PeerCA, OwnCa, Cert, Key, _Config) -> + CA = new_openssl_ca("openssl_client_ca", PeerCA, OwnCa), Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Exe = "openssl", Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", Cert, "-CAfile", NewCA, + "-cert", Cert, "-CAfile", CA, "-key", Key, "-host","localhost", "-msg", "-debug"], OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), OpenSslPort; -start_client(erlang, Port, CA, _, Cert, Key, Config) -> +start_client(erlang, Port, PeerCA, OwnCa, Cert, Key, Config) -> + CA = new_ca("erlang_client_ca", PeerCA, OwnCa), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -267,20 +267,19 @@ start_client(erlang, Port, CA, _, Cert, Key, Config) -> {cacertfile, CA}, {certfile, Cert}, {keyfile, Key}]}]). -start_server(openssl, CA, OwnCa, Cert, Key, Config) -> - PrivDir = proplists:get_value(priv_dir, Config), - NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa), - +start_server(openssl, PeerCA, OwnCa, Cert, Key, _Config) -> + CA = new_openssl_ca("openssl_server_ca", PeerCA, OwnCa), Port = ssl_test_lib:inet_port(node()), Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Exe = "openssl", Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-verify", "2", "-cert", Cert, "-CAfile", NewCA, + "-verify", "2", "-cert", Cert, "-CAfile", CA, "-key", Key, "-msg", "-debug"], OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), {OpenSslPort, Port}; -start_server(erlang, CA, _, Cert, Key, Config) -> +start_server(erlang, PeerCA, OwnCa, Cert, Key, Config) -> + CA = new_ca("erlang_server_ca", PeerCA, OwnCa), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, @@ -291,7 +290,8 @@ start_server(erlang, CA, _, Cert, Key, Config) -> [{verify, verify_peer}, {cacertfile, CA}, {certfile, Cert}, {keyfile, Key}]}]), {Server, ssl_test_lib:inet_port(Server)}. -start_server_with_raw_key(erlang, CA, _, Cert, Key, Config) -> +start_server_with_raw_key(erlang, PeerCA, OwnCa, Cert, Key, Config) -> + CA = new_ca("erlang_server_ca", PeerCA, OwnCa), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, @@ -336,17 +336,27 @@ close(Client, Server) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -%% Work around OpenSSL bug, apparently the same bug as we had fixed in -%% 11629690ba61f8e0c93ef9b2b6102fd279825977 new_ca(FileName, CA, OwnCa) -> {ok, P1} = file:read_file(CA), E1 = public_key:pem_decode(P1), {ok, P2} = file:read_file(OwnCa), E2 = public_key:pem_decode(P2), + Pem = public_key:pem_encode(E1 ++E2), + file:write_file(FileName, Pem), + FileName. + +new_openssl_ca(FileName, CA, OwnCa) -> + {ok, P1} = file:read_file(CA), + E1 = public_key:pem_decode(P1), + {ok, P2} = file:read_file(OwnCa), + E2 = public_key:pem_decode(P2), case os:cmd("openssl version") of "OpenSSL 1.0.1p-freebsd" ++ _ -> Pem = public_key:pem_encode(E1 ++E2), file:write_file(FileName, Pem); + "LibreSSL" ++ _ -> + Pem = public_key:pem_encode(E1 ++E2), + file:write_file(FileName, Pem); _ -> Pem = public_key:pem_encode(E2 ++E1), file:write_file(FileName, Pem) diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index efa5faa218..665dbb1df3 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -408,8 +408,13 @@ init_per_testcase(TestCase, Config) when TestCase == tls_ssl_accept_timeout; ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 15}), Config; -init_per_testcase(clear_pem_cache, Config) -> +init_per_testcase(TestCase, Config) when TestCase == clear_pem_cache; + TestCase == der_input; + TestCase == defaults -> ssl_test_lib:ct_log_supported_protocol_versions(Config), + %% White box test need clean start + ssl:stop(), + ssl:start(), ct:timetrap({seconds, 20}), Config; init_per_testcase(raw_ssl_option, Config) -> @@ -567,8 +572,8 @@ prf(Config) when is_list(Config) -> connection_info() -> [{doc,"Test the API function ssl:connection_information/1"}]. connection_info(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1144,8 +1149,8 @@ cipher_suites_mix() -> cipher_suites_mix(Config) when is_list(Config) -> CipherSuites = [{ecdh_rsa,aes_128_cbc,sha256,sha256}, {rsa,aes_128_cbc,sha}], - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -4409,14 +4414,14 @@ run_suites(Ciphers, Version, Config, Type) -> {ClientOpts, ServerOpts} = case Type of rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), - ssl_test_lib:ssl_options(server_opts, Config)}; + {ssl_test_lib:ssl_options(client_verification_opts, Config), + ssl_test_lib:ssl_options(server_verification_opts, Config)}; dsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_dsa_opts, Config)}; anonymous -> %% No certs in opts! - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_anon, Config)}; psk -> {ssl_test_lib:ssl_options(client_psk, Config), @@ -4440,31 +4445,31 @@ run_suites(Ciphers, Version, Config, Type) -> {ssl_test_lib:ssl_options(client_srp_dsa, Config), ssl_test_lib:ssl_options(server_srp_dsa, Config)}; ecdsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_ecdsa_opts, Config)}; ecdh_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}; rc4_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_opts, Config)]}; + ssl_test_lib:ssl_options(server_verification_opts, Config)]}; rc4_ecdh_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; rc4_ecdsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; des_dhe_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_opts, Config)]}; + ssl_test_lib:ssl_options(server_verification_opts, Config)]}; des_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), + {ssl_test_lib:ssl_options(client_verification_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_opts, Config)]} + ssl_test_lib:ssl_options(server_verification_opts, Config)]} end, Result = lists:map(fun(Cipher) -> diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 20165c70f0..c83c513eb3 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -65,9 +65,10 @@ tests() -> cert_expired, invalid_signature_client, invalid_signature_server, - extended_key_usage_verify_peer, - extended_key_usage_verify_none, - critical_extension_verify_peer, + extended_key_usage_verify_client, + extended_key_usage_verify_server, + critical_extension_verify_client, + critical_extension_verify_server, critical_extension_verify_none]. error_handling_tests()-> @@ -122,6 +123,8 @@ init_per_testcase(TestCase, Config) when TestCase == cert_expired; ssl:clear_pem_cache(), init_per_testcase(common, Config); init_per_testcase(_TestCase, Config) -> + ssl:stop(), + ssl:start(), ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 5}), Config. @@ -136,7 +139,7 @@ end_per_testcase(_TestCase, Config) -> verify_peer() -> [{doc,"Test option verify_peer"}]. verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -190,7 +193,7 @@ server_verify_client_once() -> [{doc,"Test server option verify_client_once"}]. server_verify_client_once(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -230,7 +233,7 @@ server_require_peer_cert_ok() -> server_require_peer_cert_ok(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -259,7 +262,7 @@ server_require_peer_cert_fail() -> server_require_peer_cert_fail(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - BadClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + BadClientOpts = ssl_test_lib:ssl_options(client_opts, []), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, @@ -290,7 +293,7 @@ server_require_peer_cert_partial_chain() -> server_require_peer_cert_partial_chain(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), @@ -325,13 +328,13 @@ server_require_peer_cert_allow_partial_chain() -> server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), - {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), - [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs), + {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), + [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ClientCAs), PartialChain = fun(CertChain) -> case lists:member(IntermidiateCA, CertChain) of @@ -367,7 +370,7 @@ server_require_peer_cert_do_not_allow_partial_chain() -> server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), @@ -408,7 +411,7 @@ server_require_peer_cert_partial_chain_fun_fail() -> server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) -> ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = proplists:get_value(client_verification_opts, Config), + ClientOpts = proplists:get_value(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), @@ -448,7 +451,7 @@ verify_fun_always_run_client() -> verify_fun_always_run_client(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -492,7 +495,7 @@ verify_fun_always_run_client(Config) when is_list(Config) -> verify_fun_always_run_server() -> [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. verify_fun_always_run_server(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -524,9 +527,7 @@ verify_fun_always_run_server(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, - {options, - [{verify, verify_peer} - | ClientOpts]}]), + {options, ClientOpts}]), %% Client error may be {tls_alert, "handshake failure" } or closed depending on timing %% this is not a bug it is a circumstance of how tcp works! @@ -544,7 +545,7 @@ cert_expired() -> cert_expired(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), @@ -607,11 +608,11 @@ two_digits_str(N) -> lists:flatten(io_lib:format("~p", [N])). %%-------------------------------------------------------------------- -extended_key_usage_verify_peer() -> - [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}]. +extended_key_usage_verify_server() -> + [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode for server"}]. -extended_key_usage_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), +extended_key_usage_verify_server(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), @@ -660,7 +661,7 @@ extended_key_usage_verify_peer(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | + {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -669,12 +670,12 @@ extended_key_usage_verify_peer(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -extended_key_usage_verify_none() -> - [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}]. +extended_key_usage_verify_client() -> + [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}]. -extended_key_usage_verify_none(Config) when is_list(Config) -> +extended_key_usage_verify_client(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -730,11 +731,11 @@ extended_key_usage_verify_none(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -critical_extension_verify_peer() -> +critical_extension_verify_server() -> [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. -critical_extension_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), +critical_extension_verify_server(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), @@ -766,7 +767,7 @@ critical_extension_verify_peer(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]), + {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]), %% This certificate has a critical extension that we don't %% understand. Therefore, verification should fail. @@ -775,14 +776,60 @@ critical_extension_verify_peer(Config) when is_list(Config) -> ssl_test_lib:close(Server), ok. +%%-------------------------------------------------------------------- + +critical_extension_verify_client() -> + [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. + +critical_extension_verify_client(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + PrivDir = proplists:get_value(priv_dir, Config), + Active = proplists:get_value(active, Config), + ReceiveFunction = proplists:get_value(receive_function, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem", + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join([PrivDir, "server", NewCertName]), + add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join([PrivDir, "client", NewCertName]), + add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]), + + %% This certificate has a critical extension that we don't + %% understand. Therefore, verification should fail. + tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}}, + Client, {error, {tls_alert, "unsupported certificate"}}), + ssl_test_lib:close(Server), + ok. %%-------------------------------------------------------------------- critical_extension_verify_none() -> [{doc,"Test cert that has a critical unknown extension in verify_none mode"}]. critical_extension_verify_none(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), PrivDir = proplists:get_value(priv_dir, Config), Active = proplists:get_value(active, Config), ReceiveFunction = proplists:get_value(receive_function, Config), @@ -1070,7 +1117,7 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> server_verify_no_cacerts() -> [{doc,"Test server must have cacerts if it wants to verify client"}]. server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_opts, Config)), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -1084,7 +1131,7 @@ server_verify_no_cacerts(Config) when is_list(Config) -> unknown_server_ca_fail() -> [{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}]. unknown_server_ca_fail(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, @@ -1128,7 +1175,7 @@ unknown_server_ca_fail(Config) when is_list(Config) -> unknown_server_ca_accept_verify_none() -> [{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}]. unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1153,7 +1200,7 @@ unknown_server_ca_accept_verify_peer() -> [{doc, "Test that the client succeds if the ca is unknown in verify_peer mode" " with a verify_fun that accepts the unknown ca error"}]. unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts =ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1192,7 +1239,7 @@ unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> unknown_server_ca_accept_backwardscompatibility() -> [{doc,"Test that old style verify_funs will work"}]. unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, []), ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 5ebf9bb2de..8740e8c8f0 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -109,11 +109,11 @@ common_end(_, _Config) -> basic() -> [{doc,"Test that two nodes can connect via ssl distribution"}]. basic(Config) when is_list(Config) -> - NH1 = start_ssl_node(Config), + gen_dist_test(basic_test, Config). + +basic_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(Config), Node2 = NH2#node_handle.nodename, - pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), @@ -161,18 +161,16 @@ basic(Config) when is_list(Config) -> ok end end) - end, - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + end. %%-------------------------------------------------------------------- payload() -> [{doc,"Test that send a lot of data between the ssl distributed noes"}]. payload(Config) when is_list(Config) -> - NH1 = start_ssl_node(Config), + gen_dist_test(payload_test, Config). + +payload_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(Config), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), @@ -204,10 +202,8 @@ payload(Config) when is_list(Config) -> ok end end) - end, - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + end. + %%-------------------------------------------------------------------- plain_options() -> [{doc,"Test specifying additional options"}]. @@ -218,20 +214,17 @@ plain_options(Config) when is_list(Config) -> "client_verify verify_none server_verify verify_none " "server_depth 1 client_depth 1 " "server_hibernate_after 500 client_hibernate_after 500", + gen_dist_test(plain_options_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +plain_options_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- plain_verify_options() -> [{doc,"Test specifying additional options"}]. @@ -240,20 +233,18 @@ plain_verify_options(Config) when is_list(Config) -> "client_secure_renegotiate true " "server_reuse_sessions true client_reuse_sessions true " "server_hibernate_after 500 client_hibernate_after 500", + gen_dist_test(plain_verify_options_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +plain_verify_options_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), Node2 = NH2#node_handle.nodename, - + pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), - + [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). + - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- nodelay_option() -> [{doc,"Test specifying dist_nodelay option"}]. @@ -265,6 +256,7 @@ nodelay_option(Config) -> after application:unset_env(kernel, dist_nodelay) end. +%%-------------------------------------------------------------------- listen_port_options() -> [{doc, "Test specifying listening ports"}]. @@ -285,32 +277,39 @@ listen_port_options(Config) when is_list(Config) -> #node_handle{} -> %% If the node was able to start, it didn't take the port %% option into account. + stop_ssl_node(NH1), exit(unexpected_success) catch exit:{accept_failed, timeout} -> %% The node failed to start, as expected. ok end, - + %% Try again, now specifying a high max port. PortOpt2 = "-kernel inet_dist_listen_min " ++ integer_to_list(Port1) ++ - " inet_dist_listen_max 65535", + " inet_dist_listen_max 65535", NH2 = start_ssl_node([{additional_dist_opts, PortOpt2} | Config]), - Node2 = NH2#node_handle.nodename, - Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)), - {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0), - {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2), - - %% The new port should be higher: - if Port2 > Port1 -> - ok; - true -> - error({port, Port2, not_higher_than, Port1}) + + try + Node2 = NH2#node_handle.nodename, + Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)), + {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0), + {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2), + + %% The new port should be higher: + if Port2 > Port1 -> + ok; + true -> + error({port, Port2, not_higher_than, Port1}) + end + catch + _:Reason -> + stop_ssl_node(NH2), + ct:fail(Reason) end, - - stop_ssl_node(NH1), stop_ssl_node(NH2), success(Config). + %%-------------------------------------------------------------------- listen_options() -> [{doc, "Test inet_dist_listen_options"}]. @@ -329,28 +328,25 @@ do_listen_options(Prio, Config) -> end, Options = "-kernel inet_dist_listen_options " ++ PriorityString, - - NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]), - NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]), - Node2 = NH2#node_handle.nodename, - + gen_dist_test(listen_options_test, [{prio, Prio}, {additional_dist_opts, Options} | Config]). + +listen_options_test(NH1, NH2, Config) -> + Prio = proplists:get_value(prio, Config), + Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), PrioritiesNode1 = apply_on_ssl_node(NH1, fun get_socket_priorities/0), PrioritiesNode2 = apply_on_ssl_node(NH2, fun get_socket_priorities/0), - + Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio], - ?t:format("Elevated1: ~p~n", [Elevated1]), + ct:pal("Elevated1: ~p~n", [Elevated1]), Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio], - ?t:format("Elevated2: ~p~n", [Elevated2]), + ct:pal("Elevated2: ~p~n", [Elevated2]), [_|_] = Elevated1, - [_|_] = Elevated2, + [_|_] = Elevated2. - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- connect_options() -> [{doc, "Test inet_dist_connect_options"}]. @@ -369,9 +365,11 @@ do_connect_options(Prio, Config) -> end, Options = "-kernel inet_dist_connect_options " ++ PriorityString, + gen_dist_test(connect_options_test, + [{prio, Prio}, {additional_dist_opts, Options} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]), - NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]), +connect_options_test(NH1, NH2, Config) -> + Prio = proplists:get_value(prio, Config), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), @@ -382,17 +380,14 @@ do_connect_options(Prio, Config) -> apply_on_ssl_node(NH2, fun get_socket_priorities/0), Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio], - ?t:format("Elevated1: ~p~n", [Elevated1]), + ct:pal("Elevated1: ~p~n", [Elevated1]), Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio], - ?t:format("Elevated2: ~p~n", [Elevated2]), + ct:pal("Elevated2: ~p~n", [Elevated2]), %% Node 1 will have a socket with elevated priority. [_|_] = Elevated1, %% Node 2 will not, since it only applies to outbound connections. - [] = Elevated2, + [] = Elevated2. - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). %%-------------------------------------------------------------------- use_interface() -> [{doc, "Test inet_dist_use_interface"}]. @@ -403,22 +398,28 @@ use_interface(Config) when is_list(Config) -> %% Start a node, and get the port number it's listening on. NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]), - Node1 = NH1#node_handle.nodename, - Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)), - {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0), - {Name, Port} = lists:keyfind(Name, 1, NodesPorts), - - %% Now find the socket listening on that port, and check its sockname. - Sockets = apply_on_ssl_node( - NH1, - fun() -> - [inet:sockname(P) || - P <- inet_ports(), - {ok, Port} =:= (catch inet:port(P))] - end), - %% And check that it's actually listening on localhost. - [{ok,{{127,0,0,1},Port}}] = Sockets, - + + try + Node1 = NH1#node_handle.nodename, + Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)), + {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0), + {Name, Port} = lists:keyfind(Name, 1, NodesPorts), + + %% Now find the socket listening on that port, and check its sockname. + Sockets = apply_on_ssl_node( + NH1, + fun() -> + [inet:sockname(P) || + P <- inet_ports(), + {ok, Port} =:= (catch inet:port(P))] + end), + %% And check that it's actually listening on localhost. + [{ok,{{127,0,0,1},Port}}] = Sockets + catch + _:Reason -> + stop_ssl_node(NH1), + ct:fail(Reason) + end, stop_ssl_node(NH1), success(Config). %%-------------------------------------------------------------------- @@ -430,11 +431,11 @@ verify_fun_fail(Config) when is_list(Config) -> "\"{ssl_dist_SUITE,verify_fail_always,{}}\" " "client_verify verify_peer client_verify_fun " "\"{ssl_dist_SUITE,verify_fail_always,{}}\" ", + gen_dist_test(verify_fun_fail_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +verify_fun_fail_test(NH1, NH2, _) -> Node2 = NH2#node_handle.nodename, - + pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [] = apply_on_ssl_node(NH1, fun () -> nodes() end), @@ -446,25 +447,9 @@ verify_fun_fail(Config) when is_list(Config) -> %% On the server node, it wouldn't run, because the server didn't %% request a certificate from the client. undefined = - apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end), + apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end). - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). -verify_fail_always(_Certificate, _Event, _State) -> - %% Create an ETS table, to record the fact that the verify function ran. - %% Spawn a new process, to avoid the ETS table disappearing. - Parent = self(), - spawn( - fun() -> - ets:new(verify_fun_ran, [public, named_table]), - ets:insert(verify_fun_ran, {verify_fail_always_ran, true}), - Parent ! go_ahead, - timer:sleep(infinity) - end), - receive go_ahead -> ok end, - {fail, bad_certificate}. %%-------------------------------------------------------------------- verify_fun_pass() -> @@ -476,10 +461,10 @@ verify_fun_pass(Config) when is_list(Config) -> "server_fail_if_no_peer_cert true " "client_verify verify_peer client_verify_fun " "\"{ssl_dist_SUITE,verify_pass_always,{}}\" ", + gen_dist_test(verify_fun_pass_test, [{additional_dist_opts, DistOpts} | Config]). - NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), +verify_fun_pass_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), @@ -494,25 +479,8 @@ verify_fun_pass(Config) when is_list(Config) -> %% requested and verified the client's certificate because we %% passed fail_if_no_peer_cert. [{verify_pass_always_ran, true}] = - apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end), + apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end). - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). - -verify_pass_always(_Certificate, _Event, State) -> - %% Create an ETS table, to record the fact that the verify function ran. - %% Spawn a new process, to avoid the ETS table disappearing. - Parent = self(), - spawn( - fun() -> - ets:new(verify_fun_ran, [public, named_table]), - ets:insert(verify_fun_ran, {verify_pass_always_ran, true}), - Parent ! go_ahead, - timer:sleep(infinity) - end), - receive go_ahead -> ok end, - {valid, State}. %%-------------------------------------------------------------------- crl_check_pass() -> [{doc,"Test crl_check with non-revoked certificate"}]. @@ -520,10 +488,10 @@ crl_check_pass(Config) when is_list(Config) -> DistOpts = "-ssl_dist_opt client_crl_check true", NewConfig = [{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_check_pass_test, NewConfig). - NH1 = start_ssl_node(NewConfig), +crl_check_pass_test(NH1, NH2, Config) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), Node2 = NH2#node_handle.nodename, PrivDir = ?config(priv_dir, Config), @@ -533,11 +501,7 @@ crl_check_pass(Config) when is_list(Config) -> pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_check_fail() -> @@ -549,10 +513,9 @@ crl_check_fail(Config) when is_list(Config) -> %% The server uses a revoked certificate. {server_cert_dir, "revoked"}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_check_fail_test, NewConfig). - NH1 = start_ssl_node(NewConfig), - %%Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), +crl_check_fail_test(NH1, NH2, Config) -> Node2 = NH2#node_handle.nodename, PrivDir = ?config(priv_dir, Config), @@ -562,11 +525,7 @@ crl_check_fail(Config) when is_list(Config) -> pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_check_best_effort() -> @@ -576,22 +535,18 @@ crl_check_best_effort(Config) when is_list(Config) -> "server_verify verify_peer server_crl_check best_effort", NewConfig = [{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_check_best_effort_test, NewConfig). +crl_check_best_effort_test(NH1, NH2, _Config) -> %% We don't have the correct CRL at hand, but since crl_check is %% best_effort, we accept it anyway. - NH1 = start_ssl_node(NewConfig), Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_cache_check_pass() -> @@ -605,20 +560,16 @@ crl_cache_check_pass(Config) when is_list(Config) -> "\"{ssl_dist_SUITE,{\\\"" ++ NodeDir ++ "\\\",[]}}\"", NewConfig = [{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config, + gen_dist_test(crl_cache_check_pass_test, NewConfig). - NH1 = start_ssl_node(NewConfig), +crl_cache_check_pass_test(NH1, NH2, _) -> Node1 = NH1#node_handle.nodename, - NH2 = start_ssl_node(NewConfig), Node2 = NH2#node_handle.nodename, pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). + [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- crl_cache_check_fail() -> @@ -636,44 +587,31 @@ crl_cache_check_fail(Config) when is_list(Config) -> {server_cert_dir, "revoked"}, {additional_dist_opts, DistOpts}] ++ Config, - NH1 = start_ssl_node(NewConfig), - NH2 = start_ssl_node(NewConfig), - Node2 = NH2#node_handle.nodename, + gen_dist_test(crl_cache_check_fail_test, NewConfig). +crl_cache_check_fail_test(NH1, NH2, _) -> + Node2 = NH2#node_handle.nodename, pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end), [] = apply_on_ssl_node(NH1, fun () -> nodes() end), - [] = apply_on_ssl_node(NH2, fun () -> nodes() end), - - stop_ssl_node(NH1), - stop_ssl_node(NH2), - success(Config). - -%% ssl_crl_cache_api callbacks -lookup(_DistributionPoint, _DbHandle) -> - not_available. - -select({rdnSequence, NameParts}, {NodeDir, _}) -> - %% Extract the CN from the issuer name... - [CN] = [CN || - [#'AttributeTypeAndValue'{ - type = ?'id-at-commonName', - value = <<_, _, CN/binary>>}] <- NameParts], - %% ...and use that as the directory name to find the CRL. - error_logger:info_report([{found_cn, CN}]), - CRLFile = filename:join([NodeDir, CN, "crl.pem"]), - {ok, PemBin} = file:read_file(CRLFile), - PemEntries = public_key:pem_decode(PemBin), - CRLs = [ CRL || {'CertificateList', CRL, not_encrypted} - <- PemEntries], - CRLs. - -fresh_crl(_DistributionPoint, CRL) -> - CRL. - + [] = apply_on_ssl_node(NH2, fun () -> nodes() end). %%-------------------------------------------------------------------- %%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- +gen_dist_test(Test, Config) -> + NH1 = start_ssl_node(Config), + NH2 = start_ssl_node(Config), + try + ?MODULE:Test(NH1, NH2, Config) + catch + _:Reason -> + stop_ssl_node(NH1), + stop_ssl_node(NH2), + ct:fail(Reason) + end, + stop_ssl_node(NH1), + stop_ssl_node(NH2), + success(Config). %% ssl_node side api %% @@ -742,13 +680,15 @@ stop_ssl_node(#node_handle{connection_handler = Handler, receive {'DOWN', Mon, process, Handler, Reason} -> case Reason of - normal -> ok; - _ -> exit(Reason) + normal -> + ok; + _ -> + ct:pal("Down ~p ~n", [Reason]) end end; Error -> erlang:demonitor(Mon, [flush]), - exit(Error) + ct:pal("Warning ~p ~n", [Error]) end. start_ssl_node(Config) -> @@ -1226,3 +1166,53 @@ vsn(App) -> after application:stop(ssl) end. + +verify_fail_always(_Certificate, _Event, _State) -> + %% Create an ETS table, to record the fact that the verify function ran. + %% Spawn a new process, to avoid the ETS table disappearing. + Parent = self(), + spawn( + fun() -> + ets:new(verify_fun_ran, [public, named_table]), + ets:insert(verify_fun_ran, {verify_fail_always_ran, true}), + Parent ! go_ahead, + timer:sleep(infinity) + end), + receive go_ahead -> ok end, + {fail, bad_certificate}. + +verify_pass_always(_Certificate, _Event, State) -> + %% Create an ETS table, to record the fact that the verify function ran. + %% Spawn a new process, to avoid the ETS table disappearing. + Parent = self(), + spawn( + fun() -> + ets:new(verify_fun_ran, [public, named_table]), + ets:insert(verify_fun_ran, {verify_pass_always_ran, true}), + Parent ! go_ahead, + timer:sleep(infinity) + end), + receive go_ahead -> ok end, + {valid, State}. + +%% ssl_crl_cache_api callbacks +lookup(_DistributionPoint, _DbHandle) -> + not_available. + +select({rdnSequence, NameParts}, {NodeDir, _}) -> + %% Extract the CN from the issuer name... + [CN] = [CN || + [#'AttributeTypeAndValue'{ + type = ?'id-at-commonName', + value = <<_, _, CN/binary>>}] <- NameParts], + %% ...and use that as the directory name to find the CRL. + error_logger:info_report([{found_cn, CN}]), + CRLFile = filename:join([NodeDir, CN, "crl.pem"]), + {ok, PemBin} = file:read_file(CRLFile), + PemEntries = public_key:pem_decode(PemBin), + CRLs = [ CRL || {'CertificateList', CRL, not_encrypted} + <- PemEntries], + CRLs. + +fresh_crl(_DistributionPoint, CRL) -> + CRL. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 27c670cdc2..fd8af5efaa 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -385,7 +385,9 @@ cert_options(Config) -> SNIServerAKeyFile = filename:join([proplists:get_value(priv_dir, Config), "a.server", "key.pem"]), SNIServerBCertFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "cert.pem"]), SNIServerBKeyFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "key.pem"]), - [{client_opts, []}, + [{client_opts, [{cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, + {keyfile, ClientKeyFile}]}, {client_verification_opts, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, @@ -394,7 +396,7 @@ cert_options(Config) -> {certfile, ClientCertFileDigitalSignatureOnly}, {keyfile, ClientKeyFile}, {ssl_imp, new}]}, - {server_opts, [{ssl_imp, new},{reuseaddr, true}, + {server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, {client_psk, [{ssl_imp, new},{reuseaddr, true}, @@ -494,7 +496,7 @@ make_ecdsa_cert(Config) -> {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, - {cacertfile, ServerCaCertFile}, + {cacertfile, ClientCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, {verify, verify_peer}]}, {client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true}, @@ -519,7 +521,7 @@ make_ecdh_rsa_cert(Config) -> {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, - {cacertfile, ServerCaCertFile}, + {cacertfile, ClientCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, {verify, verify_peer}]}, {client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true}, @@ -815,6 +817,12 @@ rsa_suites(CounterPart) -> true; ({ecdhe_rsa, _, _}) when ECC == true -> true; + ({rsa, _, _, _}) -> + true; + ({dhe_rsa, _, _,_}) -> + true; + ({ecdhe_rsa, _, _,_}) when ECC == true -> + true; (_) -> false end, diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 87f5335723..ad2599c5a0 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -31,6 +31,320 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 3.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a race bug affecting <c>dets:open_file/2</c>. + </p> + <p> + Own Id: OTP-13260 Aux Id: seq13002 </p> + </item> + <item> + <p>Don't search for non-existing Map keys twice</p> + <p>For <c>maps:get/2,3</c> and <c>maps:find/2</c>, + searching for an immediate key, e.g. an atom, in a small + map, the search was performed twice if the key did not + exist.</p> + <p> + Own Id: OTP-13459</p> + </item> + <item> + <p> + Avoid stray corner-case math errors on Solaris, e.g. an + error is thrown on underflows in exp() and pow() when it + shouldn't be.</p> + <p> + Own Id: OTP-13531</p> + </item> + <item> + <p>Fix linting of map key variables</p> + <p>Map keys cannot be unbound and then used in parallel + matching.</p> + <p>Example: <c> #{ K := V } = #{ k := K } = M.</c> This + is illegal if <c>'K'</c> is not bound.</p> + <p> + Own Id: OTP-13534 Aux Id: ERL-135 </p> + </item> + <item> + <p> + Fixed a bug in re on openbsd where sometimes re:run would + return an incorrect result.</p> + <p> + Own Id: OTP-13602</p> + </item> + <item> + <p> + To avoid potential timer bottleneck on supervisor + restart, timer server is no longer used when the + supervisor is unable to restart a child.</p> + <p> + Own Id: OTP-13618 Aux Id: PR-1001 </p> + </item> + <item> + <p> The Erlang code preprocessor (<c>epp</c>) can handle + file names spanning over many tokens. Example: + <c>-include("a" "file" "name").</c>. </p> + <p> + Own Id: OTP-13662 Aux Id: seq13136 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The types of The Abstract Format in the + <c>erl_parse</c> module have been refined. </p> + <p> + Own Id: OTP-10292</p> + </item> + <item> + <p> Undocumented syntax for function specifications, + <c>-spec F/A :: Domain -> Range</c>, has been removed + (without deprecation). </p> <p> Using the + <c>is_subtype(V, T)</c> syntax for constraints (in + function specifications) is no longer documented, and the + newer syntax <c>V :: T</c> should be used instead. The + Erlang Parser still recognizes the <c>is_subtype</c> + syntax, and will continue to do so for some time. </p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-11879</p> + </item> + <item> + <p>The '<c>random</c>' module has been deprecated. Use + the '<c>rand</c>' module instead.</p> + <p> + Own Id: OTP-12502 Aux Id: OTP-12501 </p> + </item> + <item> + <p>Background: In record fields with a type declaration + but without an initializer, the Erlang parser inserted + automatically the singleton type <c>'undefined'</c> to + the list of declared types, if that value was not present + there. That is, the record declaration:</p> + <p> + -record(rec, {f1 :: float(), f2 = 42 :: integer(), f3 :: + some_mod:some_typ()}).</p> + <p>was translated by the parser to:</p> + <p> + -record(rec, {f1 :: float() | 'undefined', f2 = 42 :: + integer(), f3 :: some_mod:some_typ() | 'undefined'}).</p> + <p>The rationale for this was that creation of a "dummy" + <c>#rec{}</c> record should not result in a warning from + dialyzer that, for example, the implicit initialization + of the <c>#rec.f1</c> field violates its type + declaration.</p> + <p>Problems: This seemingly innocent action has some + unforeseen consequences.</p> + <p>For starters, there is no way for programmers to + declare that e.g. only floats make sense for the + <c>f1</c> field of <c>#rec{}</c> records when there is no + "obvious" default initializer for this field. (This also + affects tools like PropEr that use these declarations + produced by the Erlang parser to generate random + instances of records for testing purposes.)</p> + <p>It also means that dialyzer does not warn if e.g. an + <c>is_atom/1</c> test or something more exotic like an + <c>atom_to_list/1</c> call is performed on the value of + the <c>f1</c> field.</p> + <p>Similarly, there is no way to extend dialyzer to warn + if it finds record constructions where <c>f1</c> is not + initialized to some float.</p> + <p>Last but not least, it is semantically problematic + when the type of the field is an opaque type: creating a + union of an opaque and a structured type is very + problematic for analysis because it fundamentally breaks + the opacity of the term at that point.</p> + <p>Change: To solve these problems the parser will not + automatically insert the <c>'undefined'</c> value + anymore; instead the user has the option to choose the + places where this value makes sense (for the field) and + where it does not and insert the <c>| 'undefined'</c> + there manually.</p> + <p>Consequences of this change: This change means that + dialyzer will issue a warning for all places where + records with uninitialized fields are created and those + fields have a declared type that is incompatible with + <c>'undefined'</c> (e.g. <c>float()</c>). This warning + can be suppressed easily by adding <c>| 'undefined'</c> + to the type of this field. This also adds documentation + that the user really intends to create records where this + field is uninitialized.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-12719</p> + </item> + <item> + <p> Remove deprecated functions in the modules + <c>erl_scan</c> and <c>erl_parse</c>. </p> + <p> + Own Id: OTP-12861</p> + </item> + <item> + <p>The pre-processor can now expand the ?FUNCTION_NAME + and ?FUNCTION_ARITY macros.</p> + <p> + Own Id: OTP-13059</p> + </item> + <item> + <p> A new behaviour <c>gen_statem</c> has been + implemented. It has been thoroughly reviewed, is stable + enough to be used by at least two heavy OTP applications, + and is here to stay. But depending on user feedback, we + do not expect but might find it necessary to make minor + not backwards compatible changes into OTP-20.0, so its + state can be designated as "not quite experimental"... + </p> <p> The <c>gen_statem</c> behaviour is intended to + replace <c>gen_fsm</c> for new code. It has the same + features and add some really useful: </p> <list + type="bulleted"> <item>State code is gathered</item> + <item>The state can be any term</item> <item>Events can + be postponed</item> <item>Events can be self + generated</item> <item>A reply can be sent from a later + state</item> <item>There can be multiple sys traceable + replies</item> </list> <p> The callback model(s) for + <c>gen_statem</c> differs from the one for + <c>gen_fsm</c>, but it is still fairly easy to rewrite + from <c>gen_fsm</c> to <c>gen_statem</c>. </p> + <p> + Own Id: OTP-13065 Aux Id: PR-960 </p> + </item> + <item> + <p> + Optimize binary:split/2 and binary:split/3 with native + BIF implementation.</p> + <p> + Own Id: OTP-13082</p> + </item> + <item> + <p>Background: The types of record fields have since R12B + been put in a separate form by <c>epp:parse_file()</c>, + leaving the record declaration form untyped. The separate + form, however, does not follow the syntax of type + declarations, and parse transforms inspecting + <c>-type()</c> attributes need to know about the special + syntax. Since the compiler stores the return value of + <c>epp:parse_file()</c> as debug information in the + abstract code chunk (<c>"Abst"</c> or + <c>abstract_code</c>), tools too need to know about the + special syntax, if they inspect <c>-type()</c> attributes + in abstract code.</p> + <p>Change: No separate type form is created by + <c>epp:parse_file()</c>, but the type information is kept + in the record fields. This means that all parse + transforms and all tools inspecting <c>-record()</c> + declarations need to recognize <c>{typed_record_field, + Field, Type}</c>.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13148</p> + </item> + <item> + <p> + Unsized fields of the type <c>bytes</c> in binary + generators are now forbidden. (The other ways of writing + unsized fields, such as <c>binary</c>, are already + forbidden.)</p> + <p> + Own Id: OTP-13152</p> + </item> + <item> + <p> The type <c>map()</c> is built-in, and cannot be + redefined. </p> + <p> + Own Id: OTP-13153</p> + </item> + <item> + <p> Let <c>dets:open_file()</c> exit with a <c>badarg</c> + message if given a raw file name (a binary). </p> + <p> + Own Id: OTP-13229 Aux Id: ERL-55 </p> + </item> + <item> + <p> Add <c>filename:basedir/2,3</c></p> <p>basedir + returns suitable path(s) for 'user_cache', 'user_config', + 'user_data', 'user_log', 'site_config' and 'site_data'. + On linux and linux like systems the paths will respect + the XDG environment variables.</p> + <p> + Own Id: OTP-13392</p> + </item> + <item> + <p>There are new preprocessor directives + <c>-error(Term)</c> and <c>-warning(Term)</c> to cause a + compilation error or a compilation warning, + respectively.</p> + <p> + Own Id: OTP-13476</p> + </item> + <item> + <p> + Optimize <c>'++'</c> operator and <c>lists:append/2</c> + by using a single pass to build a new list while checking + for properness.</p> + <p> + Own Id: OTP-13487</p> + </item> + <item> + <p> + Add <c>maps:update_with/3,4</c> and <c>maps:take/2</c></p> + <p> + Own Id: OTP-13522 Aux Id: PR-1025 </p> + </item> + <item> + <p><c>lists:join/2</c> has been added. Similar to + <c>string:join/2</c> but works with arbitrary lists.</p> + <p> + Own Id: OTP-13523</p> + </item> + <item> + <p>Obfuscate asserts to make Dialyzer shut up.</p> + <p> + Own Id: OTP-13524 Aux Id: PR-1002 </p> + </item> + <item> + <p> + Supervisors now explicitly add their callback module in + the return from sys:get_status/1,2. This is to simplify + custom supervisor implementations. The Misc part of the + return value from sys:get_status/1,2 for a supervisor is + now:</p> + <p> + [{data, [{"State", + State}]},{supervisor,[{"Callback",Module}]}]</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13619 Aux Id: PR-1000 </p> + </item> + <item> + <p> + Relax translation of initial calls in <c>proc_lib</c>, + i.e. remove the restriction to only do the translation + for <c>gen_server</c> and <c>gen_fsm</c>. This enables + user defined <c>gen</c> based generic callback modules to + be displayed nicely in <c>c:i()</c> and observer.</p> + <p> + Own Id: OTP-13623</p> + </item> + <item> + <p>The function <c>queue:lait/1</c> (misspelling of + <c>liat/1</c>) is now deprecated.</p> + <p> + Own Id: OTP-13658</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 2.8</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 3f74e01692..20de06fd0b 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -232,20 +232,20 @@ match(_) -> match_object(_, _) -> erlang:nif_error(undef). --spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} | +-spec match_object(Tab, Pattern, Limit) -> {[Object], Continuation} | '$end_of_table' when Tab :: tab(), Pattern :: match_pattern(), Limit :: pos_integer(), - Match :: [term()], + Object :: tuple(), Continuation :: continuation(). match_object(_, _, _) -> erlang:nif_error(undef). --spec match_object(Continuation) -> {[Match], Continuation} | +-spec match_object(Continuation) -> {[Object], Continuation} | '$end_of_table' when - Match :: [term()], + Object :: tuple(), Continuation :: continuation(). match_object(_) -> diff --git a/lib/syntax_tools/doc/specs/.gitignore b/lib/syntax_tools/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/syntax_tools/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile index ff4f3f78ff..e55222e59c 100644 --- a/lib/syntax_tools/doc/src/Makefile +++ b/lib/syntax_tools/doc/src/Makefile @@ -81,10 +81,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include DVIPS_FLAGS += # ---------------------------------------------------- @@ -93,7 +98,7 @@ DVIPS_FLAGS += $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -120,6 +125,7 @@ clean clean_docs: rm -f $(MAN3DIR)/* rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ # ---------------------------------------------------- diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index 78b2c7c7a4..ef207f7c3d 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -32,6 +32,32 @@ <p>This document describes the changes made to the Syntax_Tools application.</p> +<section><title>Syntax_Tools 2.0</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The abstract data type in <c>erl_syntax</c> is + augmented with types and function specifications.</p> + <p>The module <c>erl_prettypr</c> pretty prints types and + function specification, and the output can be parsed.</p> + <p>The types of record fields are no longer ignored. As a + consequence <c>erl_syntax_lib:analyze_record_field/1</c> + returns <c>{Default, Type}</c> instead of <c>Default</c>. + The functions <c>analyze_record_attribute</c>, + <c>analyze_attribute</c>, <c>analyze_form</c>, and + <c>analyze_forms</c> in the <c>erl_syntax_lib</c> module + are also affected by this incompatible change.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-12863</p> + </item> + </list> + </section> + +</section> + <section><title>Syntax_Tools 1.7</title> <section><title>Improvements and New Features</title> diff --git a/lib/syntax_tools/doc/src/specs.xml b/lib/syntax_tools/doc/src/specs.xml new file mode 100644 index 0000000000..04c3a494e7 --- /dev/null +++ b/lib/syntax_tools/doc/src/specs.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_epp_dodger.xml"/> + <xi:include href="../specs/specs_erl_comment_scan.xml"/> + <xi:include href="../specs/specs_erl_prettypr.xml"/> + <xi:include href="../specs/specs_erl_recomment.xml"/> + <xi:include href="../specs/specs_erl_syntax.xml"/> + <xi:include href="../specs/specs_erl_syntax_lib.xml"/> + <xi:include href="../specs/specs_erl_tidy.xml"/> + <xi:include href="../specs/specs_igor.xml"/> + <xi:include href="../specs/specs_merl.xml"/> + <xi:include href="../specs/specs_merl_transform.xml"/> + <xi:include href="../specs/specs_prettypr.xml"/> +</specs> diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl index 03429d4d42..b5ac564146 100644 --- a/lib/syntax_tools/src/erl_comment_scan.erl +++ b/lib/syntax_tools/src/erl_comment_scan.erl @@ -30,8 +30,14 @@ %% ===================================================================== --type comment() :: {integer(), integer(), integer(), [string()]}. --type commentLine() :: {integer(), integer(), integer(), string()}. +-type comment() :: {Line:: integer(), + Column:: integer(), + Indentation :: integer(), + Text :: [string()]}. +-type commentLine() :: {Line :: integer(), + Column :: integer(), + Indent :: integer(), + Text :: string()}. %% ===================================================================== %% @spec file(FileName::file:filename()) -> [Comment] diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index f1615b2610..df0d78031c 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -195,10 +195,16 @@ format(Node) -> %% ===================================================================== %% @spec format(Tree::syntaxTree(), Options::[term()]) -> string() -%% syntaxTree() = erl_syntax:syntaxTree() %% -%% @type hook() = (syntaxTree(), context(), Continuation) -> document() -%% Continuation = (syntaxTree(), context()) -> document(). +%% @type syntaxTree() = erl_syntax:syntaxTree(). +%% +%% An abstract syntax tree. See the {@link erl_syntax} module for +%% details. +%% +%% @type hook() = (syntaxTree(), context(), Continuation) -> +%% prettypr:document() +%% Continuation = (syntaxTree(), context()) -> +%% prettypr:document(). %% %% A call-back function for user-controlled formatting. See {@link %% format/2}. @@ -277,7 +283,7 @@ format(Node, Options) -> %% ===================================================================== -%% @spec best(Tree::syntaxTree()) -> empty | document() +%% @spec best(Tree::syntaxTree()) -> empty | prettypr:document() %% @equiv best(Tree, []) -spec best(erl_syntax:syntaxTree()) -> 'empty' | prettypr:document(). @@ -288,7 +294,7 @@ best(Node) -> %% ===================================================================== %% @spec best(Tree::syntaxTree(), Options::[term()]) -> -%% empty | document() +%% empty | prettypr:document() %% %% @doc Creates a fixed "best" abstract layout for a syntax tree. This %% is similar to the `layout/2' function, except that here, the final @@ -310,7 +316,7 @@ best(Node, Options) -> %% ===================================================================== -%% @spec layout(Tree::syntaxTree()) -> document() +%% @spec layout(Tree::syntaxTree()) -> prettypr:document() %% @equiv layout(Tree, []) -spec layout(erl_syntax:syntaxTree()) -> prettypr:document(). @@ -320,8 +326,7 @@ layout(Node) -> %% ===================================================================== -%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> document() -%% document() = prettypr:document() +%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> prettypr:document() %% %% @doc Creates an abstract document layout for a syntax tree. The %% result represents a set of possible layouts (cf. module `prettypr'). diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl index c1141b2bc6..1d23034991 100644 --- a/lib/syntax_tools/src/erl_recomment.erl +++ b/lib/syntax_tools/src/erl_recomment.erl @@ -30,6 +30,9 @@ -export([recomment_forms/2, quick_recomment_forms/2, recomment_tree/2]). +%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax +%% tree. See the {@link erl_syntax} module for details. + %% ===================================================================== %% @spec quick_recomment_forms(Forms, Comments::[Comment]) -> %% syntaxTree() @@ -55,7 +58,6 @@ quick_recomment_forms(Tree, Cs) -> %% ===================================================================== %% @spec recomment_forms(Forms, Comments::[Comment]) -> syntaxTree() %% -%% syntaxTree() = erl_syntax:syntaxTree() %% Forms = syntaxTree() | [syntaxTree()] %% Comment = {Line, Column, Indentation, Text} %% Line = integer() diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 9815559779..5aecf5d774 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -280,7 +280,7 @@ mapfoldl(_, S, []) -> %% ===================================================================== %% @spec variables(syntaxTree()) -> set(atom()) %% -%% set(T) = //stdlib/sets:set(T) +%% @type set(T) = //stdlib/sets:set(T) %% %% @doc Returns the names of variables occurring in a syntax tree, The %% result is a set of variable names represented by atoms. Macro names @@ -1955,7 +1955,7 @@ analyze_application(Node) -> %% ===================================================================== -%% @spec analyze_type_application(Node::syntaxTree()) -> typeName() +%% @spec analyze_type_application(Node::syntaxTree()) -> TypeName %% %% TypeName = {atom(), integer()} %% | {ModuleName, {atom(), integer()}} diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index f2de12b410..5d3fc6f062 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -36,6 +36,11 @@ %% been reasonably well tested, but the possibility of errors remains. %% Keep backups of your original code safely stored, until you feel %% confident that the new, modified code can be trusted. +%% +%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax +%% tree. See the {@link erl_syntax} module for details. +%% +%% @type filename() = file:filename(). -module(erl_tidy). @@ -79,7 +84,6 @@ dir(Dir) -> %% ===================================================================== %% @spec dir(Directory::filename(), Options::[term()]) -> ok -%% filename() = file:filename() %% %% @doc Tidies Erlang source files in a directory and its %% subdirectories. @@ -414,7 +418,7 @@ write_module(Tree, Name, Opts) -> print_module(Tree, Opts) -> Printer = proplists:get_value(printer, Opts), - io:format(Printer(Tree, Opts)). + io:put_chars(Printer(Tree, Opts)). output(FD, Printer, Tree, Opts) -> io:put_chars(FD, Printer(Tree, Opts)), @@ -513,7 +517,6 @@ module(Forms) -> %% @spec module(Forms, Options::[term()]) -> syntaxTree() %% %% Forms = syntaxTree() | [syntaxTree()] -%% syntaxTree() = erl_syntax:syntaxTree() %% %% @doc Tidies a syntax tree representation of a module %% definition. The given `Forms' may be either a single diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index 1d14bd7c3a..943250e5cd 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -151,7 +151,8 @@ default_printer(Tree, Options) -> %% @spec parse_transform(Forms::[syntaxTree()], Options::[term()]) -> %% [syntaxTree()] %% -%% syntaxTree() = erl_syntax:syntaxTree() +%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax +%% tree. See the {@link erl_syntax} module for details. %% %% @doc Allows Igor to work as a component of the Erlang compiler. %% Including the term `{parse_transform, igor}' in the @@ -212,7 +213,7 @@ merge(Name, Files) -> %% @spec merge(Name::atom(), Files::[filename()], Options::[term()]) -> %% [filename()] %% -%% filename() = file:filename() +%% @type filename() = file:filename() %% %% @doc Merges source code files to a single file. `Name' %% specifies the name of the resulting module - not the name of the @@ -367,6 +368,7 @@ merge_files(Name, Files, Options) -> %% @spec merge_files(Name::atom(), Sources::[Forms], %% Files::[filename()], Options::[term()]) -> %% {syntaxTree(), [stubDescriptor()]} +%% %% Forms = syntaxTree() | [syntaxTree()] %% %% @doc Merges source code files and syntax trees to a single syntax diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index b935d42bb7..43c17e9f1f 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -27,14 +27,14 @@ %% Test cases -export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1, t_abstract_type/1,t_erl_parse_type/1,t_epp_dodger/1, - t_comment_scan/1,t_igor/1]). + t_comment_scan/1,t_igor/1,t_erl_tidy/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [app_test,appup_test,smoke_test,revert,revert_map, t_abstract_type,t_erl_parse_type,t_epp_dodger, - t_comment_scan,t_igor]. + t_comment_scan,t_igor,t_erl_tidy]. groups() -> []. @@ -237,6 +237,12 @@ t_igor(Config) when is_list(Config) -> ok. +t_erl_tidy(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + File = filename:join(DataDir,"erl_tidy_tilde.erl"), + ok = erl_tidy:file(File, [{stdout, true}]), + ok. + test_comment_scan([],_) -> ok; test_comment_scan([File|Files],DataDir) -> Filename = filename:join(DataDir,File), diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl new file mode 100644 index 0000000000..888264bad6 --- /dev/null +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl @@ -0,0 +1,13 @@ +%% +%% File: erl_tidy_tilde.erl +%% Author: Mark Bucciarelli +%% Created: 2016-06-05 +%% + +-module(erl_tidy_tilde). + +-export([start/0]). + +start() -> + io:put_chars("tilde characters ('~')in source were " + "breaking erl_tidy\n"). diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index 3a6ac37eef..a0a817c0f2 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -31,6 +31,56 @@ </header> <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 2.8.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Correct a bug when adding multiple modules to an Xref + server. The bug was introduced in OTP-19.0. </p> + <p> + Own Id: OTP-13708 Aux Id: ERL-173 </p> + </item> + </list> + </section> + +</section> + +<section><title>Tools 2.8.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Update fprof to use the new 'spawned' trace event to + determine when a process has been created.</p> + <p> + Own Id: OTP-13499</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Optimize adding multiple modules to an Xref server. + </p> + <p> + Own Id: OTP-13593</p> + </item> + <item> + <p> + Various emacs mode improvements, such as better tags + support.</p> + <p> + Own Id: OTP-13610</p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.8.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl index bb9815f9b0..f298a1ce81 100644 --- a/lib/tools/src/xref_base.erl +++ b/lib/tools/src/xref_base.erl @@ -746,7 +746,7 @@ read_a_module({Dir, BaseName}, AppName, Builtins, Verbose, Warnings, Mode) -> message(Warnings, no_debug_info, [File]), no; {ok, M, Data, UnresCalls0} -> - message(Verbose, done, [File]), + message(Verbose, done_file, [File]), %% Remove duplicates. Identical unresolved calls on the %% same line are counted as _one_ unresolved call. UnresCalls = usort(UnresCalls0), @@ -1842,6 +1842,8 @@ message(true, What, Arg) -> set_up -> io:format("Setting up...", Arg); done -> + io:format("done~n", Arg); + done_file -> io:format("done reading ~ts~n", Arg); error -> io:format("error~n", Arg); diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index ce30fb711a..01dbac6ecb 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -50,7 +50,7 @@ -export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]). --export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]). +-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1]). -import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]). @@ -82,7 +82,7 @@ groups() -> fun_mfa_r14, fun_mfa_vars, qlc]}, {analyses, [], [analyze, basic, md, q, variables, unused_locals]}, - {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}]. + {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708]}]. init_per_suite(Conf) when is_list(Conf) -> @@ -2393,6 +2393,19 @@ otp_10192(Conf) when is_list(Conf) -> xref:stop(s), ok. +%% OTP-10192. Allow filenames with character codes greater than 126. +otp_13708(Conf) when is_list(Conf) -> + {ok, _} = start(s), + ok = xref:set_default(s, [{verbose, true}]), + {ok, []} = xref:q(s,"E"), + xref:stop(s), + + CopyDir = ?copydir, + Dir = fname(CopyDir,"lib_test"), + {ok, _} = start(s), + ok = xref:set_library_path(s, [Dir], [{verbose, true}]), + xref:stop(s). + %%% %%% Utilities %%% diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 70564f05c6..e6287b0430 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.8.3 +TOOLS_VSN = 2.8.5 diff --git a/lib/typer/doc/src/notes.xml b/lib/typer/doc/src/notes.xml index d6d545d0e4..9ef5ca1c70 100644 --- a/lib/typer/doc/src/notes.xml +++ b/lib/typer/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to TypEr.</p> +<section><title>TypEr 0.9.11</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + <section><title>TypEr 0.9.10</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk index 507593ef56..ed12e067c1 100644 --- a/lib/typer/vsn.mk +++ b/lib/typer/vsn.mk @@ -1 +1 @@ -TYPER_VSN = 0.9.10 +TYPER_VSN = 0.9.11 diff --git a/lib/wx/doc/specs/.gitignore b/lib/wx/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/wx/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile index cae2f9fe4e..23890f47f0 100644 --- a/lib/wx/doc/src/Makefile +++ b/lib/wx/doc/src/Makefile @@ -63,10 +63,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include -I../../src DVIPS_FLAGS += # ---------------------------------------------------- @@ -111,6 +116,7 @@ clean clean_docs: rm -rf $(HTMLDIR)/* rm -f $(MAN3DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ ../html/edoc-info rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml index c7400206ab..4f0e166924 100644 --- a/lib/wx/doc/src/notes.xml +++ b/lib/wx/doc/src/notes.xml @@ -32,6 +32,43 @@ <p>This document describes the changes made to the wxErlang application.</p> +<section><title>Wx 1.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bugs which could cause called functions to be + invoked twice or not at all when callbacks where invoked + at the same time.</p> + <p> + Own Id: OTP-13491</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Changed atom 'boolean' fields in #wxMouseState{} to + 'boolean()'.</p> + <p> + Moved out arguments in wxListCtrl:hitTest to result.</p> + <p> + Removed no-op functions in wxGauge that have been removed + from wxWidgets-3.1.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13553</p> + </item> + </list> + </section> + +</section> + <section><title>Wx 1.6.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/wx/doc/src/specs.xml b/lib/wx/doc/src/specs.xml new file mode 100644 index 0000000000..2e19baafc4 --- /dev/null +++ b/lib/wx/doc/src/specs.xml @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="utf-8" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_wx.xml"/> + <xi:include href="../specs/specs_wx_object.xml"/> + <xi:include href="../specs/specs_wxAcceleratorEntry.xml"/> + <xi:include href="../specs/specs_wxAcceleratorTable.xml"/> + <xi:include href="../specs/specs_wxActivateEvent.xml"/> + <xi:include href="../specs/specs_wxArtProvider.xml"/> + <xi:include href="../specs/specs_wxAuiDockArt.xml"/> + <xi:include href="../specs/specs_wxAuiManager.xml"/> + <xi:include href="../specs/specs_wxAuiManagerEvent.xml"/> + <xi:include href="../specs/specs_wxAuiNotebook.xml"/> + <xi:include href="../specs/specs_wxAuiNotebookEvent.xml"/> + <xi:include href="../specs/specs_wxAuiPaneInfo.xml"/> + <xi:include href="../specs/specs_wxAuiSimpleTabArt.xml"/> + <xi:include href="../specs/specs_wxAuiTabArt.xml"/> + <xi:include href="../specs/specs_wxBitmapButton.xml"/> + <xi:include href="../specs/specs_wxBitmapDataObject.xml"/> + <xi:include href="../specs/specs_wxBitmap.xml"/> + <xi:include href="../specs/specs_wxBoxSizer.xml"/> + <xi:include href="../specs/specs_wxBrush.xml"/> + <xi:include href="../specs/specs_wxBufferedDC.xml"/> + <xi:include href="../specs/specs_wxBufferedPaintDC.xml"/> + <xi:include href="../specs/specs_wxButton.xml"/> + <xi:include href="../specs/specs_wxCalendarCtrl.xml"/> + <xi:include href="../specs/specs_wxCalendarDateAttr.xml"/> + <xi:include href="../specs/specs_wxCalendarEvent.xml"/> + <xi:include href="../specs/specs_wxCaret.xml"/> + <xi:include href="../specs/specs_wxCheckBox.xml"/> + <xi:include href="../specs/specs_wxCheckListBox.xml"/> + <xi:include href="../specs/specs_wxChildFocusEvent.xml"/> + <xi:include href="../specs/specs_wxChoicebook.xml"/> + <xi:include href="../specs/specs_wxChoice.xml"/> + <xi:include href="../specs/specs_wxClientDC.xml"/> + <xi:include href="../specs/specs_wxClipboard.xml"/> + <xi:include href="../specs/specs_wxClipboardTextEvent.xml"/> + <xi:include href="../specs/specs_wxCloseEvent.xml"/> + <xi:include href="../specs/specs_wxColourData.xml"/> + <xi:include href="../specs/specs_wxColourDialog.xml"/> + <xi:include href="../specs/specs_wxColourPickerCtrl.xml"/> + <xi:include href="../specs/specs_wxColourPickerEvent.xml"/> + <xi:include href="../specs/specs_wxComboBox.xml"/> + <xi:include href="../specs/specs_wxCommandEvent.xml"/> + <xi:include href="../specs/specs_wxContextMenuEvent.xml"/> + <xi:include href="../specs/specs_wxControl.xml"/> + <xi:include href="../specs/specs_wxControlWithItems.xml"/> + <xi:include href="../specs/specs_wxCursor.xml"/> + <xi:include href="../specs/specs_wxDataObject.xml"/> + <xi:include href="../specs/specs_wxDateEvent.xml"/> + <xi:include href="../specs/specs_wxDatePickerCtrl.xml"/> + <xi:include href="../specs/specs_wxDC.xml"/> + <xi:include href="../specs/specs_wxDCOverlay.xml"/> + <xi:include href="../specs/specs_wxDialog.xml"/> + <xi:include href="../specs/specs_wxDirDialog.xml"/> + <xi:include href="../specs/specs_wxDirPickerCtrl.xml"/> + <xi:include href="../specs/specs_wxDisplayChangedEvent.xml"/> + <xi:include href="../specs/specs_wxEraseEvent.xml"/> + <xi:include href="../specs/specs_wxEvent.xml"/> + <xi:include href="../specs/specs_wxEvtHandler.xml"/> + <xi:include href="../specs/specs_wxFileDataObject.xml"/> + <xi:include href="../specs/specs_wxFileDialog.xml"/> + <xi:include href="../specs/specs_wxFileDirPickerEvent.xml"/> + <xi:include href="../specs/specs_wxFilePickerCtrl.xml"/> + <xi:include href="../specs/specs_wxFindReplaceData.xml"/> + <xi:include href="../specs/specs_wxFindReplaceDialog.xml"/> + <xi:include href="../specs/specs_wxFlexGridSizer.xml"/> + <xi:include href="../specs/specs_wxFocusEvent.xml"/> + <xi:include href="../specs/specs_wxFontData.xml"/> + <xi:include href="../specs/specs_wxFontDialog.xml"/> + <xi:include href="../specs/specs_wxFont.xml"/> + <xi:include href="../specs/specs_wxFontPickerCtrl.xml"/> + <xi:include href="../specs/specs_wxFontPickerEvent.xml"/> + <xi:include href="../specs/specs_wxFrame.xml"/> + <xi:include href="../specs/specs_wxGauge.xml"/> + <xi:include href="../specs/specs_wxGBSizerItem.xml"/> + <xi:include href="../specs/specs_wxGenericDirCtrl.xml"/> + <xi:include href="../specs/specs_wxGLCanvas.xml"/> + <xi:include href="../specs/specs_wxGraphicsBrush.xml"/> + <xi:include href="../specs/specs_wxGraphicsContext.xml"/> + <xi:include href="../specs/specs_wxGraphicsFont.xml"/> + <xi:include href="../specs/specs_wxGraphicsMatrix.xml"/> + <xi:include href="../specs/specs_wxGraphicsObject.xml"/> + <xi:include href="../specs/specs_wxGraphicsPath.xml"/> + <xi:include href="../specs/specs_wxGraphicsPen.xml"/> + <xi:include href="../specs/specs_wxGraphicsRenderer.xml"/> + <xi:include href="../specs/specs_wxGridBagSizer.xml"/> + <xi:include href="../specs/specs_wxGridCellAttr.xml"/> + <xi:include href="../specs/specs_wxGridCellBoolEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellBoolRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellChoiceEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellFloatEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellFloatRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellNumberEditor.xml"/> + <xi:include href="../specs/specs_wxGridCellNumberRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellStringRenderer.xml"/> + <xi:include href="../specs/specs_wxGridCellTextEditor.xml"/> + <xi:include href="../specs/specs_wxGrid.xml"/> + <xi:include href="../specs/specs_wxGridEvent.xml"/> + <xi:include href="../specs/specs_wxGridSizer.xml"/> + <xi:include href="../specs/specs_wxHelpEvent.xml"/> + <xi:include href="../specs/specs_wxHtmlEasyPrinting.xml"/> + <xi:include href="../specs/specs_wxHtmlLinkEvent.xml"/> + <xi:include href="../specs/specs_wxHtmlWindow.xml"/> + <xi:include href="../specs/specs_wxIconBundle.xml"/> + <xi:include href="../specs/specs_wxIcon.xml"/> + <xi:include href="../specs/specs_wxIconizeEvent.xml"/> + <xi:include href="../specs/specs_wxIdleEvent.xml"/> + <xi:include href="../specs/specs_wxImage.xml"/> + <xi:include href="../specs/specs_wxImageList.xml"/> + <xi:include href="../specs/specs_wxInitDialogEvent.xml"/> + <xi:include href="../specs/specs_wxJoystickEvent.xml"/> + <xi:include href="../specs/specs_wxKeyEvent.xml"/> + <xi:include href="../specs/specs_wxLayoutAlgorithm.xml"/> + <xi:include href="../specs/specs_wxListbook.xml"/> + <xi:include href="../specs/specs_wxListBox.xml"/> + <xi:include href="../specs/specs_wxListCtrl.xml"/> + <xi:include href="../specs/specs_wxListEvent.xml"/> + <xi:include href="../specs/specs_wxListItemAttr.xml"/> + <xi:include href="../specs/specs_wxListItem.xml"/> + <xi:include href="../specs/specs_wxListView.xml"/> + <xi:include href="../specs/specs_wxLocale.xml"/> + <xi:include href="../specs/specs_wxLogNull.xml"/> + <xi:include href="../specs/specs_wxMask.xml"/> + <xi:include href="../specs/specs_wxMaximizeEvent.xml"/> + <xi:include href="../specs/specs_wxMDIChildFrame.xml"/> + <xi:include href="../specs/specs_wxMDIClientWindow.xml"/> + <xi:include href="../specs/specs_wxMDIParentFrame.xml"/> + <xi:include href="../specs/specs_wxMemoryDC.xml"/> + <xi:include href="../specs/specs_wxMenuBar.xml"/> + <xi:include href="../specs/specs_wxMenu.xml"/> + <xi:include href="../specs/specs_wxMenuEvent.xml"/> + <xi:include href="../specs/specs_wxMenuItem.xml"/> + <xi:include href="../specs/specs_wxMessageDialog.xml"/> + <xi:include href="../specs/specs_wxMiniFrame.xml"/> + <xi:include href="../specs/specs_wxMirrorDC.xml"/> + <xi:include href="../specs/specs_wxMouseCaptureChangedEvent.xml"/> + <xi:include href="../specs/specs_wxMouseCaptureLostEvent.xml"/> + <xi:include href="../specs/specs_wxMouseEvent.xml"/> + <xi:include href="../specs/specs_wxMoveEvent.xml"/> + <xi:include href="../specs/specs_wxMultiChoiceDialog.xml"/> + <xi:include href="../specs/specs_wxNavigationKeyEvent.xml"/> + <xi:include href="../specs/specs_wxNotebook.xml"/> + <xi:include href="../specs/specs_wxNotebookEvent.xml"/> + <xi:include href="../specs/specs_wxNotifyEvent.xml"/> + <xi:include href="../specs/specs_wxOverlay.xml"/> + <xi:include href="../specs/specs_wxPageSetupDialogData.xml"/> + <xi:include href="../specs/specs_wxPageSetupDialog.xml"/> + <xi:include href="../specs/specs_wxPaintDC.xml"/> + <xi:include href="../specs/specs_wxPaintEvent.xml"/> + <xi:include href="../specs/specs_wxPaletteChangedEvent.xml"/> + <xi:include href="../specs/specs_wxPalette.xml"/> + <xi:include href="../specs/specs_wxPanel.xml"/> + <xi:include href="../specs/specs_wxPasswordEntryDialog.xml"/> + <xi:include href="../specs/specs_wxPen.xml"/> + <xi:include href="../specs/specs_wxPickerBase.xml"/> + <xi:include href="../specs/specs_wxPopupTransientWindow.xml"/> + <xi:include href="../specs/specs_wxPopupWindow.xml"/> + <xi:include href="../specs/specs_wxPostScriptDC.xml"/> + <xi:include href="../specs/specs_wxPreviewCanvas.xml"/> + <xi:include href="../specs/specs_wxPreviewControlBar.xml"/> + <xi:include href="../specs/specs_wxPreviewFrame.xml"/> + <xi:include href="../specs/specs_wxPrintData.xml"/> + <xi:include href="../specs/specs_wxPrintDialogData.xml"/> + <xi:include href="../specs/specs_wxPrintDialog.xml"/> + <xi:include href="../specs/specs_wxPrinter.xml"/> + <xi:include href="../specs/specs_wxPrintout.xml"/> + <xi:include href="../specs/specs_wxPrintPreview.xml"/> + <xi:include href="../specs/specs_wxProgressDialog.xml"/> + <xi:include href="../specs/specs_wxQueryNewPaletteEvent.xml"/> + <xi:include href="../specs/specs_wxRadioBox.xml"/> + <xi:include href="../specs/specs_wxRadioButton.xml"/> + <xi:include href="../specs/specs_wxRegion.xml"/> + <xi:include href="../specs/specs_wxSashEvent.xml"/> + <xi:include href="../specs/specs_wxSashLayoutWindow.xml"/> + <xi:include href="../specs/specs_wxSashWindow.xml"/> + <xi:include href="../specs/specs_wxScreenDC.xml"/> + <xi:include href="../specs/specs_wxScrollBar.xml"/> + <xi:include href="../specs/specs_wxScrolledWindow.xml"/> + <xi:include href="../specs/specs_wxScrollEvent.xml"/> + <xi:include href="../specs/specs_wxScrollWinEvent.xml"/> + <xi:include href="../specs/specs_wxSetCursorEvent.xml"/> + <xi:include href="../specs/specs_wxShowEvent.xml"/> + <xi:include href="../specs/specs_wxSingleChoiceDialog.xml"/> + <xi:include href="../specs/specs_wxSizeEvent.xml"/> + <xi:include href="../specs/specs_wxSizer.xml"/> + <xi:include href="../specs/specs_wxSizerFlags.xml"/> + <xi:include href="../specs/specs_wxSizerItem.xml"/> + <xi:include href="../specs/specs_wxSlider.xml"/> + <xi:include href="../specs/specs_wxSpinButton.xml"/> + <xi:include href="../specs/specs_wxSpinCtrl.xml"/> + <xi:include href="../specs/specs_wxSpinEvent.xml"/> + <xi:include href="../specs/specs_wxSplashScreen.xml"/> + <xi:include href="../specs/specs_wxSplitterEvent.xml"/> + <xi:include href="../specs/specs_wxSplitterWindow.xml"/> + <xi:include href="../specs/specs_wxStaticBitmap.xml"/> + <xi:include href="../specs/specs_wxStaticBox.xml"/> + <xi:include href="../specs/specs_wxStaticBoxSizer.xml"/> + <xi:include href="../specs/specs_wxStaticLine.xml"/> + <xi:include href="../specs/specs_wxStaticText.xml"/> + <xi:include href="../specs/specs_wxStatusBar.xml"/> + <xi:include href="../specs/specs_wxStdDialogButtonSizer.xml"/> + <xi:include href="../specs/specs_wxStyledTextCtrl.xml"/> + <xi:include href="../specs/specs_wxStyledTextEvent.xml"/> + <xi:include href="../specs/specs_wxSysColourChangedEvent.xml"/> + <xi:include href="../specs/specs_wxSystemOptions.xml"/> + <xi:include href="../specs/specs_wxSystemSettings.xml"/> + <xi:include href="../specs/specs_wxTaskBarIcon.xml"/> + <xi:include href="../specs/specs_wxTaskBarIconEvent.xml"/> + <xi:include href="../specs/specs_wxTextAttr.xml"/> + <xi:include href="../specs/specs_wxTextCtrl.xml"/> + <xi:include href="../specs/specs_wxTextDataObject.xml"/> + <xi:include href="../specs/specs_wxTextEntryDialog.xml"/> + <xi:include href="../specs/specs_wxToggleButton.xml"/> + <xi:include href="../specs/specs_wxToolBar.xml"/> + <xi:include href="../specs/specs_wxToolbook.xml"/> + <xi:include href="../specs/specs_wxToolTip.xml"/> + <xi:include href="../specs/specs_wxTopLevelWindow.xml"/> + <xi:include href="../specs/specs_wxTreebook.xml"/> + <xi:include href="../specs/specs_wxTreeCtrl.xml"/> + <xi:include href="../specs/specs_wxTreeEvent.xml"/> + <xi:include href="../specs/specs_wxUpdateUIEvent.xml"/> + <xi:include href="../specs/specs_wxWindowCreateEvent.xml"/> + <xi:include href="../specs/specs_wxWindowDC.xml"/> + <xi:include href="../specs/specs_wxWindowDestroyEvent.xml"/> + <xi:include href="../specs/specs_wxWindow.xml"/> + <xi:include href="../specs/specs_wxXmlResource.xml"/> + <xi:include href="../specs/specs_wx_misc.xml"/> + <xi:include href="../specs/specs_glu.xml"/> + <xi:include href="../specs/specs_gl.xml"/> +</specs> diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk index de723b2a2d..de4e5e1935 100644 --- a/lib/wx/vsn.mk +++ b/lib/wx/vsn.mk @@ -1 +1 @@ -WX_VSN = 1.6.1 +WX_VSN = 1.7 diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index 4f61d4b52c..0abcb87998 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the Xmerl application.</p> +<section><title>Xmerl 1.3.11</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Internal changes</p> + <p> + Own Id: OTP-13551</p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.10</title> <section><title>Improvements and New Features</title> diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index 09d81e0533..a78a035a1f 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3.10 +XMERL_VSN = 1.3.11 diff --git a/make/otp.mk.in b/make/otp.mk.in index c05c499d66..3b2e1a5bba 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -4,7 +4,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. 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. @@ -271,6 +271,8 @@ SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript # Extract specifications and types from Erlang source files (-spec, -type) $(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $< +$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/gen/%.erl + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $< $(MAN1DIR)/%.1: %.xml diff --git a/otp_versions.table b/otp_versions.table index af476841ab..102d57deff 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,3 +1,6 @@ +OTP-19.0.1 : dialyzer-3.0.1 erts-8.0.1 inets-6.3.1 observer-2.2.1 ssh-4.3.1 tools-2.8.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 typer-0.9.11 wx-1.7 xmerl-1.3.11 : +OTP-19.0 : asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 tools-2.8.4 typer-0.9.11 wx-1.7 xmerl-1.3.11 # : +OTP-18.3.4.1 : ssh-4.2.2.1 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : OTP-18.3.4 : inets-6.2.4 ssl-7.3.3 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : OTP-18.3.3 : common_test-1.12.1 inets-6.2.3 ssl-7.3.2 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : OTP-18.3.2 : inets-6.2.2 ssl-7.3.1 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 : |