diff options
608 files changed, 21050 insertions, 13253 deletions
diff --git a/OTP_VERSION b/OTP_VERSION index c598172d8d..cd23cb3bd7 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -21.2 +21.2.6 diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam Binary files differindex 05894640cb..1e29538db9 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam Binary files differindex 9b8d7d8a9e..5fa6974ac7 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam Binary files differindex bd65ecfc30..17ff848921 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam diff --git a/erts/doc/src/atomics.xml b/erts/doc/src/atomics.xml index 3fca92fb97..455973f011 100644 --- a/erts/doc/src/atomics.xml +++ b/erts/doc/src/atomics.xml @@ -23,14 +23,14 @@ <title>atomics</title> </header> - <module>atomics</module> + <module since="OTP 21.2">atomics</module> <modulesummary>Atomic Functions</modulesummary> <description> <p>This module provides a set of functions to do atomic operations towards mutable atomic variables. The implementation utilizes only atomic hardware instructions without any software level locking, which makes it very efficient for concurrent access. The atomics are organized into - arrays with the follwing semantics:</p> + arrays with the following semantics:</p> <list type="bulleted"> <item> <p>Atomics are 64 bit integers.</p> @@ -70,7 +70,7 @@ <funcs> <func> - <name name="new" arity="2"/> + <name name="new" arity="2" since="OTP 21.2"/> <fsummary>Create atomic array</fsummary> <desc> <p>Create a new atomic array of <c><anno>Arity</anno></c> atomics.</p> @@ -85,11 +85,13 @@ bsl 64)-1</c>.</p> </item> </taglist> + <p>Atomics are not tied to the current process and are automatically + garbage collected when they are no longer referenced.</p> </desc> </func> <func> - <name name="put" arity="3"/> + <name name="put" arity="3" since="OTP 21.2"/> <fsummary>Set atomic value</fsummary> <desc> <p>Set atomic to <c><anno>Value</anno></c>.</p> @@ -97,7 +99,7 @@ </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since="OTP 21.2"/> <fsummary>Read atomic value</fsummary> <desc> <p>Read atomic value.</p> @@ -105,7 +107,7 @@ </func> <func> - <name name="add" arity="3"/> + <name name="add" arity="3" since="OTP 21.2"/> <fsummary>Add to atomic</fsummary> <desc> <p>Add <c><anno>Incr</anno></c> to atomic.</p> @@ -113,7 +115,7 @@ </func> <func> - <name name="add_get" arity="3"/> + <name name="add_get" arity="3" since="OTP 21.2"/> <fsummary>Atomic add and get</fsummary> <desc> <p>Atomic addition and return of the result.</p> @@ -121,7 +123,7 @@ </func> <func> - <name name="sub" arity="3"/> + <name name="sub" arity="3" since="OTP 21.2"/> <fsummary>Subtract from atomic</fsummary> <desc> <p>Subtract <c><anno>Decr</anno></c> from atomic.</p> @@ -129,7 +131,7 @@ </func> <func> - <name name="sub_get" arity="3"/> + <name name="sub_get" arity="3" since="OTP 21.2"/> <fsummary>Atomic sub and get</fsummary> <desc> <p>Atomic subtraction and return of the result.</p> @@ -137,7 +139,7 @@ </func> <func> - <name name="exchange" arity="3"/> + <name name="exchange" arity="3" since="OTP 21.2"/> <fsummary>Atomic exchange.</fsummary> <desc> <p>Atomically replaces the value of the atomic with @@ -147,7 +149,7 @@ </func> <func> - <name name="compare_exchange" arity="4"/> + <name name="compare_exchange" arity="4" since="OTP 21.2"/> <fsummary>Atomic compare and exchange.</fsummary> <desc> <p>Atomically compares the atomic with <c><anno>Expected</anno></c>, @@ -158,7 +160,7 @@ </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since="OTP 21.2"/> <fsummary>Get information about atomic array.</fsummary> <desc> <p>Return information about an atomic array in a map. The map diff --git a/erts/doc/src/counters.xml b/erts/doc/src/counters.xml index ba4a22759f..36816bd68d 100644 --- a/erts/doc/src/counters.xml +++ b/erts/doc/src/counters.xml @@ -23,13 +23,13 @@ <title>counters</title> </header> - <module>counters</module> + <module since="OTP 21.2">counters</module> <modulesummary>Counter Functions</modulesummary> <description> <p>This module provides a set of functions to do operations towards shared mutable counter variables. The implementation does not utilize any software level locking, which makes it very efficient for concurrent - access. The counters are organized into arrays with the follwing + access. The counters are organized into arrays with the following semantics:</p> <list type="bulleted"> <item> @@ -71,7 +71,7 @@ <funcs> <func> - <name name="new" arity="2"/> + <name name="new" arity="2" since="OTP 21.2"/> <fsummary>Create counter array</fsummary> <desc> <p>Create a new counter array of <c><anno>Size</anno></c> counters.</p> @@ -80,7 +80,7 @@ <taglist> <tag><c>atomics</c> (Default)</tag> <item><p>Counters will be sequentially consistent. If write - operation A is done sequencially before write operation B, then a concurrent reader + operation A is done sequentially before write operation B, then a concurrent reader may see none of them, only A, or both A and B. It cannot see only B.</p> </item> <tag><c>write_concurrency</c></tag> @@ -90,7 +90,7 @@ inconsistency and memory consumption per counter.</p> <p>Read operations may see sequentially inconsistent results with regard to concurrent write operations. Even if write operation A is done - sequencially before write operation B, a concurrent reader may see any + sequentially before write operation B, a concurrent reader may see any combination of A and B, including only B. A read operation is only guaranteed to see all writes done sequentially before the read. No writes are ever lost, but will eventually all be seen.</p> @@ -103,11 +103,13 @@ acceptable.</p> </item> </taglist> + <p>Counters are not tied to the current process and are automatically + garbage collected when they are no longer referenced.</p> </desc> </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since="OTP 21.2"/> <fsummary>Read counter value</fsummary> <desc> <p>Read counter value.</p> @@ -115,7 +117,7 @@ </func> <func> - <name name="add" arity="3"/> + <name name="add" arity="3" since="OTP 21.2"/> <fsummary>Add to counter</fsummary> <desc> <p>Add <c><anno>Incr</anno></c> to counter at index @@ -124,7 +126,7 @@ </func> <func> - <name name="sub" arity="3"/> + <name name="sub" arity="3" since="OTP 21.2"/> <fsummary>Subtract from counter</fsummary> <desc> <p>Subtract <c><anno>Decr</anno></c> from counter at index @@ -133,25 +135,25 @@ </func> <func> - <name name="put" arity="3"/> + <name name="put" arity="3" since="OTP 21.2"/> <fsummary>Set counter to value</fsummary> <desc> <p>Write <c><anno>Value</anno></c> to counter at index <c><anno>Ix</anno></c>.</p> <note> <p>Despite its name, the <c>write_concurrency</c> optimization does not - improve <c>put</c>. A call to <c>put</c> is a relative heavy + improve <c>put</c>. A call to <c>put</c> is a relatively heavy operation compared to the very lightweight and scalable <seealso marker="#add/3"><c>add</c></seealso> and <seealso marker="#sub/3"> <c>sub</c></seealso>. The cost for a <c>put</c> with - <c>write_concurrency</c> is lika a <seealso marker="#get/2"><c>get</c> + <c>write_concurrency</c> is like a <seealso marker="#get/2"><c>get</c> </seealso> plus a <c>put</c> without <c>write_concurrency</c>.</p> </note> </desc> </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since="OTP 21.2"/> <fsummary>Get information about counter array.</fsummary> <desc> <p>Return information about a counter array in a map. The map diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 05a9895687..133f160dc9 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1552,6 +1552,15 @@ parameter determines. The lingering prevents repeated deletions and insertions in the tables from occurring.</p> </item> + <tag><marker id="+ztma"/><c>+ztma true | false</c></tag> + <item> + <p>Enables or disables support for tuple module apply in + the emulator. This is a transitional flag for running code + that uses parameterized modules and was compiled under OTP 20 + or earlier. For future compatibility, the modules will need + to be recompiled with the +tuple_calls compiler option. + Defaults to false.</p> + </item> </taglist> </item> </taglist> diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 7055889e4a..58678f2393 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -944,7 +944,7 @@ int suggested_stack_size;</code> <funcs> <func> - <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry + <name since=""><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry *de)</nametext></name> <fsummary>Add a driver entry.</fsummary> <desc> @@ -968,7 +968,7 @@ int suggested_stack_size;</code> </func> <func> - <name><ret>void *</ret> + <name since=""><ret>void *</ret> <nametext>driver_alloc(ErlDrvSizeT size)</nametext></name> <fsummary>Allocate memory.</fsummary> <desc> @@ -985,7 +985,7 @@ int suggested_stack_size;</code> </func> <func> - <name><ret>ErlDrvBinary *</ret> + <name since=""><ret>ErlDrvBinary *</ret> <nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name> <fsummary>Allocate a driver binary.</fsummary> <desc> @@ -1008,7 +1008,7 @@ int suggested_stack_size;</code> </func> <func> - <name><ret>long</ret><nametext>driver_async(ErlDrvPort port, unsigned + <name since=""><ret>long</ret><nametext>driver_async(ErlDrvPort port, unsigned int* key, void (*async_invoke)(void*), void* async_data, void (*async_free)(void*))</nametext></name> <fsummary>Perform an asynchronous call within a driver.</fsummary> @@ -1076,7 +1076,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort + <name since="OTP R16B02"><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort port)</nametext></name> <fsummary>Calculate an async key from an ErlDrvPort.</fsummary> <desc> @@ -1096,7 +1096,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name> <fsummary>Decrement the reference count of a driver binary.</fsummary> <desc> @@ -1117,7 +1117,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name> <fsummary>Get the reference count of a driver binary.</fsummary> <desc> @@ -1128,7 +1128,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name> <fsummary>Increment the reference count of a driver binary.</fsummary> <desc> @@ -1140,7 +1140,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort port)</nametext></name> <fsummary>Return the process making the driver call.</fsummary> <desc> @@ -1183,7 +1183,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name> <fsummary>Cancel a previously set timer.</fsummary> <desc> @@ -1196,7 +1196,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor + <name since=""><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)</nametext></name> <fsummary>Compare two monitors.</fsummary> <desc> @@ -1211,7 +1211,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort port)</nametext></name> <fsummary>Return the port owner process.</fsummary> <desc> @@ -1223,7 +1223,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, + <name since=""><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, ErlDrvTermData owner_pid, char* name, ErlDrvData drv_data)</nametext></name> <fsummary>Create a new port (driver instance).</fsummary> @@ -1269,7 +1269,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name> <fsummary>Stop monitoring a process from a driver.</fsummary> <desc> @@ -1281,7 +1281,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, + <name since=""><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, ErlDrvSizeT size)</nametext></name> <fsummary>Dequeue data from the head of the driver queue.</fsummary> <desc> @@ -1299,7 +1299,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, + <name since=""><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name> <fsummary>Enqueue data in the driver queue.</fsummary> <desc> @@ -1325,7 +1325,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext> </name> <fsummary>Enqueue binary in the driver queue.</fsummary> @@ -1346,7 +1346,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, + <name since=""><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> <fsummary>Enqueue vector in the driver queue.</fsummary> <desc> @@ -1365,11 +1365,11 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int + <name since=""><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int error)</nametext></name> - <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char + <name since=""><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char *string)</nametext></name> - <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int + <name since=""><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int error)</nametext></name> <fsummary>Fail with error.</fsummary> <desc> @@ -1393,7 +1393,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort + <name since=""><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort port)</nametext></name> <fsummary>Fail with EOF.</fsummary> <desc> @@ -1408,7 +1408,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name> + <name since=""><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name> <fsummary>Free an allocated memory block.</fsummary> <desc> <marker id="driver_free"></marker> @@ -1422,7 +1422,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name> <fsummary>Free a driver binary.</fsummary> <desc> @@ -1436,7 +1436,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret> + <name since=""><ret>ErlDrvTermData</ret> <nametext>driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name> <fsummary>Retrieve the process ID from a monitor.</fsummary> @@ -1452,7 +1452,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>driver_get_now(ErlDrvNowData *now)</nametext></name> <fsummary>Read a system time stamp.</fsummary> <desc> @@ -1473,7 +1473,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort + <name since=""><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort port)</nametext></name> <fsummary>Ensure the driver is never unloaded.</fsummary> <desc> @@ -1486,7 +1486,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* string)</nametext></name> <fsummary>Make an atom from a name.</fsummary> <desc> @@ -1501,7 +1501,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort port)</nametext></name> <fsummary>Make an Erlang term port from a port.</fsummary> <desc> @@ -1517,7 +1517,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name> <fsummary>Monitor a process from a driver.</fsummary> <desc> @@ -1540,7 +1540,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, + <name since=""><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)</nametext></name> <fsummary>Send data from driver to port owner.</fsummary> <desc> @@ -1560,7 +1560,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char + <name since=""><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name> <fsummary>Send data from a driver binary to port owner.</fsummary> @@ -1589,7 +1589,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name> <fsummary>Send term data from driver to port owner.</fsummary> <desc> @@ -1608,7 +1608,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, + <name since=""><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name> <fsummary>Send data and binary data to port owner.</fsummary> <desc> @@ -1625,7 +1625,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, + <name since=""><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> <fsummary>Send vectorized data to port owner.</fsummary> <desc> @@ -1654,7 +1654,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvPDL</ret> + <name since=""><ret>ErlDrvPDL</ret> <nametext>driver_pdl_create(ErlDrvPort port)</nametext></name> <fsummary>Create a port data lock.</fsummary> <desc> @@ -1672,7 +1672,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL + <name since=""><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> @@ -1686,7 +1686,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> @@ -1698,7 +1698,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> @@ -1712,7 +1712,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name> <fsummary>Lock port data lock.</fsummary> <desc> @@ -1723,7 +1723,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name> <fsummary>Unlock port data lock.</fsummary> <desc> @@ -1734,7 +1734,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int + <name since=""><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name> <fsummary>Get the driver queue as a vector.</fsummary> <desc> @@ -1755,7 +1755,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, + <name since="OTP R15B"><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, ErlIOVec *ev)</nametext></name> <fsummary>Get the driver queue as an I/O vector.</fsummary> <desc> @@ -1775,7 +1775,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, + <name since=""><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name> <fsummary>Push data at the head of the driver queue.</fsummary> <desc> @@ -1792,7 +1792,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext> </name> <fsummary>Push binary at the head of the driver queue.</fsummary> @@ -1812,7 +1812,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec + <name since=""><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> <fsummary>Push vector at the head of the driver queue.</fsummary> <desc> @@ -1831,7 +1831,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned + <name since=""><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned long *time_left)</nametext></name> <fsummary>Read the time left before time-out.</fsummary> <desc> @@ -1844,7 +1844,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void *</ret> + <name since=""><ret>void *</ret> <nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name> <fsummary>Resize an allocated memory block.</fsummary> <desc> @@ -1859,7 +1859,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvBinary *</ret> + <name since=""><ret>ErlDrvBinary *</ret> <nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size) </nametext></name> <fsummary>Resize a driver binary.</fsummary> @@ -1873,7 +1873,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent + <name since=""><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)</nametext></name> <fsummary>Provides an event for having the emulator call the driver. </fsummary> @@ -1932,7 +1932,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> <fsummary>Send term data to other process than port owner process. </fsummary> @@ -1958,7 +1958,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned + <name since=""><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name> <fsummary>Set a timer to call the driver.</fsummary> <desc> @@ -1977,7 +1977,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret> + <name since=""><ret>ErlDrvSizeT</ret> <nametext>driver_sizeq(ErlDrvPort port)</nametext></name> <fsummary>Return the size of the driver queue.</fsummary> <desc> @@ -1991,7 +1991,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo + <name since=""><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)</nametext></name> <fsummary>Get information about the Erlang runtime system.</fsummary> <desc> @@ -2008,7 +2008,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, + <name since=""><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)</nametext></name> <fsummary>Collect data segments into a buffer.</fsummary> <desc> @@ -2029,7 +2029,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, + <name since="OTP R16B"><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name> <fsummary>Set and get limits for busy port message queue.</fsummary> <desc> @@ -2083,7 +2083,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond + <name since=""><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond *cnd)</nametext></name> <fsummary>Broadcast on a condition variable.</fsummary> <desc> @@ -2097,7 +2097,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char + <name since=""><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char *name)</nametext></name> <fsummary>Create a condition variable.</fsummary> <desc> @@ -2114,7 +2114,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond + <name since=""><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond *cnd)</nametext></name> <fsummary>Destroy a condition variable.</fsummary> <desc> @@ -2128,7 +2128,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond *cnd)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2142,7 +2142,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond + <name since=""><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond *cnd)</nametext></name> <fsummary>Signal on a condition variable.</fsummary> <desc> @@ -2156,7 +2156,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, + <name since=""><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)</nametext></name> <fsummary>Wait on a condition variable.</fsummary> <desc> @@ -2185,7 +2185,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, + <name since="OTP R16B"><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, int percent)</nametext></name> <fsummary>Give the runtime system a hint about how much CPU time the current driver callback call has consumed.</fsummary> @@ -2228,7 +2228,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime + <name since="OTP 18.3"><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name> <fsummary>Convert time unit of a time value.</fsummary> <desc> @@ -2254,7 +2254,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, + <name since=""><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)</nametext></name> <fsummary>Compare thread identifiers for equality.</fsummary> <desc> @@ -2276,7 +2276,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char + <name since=""><ret>int</ret><nametext>erl_drv_getenv(const char *key, char *value, size_t *value_size)</nametext></name> <fsummary>Get the value of an environment variable.</fsummary> <desc> @@ -2317,7 +2317,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, + <name since="OTP 19.0"><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, ErlDrvData res)</nametext></name> <fsummary>Acknowledge the start of the port.</fsummary> <desc> @@ -2345,7 +2345,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTime</ret> + <name since="OTP 18.3"><ret>ErlDrvTime</ret> <nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext> </name> <fsummary>Get Erlang monotonic time.</fsummary> @@ -2365,7 +2365,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char + <name since=""><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char *name)</nametext></name> <fsummary>Create a mutex.</fsummary> <desc> @@ -2380,7 +2380,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex + <name since=""><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex *mtx)</nametext></name> <fsummary>Destroy a mutex.</fsummary> <desc> @@ -2395,7 +2395,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex + <name since=""><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex *mtx)</nametext></name> <fsummary>Lock a mutex.</fsummary> <desc> @@ -2414,7 +2414,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex *mtx)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2428,7 +2428,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex + <name since=""><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex *mtx)</nametext></name> <fsummary>Try lock a mutex.</fsummary> <desc> @@ -2447,7 +2447,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex + <name since=""><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex *mtx)</nametext></name> <fsummary>Unlock a mutex.</fsummary> <desc> @@ -2460,7 +2460,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, + <name since="OTP R16B"><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)</nametext></name> <fsummary>Send term data from driver to port owner.</fsummary> <desc> @@ -2637,7 +2637,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char + <name since=""><ret>int</ret><nametext>erl_drv_putenv(const char *key, char *value)</nametext></name> <fsummary>Set the value of an environment variable.</fsummary> <desc> @@ -2668,7 +2668,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char + <name since=""><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char *name)</nametext></name> <fsummary>Create an rwlock.</fsummary> <desc> @@ -2684,7 +2684,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Destroy an rwlock.</fsummary> <desc> @@ -2699,7 +2699,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2713,7 +2713,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read lock an rwlock.</fsummary> <desc> @@ -2733,7 +2733,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read unlock an rwlock.</fsummary> <desc> @@ -2746,7 +2746,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read/write lock an rwlock.</fsummary> <desc> @@ -2766,7 +2766,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read/write unlock an rwlock.</fsummary> <desc> @@ -2779,7 +2779,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock + <name since=""><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Try to read lock an rwlock.</fsummary> <desc> @@ -2799,7 +2799,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock + <name since=""><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Try to read/write lock an rwlock.</fsummary> <desc> @@ -2819,7 +2819,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, + <name since="OTP R16B"><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> <fsummary>Send term data to other process than port owner process. </fsummary> @@ -2843,7 +2843,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, + <name since="OTP 19.0"><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, ErlDrvSInt pid)</nametext></name> <fsummary>Set the os_pid for the port.</fsummary> <desc> @@ -2857,7 +2857,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_thread_create(char *name, ErlDrvTid + <name since=""><ret>int</ret><nametext>erl_drv_thread_create(char *name, ErlDrvTid *tid, void * (*func)(void *), void *arg, ErlDrvThreadOpts *opts)</nametext></name> <fsummary>Create a thread.</fsummary> @@ -2920,7 +2920,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_thread_exit(void + <name since=""><ret>void</ret><nametext>erl_drv_thread_exit(void *exit_value)</nametext></name> <fsummary>Terminate calling thread.</fsummary> <desc> @@ -2939,7 +2939,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void + <name since=""><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void **exit_value)</nametext></name> <fsummary>Join with another thread.</fsummary> <desc> @@ -2962,7 +2962,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid tid)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2976,7 +2976,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvThreadOpts *</ret> + <name since=""><ret>ErlDrvThreadOpts *</ret> <nametext>erl_drv_thread_opts_create(char *name)</nametext></name> <fsummary>Create thread options.</fsummary> <desc> @@ -3005,7 +3005,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext> </name> <fsummary>Destroy thread options.</fsummary> @@ -3020,7 +3020,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvTid</ret> + <name since=""><ret>ErlDrvTid</ret> <nametext>erl_drv_thread_self(void)</nametext></name> <fsummary>Get the thread identifier of the current thread.</fsummary> <desc> @@ -3031,7 +3031,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit + <name since="OTP 18.3"><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit time_unit)</nametext></name> <fsummary>Get current time offset.</fsummary> <desc> @@ -3054,7 +3054,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey + <name since=""><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey key)</nametext></name> <fsummary>Get thread-specific data.</fsummary> <desc> @@ -3069,7 +3069,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, + <name since=""><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)</nametext></name> <fsummary>Create a thread-specific data key.</fsummary> <desc> @@ -3086,7 +3086,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey + <name since=""><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey key)</nametext></name> <fsummary>Destroy a thread-specific data key.</fsummary> <desc> @@ -3111,7 +3111,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void + <name since=""><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void *data)</nametext></name> <fsummary>Set thread-specific data.</fsummary> <desc> @@ -3138,7 +3138,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name> + <name since=""><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name> <fsummary>Get Erlang error atom name from error number.</fsummary> <desc> <marker id="erl_errno_id"></marker> @@ -3150,7 +3150,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry + <name since=""><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry *de)</nametext></name> <fsummary>Remove a driver entry.</fsummary> <desc> @@ -3164,7 +3164,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int + <name since=""><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name> <fsummary>Signal or unsignal port as busy.</fsummary> <desc> @@ -3195,7 +3195,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, + <name since=""><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, int flags)</nametext></name> <fsummary>Set flags on how to handle control entry function.</fsummary> <desc> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index bbc12b0a56..5cbeddabd9 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -963,7 +963,7 @@ typedef struct { <funcs> <func> - <name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> + <name since=""><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> <fsummary>Allocate dynamic memory.</fsummary> <desc> <p>Allocates memory of <c>size</c> bytes.</p> @@ -974,7 +974,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext> </name> <fsummary>Create a new binary.</fsummary> @@ -998,7 +998,7 @@ typedef struct { </func> <func> - <name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> + <name since="OTP R14B"><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> <fsummary>Create a new environment.</fsummary> <desc> <p>Allocates a new process independent environment. The environment can @@ -1012,7 +1012,7 @@ typedef struct { </func> <func> - <name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* + <name since="OTP R13B04"><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name> <fsummary>Allocate a memory-managed resource object.</fsummary> <desc> @@ -1022,7 +1022,7 @@ typedef struct { </func> <func> - <name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, + <name since="OTP 19.0"><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts)</nametext></name> <fsummary>Create a term from the external format.</fsummary> @@ -1047,7 +1047,7 @@ typedef struct { </func> <func> - <name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext> + <name since="OTP R14B"><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext> </name> <fsummary>Clear an environment for reuse.</fsummary> <desc> @@ -1058,7 +1058,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext> </name> <fsummary>Compare two terms.</fsummary> @@ -1073,7 +1073,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>enif_compare_monitors(const ErlNifMonitor + <name since="OTP 20.0"><ret>int</ret><nametext>enif_compare_monitors(const ErlNifMonitor *monitor1, const ErlNifMonitor *monitor2)</nametext></name> <fsummary>Compare two monitors.</fsummary> <desc> @@ -1088,7 +1088,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1098,7 +1098,7 @@ typedef struct { </func> <func> - <name><ret>ErlNifCond *</ret> + <name since="OTP R13B04"><ret>ErlNifCond *</ret> <nametext>enif_cond_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -1108,7 +1108,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1118,7 +1118,7 @@ typedef struct { </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_cond_name(ErlNifCond* cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1128,7 +1128,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1138,7 +1138,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext> </name> <fsummary></fsummary> @@ -1149,7 +1149,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since="OTP R16B"><ret>int</ret> <nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext> </name> <fsummary></fsummary> @@ -1184,7 +1184,7 @@ typedef struct { </func> <func> - <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime + <name since="OTP 18.3"><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name> <fsummary>Convert time unit of a time value.</fsummary> <desc> @@ -1209,7 +1209,7 @@ typedef struct { </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP 19.0"><ret>ERL_NIF_TERM</ret> <nametext>enif_cpu_time(ErlNifEnv *)</nametext></name> <fsummary></fsummary> <desc> @@ -1225,8 +1225,8 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>enif_demonitor_process(ErlNifEnv* caller_env, void* obj, - const ErlNifMonitor* mon)</nametext></name> + <name since="OTP 20.0"><ret>int</ret><nametext>enif_demonitor_process(ErlNifEnv* caller_env, + void* obj, const ErlNifMonitor* mon)</nametext></name> <fsummary>Cancel a process monitor.</fsummary> <desc> <marker id="enif_demonitor_process"></marker> @@ -1252,7 +1252,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext> </name> <fsummary></fsummary> @@ -1263,7 +1263,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>enif_fprintf(FILE *stream, const char *format, ...)</nametext></name> + <name since="OTP 21.0"><ret>int</ret><nametext>enif_fprintf(FILE *stream, const char *format, ...)</nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> <desc> <p>Similar to <c>fprintf</c> but this format string also accepts @@ -1276,7 +1276,7 @@ typedef struct { </func> <func> - <name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> + <name since=""><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> <fsummary>Free dynamic memory.</fsummary> <desc> <p>Frees memory allocated by @@ -1285,7 +1285,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R14B"><ret>void</ret> <nametext>enif_free_env(ErlNifEnv* env)</nametext></name> <fsummary>Free an environment allocated with enif_alloc_env.</fsummary> <desc> @@ -1296,7 +1296,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP 20.1"><ret>void</ret> <nametext>enif_free_iovec(ErlNifIOvec* iov)</nametext></name> <fsummary>Free an ErlIOVec</fsummary> <desc> @@ -1321,7 +1321,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext> </name> <fsummary>Get the text representation of an atom term.</fsummary> @@ -1337,7 +1337,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext> </name> <fsummary>Get the length of atom <c>term</c>.</fsummary> @@ -1351,7 +1351,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)</nametext></name> <fsummary>Read a floating-point number term.</fsummary> <desc> @@ -1362,7 +1362,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM + <name since=""><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM term, int* ip)</nametext></name> <fsummary>Read an integer term.</fsummary> <desc> @@ -1373,7 +1373,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)</nametext></name> <fsummary>Read a 64-bit integer term.</fsummary> <desc> @@ -1384,7 +1384,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name> <fsummary>Read a local pid term.</fsummary> <desc> @@ -1396,7 +1396,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name> <fsummary>Read a local port term.</fsummary> <desc> @@ -1408,7 +1408,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, + <name since=""><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext> </name> <fsummary>Get head and tail from a list.</fsummary> @@ -1420,7 +1420,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)</nametext></name> <fsummary>Get the length of list <c>term</c>.</fsummary> <desc> @@ -1431,7 +1431,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)</nametext></name> <fsummary>Read a long integer term.</fsummary> <desc> @@ -1442,7 +1442,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)</nametext></name> <fsummary>Read the size of a map term.</fsummary> <desc> @@ -1454,7 +1454,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext> </name> <fsummary>Get the value of a key in a map.</fsummary> @@ -1467,7 +1467,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext> </name> <fsummary>Get the pointer to a resource object.</fsummary> @@ -1480,7 +1480,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env, ERL_NIF_TERM list, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext></name> <fsummary>Get a C-string from a list.</fsummary> @@ -1504,7 +1504,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)</nametext></name> <fsummary>Inspect the elements of a tuple.</fsummary> <desc> @@ -1520,7 +1520,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)</nametext></name> <fsummary>Read an unsigned integer term.</fsummary> <desc> @@ -1532,7 +1532,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name> <fsummary>Read an unsigned 64-bit integer term.</fsummary> <desc> @@ -1544,7 +1544,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM + <name since=""><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name> <fsummary>Read an unsigned integer term.</fsummary> <desc> @@ -1557,7 +1557,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_getenv(const char* key, char* value, + <name since="OTP 18.2"><ret>int</ret><nametext>enif_getenv(const char* key, char* value, size_t *value_size)</nametext></name> <fsummary>Get the value of an environment variable.</fsummary> <desc> @@ -1567,7 +1567,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)</nametext></name> <fsummary>Check if an exception has been raised.</fsummary> <desc> @@ -1588,7 +1588,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name> + <name since="OTP 20.0"> <ret>ErlNifUInt64</ret> <nametext>enif_hash(ErlNifHash type, ERL_NIF_TERM term, ErlNifUInt64 salt)</nametext> </name> @@ -1601,7 +1601,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, + <name since=""><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name> <fsummary>Inspect the content of a binary.</fsummary> <desc> @@ -1613,7 +1613,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv* + <name since="OTP R13B04"><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin)</nametext></name> <fsummary>Inspect the content of an iolist.</fsummary> <desc> @@ -1627,7 +1627,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_inspect_iovec(ErlNifEnv* + <name since="OTP 20.1"><ret>int</ret><nametext>enif_inspect_iovec(ErlNifEnv* env, size_t max_elements, ERL_NIF_TERM iovec_term, ERL_NIF_TERM* tail, ErlNifIOVec** iovec)</nametext></name> <fsummary>Inspect a list of binaries as an ErlNifIOVec.</fsummary> @@ -1667,7 +1667,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ErlNifIOQueue *</ret> + <name since="OTP 20.1"><ret>ErlNifIOQueue *</ret> <nametext>enif_ioq_create(ErlNifIOQueueOpts opts)</nametext></name> <fsummary>Create a new IO Queue</fsummary> <desc> @@ -1678,7 +1678,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>void</ret> + <name since="OTP 20.1"><ret>void</ret> <nametext>enif_ioq_destroy(ErlNifIOQueue *q)</nametext></name> <fsummary>Destroy an IO Queue and free it's content</fsummary> <desc> @@ -1687,7 +1687,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 20.1"><ret>int</ret> <nametext>enif_ioq_deq(ErlNifIOQueue *q, size_t count, size_t *size)</nametext></name> <fsummary>Dequeue count bytes from the IO Queue</fsummary> <desc> @@ -1700,7 +1700,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 20.1"><ret>int</ret> <nametext>enif_ioq_enq_binary(ErlNifIOQueue *q, ErlNifBinary *bin, size_t skip)</nametext></name> <fsummary>Enqueue the binary into the IO Queue</fsummary> <desc> @@ -1713,7 +1713,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 20.1"><ret>int</ret> <nametext>enif_ioq_enqv(ErlNifIOQueue *q, ErlNifIOVec *iovec, size_t skip)</nametext></name> <fsummary>Enqueue the iovec into the IO Queue</fsummary> <desc> @@ -1724,7 +1724,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>SysIOVec *</ret> + <name since="OTP 20.1"><ret>SysIOVec *</ret> <nametext>enif_ioq_peek(ErlNifIOQueue *q, int *iovlen)</nametext></name> <fsummary>Peek inside the IO Queue</fsummary> <desc> @@ -1738,7 +1738,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_ioq_peek_head(ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *bin_term)</nametext></name> <fsummary>Peek the head of the IO Queue.</fsummary> <desc> @@ -1753,7 +1753,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>size_t</ret> + <name since="OTP 20.1"><ret>size_t</ret> <nametext>enif_ioq_size(ErlNifIOQueue *q)</nametext></name> <fsummary>Get the current size of the IO Queue</fsummary> <desc> @@ -1762,7 +1762,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is an atom.</fsummary> @@ -1772,7 +1772,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a binary.</fsummary> @@ -1782,7 +1782,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 19.0"><ret>int</ret> <nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext> </name> <fsummary>Determine if currently executing process is alive.</fsummary> @@ -1795,7 +1795,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is an empty list.</fsummary> <desc> @@ -1804,7 +1804,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, + <name since="OTP R14B03"><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is an exception.</fsummary> <desc><marker id="enif_is_exception"/> @@ -1813,7 +1813,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a fun.</fsummary> <desc> @@ -1822,7 +1822,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name> <fsummary>Erlang operator =:=.</fsummary> <desc> @@ -1832,7 +1832,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R14B"><ret>int</ret> <nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a list.</fsummary> @@ -1842,7 +1842,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP 18.0"><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a map.</fsummary> <desc> @@ -1852,7 +1852,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R15B"><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a number (integer or float).</fsummary> <desc> @@ -1861,7 +1861,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a pid.</fsummary> @@ -1871,7 +1871,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a port.</fsummary> @@ -1881,7 +1881,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id)</nametext></name> <fsummary>Determine if a local port is alive.</fsummary> <desc> @@ -1893,7 +1893,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid)</nametext></name> <fsummary>Determine if a local process is alive.</fsummary> <desc> @@ -1905,7 +1905,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a reference.</fsummary> @@ -1915,7 +1915,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R14B"><ret>int</ret> <nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a tuple.</fsummary> @@ -1925,7 +1925,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R14B"><ret>int</ret> <nametext>enif_keep_resource(void* obj)</nametext> </name> <fsummary>Add a reference to a resource object.</fsummary> @@ -1941,7 +1941,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext> </name> <fsummary>Create an atom term.</fsummary> @@ -1955,7 +1955,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)</nametext></name> <fsummary>Create an atom term.</fsummary> <desc> @@ -1969,7 +1969,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name> <fsummary>Make a badarg exception.</fsummary> <desc> @@ -1997,7 +1997,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext> </name> <fsummary>Make a binary term.</fsummary> @@ -2010,7 +2010,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)</nametext></name> <fsummary>Make a copy of a term.</fsummary> <desc> @@ -2021,7 +2021,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name> <fsummary>Create a floating-point term.</fsummary> <desc> @@ -2033,7 +2033,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)</nametext></name> <fsummary>Create an existing atom term.</fsummary> @@ -2049,7 +2049,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)</nametext></name> <fsummary>Create an existing atom term.</fsummary> @@ -2065,7 +2065,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </desc> </func> - <func><name><ret>ERL_NIF_TERM</ret> + <func><name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name> <fsummary>Create an integer term.</fsummary> <desc> @@ -2074,7 +2074,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext> </name> <fsummary>Create an integer term.</fsummary> @@ -2084,7 +2084,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext> </name> <fsummary>Create a list term.</fsummary> @@ -2097,24 +2097,24 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext> </name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name> <fsummary>Create a list term.</fsummary> <desc> @@ -2126,7 +2126,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* + <name since=""><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name> <fsummary>Create a list cell.</fsummary> <desc> @@ -2135,7 +2135,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name> <fsummary>Create a list term from an array.</fsummary> @@ -2147,7 +2147,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name> <fsummary>Create an integer term from a long int.</fsummary> <desc> @@ -2156,7 +2156,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)</nametext></name> <fsummary>Insert key-value pair in map.</fsummary> @@ -2172,7 +2172,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext> </name> <fsummary>Remove key from map.</fsummary> @@ -2188,7 +2188,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)</nametext></name> <fsummary>Replace value for key in map.</fsummary> @@ -2203,7 +2203,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_make_map_from_arrays(ErlNifEnv* env, ERL_NIF_TERM keys[], ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out)</nametext></name> <fsummary>Make map term from the given keys and values.</fsummary> @@ -2217,7 +2217,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* + <name since="OTP R14B"><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name> <fsummary>Allocate and create a new binary term.</fsummary> <desc> @@ -2233,7 +2233,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP 18.0"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name> <fsummary>Make an empty map term.</fsummary> <desc> @@ -2242,7 +2242,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext> </name> <fsummary>Make a pid term.</fsummary> @@ -2252,7 +2252,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_ref(ErlNifEnv* env)</nametext></name> <fsummary>Create a reference.</fsummary> <desc> @@ -2262,7 +2262,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext> </name> <fsummary>Create an opaque handle to a resource object.</fsummary> @@ -2310,7 +2310,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const void* data, size_t size)</nametext></name> <fsummary>Create a custom binary term.</fsummary> @@ -2336,7 +2336,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R15B"><ret>int</ret> <nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)</nametext></name> <fsummary>Create the reverse of a list.</fsummary> @@ -2352,7 +2352,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, + <name since=""><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)</nametext></name> <fsummary>Create a string.</fsummary> <desc> @@ -2363,7 +2363,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)</nametext></name> <fsummary>Create a string.</fsummary> @@ -2376,7 +2376,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv* + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name> <fsummary>Make a subbinary term.</fsummary> <desc> @@ -2388,7 +2388,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, + <name since=""><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)</nametext></name> <fsummary>Creates a tuple term.</fsummary> <desc> @@ -2399,23 +2399,23 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name> <fsummary>Create a tuple term.</fsummary> <desc> @@ -2427,7 +2427,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name> <fsummary>Create a tuple term from an array.</fsummary> @@ -2438,7 +2438,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext> </name> <fsummary>Create an unsigned integer term.</fsummary> @@ -2448,7 +2448,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext> </name> <fsummary>Create an unsigned integer term.</fsummary> @@ -2458,7 +2458,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext> </name> <fsummary>Create an integer term from an unsigned long int.</fsummary> @@ -2468,7 +2468,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv + <name since="OTP 19.0"><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties)</nametext></name> <fsummary></fsummary> <desc> @@ -2486,7 +2486,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)</nametext></name> <fsummary>Create a map iterator.</fsummary> @@ -2521,7 +2521,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, + <name since="OTP 18.0"><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Destroy a map iterator.</fsummary> <desc> @@ -2532,7 +2532,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)</nametext></name> <fsummary>Get key and value at current map iterator position.</fsummary> @@ -2545,7 +2545,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Check if map iterator is positioned before first.</fsummary> <desc> @@ -2555,7 +2555,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Check if map iterator is positioned after last.</fsummary> <desc> @@ -2565,7 +2565,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Increment map iterator to point to next entry.</fsummary> <desc> @@ -2577,7 +2577,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Decrement map iterator to point to previous entry.</fsummary> <desc> @@ -2589,8 +2589,8 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_monitor_process(ErlNifEnv* caller_env, void* obj, - const ErlNifPid* target_pid, ErlNifMonitor* mon)</nametext></name> + <name since="OTP 20.0"><ret>int</ret><nametext>enif_monitor_process(ErlNifEnv* caller_env, + void* obj, const ErlNifPid* target_pid, ErlNifMonitor* mon)</nametext></name> <fsummary>Monitor a process from a resource.</fsummary> <desc> <marker id="enif_monitor_process"></marker> @@ -2621,7 +2621,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifTime</ret> + <name since="OTP 18.3"><ret>ErlNifTime</ret> <nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext> </name> <fsummary>Get Erlang monotonic time.</fsummary> @@ -2642,7 +2642,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifMutex *</ret> + <name since="OTP R13B04"><ret>ErlNifMutex *</ret> <nametext>enif_mutex_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -2652,7 +2652,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2662,7 +2662,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2672,7 +2672,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_mutex_name(ErlNifMutex* mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2682,7 +2682,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2692,7 +2692,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2702,7 +2702,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP 19.0"><ret>ERL_NIF_TERM</ret> <nametext>enif_now_time(ErlNifEnv *env)</nametext></name> <fsummary></fsummary> <desc> @@ -2713,7 +2713,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifResourceType *</ret> + <name since="OTP R13B04"><ret>ErlNifResourceType *</ret> <nametext>enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext> @@ -2752,7 +2752,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifResourceType *</ret> + <name since="OTP 20.0"><ret>ErlNifResourceType *</ret> <nametext>enif_open_resource_type_x(ErlNifEnv* env, const char* name, const ErlNifResourceTypeInit* init, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext> @@ -2771,7 +2771,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const + <name since="OTP 19.0"><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext> </name> <fsummary>Send a port_command to to_port.</fsummary> @@ -2814,7 +2814,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void *</ret> + <name since="OTP R13B04"><ret>void *</ret> <nametext>enif_priv_data(ErlNifEnv* env)</nametext></name> <fsummary>Get the private data of a NIF library.</fsummary> <desc> @@ -2825,7 +2825,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* + <name since="OTP 18.0"><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)</nametext></name> <fsummary>Raise a NIF error exception.</fsummary> <desc> @@ -2848,7 +2848,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void *</ret> + <name since="OTP 20.2"><ret>void *</ret> <nametext>enif_realloc(void* ptr, size_t size)</nametext></name> <fsummary>Reallocate dynamic memory.</fsummary> <desc> @@ -2862,7 +2862,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext> </name> <fsummary>Change the size of a binary.</fsummary> @@ -2876,7 +2876,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name> <fsummary>Release a binary.</fsummary> <desc> @@ -2887,7 +2887,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_release_resource(void* obj)</nametext></name> <fsummary>Release a resource object.</fsummary> <desc> @@ -2906,7 +2906,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifRWLock *</ret> + <name since="OTP R13B04"><ret>ErlNifRWLock *</ret> <nametext>enif_rwlock_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -2916,7 +2916,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2926,7 +2926,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_rwlock_name(ErlNifRWLock* rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2936,7 +2936,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2946,7 +2946,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2956,7 +2956,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2966,7 +2966,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2976,7 +2976,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2986,7 +2986,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2996,7 +2996,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, + <name since="OTP 17.3"><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[])</nametext></name> @@ -3041,7 +3041,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP 20.0"><ret>int</ret> <nametext>enif_select(ErlNifEnv* env, ErlNifEvent event, enum ErlNifSelectFlags mode, void* obj, const ErlNifPid* pid, ERL_NIF_TERM ref)</nametext> </name> @@ -3135,7 +3135,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifPid *</ret> + <name since="OTP R14B"><ret>ErlNifPid *</ret> <nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext> </name> <fsummary>Get the pid of the calling process.</fsummary> @@ -3148,8 +3148,8 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret><nametext>enif_send(ErlNifEnv* caller_env, ErlNifPid* to_pid, - ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name> + <name since="OTP R14B"><ret>int</ret><nametext>enif_send(ErlNifEnv* caller_env, + ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name> <fsummary>Send a message to a process.</fsummary> <desc> <p>Sends a message to a process.</p> @@ -3195,7 +3195,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>unsigned</ret> + <name since="OTP R13B04"><ret>unsigned</ret> <nametext>enif_sizeof_resource(void* obj)</nametext></name> <fsummary>Get the byte size of a resource object.</fsummary> <desc> @@ -3206,7 +3206,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const + <name since="OTP 19.0"><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const char *format, ...)</nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> <desc> @@ -3220,7 +3220,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo + <name since="OTP R13B04"><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size)</nametext></name> <fsummary>Get information about the Erlang runtime system.</fsummary> <desc> @@ -3230,7 +3230,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name> <fsummary>Convert a term to the external format.</fsummary> <desc> @@ -3247,7 +3247,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)</nametext></name> @@ -3259,7 +3259,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_thread_exit(void *resp)</nametext></name> <fsummary></fsummary> <desc> @@ -3269,7 +3269,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name> <fsummary></fsummary> <desc> @@ -3279,7 +3279,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_thread_name(ErlNifTid tid)</nametext></name> <fsummary>Thread name</fsummary> <desc> @@ -3289,7 +3289,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifThreadOpts *</ret> + <name since="OTP R13B04"><ret>ErlNifThreadOpts *</ret> <nametext>enif_thread_opts_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -3299,7 +3299,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext> </name> <fsummary></fsummary> @@ -3310,7 +3310,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifTid</ret> + <name since="OTP R13B04"><ret>ErlNifTid</ret> <nametext>enif_thread_self(void)</nametext></name> <fsummary></fsummary> <desc> @@ -3320,7 +3320,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 19.0"><ret>int</ret> <nametext>enif_thread_type(void)</nametext></name> <fsummary>Determine type of current thread</fsummary> <desc> @@ -3342,7 +3342,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifTime</ret> + <name since="OTP 18.3"><ret>ErlNifTime</ret> <nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name> <fsummary>Get current time offset.</fsummary> <desc> @@ -3364,7 +3364,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void *</ret> + <name since="OTP R13B04"><ret>void *</ret> <nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name> <fsummary></fsummary> <desc> @@ -3374,7 +3374,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext> </name> <fsummary></fsummary> @@ -3385,7 +3385,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name> <fsummary></fsummary> <desc> @@ -3395,7 +3395,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name> <fsummary></fsummary> <desc> @@ -3405,7 +3405,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_vfprintf(FILE *stream, const char *format, va_list ap) </nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> @@ -3417,7 +3417,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_vsnprintf(char *str, size_t size, const char *format, va_list ap) </nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> @@ -3429,7 +3429,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 20.0"><ret>int</ret> <nametext>enif_whereis_pid(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPid *pid)</nametext></name> <fsummary>Looks up a process by its registered name.</fsummary> @@ -3457,7 +3457,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 20.0"><ret>int</ret> <nametext>enif_whereis_port(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPort *port)</nametext></name> <fsummary>Looks up a port by its registered name.</fsummary> diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index 286bac6c93..043d11b7b4 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -29,7 +29,7 @@ <rev></rev> <file>erl_prim_loader.xml</file> </header> - <module>erl_prim_loader</module> + <module since="">erl_prim_loader</module> <modulesummary>Low-level Erlang loader.</modulesummary> <description> <p>This module is used to load all Erlang modules into @@ -47,7 +47,7 @@ <funcs> <func> - <name name="get_file" arity="1"/> + <name name="get_file" arity="1" since=""/> <fsummary>Get a file.</fsummary> <desc> <p>Fetches a file using the low-level loader. @@ -65,7 +65,7 @@ </func> <func> - <name name="get_path" arity="0"/> + <name name="get_path" arity="0" since=""/> <fsummary>Get the path set in the loader.</fsummary> <desc> <p>Gets the path set in the loader. The path is @@ -75,7 +75,7 @@ </func> <func> - <name name="list_dir" arity="1"/> + <name name="list_dir" arity="1" since=""/> <fsummary>List files in a directory.</fsummary> <desc> <p>Lists all the files in a directory. Returns @@ -92,7 +92,7 @@ </func> <func> - <name name="read_file_info" arity="1"/> + <name name="read_file_info" arity="1" since=""/> <fsummary>Get information about a file.</fsummary> <desc> <p>Retrieves information about a file. Returns @@ -114,7 +114,7 @@ </func> <func> - <name name="read_link_info" arity="1"/> + <name name="read_link_info" arity="1" since="OTP 17.1.2"/> <fsummary>Get information about a link or file.</fsummary> <desc> <p>Works like @@ -131,7 +131,7 @@ </func> <func> - <name name="set_path" arity="1"/> + <name name="set_path" arity="1" since=""/> <fsummary>Set the path of the loader.</fsummary> <desc> <p>Sets the path of the loader if diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index fd3c17f337..fa4717bc2f 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>erl_tracer</module> + <module since="OTP 19.0">erl_tracer</module> <modulesummary>Erlang tracer behavior.</modulesummary> <description> <p>This behavior module implements the back end of the Erlang @@ -195,7 +195,7 @@ <funcs> <func> - <name>Module:enabled(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag"> @@ -224,7 +224,7 @@ </func> <func> - <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_call"> @@ -244,7 +244,7 @@ </func> <func> - <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_gc"> @@ -264,7 +264,7 @@ </func> <func> - <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_ports"> @@ -284,7 +284,7 @@ </func> <func> - <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_procs"> @@ -304,7 +304,7 @@ </func> <func> - <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result + <name since="OTP 19.0">Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result </name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -325,7 +325,7 @@ </func> <func> - <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> + <name since="OTP 19.0">Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -346,7 +346,7 @@ </func> <func> - <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> + <name since="OTP 19.0">Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -368,7 +368,7 @@ </func> <func> - <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_send"> @@ -388,7 +388,7 @@ </func> <func> - <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -417,7 +417,7 @@ </func> <func> - <name name="trace">Module:trace(seq_trace, TracerState, Label, + <name name="trace" since="OTP 19.0">Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result</name> <fsummary>Check if a sequence trace event is to be generated.</fsummary> <type> @@ -439,7 +439,7 @@ </func> <func> - <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -463,7 +463,7 @@ </func> <func> - <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, + <name since="OTP 19.0">Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -487,7 +487,7 @@ </func> <func> - <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -511,7 +511,7 @@ </func> <func> - <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -535,7 +535,7 @@ </func> <func> - <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -559,7 +559,7 @@ </func> <func> - <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, + <name since="OTP 19.0">Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -583,7 +583,7 @@ </func> <func> - <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, + <name since="OTP 19.0">Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -607,7 +607,7 @@ </func> <func> - <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 8e014c3010..6932b18571 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -29,7 +29,7 @@ <rev></rev> <file>erlang.xml</file> </header> - <module>erlang</module> + <module since="">erlang</module> <modulesummary>The Erlang BIFs.</modulesummary> <description> <p>By convention, most Built-In Functions (BIFs) are included @@ -210,8 +210,8 @@ <funcs> <func> - <name name="abs" arity="1" clause_i="1"/> - <name name="abs" arity="1" clause_i="2"/> + <name name="abs" arity="1" clause_i="1" since=""/> + <name name="abs" arity="1" clause_i="2" since=""/> <fsummary>Arithmetical absolute value.</fsummary> <desc> <p>Returns an integer or float that is the arithmetical @@ -227,7 +227,7 @@ </func> <func> - <name name="adler32" arity="1"/> + <name name="adler32" arity="1" since=""/> <fsummary>Compute adler32 checksum.</fsummary> <desc> <p>Computes and returns the adler32 checksum for @@ -236,7 +236,7 @@ </func> <func> - <name name="adler32" arity="2"/> + <name name="adler32" arity="2" since=""/> <fsummary>Compute adler32 checksum.</fsummary> <desc> <p>Continues computing the adler32 checksum by combining @@ -253,7 +253,7 @@ Y = erlang:adler32([Data1,Data2]).</code> </func> <func> - <name name="adler32_combine" arity="3"/> + <name name="adler32_combine" arity="3" since=""/> <fsummary>Combine two adler32 checksums.</fsummary> <desc> <p>Combines two previously computed adler32 checksums. @@ -272,7 +272,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="append_element" arity="2"/> + <name name="append_element" arity="2" since=""/> <fsummary>Append an extra element to a tuple.</fsummary> <desc> <p>Returns a new tuple that has one element more than @@ -289,7 +289,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="apply" arity="2"/> + <name name="apply" arity="2" since=""/> <fsummary>Apply a function to an argument list.</fsummary> <desc> <p>Calls a fun, passing the elements in <c><anno>Args</anno></c> @@ -307,7 +307,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="apply" arity="3"/> + <name name="apply" arity="3" since=""/> <fsummary>Apply a function to an argument list.</fsummary> <desc> <p>Returns the result of applying <c>Function</c> in @@ -337,7 +337,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="atom_to_binary" arity="2"/> + <name name="atom_to_binary" arity="2" since=""/> <fsummary>Return the binary representation of an atom.</fsummary> <desc> <p>Returns a binary corresponding to the text @@ -362,7 +362,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="atom_to_list" arity="1"/> + <name name="atom_to_list" arity="1" since=""/> <fsummary>Text representation of an atom.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -374,7 +374,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_part" arity="2"/> + <name name="binary_part" arity="2" since="OTP R14B"/> <fsummary>Extract a part of a binary.</fsummary> <desc> <p>Extracts the part of the binary described by @@ -399,7 +399,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_part" arity="3"/> + <name name="binary_part" arity="3" since="OTP R14B"/> <fsummary>Extract a part of a binary.</fsummary> <desc> <p>The same as <c>binary_part(<anno>Subject</anno>, @@ -409,7 +409,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_atom" arity="2"/> + <name name="binary_to_atom" arity="2" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is @@ -438,7 +438,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_existing_atom" arity="2"/> + <name name="binary_to_existing_atom" arity="2" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>As @@ -459,7 +459,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_float" arity="1"/> + <name name="binary_to_float" arity="1" since="OTP R16B"/> <fsummary>Convert from text representation to a float.</fsummary> <desc> <p>Returns the float whose text representation is @@ -473,7 +473,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_integer" arity="1"/> + <name name="binary_to_integer" arity="1" since="OTP R16B"/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation is @@ -487,7 +487,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_integer" arity="2"/> + <name name="binary_to_integer" arity="2" since="OTP R16B"/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation in base @@ -502,7 +502,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_list" arity="1"/> + <name name="binary_to_list" arity="1" since=""/> <fsummary>Convert a binary to a list.</fsummary> <desc> <p>Returns a list of integers corresponding to the bytes of @@ -511,7 +511,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_list" arity="3"/> + <name name="binary_to_list" arity="3" since=""/> <fsummary>Convert part of a binary to a list.</fsummary> <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>) </type_desc> @@ -533,7 +533,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_term" arity="1"/> + <name name="binary_to_term" arity="1" since=""/> <fsummary>Decode an Erlang external term format binary.</fsummary> <desc> <p>Returns an Erlang term that is the result of decoding @@ -559,7 +559,7 @@ hello </func> <func> - <name name="binary_to_term" arity="2"/> + <name name="binary_to_term" arity="2" since="OTP R13B04"/> <fsummary>Decode an Erlang external term format binary.</fsummary> <desc> <p>As <c>binary_to_term/1</c>, but takes these options:</p> @@ -613,7 +613,7 @@ hello </func> <func> - <name name="bit_size" arity="1"/> + <name name="bit_size" arity="1" since=""/> <fsummary>Return the size of a bitstring.</fsummary> <desc> <p>Returns an integer that is the size in bits of @@ -628,7 +628,7 @@ hello </func> <func> - <name name="bitstring_to_list" arity="1"/> + <name name="bitstring_to_list" arity="1" since=""/> <fsummary>Convert a bitstring to a list.</fsummary> <desc> <p>Returns a list of integers corresponding to the bytes of @@ -639,7 +639,7 @@ hello </func> <func> - <name name="bump_reductions" arity="1"/> + <name name="bump_reductions" arity="1" since=""/> <fsummary>Increment the reduction counter.</fsummary> <desc> <p>This implementation-dependent function increments @@ -657,7 +657,7 @@ hello </func> <func> - <name name="byte_size" arity="1"/> + <name name="byte_size" arity="1" since=""/> <fsummary>Return the size of a bitstring (or binary).</fsummary> <desc> <p>Returns an integer that is the number of bytes needed to @@ -674,7 +674,7 @@ hello </func> <func> - <name name="cancel_timer" arity="1"/> + <name name="cancel_timer" arity="1" since=""/> <fsummary>Cancel a timer.</fsummary> <desc> <p>Cancels a timer. The same as calling @@ -684,7 +684,7 @@ hello </func> <func> - <name name="cancel_timer" arity="2"/> + <name name="cancel_timer" arity="2" since="OTP 18.0"/> <fsummary>Cancel a timer.</fsummary> <desc> <p>Cancels a timer that has been created by @@ -766,7 +766,7 @@ hello </func> <func> - <name name="ceil" arity="1"/> + <name name="ceil" arity="1" since="OTP 20.0"/> <fsummary>Returns the smallest integer not less than the argument</fsummary> <desc> <p>Returns the smallest integer not less than @@ -779,7 +779,7 @@ hello </desc> </func> <func> - <name name="check_old_code" arity="1"/> + <name name="check_old_code" arity="1" since="OTP R14B04"/> <fsummary>Check if a module has old code.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Module</anno></c> has old code, @@ -790,7 +790,7 @@ hello </func> <func> - <name name="check_process_code" arity="2"/> + <name name="check_process_code" arity="2" since=""/> <fsummary>Check if a process executes old code for a module.</fsummary> <desc> <p>The same as @@ -801,7 +801,7 @@ hello </func> <func> - <name name="check_process_code" arity="3"/> + <name name="check_process_code" arity="3" since="OTP 17.0"/> <fsummary>Check if a process executes old code for a module.</fsummary> <desc> <p>Checks if the node local process identified by @@ -904,7 +904,7 @@ hello </func> <func> - <name name="convert_time_unit" arity="3"/> + <name name="convert_time_unit" arity="3" since="OTP 18.0"/> <fsummary>Convert time unit of a time value.</fsummary> <desc> <p>Converts the <c><anno>Time</anno></c> value of time unit @@ -922,7 +922,7 @@ hello </func> <func> - <name name="crc32" arity="1"/> + <name name="crc32" arity="1" since=""/> <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary> <desc> <p>Computes and returns the crc32 (IEEE 802.3 style) checksum @@ -931,7 +931,7 @@ hello </func> <func> - <name name="crc32" arity="2"/> + <name name="crc32" arity="2" since=""/> <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary> <desc> <p>Continues computing the crc32 checksum by combining @@ -948,7 +948,7 @@ Y = erlang:crc32([Data1,Data2]).</code> </func> <func> - <name name="crc32_combine" arity="3"/> + <name name="crc32_combine" arity="3" since=""/> <fsummary>Combine two crc32 (IEEE 802.3) checksums.</fsummary> <desc> <p>Combines two previously computed crc32 checksums. @@ -967,7 +967,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="date" arity="0"/> + <name name="date" arity="0" since=""/> <fsummary>Current date.</fsummary> <desc> <p>Returns the current date as <c>{Year, Month, Day}</c>.</p> @@ -980,7 +980,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="decode_packet" arity="3"/> + <name name="decode_packet" arity="3" since=""/> <fsummary>Extract a protocol packet from a binary.</fsummary> <desc> <p>Decodes the binary <c><anno>Bin</anno></c> according to the packet @@ -1090,7 +1090,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="delete_element" arity="2"/> + <name name="delete_element" arity="2" since="OTP R16B"/> <fsummary>Delete element at index in a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc> <desc> @@ -1103,7 +1103,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="delete_module" arity="1"/> + <name name="delete_module" arity="1" since=""/> <fsummary>Make the current code for a module old.</fsummary> <desc> <p>Makes the current code for <c><anno>Module</anno></c> become old @@ -1121,7 +1121,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="demonitor" arity="1"/> + <name name="demonitor" arity="1" since=""/> <fsummary>Stop monitoring.</fsummary> <desc> <p>If <c><anno>MonitorRef</anno></c> is a reference that the @@ -1163,7 +1163,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="demonitor" arity="2"/> + <name name="demonitor" arity="2" since=""/> <fsummary>Stop monitoring.</fsummary> <desc> <p>The returned value is <c>true</c> unless <c>info</c> is part @@ -1231,7 +1231,7 @@ end</code> </func> <func> - <name name="disconnect_node" arity="1"/> + <name name="disconnect_node" arity="1" since=""/> <fsummary>Force the disconnection of a node.</fsummary> <desc> <p>Forces the disconnection of a node. This appears to @@ -1245,7 +1245,7 @@ end</code> </func> <func> - <name name="display" arity="1"/> + <name name="display" arity="1" since=""/> <fsummary>Print a term on standard output.</fsummary> <desc> <p>Prints a text representation of <c><anno>Term</anno></c> on the @@ -1257,7 +1257,7 @@ end</code> </func> <func> - <name name="dist_ctrl_get_data" arity="1"/> + <name name="dist_ctrl_get_data" arity="1" since="OTP 21.0"/> <fsummary>Get distribution channel data to pass to another node.</fsummary> <desc> <p> @@ -1290,7 +1290,7 @@ end</code> </func> <func> - <name name="dist_ctrl_get_data_notification" arity="1"/> + <name name="dist_ctrl_get_data_notification" arity="1" since="OTP 21.0"/> <fsummary>Request notification about available outgoing distribution channel data.</fsummary> <desc> <p> @@ -1326,7 +1326,7 @@ end</code> </func> <func> - <name name="dist_ctrl_input_handler" arity="2"/> + <name name="dist_ctrl_input_handler" arity="2" since="OTP 21.0"/> <fsummary>Register distribution channel input handler process.</fsummary> <desc> <p> @@ -1359,7 +1359,7 @@ end</code> </func> <func> - <name name="dist_ctrl_put_data" arity="2"/> + <name name="dist_ctrl_put_data" arity="2" since="OTP 21.0"/> <fsummary>Pass data into the VM from a distribution channel.</fsummary> <desc> <p> @@ -1392,7 +1392,7 @@ end</code> </func> <func> - <name name="element" arity="2"/> + <name name="element" arity="2" since=""/> <fsummary>Return the Nth element of a tuple.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -1406,7 +1406,7 @@ b</pre> </func> <func> - <name name="erase" arity="0"/> + <name name="erase" arity="0" since=""/> <fsummary>Return and delete the process dictionary.</fsummary> <desc> <p>Returns the process dictionary and deletes it, for @@ -1420,7 +1420,7 @@ b</pre> </func> <func> - <name name="erase" arity="1"/> + <name name="erase" arity="1" since=""/> <fsummary>Return and delete a value from the process dictionary. </fsummary> <desc> @@ -1437,7 +1437,7 @@ b</pre> </func> <func> - <name name="error" arity="1"/> + <name name="error" arity="1" since=""/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with the reason @@ -1461,7 +1461,7 @@ b</pre> </func> <func> - <name name="error" arity="2"/> + <name name="error" arity="2" since=""/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with the reason @@ -1478,7 +1478,7 @@ b</pre> </func> <func> - <name name="exit" arity="1"/> + <name name="exit" arity="1" since=""/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with exit reason @@ -1495,7 +1495,7 @@ b</pre> </func> <func> - <name name="exit" arity="2"/> + <name name="exit" arity="2" since=""/> <fsummary>Send an exit signal to a process or a port.</fsummary> <desc> <p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to @@ -1531,7 +1531,7 @@ b</pre> </func> <func> - <name name="external_size" arity="1"/> + <name name="external_size" arity="1" since="OTP R14B04"/> <fsummary>Calculate the maximum size for a term encoded in the Erlang external term format.</fsummary> <desc> @@ -1550,7 +1550,7 @@ erlang:external_size(<anno>Term</anno>, [])</code> </func> <func> - <name name="external_size" arity="2"/> + <name name="external_size" arity="2" since="OTP R14B04"/> <fsummary>Calculate the maximum size for a term encoded in the Erlang external term format.</fsummary> <desc> @@ -1570,7 +1570,7 @@ true</pre> </func> <func> - <name name="float" arity="1"/> + <name name="float" arity="1" since=""/> <fsummary>Convert a number to a float.</fsummary> <desc> <p>Returns a float by converting <c><anno>Number</anno></c> to a float, @@ -1592,7 +1592,7 @@ true</pre> </func> <func> - <name name="float_to_binary" arity="1"/> + <name name="float_to_binary" arity="1" since="OTP R16B"/> <fsummary>Text representation of a float.</fsummary> <desc> <p>The same as @@ -1601,7 +1601,7 @@ true</pre> </func> <func> - <name name="float_to_binary" arity="2"/> + <name name="float_to_binary" arity="2" since="OTP R16B"/> <fsummary>Text representation of a float formatted using specified options.</fsummary> <desc> @@ -1619,7 +1619,7 @@ true</pre> </func> <func> - <name name="float_to_list" arity="1"/> + <name name="float_to_list" arity="1" since=""/> <fsummary>Text representation of a float.</fsummary> <desc> <p>The same as @@ -1628,7 +1628,7 @@ true</pre> </func> <func> - <name name="float_to_list" arity="2"/> + <name name="float_to_list" arity="2" since="OTP R16B"/> <fsummary>Text representation of a float formatted using specified options.</fsummary> <desc> @@ -1664,7 +1664,7 @@ true</pre> </func> <func> - <name name="floor" arity="1"/> + <name name="floor" arity="1" since="OTP 20.0"/> <fsummary>Returns the largest integer not greater than the argument</fsummary> <desc> <p>Returns the largest integer not greater than @@ -1678,7 +1678,7 @@ true</pre> </func> <func> - <name name="fun_info" arity="1"/> + <name name="fun_info" arity="1" since=""/> <fsummary>Information about a fun.</fsummary> <desc> <p>Returns a list with information about the fun @@ -1779,7 +1779,7 @@ true</pre> </func> <func> - <name name="fun_info" arity="2"/> + <name name="fun_info" arity="2" since=""/> <fsummary>Information about a fun.</fsummary> <type name="fun_info_item"/> <desc> @@ -1799,7 +1799,7 @@ true</pre> </func> <func> - <name name="fun_to_list" arity="1"/> + <name name="fun_to_list" arity="1" since=""/> <fsummary>Text representation of a fun.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -1808,7 +1808,7 @@ true</pre> </func> <func> - <name name="function_exported" arity="3"/> + <name name="function_exported" arity="3" since=""/> <fsummary>Check if a function is exported and loaded.</fsummary> <desc> <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is @@ -1824,7 +1824,7 @@ true</pre> </func> <func> - <name name="garbage_collect" arity="0"/> + <name name="garbage_collect" arity="0" since=""/> <fsummary>Force an immediate garbage collection of the calling process. </fsummary> <desc> @@ -1840,7 +1840,7 @@ true</pre> </func> <func> - <name name="garbage_collect" arity="1"/> + <name name="garbage_collect" arity="1" since=""/> <fsummary>Garbage collect a process.</fsummary> <desc> <p>The same as @@ -1850,7 +1850,7 @@ true</pre> </func> <func> - <name name="garbage_collect" arity="2"/> + <name name="garbage_collect" arity="2" since="OTP 17.0"/> <fsummary>Garbage collect a process.</fsummary> <desc> <p>Garbage collects the node local process identified by @@ -1915,7 +1915,7 @@ true</pre> </func> <func> - <name name="get" arity="0"/> + <name name="get" arity="0" since=""/> <fsummary>Return the process dictionary.</fsummary> <desc> <p>Returns the process dictionary as a list of @@ -1930,7 +1930,7 @@ true</pre> </func> <func> - <name name="get" arity="1"/> + <name name="get" arity="1" since=""/> <fsummary>Return a value from the process dictionary.</fsummary> <desc> <p>Returns the value <c><anno>Val</anno></c> associated with @@ -1946,7 +1946,7 @@ true</pre> </func> <func> - <name name="get_cookie" arity="0"/> + <name name="get_cookie" arity="0" since=""/> <fsummary>Get the magic cookie of the local node.</fsummary> <desc> <p>Returns the magic cookie of the local node if the node is @@ -1955,7 +1955,7 @@ true</pre> </func> <func> - <name name="get_keys" arity="0"/> + <name name="get_keys" arity="0" since="OTP 18.0"/> <fsummary>Return a list of all keys from the process dictionary. </fsummary> <desc> @@ -1971,7 +1971,7 @@ true</pre> </func> <func> - <name name="get_keys" arity="1"/> + <name name="get_keys" arity="1" since=""/> <fsummary>Return a list of keys from the process dictionary.</fsummary> <desc> <p>Returns a list of keys that are associated with the value @@ -1989,7 +1989,7 @@ true</pre> </func> <func> - <name name="get_stacktrace" arity="0"/> + <name name="get_stacktrace" arity="0" since=""/> <fsummary>Get the call stack back-trace of the last exception.</fsummary> <type name="stack_item"/> <desc> @@ -2054,7 +2054,7 @@ end</pre> </func> <func> - <name name="group_leader" arity="0"/> + <name name="group_leader" arity="0" since=""/> <fsummary>Get the group leader for the calling process.</fsummary> <desc> <p>Returns the process identifier of the group leader for the @@ -2069,7 +2069,7 @@ end</pre> </func> <func> - <name name="group_leader" arity="2"/> + <name name="group_leader" arity="2" since=""/> <fsummary>Set the group leader for a process.</fsummary> <desc> <p>Sets the group leader of <c><anno>Pid</anno></c> @@ -2090,7 +2090,7 @@ end</pre> </func> <func> - <name name="halt" arity="0"/> + <name name="halt" arity="0" since=""/> <fsummary>Halt the Erlang runtime system and indicate normal exit to the calling environment.</fsummary> <desc> @@ -2103,7 +2103,7 @@ os_prompt%</pre> </func> <func> - <name name="halt" arity="1"/> + <name name="halt" arity="1" since=""/> <fsummary>Halt the Erlang runtime system.</fsummary> <desc> <p>The same as <seealso marker="#halt/2"> @@ -2117,7 +2117,7 @@ os_prompt%</pre> </func> <func> - <name name="halt" arity="2"/> + <name name="halt" arity="2" since="OTP R15B01"/> <fsummary>Halt the Erlang runtime system.</fsummary> <desc> <p><c><anno>Status</anno></c> must be a non-negative integer, a string, @@ -2159,7 +2159,7 @@ os_prompt%</pre> </func> <func> - <name name="hd" arity="1"/> + <name name="hd" arity="1" since=""/> <fsummary>Head of a list.</fsummary> <desc> <p>Returns the head of <c><anno>List</anno></c>, that is, @@ -2174,7 +2174,7 @@ os_prompt%</pre> </func> <func> - <name name="hibernate" arity="3"/> + <name name="hibernate" arity="3" since=""/> <fsummary>Hibernate a process until a message is sent to it.</fsummary> <desc> <p>Puts the calling process into a wait state where its memory @@ -2215,7 +2215,7 @@ os_prompt%</pre> </func> <func> - <name name="insert_element" arity="3"/> + <name name="insert_element" arity="3" since="OTP R16B"/> <fsummary>Insert an element at index in a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + 1</type_desc> @@ -2233,7 +2233,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_binary" arity="1"/> + <name name="integer_to_binary" arity="1" since="OTP R16B"/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a binary corresponding to the text @@ -2245,7 +2245,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_binary" arity="2"/> + <name name="integer_to_binary" arity="2" since="OTP R16B"/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a binary corresponding to the text @@ -2258,7 +2258,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_list" arity="1"/> + <name name="integer_to_list" arity="1" since=""/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -2270,7 +2270,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_list" arity="2"/> + <name name="integer_to_list" arity="2" since=""/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -2283,7 +2283,7 @@ os_prompt%</pre> </func> <func> - <name name="iolist_size" arity="1"/> + <name name="iolist_size" arity="1" since=""/> <fsummary>Size of an iolist.</fsummary> <desc> <p>Returns an integer, that is the size in bytes, @@ -2296,7 +2296,7 @@ os_prompt%</pre> </func> <func> - <name name="iolist_to_binary" arity="1"/> + <name name="iolist_to_binary" arity="1" since=""/> <fsummary>Convert an iolist to a binary.</fsummary> <desc> <p>Returns a binary that is made from the integers and @@ -2314,7 +2314,7 @@ os_prompt%</pre> </func> <func> - <name name="iolist_to_iovec" arity="1"/> + <name name="iolist_to_iovec" arity="1" since="OTP 20.1"/> <fsummary>Converts an iolist to a iovec.</fsummary> <desc> <p>Returns an iovec that is made from the integers and binaries in @@ -2323,7 +2323,7 @@ os_prompt%</pre> </func> <func> - <name name="is_alive" arity="0"/> + <name name="is_alive" arity="0" since=""/> <fsummary>Check whether the local node is alive.</fsummary> <desc> <p>Returns <c>true</c> if the local node is alive (that is, if @@ -2333,7 +2333,7 @@ os_prompt%</pre> </func> <func> - <name name="is_atom" arity="1"/> + <name name="is_atom" arity="1" since=""/> <fsummary>Check whether a term is an atom.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom, @@ -2343,7 +2343,7 @@ os_prompt%</pre> </func> <func> - <name name="is_binary" arity="1"/> + <name name="is_binary" arity="1" since=""/> <fsummary>Check whether a term is a binary.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary, @@ -2354,7 +2354,7 @@ os_prompt%</pre> </func> <func> - <name name="is_bitstring" arity="1"/> + <name name="is_bitstring" arity="1" since=""/> <fsummary>Check whether a term is a bitstring.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a @@ -2364,7 +2364,7 @@ os_prompt%</pre> </func> <func> - <name name="is_boolean" arity="1"/> + <name name="is_boolean" arity="1" since=""/> <fsummary>Check whether a term is a boolean.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is the @@ -2375,7 +2375,7 @@ os_prompt%</pre> </func> <func> - <name name="is_builtin" arity="3"/> + <name name="is_builtin" arity="3" since=""/> <fsummary>Check if a function is a BIF implemented in C.</fsummary> <desc> <p>This BIF is useful for builders of cross-reference tools.</p> @@ -2386,7 +2386,7 @@ os_prompt%</pre> </func> <func> - <name name="is_float" arity="1"/> + <name name="is_float" arity="1" since=""/> <fsummary>Check whether a term is a float.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point @@ -2396,7 +2396,7 @@ os_prompt%</pre> </func> <func> - <name name="is_function" arity="1"/> + <name name="is_function" arity="1" since=""/> <fsummary>Check whether a term is a fun.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun, otherwise @@ -2406,7 +2406,7 @@ os_prompt%</pre> </func> <func> - <name name="is_function" arity="2"/> + <name name="is_function" arity="2" since=""/> <fsummary>Check whether a term is a fun with a specified given arity. </fsummary> <desc> @@ -2418,7 +2418,7 @@ os_prompt%</pre> </func> <func> - <name name="is_integer" arity="1"/> + <name name="is_integer" arity="1" since=""/> <fsummary>Check whether a term is an integer.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer, @@ -2428,7 +2428,7 @@ os_prompt%</pre> </func> <func> - <name name="is_list" arity="1"/> + <name name="is_list" arity="1" since=""/> <fsummary>Check whether a term is a list.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with @@ -2438,7 +2438,7 @@ os_prompt%</pre> </func> <func> - <name name="is_map" arity="1"/> + <name name="is_map" arity="1" since="OTP 17.0"/> <fsummary>Check whether a term is a map.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a map, @@ -2448,7 +2448,7 @@ os_prompt%</pre> </func> <func> - <name name="is_map_key" arity="2"/> + <name name="is_map_key" arity="2" since="OTP 21.0"/> <fsummary></fsummary> <desc> <p>Returns <c>true</c> if map <c><anno>Map</anno></c> contains @@ -2468,7 +2468,7 @@ false</code> </func> <func> - <name name="is_number" arity="1"/> + <name name="is_number" arity="1" since=""/> <fsummary>Check whether a term is a number.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer or a @@ -2478,7 +2478,7 @@ false</code> </func> <func> - <name name="is_pid" arity="1"/> + <name name="is_pid" arity="1" since=""/> <fsummary>Check whether a term is a process identifier.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a process @@ -2488,7 +2488,7 @@ false</code> </func> <func> - <name name="is_port" arity="1"/> + <name name="is_port" arity="1" since=""/> <fsummary>Check whether a term is a port.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier, @@ -2498,7 +2498,7 @@ false</code> </func> <func> - <name name="is_process_alive" arity="1"/> + <name name="is_process_alive" arity="1" since=""/> <fsummary>Check whether a process is alive.</fsummary> <desc> <p><c><anno>Pid</anno></c> must refer to a process at the local @@ -2510,7 +2510,7 @@ false</code> </func> <func> - <name name="is_record" arity="2"/> + <name name="is_record" arity="2" since=""/> <fsummary>Check whether a term appears to be a record.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple and its @@ -2531,7 +2531,7 @@ false</code> </func> <func> - <name name="is_record" arity="3"/> + <name name="is_record" arity="3" since=""/> <fsummary>Check whether a term appears to be a record.</fsummary> <desc> <p><c><anno>RecordTag</anno></c> must be an atom.</p> @@ -2550,7 +2550,7 @@ false</code> </func> <func> - <name name="is_reference" arity="1"/> + <name name="is_reference" arity="1" since=""/> <fsummary>Check whether a term is a reference.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference, @@ -2560,7 +2560,7 @@ false</code> </func> <func> - <name name="is_tuple" arity="1"/> + <name name="is_tuple" arity="1" since=""/> <fsummary>Check whether a term is a tuple.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple, @@ -2570,7 +2570,7 @@ false</code> </func> <func> - <name name="length" arity="1"/> + <name name="length" arity="1" since=""/> <fsummary>Length of a list.</fsummary> <desc> <p>Returns the length of <c><anno>List</anno></c>, for example:</p> @@ -2582,7 +2582,7 @@ false</code> </func> <func> - <name name="link" arity="1"/> + <name name="link" arity="1" since=""/> <fsummary>Create a link to another process (or port).</fsummary> <desc> <p>Creates a link between the calling process and another @@ -2609,7 +2609,7 @@ false</code> </func> <func> - <name name="list_to_atom" arity="1"/> + <name name="list_to_atom" arity="1" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is @@ -2629,7 +2629,7 @@ false</code> </func> <func> - <name name="list_to_binary" arity="1"/> + <name name="list_to_binary" arity="1" since=""/> <fsummary>Convert a list to a binary.</fsummary> <desc> <p>Returns a binary that is made from the integers and @@ -2647,7 +2647,7 @@ false</code> </func> <func> - <name name="list_to_bitstring" arity="1"/> + <name name="list_to_bitstring" arity="1" since=""/> <fsummary>Convert a list to a bitstring.</fsummary> <type name="bitstring_list"/> <desc> @@ -2668,7 +2668,7 @@ false</code> </func> <func> - <name name="list_to_existing_atom" arity="1"/> + <name name="list_to_existing_atom" arity="1" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is @@ -2689,7 +2689,7 @@ false</code> </func> <func> - <name name="list_to_float" arity="1"/> + <name name="list_to_float" arity="1" since=""/> <fsummary>Convert from text representation to a float.</fsummary> <desc> <p>Returns the float whose text representation is @@ -2703,7 +2703,7 @@ false</code> </func> <func> - <name name="list_to_integer" arity="1"/> + <name name="list_to_integer" arity="1" since=""/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation is @@ -2717,7 +2717,7 @@ false</code> </func> <func> - <name name="list_to_integer" arity="2"/> + <name name="list_to_integer" arity="2" since=""/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation in base @@ -2732,7 +2732,7 @@ false</code> </func> <func> - <name name="list_to_pid" arity="1"/> + <name name="list_to_pid" arity="1" since=""/> <fsummary>Convert from text representation to a pid.</fsummary> <desc> <p>Returns a process identifier whose text representation is a @@ -2750,7 +2750,7 @@ false</code> </func> <func> - <name name="list_to_port" arity="1"/> + <name name="list_to_port" arity="1" since="OTP 20.0"/> <fsummary>Convert from text representation to a port.</fsummary> <desc> <p>Returns a port identifier whose text representation is a @@ -2768,7 +2768,7 @@ false</code> </func> <func> - <name name="list_to_ref" arity="1"/> + <name name="list_to_ref" arity="1" since="OTP 20.0"/> <fsummary>Convert from text representation to a ref.</fsummary> <desc> <p>Returns a reference whose text representation is a @@ -2786,7 +2786,7 @@ false</code> </func> <func> - <name name="list_to_tuple" arity="1"/> + <name name="list_to_tuple" arity="1" since=""/> <fsummary>Convert a list to a tuple.</fsummary> <desc> <p>Returns a tuple corresponding to <c><anno>List</anno></c>, @@ -2799,7 +2799,7 @@ false</code> </func> <func> - <name name="load_module" arity="2"/> + <name name="load_module" arity="2" since=""/> <fsummary>Load object code for a module.</fsummary> <desc> <p>If <c><anno>Binary</anno></c> contains the object code for module @@ -2832,7 +2832,7 @@ false</code> </func> <func> - <name name="load_nif" arity="2"/> + <name name="load_nif" arity="2" since=""/> <fsummary>Load NIF library.</fsummary> <desc> <p>Loads and links a dynamic library containing native @@ -2885,7 +2885,7 @@ false</code> </func> <func> - <name name="loaded" arity="0"/> + <name name="loaded" arity="0" since=""/> <fsummary>List all loaded modules.</fsummary> <desc> <p>Returns a list of all loaded Erlang modules (current and @@ -2896,7 +2896,7 @@ false</code> </func> <func> - <name name="localtime" arity="0"/> + <name name="localtime" arity="0" since=""/> <fsummary>Current local date and time.</fsummary> <desc> <p>Returns the current local date and time, @@ -2911,7 +2911,7 @@ false</code> </func> <func> - <name name="localtime_to_universaltime" arity="1"/> + <name name="localtime_to_universaltime" arity="1" since=""/> <fsummary>Convert from local to Universal Time Coordinated (UTC) date and time.</fsummary> <desc> @@ -2928,7 +2928,7 @@ false</code> </func> <func> - <name name="localtime_to_universaltime" arity="2"/> + <name name="localtime_to_universaltime" arity="2" since=""/> <fsummary>Convert from local to Universal Time Coordinated (UTC) date and time.</fsummary> <desc> @@ -2954,7 +2954,7 @@ false</code> </func> <func> - <name name="make_ref" arity="0"/> + <name name="make_ref" arity="0" since=""/> <fsummary>Return a unique reference.</fsummary> <desc> <p>Returns a @@ -2971,7 +2971,7 @@ false</code> </func> <func> - <name name="make_tuple" arity="2"/> + <name name="make_tuple" arity="2" since=""/> <fsummary>Create a new tuple of a specified arity.</fsummary> <desc> <p>Creates a new tuple of the specified <c><anno>Arity</anno></c>, where @@ -2983,7 +2983,7 @@ false</code> </func> <func> - <name name="make_tuple" arity="3"/> + <name name="make_tuple" arity="3" since=""/> <fsummary>Create a new tuple with specifed arity and contents.</fsummary> <desc> <p>Creates a tuple of size <c><anno>Arity</anno></c>, where each element @@ -3001,7 +3001,7 @@ false</code> </func> <func> - <name name="map_get" arity="2" /> + <name name="map_get" arity="2" since="OTP 21.0"/> <fsummary>Extract a value from a map</fsummary> <desc> <p>Returns value <c><anno>Value</anno></c> associated with @@ -3020,7 +3020,7 @@ false</code> </func> <func> - <name name="map_size" arity="1"/> + <name name="map_size" arity="1" since="OTP 17.0"/> <fsummary>Return the size of a map.</fsummary> <desc> <p>Returns an integer, which is the number of key-value pairs @@ -3033,7 +3033,7 @@ false</code> </func> <func> - <name name="match_spec_test" arity="3"/> + <name name="match_spec_test" arity="3" since="OTP 19.0"/> <fsummary>Test that a match specification works.</fsummary> <desc> <p>Tests a match specification used in calls to @@ -3071,7 +3071,7 @@ false</code> </func> <func> - <name name="max" arity="2"/> + <name name="max" arity="2" since=""/> <fsummary>Return the largest of two terms.</fsummary> <desc> <p>Returns the largest of <c><anno>Term1</anno></c> and @@ -3081,7 +3081,7 @@ false</code> </func> <func> - <name name="md5" arity="1"/> + <name name="md5" arity="1" since=""/> <fsummary>Compute an MD5 message digest.</fsummary> <desc> <p>Computes an MD5 message digest from <c><anno>Data</anno></c>, where @@ -3099,7 +3099,7 @@ false</code> </func> <func> - <name name="md5_final" arity="1"/> + <name name="md5_final" arity="1" since=""/> <fsummary>Finish the update of an MD5 context and return the computed MD5 message digest.</fsummary> <desc> @@ -3109,7 +3109,7 @@ false</code> </func> <func> - <name name="md5_init" arity="0"/> + <name name="md5_init" arity="0" since=""/> <fsummary>Create an MD5 context.</fsummary> <desc> <p>Creates an MD5 context, to be used in the following calls to @@ -3118,7 +3118,7 @@ false</code> </func> <func> - <name name="md5_update" arity="2"/> + <name name="md5_update" arity="2" since=""/> <fsummary>Update an MD5 context with data and return a new context. </fsummary> <desc> @@ -3129,7 +3129,7 @@ false</code> </func> <func> - <name name="memory" arity="0"/> + <name name="memory" arity="0" since=""/> <fsummary>Information about dynamically allocated memory.</fsummary> <type name="memory_type"/> <desc> @@ -3273,8 +3273,8 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="memory" arity="1" clause_i="1"/> - <name name="memory" arity="1" clause_i="2"/> + <name name="memory" arity="1" clause_i="1" since=""/> + <name name="memory" arity="1" clause_i="2" since=""/> <fsummary>Information about dynamically allocated memory.</fsummary> <type name="memory_type"/> <desc> @@ -3313,7 +3313,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="min" arity="2"/> + <name name="min" arity="2" since=""/> <fsummary>Return the smallest of two terms.</fsummary> <desc> <p>Returns the smallest of <c><anno>Term1</anno></c> and @@ -3323,7 +3323,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="module_loaded" arity="1"/> + <name name="module_loaded" arity="1" since=""/> <fsummary>Check if a module is loaded.</fsummary> <desc> <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> @@ -3338,9 +3338,9 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monitor" arity="2" clause_i="1"/> - <name name="monitor" arity="2" clause_i="2"/> - <name name="monitor" arity="2" clause_i="3"/> + <name name="monitor" arity="2" clause_i="1" since=""/> + <name name="monitor" arity="2" clause_i="2" since="OTP 19.0"/> + <name name="monitor" arity="2" clause_i="3" since="OTP 18.0"/> <fsummary>Start monitoring.</fsummary> <type name="registered_name"/> <type name="registered_process_identifier"/> @@ -3512,7 +3512,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monitor_node" arity="2"/> + <name name="monitor_node" arity="2" since=""/> <fsummary>Monitor the status of a node.</fsummary> <desc> <p>Monitor the status of the node <c><anno>Node</anno></c>. @@ -3536,7 +3536,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monitor_node" arity="3"/> + <name name="monitor_node" arity="3" since=""/> <fsummary>Monitor the status of a node.</fsummary> <desc> <p>Behaves as @@ -3562,7 +3562,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monotonic_time" arity="0"/> + <name name="monotonic_time" arity="0" since="OTP 18.0"/> <fsummary>Current Erlang monotonic time.</fsummary> <desc> <p>Returns the current @@ -3596,7 +3596,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monotonic_time" arity="1"/> + <name name="monotonic_time" arity="1" since="OTP 18.0"/> <fsummary>Current Erlang monotonic time.</fsummary> <desc> <p>Returns the current @@ -3614,7 +3614,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nif_error" arity="1"/> + <name name="nif_error" arity="1" since="OTP R14B"/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Works exactly like @@ -3627,7 +3627,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nif_error" arity="2"/> + <name name="nif_error" arity="2" since="OTP R14B"/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Works exactly like @@ -3640,7 +3640,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="node" arity="0"/> + <name name="node" arity="0" since=""/> <fsummary>Name of the local node.</fsummary> <desc> <p>Returns the name of the local node. If the node is not alive, @@ -3650,7 +3650,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="node" arity="1"/> + <name name="node" arity="1" since=""/> <fsummary>At which node a pid, port, or reference originates.</fsummary> <desc> <p>Returns the node where <c><anno>Arg</anno></c> originates. @@ -3663,7 +3663,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nodes" arity="0"/> + <name name="nodes" arity="0" since=""/> <fsummary>All visible nodes in the system.</fsummary> <desc> <p>Returns a list of all visible nodes in the system, except @@ -3672,7 +3672,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nodes" arity="1"/> + <name name="nodes" arity="1" since=""/> <fsummary>All nodes of a certain type in the system.</fsummary> <desc> <p>Returns a list of nodes according to the argument specified. @@ -3715,7 +3715,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="now" arity="0"/> + <name name="now" arity="0" since=""/> <fsummary>Elapsed time since 00:00 GMT.</fsummary> <type name="timestamp"/> <desc> @@ -3744,7 +3744,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="open_port" arity="2"/> + <name name="open_port" arity="2" since=""/> <fsummary>Open a port.</fsummary> <desc> <p>Returns a port identifier as the result of opening a @@ -4085,7 +4085,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="phash" arity="2"/> + <name name="phash" arity="2" since=""/> <fsummary>Portable hash function.</fsummary> <type_desc variable="Range">Range = 1..2^32, Hash = 1..Range</type_desc> <desc> @@ -4100,8 +4100,8 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="phash2" arity="1"/> - <name name="phash2" arity="2"/> + <name name="phash2" arity="1" since=""/> + <name name="phash2" arity="2" since=""/> <fsummary>Portable hash function.</fsummary> <type_desc variable="Range">1..2^32</type_desc> <type_desc variable="Hash">0..Range-1</type_desc> @@ -4125,7 +4125,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="pid_to_list" arity="1"/> + <name name="pid_to_list" arity="1" since=""/> <fsummary>Text representation of a pid.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -4134,7 +4134,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_call" arity="3"/> + <name name="port_call" arity="3" since=""/> <fsummary>Perform a synchronous call to a port with term data.</fsummary> <desc> <p>Performs a synchronous call to a port. The meaning of @@ -4170,16 +4170,23 @@ RealSystem = system + MissedSystem</code> </item> <tag><c>badarg</c></tag> <item> - If the port driver so decides for any reason (probably + <p>If the port driver so decides for any reason (probably something wrong with <c><anno>Operation</anno></c> - or <c><anno>Data</anno></c>). + or <c><anno>Data</anno></c>).</p> + <warning> + <p>Do not call <c>port_call</c> with an unknown + <c><anno>Port</anno></c> identifier and expect <c>badarg</c> + exception. Any undefined behavior is possible (including node + crash) depending on how the port driver interprets the supplied + arguments.</p> + </warning> </item> </taglist> </desc> </func> <func> - <name name="port_close" arity="1"/> + <name name="port_close" arity="1" since=""/> <fsummary>Close an open port.</fsummary> <desc> <p>Closes an open port. Roughly the same as <c><anno>Port</anno> ! @@ -4219,7 +4226,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_command" arity="2"/> + <name name="port_command" arity="2" since=""/> <fsummary>Send data to a port.</fsummary> <desc> <p>Sends data to a port. Same as @@ -4262,11 +4269,16 @@ RealSystem = system + MissedSystem</code> <p>If <c><anno>Data</anno></c> is an invalid I/O list.</p> </item> </taglist> + <warning> + <p>Do not send data to an unknown port. Any undefined behavior is + possible (including node crash) depending on how the port driver + interprets the data.</p> + </warning> </desc> </func> <func> - <name name="port_command" arity="3"/> + <name name="port_command" arity="3" since=""/> <fsummary>Send data to a port.</fsummary> <desc> <p>Sends data to a port. <c>port_command(Port, Data, [])</c> @@ -4321,11 +4333,16 @@ RealSystem = system + MissedSystem</code> a busy port. </item> </taglist> + <warning> + <p>Do not send data to an unknown port. Any undefined behavior is + possible (including node crash) depending on how the port driver + interprets the data.</p> + </warning> </desc> </func> <func> - <name name="port_connect" arity="2"/> + <name name="port_connect" arity="2" since=""/> <fsummary>Set the owner of a port.</fsummary> <desc> <p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>. @@ -4394,7 +4411,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_control" arity="3"/> + <name name="port_control" arity="3" since=""/> <fsummary>Perform a synchronous control operation on a port.</fsummary> <desc> <p>Performs a synchronous control operation on a port. @@ -4425,13 +4442,20 @@ RealSystem = system + MissedSystem</code> If the port driver so decides for any reason (probably something wrong with <c><anno>Operation</anno></c> or <c><anno>Data</anno></c>). + <warning> + <p>Do not call <c>port_control/3</c> with an unknown + <c><anno>Port</anno></c> identifier and expect <c>badarg</c> + exception. Any undefined behavior is possible (including node + crash) depending on how the port driver interprets the supplied + arguments.</p> + </warning> </item> </taglist> </desc> </func> <func> - <name name="port_info" arity="1"/> + <name name="port_info" arity="1" since=""/> <fsummary>Information about a port.</fsummary> <desc> <p>Returns a list containing tuples with information about @@ -4462,7 +4486,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="1"/> + <name name="port_info" arity="2" clause_i="1" since=""/> <fsummary>Information about the connected process of a port.</fsummary> <desc> <p><c><anno>Pid</anno></c> is the process identifier of the process @@ -4478,7 +4502,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="2"/> + <name name="port_info" arity="2" clause_i="2" since=""/> <fsummary>Information about the internal index of a port.</fsummary> <desc> <p><c><anno>Index</anno></c> is the internal index of the port. This @@ -4494,7 +4518,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="3"/> + <name name="port_info" arity="2" clause_i="3" since=""/> <fsummary>Information about the input of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of bytes @@ -4510,7 +4534,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="4"/> + <name name="port_info" arity="2" clause_i="4" since=""/> <fsummary>Information about the links of a port.</fsummary> <desc> <p><c><anno>Pids</anno></c> is a list of the process identifiers @@ -4526,7 +4550,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="5"/> + <name name="port_info" arity="2" clause_i="5" since="OTP R16B"/> <fsummary>Information about the locking of a port.</fsummary> <desc> <p><c><anno>Locking</anno></c> is one of the following:</p> @@ -4547,7 +4571,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="6"/> + <name name="port_info" arity="2" clause_i="6" since="OTP R16B"/> <fsummary>Information about the memory size of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of @@ -4565,7 +4589,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="7"/> + <name name="port_info" arity="2" clause_i="7" since="OTP R16B"/> <fsummary>Information about the monitors of a port.</fsummary> <desc> <p><c><anno>Monitors</anno></c> represent processes monitored by @@ -4581,7 +4605,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="8"/> + <name name="port_info" arity="2" clause_i="8" since="OTP 19.0"/> <fsummary>Which processes are monitoring this port.</fsummary> <desc> <p>Returns list of pids that are monitoring given port at the @@ -4597,7 +4621,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="9"/> + <name name="port_info" arity="2" clause_i="9" since=""/> <fsummary>Information about the name of a port.</fsummary> <desc> <p><c><anno>Name</anno></c> is the command name set by @@ -4613,7 +4637,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="10"/> + <name name="port_info" arity="2" clause_i="10" since="OTP R16B"/> <fsummary>Information about the OS pid of a port.</fsummary> <desc> <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent) @@ -4632,7 +4656,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="11"/> + <name name="port_info" arity="2" clause_i="11" since=""/> <fsummary>Information about the output of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of bytes written @@ -4651,7 +4675,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="12"/> + <name name="port_info" arity="2" clause_i="12" since="OTP R16B"/> <fsummary>Information about the parallelism hint of a port.</fsummary> <desc> <p><c><anno>Boolean</anno></c> corresponds to the port parallelism @@ -4662,7 +4686,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="13"/> + <name name="port_info" arity="2" clause_i="13" since="OTP R16B"/> <fsummary>Information about the queue size of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number @@ -4679,7 +4703,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="14"/> + <name name="port_info" arity="2" clause_i="14" since=""/> <fsummary>Information about the registered name of a port.</fsummary> <desc> <p><c><anno>RegisteredName</anno></c> is the registered name of @@ -4696,7 +4720,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_to_list" arity="1"/> + <name name="port_to_list" arity="1" since=""/> <fsummary>Text representation of a port identifier.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -4705,7 +4729,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="ports" arity="0"/> + <name name="ports" arity="0" since=""/> <fsummary>List all existing ports.</fsummary> <desc> <p>Returns a list of port identifiers corresponding to all the @@ -4715,7 +4739,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="pre_loaded" arity="0"/> + <name name="pre_loaded" arity="0" since=""/> <fsummary>List all preloaded modules.</fsummary> <desc> <p>Returns a list of Erlang modules that are preloaded in @@ -4726,7 +4750,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_display" arity="2"/> + <name name="process_display" arity="2" since=""/> <fsummary>Write information about a local process on standard error. </fsummary> <desc> @@ -4740,7 +4764,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="1"/> + <name name="process_flag" arity="2" clause_i="1" since=""/> <fsummary>Set process flag trap_exit for the calling process.</fsummary> <desc> <p>When <c>trap_exit</c> is set to <c>true</c>, exit signals @@ -4757,7 +4781,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="2"/> + <name name="process_flag" arity="2" clause_i="2" since=""/> <fsummary>Set process flag error_handler for the calling process. </fsummary> <desc> @@ -4772,7 +4796,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="3" - anchor="process_flag_min_heap_size"/> + anchor="process_flag_min_heap_size" since=""/> <fsummary>Set process flag min_heap_size for the calling process. </fsummary> <desc> @@ -4782,7 +4806,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="4"/> + <name name="process_flag" arity="2" clause_i="4" since="OTP R13B04"/> <fsummary>Set process flag min_bin_vheap_size for the calling process. </fsummary> <desc> @@ -4794,7 +4818,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="5" - anchor="process_flag_max_heap_size"/> + anchor="process_flag_max_heap_size" since="OTP 19.0"/> <fsummary>Set process flag max_heap_size for the calling process. </fsummary> <type name="max_heap_size"/> @@ -4868,7 +4892,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="6" - anchor="process_flag_message_queue_data"/> + anchor="process_flag_message_queue_data" since="OTP 19.0"/> <fsummary>Set process flag message_queue_data for the calling process. </fsummary> <type name="message_queue_data"/> @@ -4910,7 +4934,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="7" - anchor="process_flag_priority"/> + anchor="process_flag_priority" since=""/> <fsummary>Set process flag priority for the calling process.</fsummary> <type name="priority_level"/> <desc> @@ -4982,7 +5006,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="8"/> + <name name="process_flag" arity="2" clause_i="8" since=""/> <fsummary>Set process flag save_calls for the calling process.</fsummary> <desc> <p><c><anno>N</anno></c> must be an integer in the interval 0..10000. @@ -5013,7 +5037,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="9"/> + <name name="process_flag" arity="2" clause_i="9" since=""/> <fsummary>Set process flag sensitive for the calling process.</fsummary> <desc> <p>Sets or clears flag <c>sensitive</c> for the current process. @@ -5047,7 +5071,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="3"/> + <name name="process_flag" arity="3" since=""/> <fsummary>Set process flags for a process.</fsummary> <desc> <p>Sets certain flags for the process <c><anno>Pid</anno></c>, @@ -5062,7 +5086,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_info" arity="1"/> + <name name="process_info" arity="1" since=""/> <fsummary>Information about a process.</fsummary> <type name="process_info_result_item"/> <type name="priority_level"/> @@ -5113,8 +5137,8 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_info" arity="2" clause_i="1"/> - <name name="process_info" arity="2" clause_i="2"/> + <name name="process_info" arity="2" clause_i="1" since=""/> + <name name="process_info" arity="2" clause_i="2" since=""/> <fsummary>Information about a process.</fsummary> <type name="process_info_item"/> <type name="process_info_result_item"/> @@ -5447,7 +5471,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="processes" arity="0"/> + <name name="processes" arity="0" since=""/> <fsummary>All processes.</fsummary> <desc> <p>Returns a list of process identifiers corresponding to @@ -5464,7 +5488,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="purge_module" arity="1"/> + <name name="purge_module" arity="1" since=""/> <fsummary>Remove old code for a module.</fsummary> <desc> <p>Removes old code for <c><anno>Module</anno></c>. @@ -5489,7 +5513,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="put" arity="2"/> + <name name="put" arity="2" since=""/> <fsummary>Add a new value to the process dictionary.</fsummary> <desc> <p>Adds a new <c><anno>Key</anno></c> to the process dictionary, @@ -5511,7 +5535,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="raise" arity="3"/> + <name name="raise" arity="3" since=""/> <fsummary>Stop execution with an exception of specified class, reason, and call stack backtrace.</fsummary> <type name="raise_stacktrace"/> @@ -5550,7 +5574,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="read_timer" arity="1"/> + <name name="read_timer" arity="1" since=""/> <fsummary>Read the state of a timer.</fsummary> <desc> <p>Reads the state of a timer. The same as calling @@ -5560,7 +5584,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="read_timer" arity="2"/> + <name name="read_timer" arity="2" since="OTP 18.0"/> <fsummary>Read the state of a timer.</fsummary> <desc> <p>Reads the state of a timer that has been created by either @@ -5616,7 +5640,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="ref_to_list" arity="1"/> + <name name="ref_to_list" arity="1" since=""/> <fsummary>Text representation of a reference.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -5629,7 +5653,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="register" arity="2"/> + <name name="register" arity="2" since=""/> <fsummary>Register a name for a pid (or port).</fsummary> <desc> <p>Associates the name <c><anno>RegName</anno></c> with a process @@ -5658,7 +5682,7 @@ true</pre> </func> <func> - <name name="registered" arity="0"/> + <name name="registered" arity="0" since=""/> <fsummary>All registered names.</fsummary> <desc> <p>Returns a list of names that have been registered using @@ -5671,7 +5695,7 @@ true</pre> </func> <func> - <name name="resume_process" arity="1"/> + <name name="resume_process" arity="1" since=""/> <fsummary>Resume a suspended process.</fsummary> <desc> <p>Decreases the suspend count on the process identified by @@ -5712,7 +5736,7 @@ true</pre> </func> <func> - <name name="round" arity="1"/> + <name name="round" arity="1" since=""/> <fsummary>Return an integer by rounding a number.</fsummary> <desc> <p>Returns an integer by rounding <c><anno>Number</anno></c>, @@ -5725,7 +5749,7 @@ true</pre> </func> <func> - <name name="self" arity="0"/> + <name name="self" arity="0" since=""/> <fsummary>Return pid of the calling process.</fsummary> <desc> <p>Returns the process identifier of the calling process, for @@ -5738,7 +5762,7 @@ true</pre> </func> <func> - <name name="send" arity="2"/> + <name name="send" arity="2" since=""/> <fsummary>Send a message.</fsummary> <type name="dst"/> <desc> @@ -5758,7 +5782,7 @@ true</pre> </func> <func> - <name name="send" arity="3"/> + <name name="send" arity="3" since=""/> <fsummary>Send a message conditionally.</fsummary> <type name="dst"/> <desc> @@ -5790,7 +5814,7 @@ true</pre> </func> <func> - <name name="send_after" arity="3"/> + <name name="send_after" arity="3" since=""/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. The same as calling @@ -5801,7 +5825,7 @@ true</pre> </func> <func> - <name name="send_after" arity="4"/> + <name name="send_after" arity="4" since="OTP 18.0"/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. When the timer expires, the message @@ -5814,7 +5838,7 @@ true</pre> </func> <func> - <name name="send_nosuspend" arity="2"/> + <name name="send_nosuspend" arity="2" since=""/> <fsummary>Try to send a message without ever blocking.</fsummary> <type name="dst"/> <desc> @@ -5864,7 +5888,7 @@ true</pre> </func> <func> - <name name="send_nosuspend" arity="3"/> + <name name="send_nosuspend" arity="3" since=""/> <fsummary>Try to send a message without ever blocking.</fsummary> <type name="dst"/> <desc> @@ -5897,7 +5921,7 @@ true</pre> </func> <func> - <name name="set_cookie" arity="2"/> + <name name="set_cookie" arity="2" since=""/> <fsummary>Set the magic cookie of a node.</fsummary> <desc> <p>Sets the magic cookie of <c><anno>Node</anno></c> to the atom @@ -5914,7 +5938,7 @@ true</pre> </func> <func> - <name name="setelement" arity="3"/> + <name name="setelement" arity="3" since=""/> <fsummary>Set the Nth element of a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno></type_desc> <desc> @@ -5931,7 +5955,7 @@ true</pre> </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Size of a tuple or binary.</fsummary> <desc> <p>Returns the number of elements in a tuple or the number of @@ -5954,7 +5978,7 @@ true</pre> </func> <func> - <name name="spawn" arity="1"/> + <name name="spawn" arity="1" since=""/> <fsummary>Create a new process with a fun as entry point.</fsummary> <desc> <p>Returns the process identifier of a new process started by the @@ -5965,7 +5989,7 @@ true</pre> </func> <func> - <name name="spawn" arity="2"/> + <name name="spawn" arity="2" since=""/> <fsummary>Create a new process with a fun as entry point on a specified node.</fsummary> <desc> @@ -5979,7 +6003,7 @@ true</pre> </func> <func> - <name name="spawn" arity="3"/> + <name name="spawn" arity="3" since=""/> <fsummary>Create a new process with a function as entry point.</fsummary> <desc> <p>Returns the process identifier of a new process started by @@ -6004,7 +6028,7 @@ true</pre> </func> <func> - <name name="spawn" arity="4"/> + <name name="spawn" arity="4" since=""/> <fsummary>Create a new process with a function as entry point on a specified node.</fsummary> <desc> @@ -6019,7 +6043,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="1"/> + <name name="spawn_link" arity="1" since=""/> <fsummary>Create and link to a new process with a fun as entry point. </fsummary> <desc> @@ -6033,7 +6057,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="2"/> + <name name="spawn_link" arity="2" since=""/> <fsummary>Create and link to a new process with a fun as entry point on a specified node.</fsummary> <desc> @@ -6050,7 +6074,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="3"/> + <name name="spawn_link" arity="3" since=""/> <fsummary>Create and link to a new process with a function as entry point. </fsummary> <desc> @@ -6064,7 +6088,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="4"/> + <name name="spawn_link" arity="4" since=""/> <fsummary>Create and link to a new process with a function as entry point on a specified node.</fsummary> <desc> @@ -6082,7 +6106,7 @@ true</pre> </func> <func> - <name name="spawn_monitor" arity="1"/> + <name name="spawn_monitor" arity="1" since=""/> <fsummary>Create and monitor a new process with a fun as entry point. </fsummary> <desc> @@ -6096,7 +6120,7 @@ true</pre> </func> <func> - <name name="spawn_monitor" arity="3"/> + <name name="spawn_monitor" arity="3" since=""/> <fsummary>Create and monitor a new process with a function as entry point. </fsummary> <desc> @@ -6110,7 +6134,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="2"/> + <name name="spawn_opt" arity="2" since=""/> <fsummary>Create a new process with a fun as entry point.</fsummary> <type name="priority_level"/> <type name="max_heap_size"/> @@ -6128,7 +6152,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="3"/> + <name name="spawn_opt" arity="3" since=""/> <fsummary>Create a new process with a fun as entry point on a specified node.</fsummary> <type name="priority_level"/> @@ -6146,7 +6170,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="4"/> + <name name="spawn_opt" arity="4" since=""/> <fsummary>Create a new process with a function as entry point.</fsummary> <type name="priority_level"/> <type name="max_heap_size"/> @@ -6282,7 +6306,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="5"/> + <name name="spawn_opt" arity="5" since=""/> <fsummary>Create a new process with a function as entry point on a specified node.</fsummary> <type name="priority_level"/> @@ -6305,7 +6329,7 @@ true</pre> </func> <func> - <name name="split_binary" arity="2"/> + <name name="split_binary" arity="2" since=""/> <fsummary>Split a binary into two.</fsummary> <type_desc variable="Pos">0..byte_size(Bin)</type_desc> <desc> @@ -6329,7 +6353,7 @@ true</pre> </func> <func> - <name name="start_timer" arity="3"/> + <name name="start_timer" arity="3" since=""/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. The same as calling @@ -6340,7 +6364,7 @@ true</pre> </func> <func> - <name name="start_timer" arity="4"/> + <name name="start_timer" arity="4" since="OTP 18.0"/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. When the timer expires, the message @@ -6399,7 +6423,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="1" - anchor="statistics_active_tasks"/> + anchor="statistics_active_tasks" since="OTP 18.3"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>Returns the same as @@ -6414,7 +6438,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="2" - anchor="statistics_active_tasks_all"/> + anchor="statistics_active_tasks_all" since="OTP 20.0"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>Returns a list where each element represents the amount @@ -6455,7 +6479,7 @@ true</pre> </func> <func> - <name name="statistics" arity="1" clause_i="3"/> + <name name="statistics" arity="1" clause_i="3" since=""/> <fsummary>Information about context switches.</fsummary> <desc> <p>Returns the total number of context switches since the @@ -6465,7 +6489,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="4" - anchor="statistics_exact_reductions"/> + anchor="statistics_exact_reductions" since=""/> <fsummary>Information about exact reductions.</fsummary> <desc> <p>Returns the number of exact reductions.</p> @@ -6479,7 +6503,7 @@ true</pre> </func> <func> - <name name="statistics" arity="1" clause_i="5"/> + <name name="statistics" arity="1" clause_i="5" since=""/> <fsummary>Information about garbage collection.</fsummary> <desc> <p>Returns information about garbage collection, for example:</p> @@ -6491,7 +6515,7 @@ true</pre> </func> <func> - <name name="statistics" arity="1" clause_i="6"/> + <name name="statistics" arity="1" clause_i="6" since=""/> <fsummary>Information about I/O.</fsummary> <desc> <p>Returns <c><anno>Input</anno></c>, @@ -6503,7 +6527,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="7" - anchor="statistics_microstate_accounting"/> + anchor="statistics_microstate_accounting" since="OTP 19.0"/> <fsummary>Information about microstate accounting.</fsummary> <desc> <p>Microstate accounting can be used to measure how much time the Erlang @@ -6646,7 +6670,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="8" - anchor="statistics_reductions"/> + anchor="statistics_reductions" since=""/> <fsummary>Information about reductions.</fsummary> <desc> <p>Returns information about reductions, for example:</p> @@ -6665,7 +6689,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="9" - anchor="statistics_run_queue"/> + anchor="statistics_run_queue" since=""/> <fsummary>Information about the run-queues.</fsummary> <desc> <p>Returns the total length of all normal run-queues. That is, the number @@ -6682,7 +6706,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="10" - anchor="statistics_run_queue_lengths"/> + anchor="statistics_run_queue_lengths" since="OTP 18.3"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>Returns the same as @@ -6697,7 +6721,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="11" - anchor="statistics_run_queue_lengths_all"/> + anchor="statistics_run_queue_lengths_all" since="OTP 20.0"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>Returns a list where each element represents the amount @@ -6739,7 +6763,7 @@ lists:map( </func> <func> - <name name="statistics" arity="1" clause_i="12"/> + <name name="statistics" arity="1" clause_i="12" since=""/> <fsummary>Information about runtime.</fsummary> <desc> <p>Returns information about runtime, in milliseconds.</p> @@ -6758,7 +6782,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="13" - anchor="statistics_scheduler_wall_time"/> + anchor="statistics_scheduler_wall_time" since="OTP R15B01"/> <fsummary>Information about each schedulers work time.</fsummary> <desc> <p>Returns a list of tuples with @@ -6882,7 +6906,7 @@ ok <func> <name name="statistics" arity="1" clause_i="14" - anchor="statistics_scheduler_wall_time_all"/> + anchor="statistics_scheduler_wall_time_all" since="OTP 20.0"/> <fsummary>Information about each schedulers work time.</fsummary> <desc> <p>The same as @@ -6910,7 +6934,7 @@ ok </func> <func> <name name="statistics" arity="1" clause_i="15" - anchor="statistics_total_active_tasks"/> + anchor="statistics_total_active_tasks" since="OTP 18.3"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>The same as calling @@ -6921,7 +6945,7 @@ ok <func> <name name="statistics" arity="1" clause_i="16" - anchor="statistics_total_active_tasks_all"/> + anchor="statistics_total_active_tasks_all" since="OTP 20.0"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>The same as calling @@ -6932,7 +6956,7 @@ ok <func> <name name="statistics" arity="1" clause_i="17" - anchor="statistics_total_run_queue_lengths"/> + anchor="statistics_total_run_queue_lengths" since="OTP 18.3"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>The same as calling @@ -6943,7 +6967,7 @@ ok <func> <name name="statistics" arity="1" clause_i="18" - anchor="statistics_total_run_queue_lengths_all"/> + anchor="statistics_total_run_queue_lengths_all" since="OTP 20.0"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>The same as calling @@ -6953,7 +6977,7 @@ ok </func> <func> - <name name="statistics" arity="1" clause_i="19"/> + <name name="statistics" arity="1" clause_i="19" since=""/> <fsummary>Information about wall clock.</fsummary> <desc> <p>Returns information about wall clock. <c>wall_clock</c> can @@ -6964,7 +6988,7 @@ ok </func> <func> - <name name="suspend_process" arity="1"/> + <name name="suspend_process" arity="1" since=""/> <fsummary>Suspend a process.</fsummary> <desc> <p>Suspends the process identified by @@ -6979,7 +7003,7 @@ ok </func> <func> - <name name="suspend_process" arity="2"/> + <name name="suspend_process" arity="2" since=""/> <fsummary>Suspend a process.</fsummary> <desc> <p>Increases the suspend count on the process identified by @@ -7125,7 +7149,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="1"/> + <name name="system_flag" arity="2" clause_i="1" since=""/> <fsummary>Set system flag <c>backtrace_depth</c>.</fsummary> <desc> <p>Sets the maximum depth of call stack back-traces in the @@ -7138,7 +7162,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="2" - anchor="system_flag_cpu_topology"/> + anchor="system_flag_cpu_topology" since=""/> <fsummary>Set system flag <c>cpu_topology</c>.</fsummary> <type name="cpu_topology"/> <type name="level_entry"/> @@ -7186,7 +7210,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="3" - anchor="system_flag_dirty_cpu_schedulers_online"/> + anchor="system_flag_dirty_cpu_schedulers_online" since="OTP 17.0"/> <fsummary>Set system_flag_dirty_cpu_schedulers_online.</fsummary> <desc> <p> @@ -7214,7 +7238,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="4"/> + <name name="system_flag" arity="2" clause_i="4" since="OTP 20.2.3"/> <fsummary>Set system flag for erts_alloc.</fsummary> <desc> <p>Sets system flags for @@ -7231,7 +7255,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="5"/> + <name name="system_flag" arity="2" clause_i="5" since=""/> <fsummary>Set system flag fullsweep_after.</fsummary> <desc> <p>Sets system flag <c>fullsweep_after</c>. @@ -7251,7 +7275,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="6" - anchor="system_flag_microstate_accounting"/> + anchor="system_flag_microstate_accounting" since="OTP 19.0"/> <fsummary>Set system flag microstate_accounting.</fsummary> <desc> <p> @@ -7264,7 +7288,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="7"/> + <name name="system_flag" arity="2" clause_i="7" since=""/> <fsummary>Set system flag min_heap_size.</fsummary> <desc> <p>Sets the default minimum heap size for processes. The size @@ -7279,7 +7303,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="8"/> + <name name="system_flag" arity="2" clause_i="8" since="OTP R13B04"/> <fsummary>Set system flag min_bin_vheap_size.</fsummary> <desc> <p>Sets the default minimum binary virtual heap size for @@ -7297,7 +7321,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="9" - anchor="system_flag_max_heap_size"/> + anchor="system_flag_max_heap_size" since="OTP 19.0"/> <fsummary>Set system flag max_heap_size.</fsummary> <type name="max_heap_size"/> <desc> @@ -7315,7 +7339,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="10" - anchor="system_flag_multi_scheduling"/> + anchor="system_flag_multi_scheduling" since=""/> <fsummary>Set system flag multi_scheduling.</fsummary> <desc> <p> @@ -7371,7 +7395,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="11" - anchor="system_flag_scheduler_bind_type"/> + anchor="system_flag_scheduler_bind_type" since=""/> <fsummary>Set system flag scheduler_bind_type.</fsummary> <type name="scheduler_bind_type"/> <desc> @@ -7498,7 +7522,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="12" - anchor="system_flag_scheduler_wall_time"/> + anchor="system_flag_scheduler_wall_time" since="OTP R15B01"/> <fsummary>Set system flag scheduler_wall_time.</fsummary> <desc> <p> @@ -7511,7 +7535,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="13" - anchor="system_flag_schedulers_online"/> + anchor="system_flag_schedulers_online" since=""/> <fsummary>Set system flag schedulers_online.</fsummary> <desc> <p> @@ -7539,7 +7563,39 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="14"/> + <name name="system_flag" arity="2" clause_i="14" since="OTP 21.3"/> + <fsummary>Set system logger process.</fsummary> + <desc> + <p>Sets the process that will receive the logging + messages generated by ERTS. If set to <c>undefined</c>, + all logging messages generated by ERTS will be dropped. + The messages will be in the format:</p> + <code> +{log,Level,Format,ArgList,Metadata} where + +Level = atom(), +Format = string(), +ArgList = list(term()), +Metadata = #{ pid => pid(), + group_leader => pid(), + time := logger:timestamp(), + error_logger := #{ emulator := true, tag := atom() } + </code> + <p>If the <c>system_logger</c> process dies, + this flag will be reset to <c>logger</c>.</p> + <p>The default is the process named <c>logger</c>.</p> + <p>Returns the old value of the flag.</p> + <note><p>This function is designed to be used by the + KERNEL <seealso marker="kernel:logger"><c>logger</c></seealso>. + Be careful if you change it to something else as + log messages may be lost. If you want to intercept + emulator log messages, do it by adding a specialized handler + to the KERNEL logger.</p></note> + </desc> + </func> + + <func> + <name name="system_flag" arity="2" clause_i="15" since=""/> <fsummary>Set system flag trace_control_word.</fsummary> <desc> <p>Sets the value of the node trace control word to @@ -7553,8 +7609,8 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="15" - anchor="system_flag_time_offset"/> + <name name="system_flag" arity="2" clause_i="16" + anchor="system_flag_time_offset" since="OTP 18.0"/> <fsummary>Finalize the time offset.</fsummary> <desc> <p> @@ -7583,7 +7639,7 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="76"/> + <name name="system_info" arity="1" clause_i="76" since=""/> <fsummary>System info overview.</fsummary> <desc> <p>Returns information about the current system. @@ -7703,8 +7759,9 @@ ok <seealso marker="#system_info_nif_version"><c>nif_version</c></seealso>, <seealso marker="#system_info_otp_release"><c>otp_release</c></seealso>, <seealso marker="#system_info_port_parallelism"><c>port_parallelism</c></seealso>, - <seealso marker="#system_info_system_version"><c>system_version</c></seealso>, <seealso marker="#system_info_system_architecture"><c>system_architecture</c></seealso>, + <seealso marker="#system_info_system_logger"><c>system_logger</c></seealso>, + <seealso marker="#system_info_system_version"><c>system_version</c></seealso>, <seealso marker="#system_info_trace_control_word"><c>trace_control_word</c></seealso>, <seealso marker="#system_info_version"><c>version</c></seealso>, <seealso marker="#system_info_wordsize"><c>wordsize</c></seealso> @@ -7716,12 +7773,12 @@ ok <func> <name name="system_info" arity="1" clause_i="1" - anchor="system_info_allocator"/> <!-- allocated_areas --> - <name name="system_info" arity="1" clause_i="2"/> <!-- allocator --> - <name name="system_info" arity="1" clause_i="3"/> <!-- {allocator, _} --> - <name name="system_info" arity="1" clause_i="4"/> <!-- alloc_util_allocators --> - <name name="system_info" arity="1" clause_i="5"/> <!-- {allocator_sizes, _} --> - <name name="system_info" arity="1" clause_i="27"/> <!-- elib_malloc --> + anchor="system_info_allocator" since=""/> <!-- allocated_areas --> + <name name="system_info" arity="1" clause_i="2" since=""/> <!-- allocator --> + <name name="system_info" arity="1" clause_i="3" since=""/> <!-- {allocator, _} --> + <name name="system_info" arity="1" clause_i="4" since=""/> <!-- alloc_util_allocators --> + <name name="system_info" arity="1" clause_i="5" since=""/> <!-- {allocator_sizes, _} --> + <name name="system_info" arity="1" clause_i="27" since=""/> <!-- elib_malloc --> <fsummary>Information about the system allocators.</fsummary> <type variable="Allocator" name_i="2"/> <type variable="Version" name_i="2"/> @@ -7873,10 +7930,10 @@ ok <func> <name name="system_info" arity="1" clause_i="12" - anchor="system_info_cpu_topology"/> <!-- cpu_topology --> - <name name="system_info" arity="1" clause_i="13"/> <!-- {cpu_topology, _} --> - <name name="system_info" arity="1" clause_i="38"/> <!-- logical_processors --> - <name name="system_info" arity="1" clause_i="73"/> <!-- update_cpu_info --> + anchor="system_info_cpu_topology" since=""/> <!-- cpu_topology --> + <name name="system_info" arity="1" clause_i="13" since=""/> <!-- {cpu_topology, _} --> + <name name="system_info" arity="1" clause_i="38" since=""/> <!-- logical_processors --> + <name name="system_info" arity="1" clause_i="74" since="OTP R14B"/> <!-- update_cpu_info --> <fsummary>Information about the CPU topology of the system.</fsummary> <type name="cpu_topology"/> <type name="level_entry"/> @@ -8028,15 +8085,15 @@ ok <func> <name name="system_info" arity="1" clause_i="31" - anchor="system_info_process"/> <!-- fullsweep_after --> - <name name="system_info" arity="1" clause_i="32"/> <!-- garbage_collection --> - <name name="system_info" arity="1" clause_i="33"/> <!-- heap_sizes --> - <name name="system_info" arity="1" clause_i="34"/> <!-- heap_type --> - <name name="system_info" arity="1" clause_i="40"/> <!-- max_heap_size --> - <name name="system_info" arity="1" clause_i="41"/> <!-- message_queue_data --> - <name name="system_info" arity="1" clause_i="42"/> <!-- min_heap_size --> - <name name="system_info" arity="1" clause_i="43"/> <!-- min_bin_vheap_size --> - <name name="system_info" arity="1" clause_i="57"/> <!-- procs --> + anchor="system_info_process" since=""/> <!-- fullsweep_after --> + <name name="system_info" arity="1" clause_i="32" since=""/> <!-- garbage_collection --> + <name name="system_info" arity="1" clause_i="33" since=""/> <!-- heap_sizes --> + <name name="system_info" arity="1" clause_i="34" since=""/> <!-- heap_type --> + <name name="system_info" arity="1" clause_i="40" since="OTP 19.0"/> <!-- max_heap_size --> + <name name="system_info" arity="1" clause_i="41" since="OTP 19.0"/> <!-- message_queue_data --> + <name name="system_info" arity="1" clause_i="42" since="OTP R13B04"/> <!-- min_heap_size --> + <name name="system_info" arity="1" clause_i="43" since="OTP R13B04"/> <!-- min_bin_vheap_size --> + <name name="system_info" arity="1" clause_i="57" since=""/> <!-- procs --> <fsummary>Information about the default process heap settings.</fsummary> <type name="message_queue_data"/> <type name="max_heap_size"/> @@ -8146,14 +8203,14 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="6" anchor="system_info_limits"/> <!-- atom_count --> - <name name="system_info" arity="1" clause_i="7"/> <!-- atom_limit --> - <name name="system_info" arity="1" clause_i="29"/> <!-- ets_count --> - <name name="system_info" arity="1" clause_i="30"/> <!-- ets_limit --> - <name name="system_info" arity="1" clause_i="53"/> <!-- port_count --> - <name name="system_info" arity="1" clause_i="54"/> <!-- port_limit --> - <name name="system_info" arity="1" clause_i="55"/> <!-- process_count --> - <name name="system_info" arity="1" clause_i="56"/> <!-- process_limit --> + <name name="system_info" arity="1" clause_i="6" anchor="system_info_limits" since="OTP 20.0"/> <!-- atom_count --> + <name name="system_info" arity="1" clause_i="7" since="OTP 20.0"/> <!-- atom_limit --> + <name name="system_info" arity="1" clause_i="29" since="OTP 21.1"/> <!-- ets_count --> + <name name="system_info" arity="1" clause_i="30" since="OTP R16B03"/> <!-- ets_limit --> + <name name="system_info" arity="1" clause_i="53" since="OTP R16B"/> <!-- port_count --> + <name name="system_info" arity="1" clause_i="54" since="OTP R16B"/> <!-- port_limit --> + <name name="system_info" arity="1" clause_i="55" since=""/> <!-- process_count --> + <name name="system_info" arity="1" clause_i="56" since=""/> <!-- process_limit --> <fsummary>Information about various system limits.</fsummary> <desc> <marker id="system_info_limits"/> @@ -8227,14 +8284,14 @@ ok <func> <name name="system_info" arity="1" clause_i="26" - anchor="system_info_time"/> <!-- end_time --> - <name name="system_info" arity="1" clause_i="50"/> <!-- os_monotonic_time_source --> - <name name="system_info" arity="1" clause_i="51"/> <!-- os_system_time_source --> - <name name="system_info" arity="1" clause_i="63"/> <!-- start_time --> - <name name="system_info" arity="1" clause_i="68"/> <!-- time_correction --> - <name name="system_info" arity="1" clause_i="69"/> <!-- time_offset --> - <name name="system_info" arity="1" clause_i="70"/> <!-- time_warp_mode --> - <name name="system_info" arity="1" clause_i="71"/> <!-- tolerant_timeofday --> + anchor="system_info_time" since="OTP 18.0"/> <!-- end_time --> + <name name="system_info" arity="1" clause_i="50" since="OTP 18.0"/> <!-- os_monotonic_time_source --> + <name name="system_info" arity="1" clause_i="51" since="OTP 18.0"/> <!-- os_system_time_source --> + <name name="system_info" arity="1" clause_i="63" since="OTP 18.0"/> <!-- start_time --> + <name name="system_info" arity="1" clause_i="69" since="OTP 18.0"/> <!-- time_correction --> + <name name="system_info" arity="1" clause_i="70" since="OTP 18.0"/> <!-- time_offset --> + <name name="system_info" arity="1" clause_i="71" since="OTP 18.0"/> <!-- time_warp_mode --> + <name name="system_info" arity="1" clause_i="72" since="OTP 17.1"/> <!-- tolerant_timeofday --> <fsummary>Information about system time.</fsummary> <desc> <marker id="system_info_time_tags"/> @@ -8324,7 +8381,7 @@ ok system time</seealso> that is used by the runtime system.</p> <p>The list contains two-tuples with <c>Key</c>s as first element, and <c>Value</c>s as second element. The - order if these tuples is undefined. The following + order of these tuples is undefined. The following tuples can be part of the list, but more tuples can be introduced in the future:</p> <taglist> @@ -8455,19 +8512,19 @@ ok <func> <name name="system_info" arity="1" clause_i="17" - anchor="system_info_scheduler"/> <!-- dirty_cpu_schedulers --> - <name name="system_info" arity="1" clause_i="18"/> <!-- dirty_cpu_schedulers_online --> - <name name="system_info" arity="1" clause_i="19"/> <!-- dirty_io_schedulers --> - <name name="system_info" arity="1" clause_i="45"/> <!-- multi_scheduling --> - <name name="system_info" arity="1" clause_i="46"/> <!-- multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="49"/> <!-- normal_multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="58"/> <!-- scheduler_bind_type --> - <name name="system_info" arity="1" clause_i="59"/> <!-- scheduler_bindings --> - <name name="system_info" arity="1" clause_i="60"/> <!-- scheduler_id --> - <name name="system_info" arity="1" clause_i="61"/> <!-- schedulers --> - <name name="system_info" arity="1" clause_i="62"/> <!-- smp_support --> - <name name="system_info" arity="1" clause_i="66"/> <!-- threads --> - <name name="system_info" arity="1" clause_i="67"/> <!-- thread_pool_size --> + anchor="system_info_scheduler" since="OTP 17.0"/> <!-- dirty_cpu_schedulers --> + <name name="system_info" arity="1" clause_i="18" since="OTP 17.0"/> <!-- dirty_cpu_schedulers_online --> + <name name="system_info" arity="1" clause_i="19" since="OTP 17.0"/> <!-- dirty_io_schedulers --> + <name name="system_info" arity="1" clause_i="45" since=""/> <!-- multi_scheduling --> + <name name="system_info" arity="1" clause_i="46" since=""/> <!-- multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="49" since="OTP 19.0"/> <!-- normal_multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="58" since=""/> <!-- scheduler_bind_type --> + <name name="system_info" arity="1" clause_i="59" since=""/> <!-- scheduler_bindings --> + <name name="system_info" arity="1" clause_i="60" since=""/> <!-- scheduler_id --> + <name name="system_info" arity="1" clause_i="61" since=""/> <!-- schedulers --> + <name name="system_info" arity="1" clause_i="62" since=""/> <!-- smp_support --> + <name name="system_info" arity="1" clause_i="67" since=""/> <!-- threads --> + <name name="system_info" arity="1" clause_i="68" since=""/> <!-- thread_pool_size --> <fsummary>Information about system schedulers.</fsummary> <desc> <marker id="system_info_scheduler_tags"/> @@ -8755,11 +8812,11 @@ ok <func> <name name="system_info" arity="1" clause_i="14" - anchor="system_info_dist"/> <!-- creation --> - <name name="system_info" arity="1" clause_i="16"/> <!-- delayed_node_table_gc --> - <name name="system_info" arity="1" clause_i="20"/> <!-- dist --> - <name name="system_info" arity="1" clause_i="21"/> <!-- dist_buf_busy_limit --> - <name name="system_info" arity="1" clause_i="22"/> <!-- dist_ctrl --> + anchor="system_info_dist" since=""/> <!-- creation --> + <name name="system_info" arity="1" clause_i="16" since="OTP 18.0"/> <!-- delayed_node_table_gc --> + <name name="system_info" arity="1" clause_i="20" since=""/> <!-- dist --> + <name name="system_info" arity="1" clause_i="21" since="OTP R14B01"/> <!-- dist_buf_busy_limit --> + <name name="system_info" arity="1" clause_i="22" since=""/> <!-- dist_ctrl --> <fsummary>Information about erlang distribution.</fsummary> <desc> <marker id="system_info_dist_tags"/> @@ -8833,14 +8890,14 @@ ok <!-- <name name="system_info" arity="1" clause_i="6"/> atom_count --> <!-- <name name="system_info" arity="1" clause_i="7"/> atom_limit --> <name name="system_info" arity="1" clause_i="8" - anchor="system_info_misc"/> <!-- build_type --> - <name name="system_info" arity="1" clause_i="9"/> <!-- c_compiler_used --> - <name name="system_info" arity="1" clause_i="10"/> <!-- check_io --> - <name name="system_info" arity="1" clause_i="11"/> <!-- compat_rel --> + anchor="system_info_misc" since="OTP R14B"/> <!-- build_type --> + <name name="system_info" arity="1" clause_i="9" since=""/> <!-- c_compiler_used --> + <name name="system_info" arity="1" clause_i="10" since=""/> <!-- check_io --> + <name name="system_info" arity="1" clause_i="11" since=""/> <!-- compat_rel --> <!-- <name name="system_info" arity="1" clause_i="12"/> cpu_topology --> <!-- <name name="system_info" arity="1" clause_i="13"/> {cpu_topology, _} --> <!-- <name name="system_info" arity="1" clause_i="14"/> creation --> - <name name="system_info" arity="1" clause_i="15"/> <!-- debug_compiled --> + <name name="system_info" arity="1" clause_i="15" since=""/> <!-- debug_compiled --> <!-- <name name="system_info" arity="1" clause_i="16"/> delayed_node_table_gc --> <!-- <name name="system_info" arity="1" clause_i="17"/> dirty_cpu_schedulers --> <!-- <name name="system_info" arity="1" clause_i="18"/> dirty_cpu_schedulers_online --> @@ -8848,9 +8905,9 @@ ok <!-- <name name="system_info" arity="1" clause_i="20"/> dist --> <!-- <name name="system_info" arity="1" clause_i="21"/> dist_buf_busy_limit --> <!-- <name name="system_info" arity="1" clause_i="22"/> dist_ctrl --> - <name name="system_info" arity="1" clause_i="23"/> <!-- driver_version --> - <name name="system_info" arity="1" clause_i="24"/> <!-- dynamic_trace --> - <name name="system_info" arity="1" clause_i="25"/> <!-- dynamic_trace_probes --> + <name name="system_info" arity="1" clause_i="23" since=""/> <!-- driver_version --> + <name name="system_info" arity="1" clause_i="24" since="OTP R15B01"/> <!-- dynamic_trace --> + <name name="system_info" arity="1" clause_i="25" since="OTP R15B01"/> <!-- dynamic_trace_probes --> <!-- <name name="system_info" arity="1" clause_i="26"/> end_time --> <!-- <name name="system_info" arity="1" clause_i="27"/> elib_malloc --> <!-- <name name="system_info" arity="1" clause_i="28"/> eager_check_io, removed --> @@ -8860,24 +8917,24 @@ ok <!-- <name name="system_info" arity="1" clause_i="32"/> garbage_collection --> <!-- <name name="system_info" arity="1" clause_i="33"/> heap_sizes --> <!-- <name name="system_info" arity="1" clause_i="34"/> heap_type --> - <name name="system_info" arity="1" clause_i="35"/> <!-- info --> - <name name="system_info" arity="1" clause_i="36"/> <!-- kernel_poll --> - <name name="system_info" arity="1" clause_i="37"/> <!-- loaded --> + <name name="system_info" arity="1" clause_i="35" since=""/> <!-- info --> + <name name="system_info" arity="1" clause_i="36" since=""/> <!-- kernel_poll --> + <name name="system_info" arity="1" clause_i="37" since=""/> <!-- loaded --> <!-- <name name="system_info" arity="1" clause_i="38"/> logical_processors --> - <name name="system_info" arity="1" clause_i="39"/> <!-- machine --> + <name name="system_info" arity="1" clause_i="39" since=""/> <!-- machine --> <!-- <name name="system_info" arity="1" clause_i="40"/> max_heap_size --> <!-- <name name="system_info" arity="1" clause_i="41"/> message_queue_data --> <!-- <name name="system_info" arity="1" clause_i="42"/> min_heap_size --> <!-- <name name="system_info" arity="1" clause_i="43"/> min_bin_vheap_size --> - <name name="system_info" arity="1" clause_i="44"/> <!-- modified_timing_level --> + <name name="system_info" arity="1" clause_i="44" since=""/> <!-- modified_timing_level --> <!-- <name name="system_info" arity="1" clause_i="45"/> multi_scheduling --> <!-- <name name="system_info" arity="1" clause_i="46"/> multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="47"/> <!-- nif_version --> + <name name="system_info" arity="1" clause_i="47" since="OTP 17.4"/> <!-- nif_version --> <!-- n<name name="system_info" arity="1" clause_i="48"/> ormal_multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="49"/> <!-- otp_release --> + <name name="system_info" arity="1" clause_i="49" since=""/> <!-- otp_release --> <!-- <name name="system_info" arity="1" clause_i="50"/> os_monotonic_time_source --> <!-- <name name="system_info" arity="1" clause_i="51"/> os_system_time_source --> - <name name="system_info" arity="1" clause_i="52"/> <!-- port_parallelism --> + <name name="system_info" arity="1" clause_i="52" since="OTP R16B"/> <!-- port_parallelism --> <!-- <name name="system_info" arity="1" clause_i="53"/> port_count --> <!-- <name name="system_info" arity="1" clause_i="54"/> port_limit --> <!-- <name name="system_info" arity="1" clause_i="55"/> process_count --> @@ -8889,19 +8946,20 @@ ok <!-- <name name="system_info" arity="1" clause_i="61"/> schedulers --> <!-- <name name="system_info" arity="1" clause_i="62"/> smp_support --> <!-- <name name="system_info" arity="1" clause_i="63"/> start_time --> - <name name="system_info" arity="1" clause_i="64"/> <!-- system_version --> - <name name="system_info" arity="1" clause_i="65"/> <!-- system_architecture --> - <!-- <name name="system_info" arity="1" clause_i="66"/> threads --> - <!-- <name name="system_info" arity="1" clause_i="67"/> thread_pool_size --> - <!-- <name name="system_info" arity="1" clause_i="68"/> time_correction --> - <!-- <name name="system_info" arity="1" clause_i="69"/> time_offset --> - <!-- <name name="system_info" arity="1" clause_i="70"/> time_warp_mode --> - <!-- <name name="system_info" arity="1" clause_i="71"/> tolerant_timeofday --> - <name name="system_info" arity="1" clause_i="72"/> <!-- trace_control_word --> - <!-- <name name="system_info" arity="1" clause_i="73"/> update_cpu_info --> - <name name="system_info" arity="1" clause_i="74"/> <!-- version --> - <name name="system_info" arity="1" clause_i="75"/> <!-- wordsize --> - <!-- <name name="system_info" arity="1" clause_i="76"/> overview --> + <name name="system_info" arity="1" clause_i="64" since=""/> <!-- system_architecture --> + <name name="system_info" arity="1" clause_i="65" since="OTP 21.3"/> <!-- system_logger --> + <name name="system_info" arity="1" clause_i="66" since=""/> <!-- system_version --> + <!-- <name name="system_info" arity="1" clause_i="67"/> threads --> + <!-- <name name="system_info" arity="1" clause_i="68"/> thread_pool_size --> + <!-- <name name="system_info" arity="1" clause_i="69"/> time_correction --> + <!-- <name name="system_info" arity="1" clause_i="70"/> time_offset --> + <!-- <name name="system_info" arity="1" clause_i="71"/> time_warp_mode --> + <!-- <name name="system_info" arity="1" clause_i="72"/> tolerant_timeofday --> + <name name="system_info" arity="1" clause_i="73" since=""/> <!-- trace_control_word --> + <!-- <name name="system_info" arity="1" clause_i="74"/> update_cpu_info --> + <name name="system_info" arity="1" clause_i="75" since=""/> <!-- version --> + <name name="system_info" arity="1" clause_i="76" since=""/> <!-- wordsize --> + <!-- <name name="system_info" arity="1" clause_i="77"/> overview --> <fsummary>Information about the system.</fsummary> <desc> <marker id="system_info_misc_tags"/> @@ -9057,18 +9115,24 @@ ok <seealso marker="erl#+spp"><c>+spp</c></seealso> in <c>erl(1)</c>.</p> </item> - <tag><marker id="system_info_system_version"/> - <c>system_version</c></tag> - <item> - <p>Returns a string containing version number and - some important properties, such as the number of schedulers.</p> - </item> <tag><marker id="system_info_system_architecture"/> <c>system_architecture</c></tag> <item> <p>Returns a string containing the processor and OS architecture the emulator is built for.</p> </item> + <tag><marker id="system_info_system_logger"/> + <c>system_logger</c></tag> + <item> + <p>Returns the current <c>system_logger</c> as set by + <seealso marker="#system_flag/2"><c>erlang:system_flag(system_logger, _)</c></seealso>.</p> + </item> + <tag><marker id="system_info_system_version"/> + <c>system_version</c></tag> + <item> + <p>Returns a string containing version number and + some important properties, such as the number of schedulers.</p> + </item> <tag><marker id="system_info_trace_control_word"/> <c>trace_control_word</c></tag> <item> @@ -9110,7 +9174,7 @@ ok </func> <func> - <name name="system_monitor" arity="0"/> + <name name="system_monitor" arity="0" since=""/> <fsummary>Current system performance monitoring settings.</fsummary> <type name="system_monitor_option"/> <desc> @@ -9124,7 +9188,7 @@ ok </func> <func> - <name name="system_monitor" arity="1"/> + <name name="system_monitor" arity="1" since=""/> <fsummary>Set or clear system performance monitoring options.</fsummary> <type name="system_monitor_option"/> <desc> @@ -9142,7 +9206,7 @@ ok </func> <func> - <name name="system_monitor" arity="2"/> + <name name="system_monitor" arity="2" since=""/> <fsummary>Set system performance monitoring options.</fsummary> <type name="system_monitor_option"/> <desc> @@ -9274,7 +9338,7 @@ ok </func> <func> - <name name="system_profile" arity="0"/> + <name name="system_profile" arity="0" since=""/> <fsummary>Current system profiling settings.</fsummary> <type name="system_profile_option"/> <desc> @@ -9289,7 +9353,7 @@ ok </func> <func> - <name name="system_profile" arity="2"/> + <name name="system_profile" arity="2" since=""/> <fsummary>Current system profiling settings.</fsummary> <type name="system_profile_option"/> <desc> @@ -9363,7 +9427,7 @@ ok </func> <func> - <name name="system_time" arity="0"/> + <name name="system_time" arity="0" since="OTP 18.0"/> <fsummary>Current Erlang system time.</fsummary> <desc> <p>Returns current @@ -9385,7 +9449,7 @@ ok </func> <func> - <name name="system_time" arity="1"/> + <name name="system_time" arity="1" since="OTP 18.0"/> <fsummary>Current Erlang system time.</fsummary> <desc> <p>Returns current @@ -9407,7 +9471,7 @@ ok </func> <func> - <name name="term_to_binary" arity="1"/> + <name name="term_to_binary" arity="1" since=""/> <fsummary>Encode a term to an Erlang external term format binary. </fsummary> <desc> @@ -9435,7 +9499,7 @@ hello </func> <func> - <name name="term_to_binary" arity="2"/> + <name name="term_to_binary" arity="2" since=""/> <fsummary>Encode a term to en Erlang external term format binary. </fsummary> <desc> @@ -9500,7 +9564,7 @@ hello </func> <func> - <name name="throw" arity="1"/> + <name name="throw" arity="1" since=""/> <fsummary>Throw an exception.</fsummary> <desc> <p>A non-local return from a function. If evaluated within a @@ -9514,7 +9578,7 @@ hello </func> <func> - <name name="time" arity="0"/> + <name name="time" arity="0" since=""/> <fsummary>Current time.</fsummary> <desc> <p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p> @@ -9527,7 +9591,7 @@ hello </func> <func> - <name name="time_offset" arity="0"/> + <name name="time_offset" arity="0" since="OTP 18.0"/> <fsummary>Current time offset.</fsummary> <desc> <p>Returns the current time offset between @@ -9559,7 +9623,7 @@ hello </func> <func> - <name name="time_offset" arity="1"/> + <name name="time_offset" arity="1" since="OTP 18.0"/> <fsummary>Current time offset.</fsummary> <desc> <p>Returns the current time offset between @@ -9578,7 +9642,7 @@ hello </func> <func> - <name name="timestamp" arity="0"/> + <name name="timestamp" arity="0" since="OTP 18.0"/> <fsummary>Current Erlang System time.</fsummary> <type name="timestamp"/> <desc> @@ -9617,7 +9681,7 @@ timestamp() -> </func> <func> - <name name="tl" arity="1"/> + <name name="tl" arity="1" since=""/> <fsummary>Tail of a list.</fsummary> <desc> <p>Returns the tail of <c><anno>List</anno></c>, that is, @@ -9632,7 +9696,7 @@ timestamp() -> </func> <func> - <name name="trace" arity="3"/> + <name name="trace" arity="3" since=""/> <fsummary>Set trace flags for a process or processes.</fsummary> <type name="trace_flag"/> <desc> @@ -10288,7 +10352,7 @@ timestamp() -> </func> <func> - <name name="trace_delivered" arity="1"/> + <name name="trace_delivered" arity="1" since=""/> <fsummary>Notification when trace has been delivered.</fsummary> <desc> <p>The delivery of trace messages (generated by @@ -10343,7 +10407,7 @@ timestamp() -> </func> <func> - <name name="trace_info" arity="2"/> + <name name="trace_info" arity="2" since=""/> <fsummary>Trace information about a process or function.</fsummary> <type name="trace_info_return"/> <type name="trace_info_item_result"/> @@ -10479,7 +10543,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="2" clause_i="1"/> + <name name="trace_pattern" arity="2" clause_i="1" since=""/> <fsummary>Set trace patterns for call, send, or 'receive' tracing. </fsummary> <type name="trace_pattern_mfa"/> @@ -10497,7 +10561,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="3" clause_i="1"/> + <name name="trace_pattern" arity="3" clause_i="1" since="OTP 19.0"/> <fsummary>Set trace pattern for message sending.</fsummary> <type name="trace_match_spec"/> <type name="match_variable"/> @@ -10568,7 +10632,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="3" clause_i="2"/> + <name name="trace_pattern" arity="3" clause_i="2" since="OTP 19.0"/> <fsummary>Set trace pattern for tracing of message receiving.</fsummary> <type name="trace_match_spec"/> <type name="match_variable"/> @@ -10640,7 +10704,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="3" clause_i="3"/> + <name name="trace_pattern" arity="3" clause_i="3" since=""/> <fsummary>Set trace patterns for tracing of function calls.</fsummary> <type name="trace_pattern_mfa"/> <type name="trace_match_spec"/> @@ -10831,7 +10895,7 @@ timestamp() -> </func> <func> - <name name="trunc" arity="1"/> + <name name="trunc" arity="1" since=""/> <fsummary>Return an integer by truncating a number.</fsummary> <desc> <p>Returns an integer by truncating <c><anno>Number</anno></c>, @@ -10844,7 +10908,7 @@ timestamp() -> </func> <func> - <name name="tuple_size" arity="1"/> + <name name="tuple_size" arity="1" since=""/> <fsummary>Return the size of a tuple.</fsummary> <desc> <p>Returns an integer that is the number of elements in @@ -10857,7 +10921,7 @@ timestamp() -> </func> <func> - <name name="tuple_to_list" arity="1"/> + <name name="tuple_to_list" arity="1" since=""/> <fsummary>Convert a tuple to a list.</fsummary> <desc> <p>Returns a list corresponding to <c><anno>Tuple</anno></c>. @@ -10870,7 +10934,7 @@ timestamp() -> </func> <func> - <name name="unique_integer" arity="0"/> + <name name="unique_integer" arity="0" since="OTP 18.0"/> <fsummary>Get a unique integer value.</fsummary> <desc> <p>Generates and returns an @@ -10883,7 +10947,7 @@ timestamp() -> </func> <func> - <name name="unique_integer" arity="1"/> + <name name="unique_integer" arity="1" since="OTP 18.0"/> <fsummary>Get a unique integer value.</fsummary> <desc> <p>Generates and returns an @@ -10965,7 +11029,7 @@ timestamp() -> </func> <func> - <name name="universaltime" arity="0"/> + <name name="universaltime" arity="0" since=""/> <fsummary>Current date and time according to Universal Time Coordinated (UTC).</fsummary> <desc> @@ -10982,7 +11046,7 @@ timestamp() -> </func> <func> - <name name="universaltime_to_localtime" arity="1"/> + <name name="universaltime_to_localtime" arity="1" since=""/> <fsummary>Convert from Universal Time Coordinated (UTC) to local date and time.</fsummary> <desc> @@ -11001,7 +11065,7 @@ timestamp() -> </func> <func> - <name name="unlink" arity="1"/> + <name name="unlink" arity="1" since=""/> <fsummary>Remove a link to another process or port.</fsummary> <desc> <p>Removes the link, if there is one, between the calling @@ -11047,7 +11111,7 @@ end</code> </func> <func> - <name name="unregister" arity="1"/> + <name name="unregister" arity="1" since=""/> <fsummary>Remove the registered name for a process (or port).</fsummary> <desc> <p>Removes the registered name <c><anno>RegName</anno></c> @@ -11063,7 +11127,7 @@ true</pre> </func> <func> - <name name="whereis" arity="1"/> + <name name="whereis" arity="1" since=""/> <fsummary>Get the pid (or port) with a specified registered name. </fsummary> <desc> @@ -11077,7 +11141,7 @@ true</pre> </func> <func> - <name name="yield" arity="0"/> + <name name="yield" arity="0" since=""/> <fsummary>Let other processes get a chance to execute.</fsummary> <desc> <p>Voluntarily lets other processes (if any) get a chance to diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index c14f0a558d..c824e37976 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -29,7 +29,7 @@ <rev></rev> <file>init.xml</file> </header> - <module>init</module> + <module since="">init</module> <modulesummary>Coordination of system startup.</modulesummary> <description> <p>This module is preloaded and contains the code for @@ -50,7 +50,7 @@ <funcs> <func> - <name name="boot" arity="1"/> + <name name="boot" arity="1" since=""/> <fsummary>Start the Erlang runtime system.</fsummary> <desc> <p>Starts the Erlang runtime system. This function is called @@ -69,7 +69,7 @@ </func> <func> - <name name="get_argument" arity="1"/> + <name name="get_argument" arity="1" since=""/> <fsummary>Get the values associated with a command-line user flag. </fsummary> <desc> @@ -112,7 +112,7 @@ </func> <func> - <name name="get_arguments" arity="0"/> + <name name="get_arguments" arity="0" since=""/> <fsummary>Get all command-line user flags.</fsummary> <desc> <p>Returns all command-line flags and the system-defined flags, see @@ -121,7 +121,7 @@ </func> <func> - <name name="get_plain_arguments" arity="0"/> + <name name="get_plain_arguments" arity="0" since=""/> <fsummary>Get all non-flag command-line arguments.</fsummary> <desc> <p>Returns any plain command-line arguments as a list of strings @@ -130,7 +130,7 @@ </func> <func> - <name name="get_status" arity="0"/> + <name name="get_status" arity="0" since=""/> <fsummary>Get system status information.</fsummary> <type name="internal_status"/> <desc> @@ -146,7 +146,7 @@ </func> <func> - <name name="reboot" arity="0"/> + <name name="reboot" arity="0" since=""/> <fsummary>Take down and restart an Erlang node smoothly.</fsummary> <desc> <p>All applications are taken down smoothly, all code is @@ -162,7 +162,7 @@ </func> <func> - <name name="restart" arity="0"/> + <name name="restart" arity="0" since=""/> <fsummary>Restart the running Erlang node.</fsummary> <desc> <p>The system is restarted <em>inside</em> the running Erlang @@ -178,7 +178,7 @@ </func> <func> - <name name="script_id" arity="0"/> + <name name="script_id" arity="0" since=""/> <fsummary>Get the identity of the used boot script.</fsummary> <desc> <p>Gets the identity of the boot script used to boot the system. @@ -189,7 +189,7 @@ </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary>Take down an Erlang node smoothly.</fsummary> <desc> <p>The same as @@ -198,7 +198,7 @@ </func> <func> - <name name="stop" arity="1"/> + <name name="stop" arity="1" since=""/> <fsummary>Take down an Erlang node smoothly.</fsummary> <desc> <p>All applications are taken down smoothly, all code is diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index a34b3edd7a..9ea1beb887 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,132 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.2.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + When using the <c>{linger,{true,T}}</c> option; + <c>gen_tcp:listen/2</c> used the full linger time before + returning for example <c>eaddrinuse</c>. This bug has now + been corrected.</p> + <p> + Own Id: OTP-14728 Aux Id: ERIERL-303 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.2.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix bug where doing a <c>gen_tcp:send</c> on a socket + with <c>delay_send</c> set to true could cause a segfault + if the other side closes the connection.</p> + <p> + Bug was introduced in erts-10.2 (OTP-21.2).</p> + <p> + Own Id: OTP-15536 Aux Id: ERL-827 </p> + </item> + <item> + <p> + Fix a race condition when a port program closes that + could result in the next started port to hang during + startup.</p> + <p> + When this fault happens the following error is normally + (but not always) logged:</p> + <p> + <c> =ERROR REPORT==== 14-Jan-2019::10:45:52.868246 + ===</c><br/><c> Bad input fd in erts_poll()! fd=11, + port=#Port<0.505>, driver=spawn, name=/bin/sh -s + unix:cmd </c></p> + <p> + Bug was introduced in erts-10.0 (OTP-21.0).</p> + <p> + Own Id: OTP-15537</p> + </item> + <item> + <p> + Fix a bug where polling for external events could be + delayed for a very long time if all active schedulers + were 100% loaded.</p> + <p> + Bug was introduced in erts-10.2 (OTP-21.2).</p> + <p> + Own Id: OTP-15538 Aux Id: ERIERL-229 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a crash when dangling files were closed after + <c>init:restart/0</c>.</p> + <p> + Own Id: OTP-15495 Aux Id: ERL-821 </p> + </item> + <item> + <p> + A bug that could cause dirty schedulers to become + unresponsive has been fixed.</p> + <p> + Own Id: OTP-15509 Aux Id: PR-2027, PR-2093 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug on big endian architectures when changing file + permissions or ownership with <c>file:change_mode</c>, + <c>change_owner</c>, <c>change_group</c> or + <c>write_file_info</c>. Bug exists since OTP-21.0.</p> + <p> + Own Id: OTP-15485</p> + </item> + <item> + <p> + Fixed bug in <c>atomics</c> with option + <c>{signed,false}</c> when returned values are <c>(1 bsl + 63)</c> or larger. Could cause heap corruption leading to + VM crash or other unpleasant symptoms. Bug exists since + OTP-21.2 when module <c>atomics</c> was introduced.</p> + <p> + Own Id: OTP-15486 Aux Id: PR-2061 </p> + </item> + <item> + <p> + Fixed bug in operator <c>band</c> of two negative + operands causing erroneous result if the absolute value + of one of the operands have the lowest <c>N*W</c> bits as + zero and the other absolute value is not larger than + <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger + and <c>W</c> is 32 or 64 depending on word size.</p> + <p> + Own Id: OTP-15487 Aux Id: ERL-804 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -1586,6 +1712,62 @@ </section> +<section><title>Erts 9.3.3.9</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Added an optional <c>./configure</c> flag to compile + the emulator with spectre mitigation: + <c>--with-spectre-mitigation</c></p> + <p>Note that this requires a recent version of GCC with + support for spectre mitigation and the + <c>--mindirect-branch=thunk</c> flag, such as + <c>8.1</c>.</p> + <p> + Own Id: OTP-15430 Aux Id: ERIERL-237 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 9.3.3.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A bug that could cause dirty schedulers to become + unresponsive has been fixed.</p> + <p> + Own Id: OTP-15509 Aux Id: PR-2027, PR-2093 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 9.3.3.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug in operator <c>band</c> of two negative + operands causing erroneous result if the absolute value + of one of the operands have the lowest <c>N*W</c> bits as + zero and the other absolute value is not larger than + <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger + and <c>W</c> is 32 or 64 depending on word size.</p> + <p> + Own Id: OTP-15487 Aux Id: ERL-804 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.3.3.6</title> <section><title>Improvements and New Features</title> @@ -1947,6 +2129,22 @@ </section> +<section><title>Erts 9.2.0.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Extra internal consistency checks wrt communication with + erl_child_setup process.</p> + <p> + Own Id: OTP-15488 Aux Id: ERIERL-231 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -3145,6 +3343,43 @@ </section> +<section><title>Erts 8.3.5.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug in operator <c>band</c> of two negative + operands causing erroneous result if the absolute value + of one of the operands have the lowest <c>N*W</c> bits as + zero and the other absolute value is not larger than + <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger + and <c>W</c> is 32 or 64 depending on word size.</p> + <p> + Own Id: OTP-15487 Aux Id: ERL-804 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Added an optional <c>./configure</c> flag to compile + the emulator with spectre mitigation: + <c>--with-spectre-mitigation</c></p> + <p>Note that this requires a recent version of GCC with + support for spectre mitigation and the + <c>--mindirect-branch=thunk</c> flag, such as + <c>8.1</c>.</p> + <p> + Own Id: OTP-15430 Aux Id: ERIERL-237 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 8.3.5.6</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/doc/src/persistent_term.xml b/erts/doc/src/persistent_term.xml index 29a6c67051..9d3c9afd80 100644 --- a/erts/doc/src/persistent_term.xml +++ b/erts/doc/src/persistent_term.xml @@ -29,7 +29,7 @@ <rev></rev> <file>persistent_term.xml</file> </header> - <module>persistent_term</module> + <module since="OTP 21.2">persistent_term</module> <modulesummary>Persistent terms.</modulesummary> <description> <p>This module is similar to <seealso @@ -213,7 +213,7 @@ will be slower as the number of persistent terms increases.</pre> <funcs> <func> - <name name="erase" arity="1"/> + <name name="erase" arity="1" since="OTP 21.2"/> <fsummary>Erase the name for a persistent term.</fsummary> <desc> <p>Erase the name for the persistent term with key @@ -229,7 +229,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="get" arity="0"/> + <name name="get" arity="0" since="OTP 21.2"/> <fsummary>Get all persistent terms.</fsummary> <desc> <p>Retrieve the keys and values for all persistent terms. @@ -239,7 +239,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="get" arity="1"/> + <name name="get" arity="1" since="OTP 21.2"/> <fsummary>Get the value for a persistent term.</fsummary> <desc> <p>Retrieve the value for the persistent term associated with @@ -256,7 +256,23 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="info" arity="0"/> + <name name="get" arity="2" since="OTP 21.3"/> + <fsummary>Get the value for a persistent term.</fsummary> + <desc> + <p>Retrieve the value for the persistent term associated with + the key <c><anno>Key</anno></c>. The lookup will be made in + constant time and the value will not be copied to the heap + of the calling process.</p> + <p>This function returns <c><anno>Default</anno></c> if no + term has been stored with the key <c><anno>Key</anno></c>.</p> + <p>If the calling process holds on to the value of the + persistent term and the persistent term is deleted in the future, + the term will be copied to the process.</p> + </desc> + </func> + + <func> + <name name="info" arity="0" since="OTP 21.2"/> <fsummary>Get information about persistent terms.</fsummary> <desc> <p>Return information about persistent terms in a map. The map @@ -272,7 +288,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="put" arity="2"/> + <name name="put" arity="2" since="OTP 21.2"/> <fsummary>Store a term.</fsummary> <desc> <p>Store the value <c><anno>Value</anno></c> as a persistent term and diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 6f4c42da27..38229456c9 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -29,7 +29,7 @@ <rev></rev> <file>zlib.xml</file> </header> - <module>zlib</module> + <module since="">zlib</module> <modulesummary>zlib compression interface.</modulesummary> <description> <p>This module provides an API for the zlib library @@ -120,7 +120,7 @@ list_to_binary([Compressed|Last])</pre> <funcs> <func> - <name name="adler32" arity="2"/> + <name name="adler32" arity="2" since=""/> <fsummary>Calculate the Adler checksum.</fsummary> <desc> <p>Calculates the Adler-32 checksum for <c><anno>Data</anno></c>.</p> @@ -133,7 +133,7 @@ list_to_binary([Compressed|Last])</pre> </func> <func> - <name name="adler32" arity="3"/> + <name name="adler32" arity="3" since=""/> <fsummary>Calculate the Adler checksum.</fsummary> <desc> <p>Updates a running Adler-32 checksum for <c><anno>Data</anno></c>. @@ -153,7 +153,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="adler32_combine" arity="4"/> + <name name="adler32_combine" arity="4" since=""/> <fsummary>Combine two Adler-32 checksums.</fsummary> <desc> <p>Combines two Adler-32 checksums into one. For two binaries or @@ -172,7 +172,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a stream.</fsummary> <desc> <p>Closes the stream referenced by <c><anno>Z</anno></c>.</p> @@ -180,7 +180,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="compress" arity="1"/> + <name name="compress" arity="1" since=""/> <fsummary>Compress data with standard zlib functionality.</fsummary> <desc> <p>Compresses data with zlib headers and checksum.</p> @@ -188,7 +188,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32" arity="1"/> + <name name="crc32" arity="1" since=""/> <fsummary>Get current CRC.</fsummary> <desc> <p>Gets the current calculated CRC checksum.</p> @@ -202,7 +202,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32" arity="2"/> + <name name="crc32" arity="2" since=""/> <fsummary>Calculate CRC.</fsummary> <desc> <p>Calculates the CRC checksum for <c><anno>Data</anno></c>.</p> @@ -215,7 +215,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32" arity="3"/> + <name name="crc32" arity="3" since=""/> <fsummary>Calculate CRC.</fsummary> <desc> <p>Updates a running CRC checksum for <c><anno>Data</anno></c>. @@ -235,7 +235,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32_combine" arity="4"/> + <name name="crc32_combine" arity="4" since=""/> <fsummary>Combine two CRCs.</fsummary> <desc> <p>Combines two CRC checksums into one. For two binaries or iolists, @@ -254,7 +254,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="deflate" arity="2"/> + <name name="deflate" arity="2" since=""/> <fsummary>Compress data.</fsummary> <desc> <p>Same as <c>deflate(<anno>Z</anno>, <anno>Data</anno>, none)</c>.</p> @@ -262,7 +262,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="deflate" arity="3"/> + <name name="deflate" arity="3" since=""/> <fsummary>Compress data.</fsummary> <desc> <p>Compresses as much data as possible, and @@ -300,7 +300,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateEnd" arity="1"/> + <name name="deflateEnd" arity="1" since=""/> <fsummary>End deflate session.</fsummary> <desc> <p>Ends the deflate session and cleans all data used. Notice that this @@ -311,7 +311,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateInit" arity="1"/> + <name name="deflateInit" arity="1" since=""/> <fsummary>Initialize a session for compression.</fsummary> <desc> <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p> @@ -319,7 +319,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateInit" arity="2"/> + <name name="deflateInit" arity="2" since=""/> <fsummary>Initialize a session for compression.</fsummary> <desc> <p>Initializes a zlib stream for compression.</p> @@ -334,7 +334,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateInit" arity="6"/> + <name name="deflateInit" arity="6" since=""/> <fsummary>Initialize a session for compression.</fsummary> <desc> <p>Initiates a zlib stream for compression.</p> @@ -410,7 +410,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateParams" arity="3"/> + <name name="deflateParams" arity="3" since=""/> <fsummary>Dynamicly update deflate parameters.</fsummary> <desc> <p>Dynamically updates the compression level and compression @@ -432,7 +432,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateReset" arity="1"/> + <name name="deflateReset" arity="1" since=""/> <fsummary>Reset the deflate session.</fsummary> <desc> <p>Equivalent to @@ -446,7 +446,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateSetDictionary" arity="2"/> + <name name="deflateSetDictionary" arity="2" since=""/> <fsummary>Initialize the compression dictionary.</fsummary> <desc> <p>Initializes the compression dictionary from the specified byte @@ -464,7 +464,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="getBufSize" arity="1"/> + <name name="getBufSize" arity="1" since=""/> <fsummary>Get buffer size.</fsummary> <desc> <p>Gets the size of the intermediate buffer.</p> @@ -476,7 +476,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="gunzip" arity="1"/> + <name name="gunzip" arity="1" since=""/> <fsummary>Uncompress data with gz header.</fsummary> <desc> <p>Uncompresses data with gz headers and checksum.</p> @@ -484,7 +484,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="gzip" arity="1"/> + <name name="gzip" arity="1" since=""/> <fsummary>Compress data with gz header.</fsummary> <desc> <p>Compresses data with gz headers and checksum.</p> @@ -492,7 +492,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflate" arity="2"/> + <name name="inflate" arity="2" since=""/> <fsummary>Decompress data.</fsummary> <desc> <p>Equivalent to @@ -502,7 +502,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflate" arity="3"/> + <name name="inflate" arity="3" since="OTP 20.1"/> <fsummary>Decompress data.</fsummary> <desc> <p>Decompresses as much data as possible. It can introduce some output @@ -524,7 +524,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflateChunk" arity="1"/> + <name name="inflateChunk" arity="1" since="OTP 18.0"/> <fsummary>Read next uncompressed chunk.</fsummary> <desc> <warning> @@ -540,7 +540,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflateChunk" arity="2"/> + <name name="inflateChunk" arity="2" since="OTP 18.0"/> <fsummary>Decompress data with limited output size.</fsummary> <desc> <warning> @@ -584,7 +584,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateEnd" arity="1"/> + <name name="inflateEnd" arity="1" since=""/> <fsummary>End inflate session.</fsummary> <desc> <p>Ends the inflate session and cleans all data used. Notice @@ -595,7 +595,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateGetDictionary" arity="1"/> + <name name="inflateGetDictionary" arity="1" since="OTP 20.0"/> <fsummary>Return the decompression dictionary.</fsummary> <desc> <p>Returns the decompression dictionary currently in use @@ -607,7 +607,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateInit" arity="1"/> + <name name="inflateInit" arity="1" since=""/> <fsummary>Initialize a session for decompression.</fsummary> <desc> <p>Initializes a zlib stream for decompression.</p> @@ -615,7 +615,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateInit" arity="2"/> + <name name="inflateInit" arity="2" since=""/> <fsummary>Initialize a session for decompression.</fsummary> <desc> <p>Initializes a decompression session on zlib stream.</p> @@ -634,7 +634,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateReset" arity="1"/> + <name name="inflateReset" arity="1" since=""/> <fsummary>>Reset the inflate session.</fsummary> <desc> <p>Equivalent to @@ -648,7 +648,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateSetDictionary" arity="2"/> + <name name="inflateSetDictionary" arity="2" since=""/> <fsummary>Initialize the decompression dictionary.</fsummary> <desc> <p>Initializes the decompression dictionary from the specified @@ -688,7 +688,7 @@ new_unpack(Z, Compressed, Dict) -> </func> <func> - <name name="open" arity="0"/> + <name name="open" arity="0" since=""/> <fsummary>Open a stream and return a stream reference.</fsummary> <desc> <p>Opens a zlib stream.</p> @@ -696,7 +696,7 @@ new_unpack(Z, Compressed, Dict) -> </func> <func> - <name name="safeInflate" arity="2"/> + <name name="safeInflate" arity="2" since="OTP 20.1"/> <fsummary>Decompress data with limited output size.</fsummary> <desc> <p>Like <seealso marker="#inflate/2"><c>inflate/2</c></seealso>, @@ -733,7 +733,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="setBufSize" arity="2"/> + <name name="setBufSize" arity="2" since=""/> <fsummary>Set buffer size.</fsummary> <desc> <p>Sets the intermediate buffer size.</p> @@ -745,7 +745,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="set_controlling_process" arity="2"/> + <name name="set_controlling_process" arity="2" since="OTP 20.1.3"/> <fsummary>Transfers ownership of a zlib stream.</fsummary> <desc> <p>Changes the controlling process of <c><anno>Z</anno></c> to @@ -754,7 +754,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="uncompress" arity="1"/> + <name name="uncompress" arity="1" since=""/> <fsummary>Uncompress data with standard zlib functionality.</fsummary> <desc> <p>Uncompresses data with zlib headers and checksum.</p> @@ -762,7 +762,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="unzip" arity="1"/> + <name name="unzip" arity="1" since=""/> <fsummary>Uncompress data without the zlib headers.</fsummary> <desc> <p>Uncompresses data without zlib headers and checksum.</p> @@ -770,7 +770,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="zip" arity="1"/> + <name name="zip" arity="1" since=""/> <fsummary>Compress data without the zlib headers.</fsummary> <desc> <p>Compresses data without zlib headers and checksum.</p> diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index a14f22b19e..291bc95604 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -546,6 +546,7 @@ atom reload atom rem atom report_errors atom reset +atom reset_seq_trace atom restart atom return_from atom return_to diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index e909a0b4da..4351dda5a7 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -379,6 +379,7 @@ do { \ # define NOINLINE #endif +int tuple_module_apply; /* * The following functions are called directly by process_main(). @@ -2210,6 +2211,7 @@ apply(Process* p, Eterm* reg, BeamInstr *I, Uint stack_offset) Eterm module = reg[0]; Eterm function = reg[1]; Eterm args = reg[2]; + Eterm this; /* * Check the arguments which should be of the form apply(Module, @@ -2232,8 +2234,20 @@ apply(Process* p, Eterm* reg, BeamInstr *I, Uint stack_offset) while (1) { Eterm m, f, a; - - if (is_not_atom(module)) goto error; + /* The module argument may be either an atom or an abstract module + * (currently implemented using tuples, but this might change). + */ + this = THE_NON_VALUE; + if (is_not_atom(module)) { + Eterm* tp; + + if (!tuple_module_apply || is_not_tuple(module)) goto error; + tp = tuple_val(module); + if (arityval(tp[0]) < 1) goto error; + this = module; + module = tp[1]; + if (is_not_atom(module)) goto error; + } if (module != am_erlang || function != am_apply) break; @@ -2268,7 +2282,9 @@ apply(Process* p, Eterm* reg, BeamInstr *I, Uint stack_offset) } /* * Walk down the 3rd parameter of apply (the argument list) and copy - * the parameters to the x registers (reg[]). + * the parameters to the x registers (reg[]). If the module argument + * was an abstract module, add 1 to the function arity and put the + * module argument in the n+1st x register as a THIS reference. */ tmp = args; @@ -2285,6 +2301,9 @@ apply(Process* p, Eterm* reg, BeamInstr *I, Uint stack_offset) if (is_not_nil(tmp)) { /* Must be well-formed list */ goto error; } + if (this != THE_NON_VALUE) { + reg[arity++] = this; + } /* * Get the index into the export table, or failing that the export @@ -2323,7 +2342,18 @@ fixed_apply(Process* p, Eterm* reg, Uint arity, return 0; } - if (is_not_atom(module)) goto error; + /* The module argument may be either an atom or an abstract module + * (currently implemented using tuples, but this might change). + */ + if (is_not_atom(module)) { + Eterm* tp; + if (!tuple_module_apply || is_not_tuple(module)) goto error; + tp = tuple_val(module); + if (arityval(tp[0]) < 1) goto error; + module = tp[1]; + if (is_not_atom(module)) goto error; + ++arity; + } /* Handle apply of apply/3... */ if (module == am_erlang && function == am_apply && arity == 3) { diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 015c051cc1..000397e790 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2810,38 +2810,110 @@ BIF_RETTYPE list_to_existing_atom_1(BIF_ALIST_1) /* convert an integer to a list of ascii integers */ -BIF_RETTYPE integer_to_list_1(BIF_ALIST_1) +static Eterm integer_to_list(Process *c_p, Eterm num, int base) { - Eterm* hp; + Eterm *hp; + Eterm res; Uint need; + if (is_small(num)) { + char s[128]; + char *c = s; + Uint digits; + + digits = Sint_to_buf(signed_val(num), base, &c, sizeof(s)); + need = 2 * digits; + + hp = HAlloc(c_p, need); + res = buf_to_intlist(&hp, c, digits, NIL); + } else { + const int DIGITS_PER_RED = 16; + Eterm *hp_end; + Uint digits; + + digits = big_integer_estimate(num, base); + + if ((digits / DIGITS_PER_RED) > ERTS_BIF_REDS_LEFT(c_p)) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + + /* This could take a very long time, tell the caller to reschedule + * us to a dirty CPU scheduler if we aren't already on one. */ + if (esdp->type == ERTS_SCHED_NORMAL) { + return THE_NON_VALUE; + } + } else { + BUMP_REDS(c_p, digits / DIGITS_PER_RED); + } + + need = 2 * digits; + + hp = HAlloc(c_p, need); + hp_end = hp + need; + + res = erts_big_to_list(num, base, &hp); + HRelease(c_p, hp_end, hp); + } + + return res; +} + +BIF_RETTYPE integer_to_list_1(BIF_ALIST_1) +{ + Eterm res; + if (is_not_integer(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(BIF_P, BADARG); } - if (is_small(BIF_ARG_1)) { - char *c; - int n; - struct Sint_buf ibuf; + res = integer_to_list(BIF_P, BIF_ARG_1, 10); - c = Sint_to_buf(signed_val(BIF_ARG_1), &ibuf); - n = sys_strlen(c); - need = 2*n; - hp = HAlloc(BIF_P, need); - BIF_RET(buf_to_intlist(&hp, c, n, NIL)); + if (is_non_value(res)) { + Eterm args[1]; + args[0] = BIF_ARG_1; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_list_1, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_list, + 1); } - else { - int n = big_decimal_estimate(BIF_ARG_1); - Eterm res; - Eterm* hp_end; - need = 2*n; - hp = HAlloc(BIF_P, need); - hp_end = hp + need; - res = erts_big_to_list(BIF_ARG_1, &hp); - HRelease(BIF_P,hp_end,hp); - BIF_RET(res); + return res; +} + +BIF_RETTYPE integer_to_list_2(BIF_ALIST_2) +{ + Eterm res; + SWord base; + + if (is_not_integer(BIF_ARG_1) || is_not_small(BIF_ARG_2)) { + BIF_ERROR(BIF_P, BADARG); } + + base = signed_val(BIF_ARG_2); + if (base < 2 || base > 36) { + BIF_ERROR(BIF_P, BADARG); + } + + res = integer_to_list(BIF_P, BIF_ARG_1, base); + + if (is_non_value(res)) { + Eterm args[2]; + args[0] = BIF_ARG_1; + args[1] = BIF_ARG_2; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_list_2, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_list, + 2); + } + + return res; } /**********************************************************************/ @@ -4490,11 +4562,12 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) ERTS_TRACER_CLEAR(&old_seq_tracer); BIF_RET(ret); - } else if (BIF_ARG_1 == make_small(1)) { + } else if (BIF_ARG_1 == am_reset_seq_trace) { int i, max; - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_thr_progress_block(); + erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_thr_progress_block(); + max = erts_ptab_max(&erts_proc); for (i = 0; i < max; i++) { Process *p = erts_pix2proc(i); @@ -4506,13 +4579,14 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) #endif p->seq_trace_clock = 0; p->seq_trace_lastcnt = 0; - + erts_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ); erts_proc_sig_clear_seq_trace_tokens(p); + erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ); } } - erts_thr_progress_unblock(); - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_thr_progress_unblock(); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); BIF_RET(am_true); } else if (BIF_ARG_1 == am_scheduler_wall_time) { @@ -4627,6 +4701,9 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) return erts_bind_schedulers(BIF_P, BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("erts_alloc", BIF_ARG_1)) { return erts_alloc_set_dyn_param(BIF_P, BIF_ARG_2); + } else if (ERTS_IS_ATOM_STR("system_logger", BIF_ARG_1)) { + Eterm res = erts_set_system_logger(BIF_ARG_2); + if (is_value(res)) BIF_RET(res); } error: BIF_ERROR(BIF_P, BADARG); @@ -5129,61 +5206,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm * return exiting; } - - -#ifdef HARDDEBUG -/* -You'll need this line in bif.tab to be able to use this debug bif - -bif erlang:send_to_logger/2 - -*/ -BIF_RETTYPE send_to_logger_2(BIF_ALIST_2) -{ - byte *buf; - ErlDrvSizeT len; - if (!is_atom(BIF_ARG_1) || !(is_list(BIF_ARG_2) || - is_nil(BIF_ARG_1))) { - BIF_ERROR(BIF_P,BADARG); - } - if (erts_iolist_size(BIF_ARG_2, &len) != 0) - BIF_ERROR(BIF_P,BADARG); - else if (len == 0) - buf = ""; - else { -#ifdef DEBUG - ErlDrvSizeT len2; -#endif - buf = (byte *) erts_alloc(ERTS_ALC_T_TMP, len+1); -#ifdef DEBUG - len2 = -#else - (void) -#endif - erts_iolist_to_buf(BIF_ARG_2, buf, len); - ASSERT(len2 == len); - buf[len] = '\0'; - switch (BIF_ARG_1) { - case am_info: - erts_send_info_to_logger(BIF_P->group_leader, buf, len); - break; - case am_warning: - erts_send_warning_to_logger(BIF_P->group_leader, buf, len); - break; - case am_error: - erts_send_error_to_logger(BIF_P->group_leader, buf, len); - break; - default: - { - BIF_ERROR(BIF_P,BADARG); - } - } - erts_free(ERTS_ALC_T_TMP, (void *) buf); - } - BIF_RET(am_true); -} -#endif /* HARDDEBUG */ - BIF_RETTYPE get_module_info_1(BIF_ALIST_1) { Eterm ret = erts_module_info_0(BIF_P, BIF_ARG_1); diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index d4ba90a61a..8419244832 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -725,3 +725,17 @@ bif erts_internal:counters_get/2 bif erts_internal:counters_add/3 bif erts_internal:counters_put/3 bif erts_internal:counters_info/1 + +# +# New in 21.2.3 +# + +bif erts_internal:spawn_system_process/3 + +# +# New in 21.3 +# + +bif erlang:integer_to_list/2 +bif erlang:integer_to_binary/2 +bif persistent_term:get/2 diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 84338769e0..522f50287a 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -429,6 +429,7 @@ static const byte digits_per_sint_lookup[36-1]; static const byte digits_per_small_lookup[36-1]; static const Sint largest_power_of_base_lookup[36-1]; +static const double lg2_lookup[36-1]; static ERTS_INLINE byte get_digits_per_signed_int(Uint base) { return digits_per_sint_lookup[base-2]; @@ -442,6 +443,10 @@ static ERTS_INLINE Sint get_largest_power_of_base(Uint base) { return largest_power_of_base_lookup[base-2]; } +static ERTS_INLINE double lookup_log2(Uint base) { + return lg2_lookup[base - 2]; +} + /* ** compare two number vectors */ @@ -668,27 +673,25 @@ static dsize_t I_mul(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, ErtsDig static dsize_t I_sqr(ErtsDigit* x, dsize_t xl, ErtsDigit* r) { - ErtsDigit d_next = *x; ErtsDigit d; ErtsDigit* r0 = r; ErtsDigit* s = r; if ((r + xl) == x) /* "Inline" operation */ *x = 0; - x++; while(xl--) { - ErtsDigit* y = x; + ErtsDigit* y; ErtsDigit y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0; ErtsDigit b0, b1; ErtsDigit z0, z1, z2; ErtsDigit t; dsize_t y_l = xl; - + + d = *x; + x++; + y = x; s = r; - d = d_next; - d_next = *x; - x++; DMUL(d, d, b1, b0); DSUMc(*s, b0, y_3, t); @@ -1159,8 +1162,11 @@ static dsize_t I_band(ErtsDigit* x, dsize_t xl, short xsgn, *r++ = ~c1 & ~c2; x++; y++; } - while(xl--) - *r++ = ~*x++; + while(xl--) { + DSUBb(*x,0,b1,c1); + *r++ = ~c1; + x++; + } } } return I_btrail(r0, r, sign); @@ -1719,23 +1725,23 @@ double_to_big(double x, Eterm *heap, Uint hsz) /* - ** Estimate the number of decimal digits (include sign) + ** Estimate the number of digits in given base (include sign) */ -int big_decimal_estimate(Wterm x) +int big_integer_estimate(Wterm x, Uint base) { Eterm* xp = big_val(x); int lg = I_lg(BIG_V(xp), BIG_SIZE(xp)); - int lg10 = ((lg+1)*28/93)+1; + int lgBase = ((lg + 1) / lookup_log2(base)) + 1; - if (BIG_SIGN(xp)) lg10++; /* add sign */ - return lg10+1; /* add null */ + if (BIG_SIGN(xp)) lgBase++; /* add sign */ + return lgBase + 1; /* add null */ } /* -** Convert a bignum into a string of decimal numbers +** Convert a bignum into a string of numbers in given base */ - -static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) +static Uint write_big(Wterm x, int base, void (*write_func)(void *, char), + void *arg) { Eterm* xp = big_val(x); ErtsDigit* dx = BIG_V(xp); @@ -1743,48 +1749,72 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) short sign = BIG_SIGN(xp); ErtsDigit rem; Uint n = 0; - const Uint digits_per_Sint = get_digits_per_signed_int(10); - const Sint largest_pow_of_base = get_largest_power_of_base(10); + const Uint digits_per_Sint = get_digits_per_signed_int(base); + const Sint largest_pow_of_base = get_largest_power_of_base(base); if (xl == 1 && *dx < largest_pow_of_base) { - rem = *dx; - if (rem == 0) { - (*write_func)(arg, '0'); n++; - } else { - while(rem) { - (*write_func)(arg, (rem % 10) + '0'); n++; - rem /= 10; - } - } + rem = *dx; + if (rem == 0) { + (*write_func)(arg, '0'); n++; + } else { + while(rem) { + int digit = rem % base; + + if (digit < 10) { + (*write_func)(arg, digit + '0'); n++; + } else { + (*write_func)(arg, 'A' + (digit - 10)); n++; + } + + rem /= base; + } + } } else { - ErtsDigit* tmp = (ErtsDigit*) erts_alloc(ERTS_ALC_T_TMP, - sizeof(ErtsDigit)*xl); - dsize_t tmpl = xl; + ErtsDigit* tmp = (ErtsDigit*) erts_alloc(ERTS_ALC_T_TMP, + sizeof(ErtsDigit) * xl); + dsize_t tmpl = xl; - MOVE_DIGITS(tmp, dx, xl); + MOVE_DIGITS(tmp, dx, xl); - while(1) { + while(1) { tmpl = D_div(tmp, tmpl, largest_pow_of_base, tmp, &rem); - if (tmpl == 1 && *tmp == 0) { - while(rem) { - (*write_func)(arg, (rem % 10)+'0'); n++; - rem /= 10; - } - break; - } else { + + if (tmpl == 1 && *tmp == 0) { + while(rem) { + int digit = rem % base; + + if (digit < 10) { + (*write_func)(arg, digit + '0'); n++; + } else { + (*write_func)(arg, 'A' + (digit - 10)); n++; + } + + rem /= base; + } + break; + } else { Uint i = digits_per_Sint; - while(i--) { - (*write_func)(arg, (rem % 10)+'0'); n++; - rem /= 10; - } - } - } - erts_free(ERTS_ALC_T_TMP, (void *) tmp); + + while(i--) { + int digit = rem % base; + + if (digit < 10) { + (*write_func)(arg, digit + '0'); n++; + } else { + (*write_func)(arg, 'A' + (digit - 10)); n++; + } + + rem /= base; + } + } + } + erts_free(ERTS_ALC_T_TMP, (void *) tmp); } if (sign) { - (*write_func)(arg, '-'); n++; + (*write_func)(arg, '-'); n++; } + return n; } @@ -1801,12 +1831,12 @@ write_list(void *arg, char c) blp->hp += 2; } -Eterm erts_big_to_list(Eterm x, Eterm **hpp) +Eterm erts_big_to_list(Eterm x, int base, Eterm **hpp) { struct big_list__ bl; bl.hp = *hpp; bl.res = NIL; - write_big(x, write_list, (void *) &bl); + write_big(x, base, write_list, (void *) &bl); *hpp = bl.hp; return bl.res; } @@ -1817,11 +1847,11 @@ write_string(void *arg, char c) *(--(*((char **) arg))) = c; } -char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz) +char *erts_big_to_string(Wterm x, int base, char *buf, Uint buf_sz) { char *big_str = buf + buf_sz - 1; *big_str = '\0'; - write_big(x, write_string, (void *) &big_str); + write_big(x, base, write_string, (void*)&big_str); ASSERT(buf <= big_str && big_str <= buf + buf_sz - 1); return big_str; } @@ -1830,11 +1860,11 @@ char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz) * e.g. 1 bsl 64 -> "18446744073709551616" */ -Uint erts_big_to_binary_bytes(Eterm x, char *buf, Uint buf_sz) +Uint erts_big_to_binary_bytes(Eterm x, int base, char *buf, Uint buf_sz) { char *big_str = buf + buf_sz; Uint n; - n = write_big(x, write_string, (void *) &big_str); + n = write_big(x, base, write_string, (void *) &big_str); ASSERT(buf <= big_str && big_str <= buf + buf_sz); return n; } @@ -2577,9 +2607,6 @@ static const double lg2_lookup[36-1] = { 4.32193, 4.39232, 4.45943, 4.52356, 4.58496, 4.64386, 4.70044, 4.75489, 4.80735, 4.85798, 4.90689, 4.9542, 5.0, 5.04439, 5.08746, 5.12928, 5.16993 }; -static ERTS_INLINE double lookup_log2(Uint base) { - return lg2_lookup[base - 2]; -} /* * How many digits can fit into a signed int (Sint) for given base, we take diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 7556205063..274482a0d2 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -81,7 +81,11 @@ typedef Uint dsize_t; /* Vector size type */ * a Uint64 argument. Therefore, we must test the size of the argument * to ensure that the cast does not discard the high-order 32 bits. */ -#define _IS_SSMALL32(x) (((Uint32) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2) +#if defined(ARCH_32) +# define _IS_SSMALL32(x) (((Uint32) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2) +#else +# define _IS_SSMALL32(x) (1) +#endif #define _IS_SSMALL64(x) (((Uint64) ((((x)) >> (SMALL_BITS-1)) + 1)) < 2) #define IS_SSMALL(x) (sizeof(x) == sizeof(Uint32) ? _IS_SSMALL32(x) : _IS_SSMALL64(x)) @@ -119,10 +123,10 @@ typedef Uint dsize_t; /* Vector size type */ #endif -int big_decimal_estimate(Wterm); -Eterm erts_big_to_list(Eterm, Eterm**); -char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz); -Uint erts_big_to_binary_bytes(Eterm x, char *buf, Uint buf_sz); +int big_integer_estimate(Wterm, Uint base); +Eterm erts_big_to_list(Eterm, int base, Eterm**); +char *erts_big_to_string(Wterm x, int base, char *buf, Uint buf_sz); +Uint erts_big_to_binary_bytes(Eterm x, int base, char *buf, Uint buf_sz); Eterm small_times(Sint, Sint, Eterm*); diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 6a349764b2..a18228b84a 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -322,40 +322,102 @@ BIF_RETTYPE binary_to_integer_2(BIF_ALIST_2) } +static Eterm integer_to_binary(Process *c_p, Eterm num, int base) +{ + Eterm res; + + if (is_small(num)) { + char s[128]; + char *c = s; + Uint digits; + + digits = Sint_to_buf(signed_val(num), base, &c, sizeof(s)); + res = new_binary(c_p, (byte*)c, digits); + } else { + const int DIGITS_PER_RED = 16; + Uint digits, n; + byte *bytes; + + digits = big_integer_estimate(num, base); + + if ((digits / DIGITS_PER_RED) > ERTS_BIF_REDS_LEFT(c_p)) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + + /* This could take a very long time, tell the caller to reschedule + * us to a dirty CPU scheduler if we aren't already on one. */ + if (esdp->type == ERTS_SCHED_NORMAL) { + return THE_NON_VALUE; + } + } else { + BUMP_REDS(c_p, digits / DIGITS_PER_RED); + } + + bytes = (byte*)erts_alloc(ERTS_ALC_T_TMP, sizeof(byte) * digits); + n = erts_big_to_binary_bytes(num, base, (char*)bytes, digits); + res = new_binary(c_p, bytes + digits - n, n); + erts_free(ERTS_ALC_T_TMP, (void*)bytes); + } + + return res; +} + BIF_RETTYPE integer_to_binary_1(BIF_ALIST_1) -{ - Uint size; +{ Eterm res; if (is_not_integer(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(BIF_P, BADARG); } - if (is_small(BIF_ARG_1)) { - char *c; - struct Sint_buf ibuf; + res = integer_to_binary(BIF_P, BIF_ARG_1, 10); + + if (is_non_value(res)) { + Eterm args[1]; + args[0] = BIF_ARG_1; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_binary_1, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_binary, + 1); + } - /* Enhancement: If we can calculate the buffer size exactly - * we could avoid an unnecessary copy of buffers. - * Useful if size determination is faster than a copy. - */ - c = Sint_to_buf(signed_val(BIF_ARG_1), &ibuf); - size = sys_strlen(c); - res = new_binary(BIF_P, (byte *)c, size); - } else { - byte* bytes; - Uint n = 0; + return res; +} - /* Here we also have multiple copies of buffers - * due to new_binary interface - */ - size = big_decimal_estimate(BIF_ARG_1) - 1; /* remove null */ - bytes = (byte*) erts_alloc(ERTS_ALC_T_TMP, sizeof(byte)*size); - n = erts_big_to_binary_bytes(BIF_ARG_1, (char *)bytes, size); - res = new_binary(BIF_P, bytes + size - n, n); - erts_free(ERTS_ALC_T_TMP, (void *) bytes); +BIF_RETTYPE integer_to_binary_2(BIF_ALIST_2) +{ + Eterm res; + SWord base; + + if (is_not_integer(BIF_ARG_1) || is_not_small(BIF_ARG_2)) { + BIF_ERROR(BIF_P, BADARG); + } + + base = signed_val(BIF_ARG_2); + if (base < 2 || base > 36) { + BIF_ERROR(BIF_P, BADARG); } - BIF_RET(res); + + res = integer_to_binary(BIF_P, BIF_ARG_1, base); + + if (is_non_value(res)) { + Eterm args[2]; + args[0] = BIF_ARG_1; + args[1] = BIF_ARG_2; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_binary_2, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_binary, + 2); + } + + return res; } #define ERTS_B2L_BYTES_PER_REDUCTION 256 diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 9ff52c92b8..92009c2345 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -82,7 +82,7 @@ process_info(fmtfn_t to, void *to_arg) * they are most likely just created and has invalid data */ if (!ERTS_PROC_IS_EXITING(p) && p->heap != NULL) - print_process_info(to, to_arg, p); + print_process_info(to, to_arg, p, 0); } } @@ -101,7 +101,7 @@ process_killer(void) rp = erts_pix2proc(i); if (rp && rp->i != ENULL) { int br; - print_process_info(ERTS_PRINT_STDOUT, NULL, rp); + print_process_info(ERTS_PRINT_STDOUT, NULL, rp, 0); erts_printf("(k)ill (n)ext (r)eturn:\n"); while(1) { if ((j = sys_get_key(0)) <= 0) @@ -199,13 +199,14 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext) /* Display info about an individual Erlang process */ void -print_process_info(fmtfn_t to, void *to_arg, Process *p) +print_process_info(fmtfn_t to, void *to_arg, Process *p, ErtsProcLocks orig_locks) { int garbing = 0; int running = 0; Sint len; struct saved_calls *scb; erts_aint32_t state; + ErtsProcLocks locks = orig_locks; /* display the PID */ erts_print(to, to_arg, "=proc:%T\n", p->common.id); @@ -222,6 +223,22 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) | ERTS_PSFLG_DIRTY_RUNNING)) running = 1; + if (!(locks & ERTS_PROC_LOCK_MAIN)) { + locks |= ERTS_PROC_LOCK_MAIN; + if (ERTS_IS_CRASH_DUMPING && running) { + if (erts_proc_trylock(p, locks)) { + /* crash dumping and main lock taken, this probably means that + the process is doing a GC on a dirty-scheduler... so we cannot + do erts_proc_sig_fetch as that would potentially cause a segfault */ + locks = 0; + } + } else { + erts_proc_lock(p, locks); + } + } else { + ERTS_ASSERT(locks == ERTS_PROC_LOCK_MAIN && "Only main lock should be held"); + } + /* * If the process is registered as a global process, display the * registered name @@ -251,13 +268,19 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) erts_print(to, to_arg, "Spawned by: %T\n", p->parent); - erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); - len = erts_proc_sig_fetch(p); - erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ); + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); + len = erts_proc_sig_fetch(p); + erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ); + } else { + len = p->sig_qs.len; + } erts_print(to, to_arg, "Message queue length: %d\n", len); - /* display the message queue only if there is anything in it */ - if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing) { + /* display the message queue only if there is anything in it + and we can do it safely */ + if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing + && (locks & ERTS_PROC_LOCK_MAIN)) { erts_print(to, to_arg, "Message queue: ["); ERTS_FOREACH_SIG_PRIVQS( p, mp, @@ -357,6 +380,8 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) /* Display all states */ erts_print(to, to_arg, "Internal State: "); erts_dump_extended_process_state(to, to_arg, state); + + erts_proc_unlock(p, locks & ~orig_locks); } static void diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 3f0ab33597..917cb1cf10 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -100,8 +100,8 @@ #define AOFF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr) -#define LIST_NEXT(N) (((AOFF_RBTree_t*)(N))->u.next) -#define LIST_PREV(N) (((AOFF_RBTree_t*)(N))->parent) +#define AOFF_LIST_NEXT(N) (((AOFF_RBTree_t*)(N))->u.next) +#define AOFF_LIST_PREV(N) (((AOFF_RBTree_t*)(N))->parent) typedef struct AOFF_Carrier_t_ AOFF_Carrier_t; @@ -152,13 +152,13 @@ static ERTS_INLINE Uint node_max_size(AOFF_RBTree_t *x) static ERTS_INLINE void lower_max_size(AOFF_RBTree_t *node, AOFF_RBTree_t* stop_at) { - AOFF_RBTree_t* x = node; + AOFF_RBTree_t* x = node; Uint old_max = x->max_sz; Uint new_max = node_max_size(x); if (new_max < old_max) { x->max_sz = new_max; - while ((x=x->parent) != stop_at && x->max_sz == old_max) { + while ((x=x->parent) != stop_at && x->max_sz == old_max) { x->max_sz = node_max_size(x); } ASSERT(x == stop_at || x->max_sz > old_max); @@ -352,7 +352,7 @@ left_rotate(AOFF_RBTree_t **root, AOFF_RBTree_t *x) x->parent = y; y->max_sz = x->max_sz; - x->max_sz = node_max_size(x); + x->max_sz = node_max_size(x); ASSERT(y->max_sz >= x->max_sz); } @@ -377,7 +377,7 @@ right_rotate(AOFF_RBTree_t **root, AOFF_RBTree_t *x) y->right = x; x->parent = y; y->max_sz = x->max_sz; - x->max_sz = node_max_size(x); + x->max_sz = node_max_size(x); ASSERT(y->max_sz >= x->max_sz); } @@ -523,23 +523,23 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk) ASSERT(del->flags & IS_BF_FLG); if (IS_LIST_ELEM(del)) { /* Remove from list */ - ASSERT(LIST_PREV(del)); - ASSERT(LIST_PREV(del)->flags & IS_BF_FLG); - LIST_NEXT(LIST_PREV(del)) = LIST_NEXT(del); - if (LIST_NEXT(del)) { - ASSERT(LIST_NEXT(del)->flags & IS_BF_FLG); - LIST_PREV(LIST_NEXT(del)) = LIST_PREV(del); + ASSERT(AOFF_LIST_PREV(del)); + ASSERT(AOFF_LIST_PREV(del)->flags & IS_BF_FLG); + AOFF_LIST_NEXT(AOFF_LIST_PREV(del)) = AOFF_LIST_NEXT(del); + if (AOFF_LIST_NEXT(del)) { + ASSERT(AOFF_LIST_NEXT(del)->flags & IS_BF_FLG); + AOFF_LIST_PREV(AOFF_LIST_NEXT(del)) = AOFF_LIST_PREV(del); } return; } - else if (LIST_NEXT(del)) { + else if (AOFF_LIST_NEXT(del)) { /* Replace tree node by next element in list... */ - - ASSERT(AOFF_BLK_SZ(LIST_NEXT(del)) == AOFF_BLK_SZ(del)); - ASSERT(IS_LIST_ELEM(LIST_NEXT(del))); - - replace(&crr->root, (AOFF_RBTree_t*)del, LIST_NEXT(del)); - + + ASSERT(AOFF_BLK_SZ(AOFF_LIST_NEXT(del)) == AOFF_BLK_SZ(del)); + ASSERT(IS_LIST_ELEM(AOFF_LIST_NEXT(del))); + + replace(&crr->root, (AOFF_RBTree_t*)del, AOFF_LIST_NEXT(del)); + HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0); return; } @@ -550,13 +550,13 @@ aoff_unlink_free_block(Allctr_t *allctr, Block_t *blk) HARD_CHECK_TREE(&crr->crr, alc->blk_order, crr->root, 0); /* Update the carrier tree with a potentially new (lower) max_sz - */ + */ if (crr->root) { if (crr->rbt_node.hdr.bhdr == crr->root->max_sz) { return; } ASSERT(crr->rbt_node.hdr.bhdr > crr->root->max_sz); - crr->rbt_node.hdr.bhdr = crr->root->max_sz; + crr->rbt_node.hdr.bhdr = crr->root->max_sz; } else { crr->rbt_node.hdr.bhdr = 0; @@ -773,7 +773,7 @@ rbt_insert(enum AOFFSortOrder order, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) #ifdef DEBUG blk->flags = (order == FF_BF) ? IS_BF_FLG : 0; #else - blk->flags = 0; + blk->flags = 0; #endif blk->left = NULL; blk->right = NULL; @@ -787,7 +787,7 @@ rbt_insert(enum AOFFSortOrder order, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) else { AOFF_RBTree_t *x = *root; while (1) { - SWord diff; + SWord diff; if (x->max_sz < blk_sz) { x->max_sz = blk_sz; } @@ -810,14 +810,14 @@ rbt_insert(enum AOFFSortOrder order, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) } else { ASSERT(order == FF_BF); - ASSERT(blk->flags & IS_BF_FLG); - ASSERT(x->flags & IS_BF_FLG); + ASSERT(blk->flags & IS_BF_FLG); + ASSERT(x->flags & IS_BF_FLG); SET_LIST_ELEM(blk); - LIST_NEXT(blk) = LIST_NEXT(x); - LIST_PREV(blk) = x; - if (LIST_NEXT(x)) - LIST_PREV(LIST_NEXT(x)) = blk; - LIST_NEXT(x) = blk; + AOFF_LIST_NEXT(blk) = AOFF_LIST_NEXT(x); + AOFF_LIST_PREV(blk) = x; + if (AOFF_LIST_NEXT(x)) + AOFF_LIST_PREV(AOFF_LIST_NEXT(x)) = blk; + AOFF_LIST_NEXT(x) = blk; return; } } @@ -831,7 +831,7 @@ rbt_insert(enum AOFFSortOrder order, AOFF_RBTree_t** root, AOFF_RBTree_t* blk) } if (order == FF_BF) { SET_TREE_NODE(blk); - LIST_NEXT(blk) = NULL; + AOFF_LIST_NEXT(blk) = NULL; } } @@ -878,7 +878,7 @@ aoff_get_free_block(Allctr_t *allctr, Uint size, #ifdef HARD_DEBUG AOFF_RBTree_t* dbg_blk; #endif - + ASSERT(!cand_blk || cand_size >= size); /* Get first-fit carrier @@ -964,7 +964,7 @@ static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier) AOFF_RBTree_t **root = &alc->mbc_root; ASSERT(!IS_CRR_IN_TREE(crr, *root)); - HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0); + HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0); rbt_insert(alc->crr_order, root, &crr->rbt_node); @@ -1105,7 +1105,7 @@ info_options(Allctr_t *allctr, } if (hpp || szp) { - + if (!atoms_initialized) erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized", __FILE__, __LINE__);; @@ -1132,7 +1132,7 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2) switch (op) { case 0x500: return (UWord) ((AOFFAllctr_t *) a1)->blk_order == FF_AOBF; case 0x501: { - AOFF_RBTree_t *node = ((AOFFAllctr_t *) a1)->mbc_root; + AOFF_RBTree_t *node = ((AOFFAllctr_t *) a1)->mbc_root; Uint size = (Uint) a2; node = node ? rbt_search(node, size) : NULL; return (UWord) (node ? RBT_NODE_TO_MBC(node)->root : NULL); @@ -1140,13 +1140,13 @@ erts_aoffalc_test(UWord op, UWord a1, UWord a2) case 0x502: return (UWord) ((AOFF_RBTree_t *) a1)->parent; case 0x503: return (UWord) ((AOFF_RBTree_t *) a1)->left; case 0x504: return (UWord) ((AOFF_RBTree_t *) a1)->right; - case 0x505: return (UWord) LIST_NEXT(a1); + case 0x505: return (UWord) AOFF_LIST_NEXT(a1); case 0x506: return (UWord) IS_BLACK((AOFF_RBTree_t *) a1); case 0x507: return (UWord) IS_TREE_NODE((AOFF_RBTree_t *) a1); case 0x508: return (UWord) 0; /* IS_BF_ALGO */ case 0x509: return (UWord) ((AOFF_RBTree_t *) a1)->max_sz; case 0x50a: return (UWord) ((AOFFAllctr_t *) a1)->blk_order == FF_BF; - case 0x50b: return (UWord) LIST_PREV(a1); + case 0x50b: return (UWord) AOFF_LIST_PREV(a1); default: ASSERT(0); return ~((UWord) 0); } } @@ -1166,7 +1166,7 @@ static int rbt_is_member(AOFF_RBTree_t* root, AOFF_RBTree_t* node) return 0; } node = node->parent; - } + } return 1; } @@ -1279,15 +1279,15 @@ check_tree(Carrier_t* within_crr, enum AOFFSortOrder order, AOFF_RBTree_t* root, } if (order == FF_BF) { AOFF_RBTree_t* y = x; - AOFF_RBTree_t* nxt = LIST_NEXT(y); + AOFF_RBTree_t* nxt = AOFF_LIST_NEXT(y); ASSERT(IS_TREE_NODE(x)); while (nxt) { ASSERT(IS_LIST_ELEM(nxt)); ASSERT(AOFF_BLK_SZ(nxt) == AOFF_BLK_SZ(x)); ASSERT(FBLK_TO_MBC(&nxt->hdr) == within_crr); - ASSERT(LIST_PREV(nxt) == y); + ASSERT(AOFF_LIST_PREV(nxt) == y); y = nxt; - nxt = LIST_NEXT(nxt); + nxt = AOFF_LIST_NEXT(nxt); } } @@ -1301,13 +1301,13 @@ check_tree(Carrier_t* within_crr, enum AOFFSortOrder order, AOFF_RBTree_t* root, if (x->left) { ASSERT(x->left->parent == x); ASSERT(cmp_blocks(order, x->left, x) < 0); - ASSERT(x->left->max_sz <= x->max_sz); + ASSERT(x->left->max_sz <= x->max_sz); } if (x->right) { ASSERT(x->right->parent == x); ASSERT(cmp_blocks(order, x->right, x) > 0); - ASSERT(x->right->max_sz <= x->max_sz); + ASSERT(x->right->max_sz <= x->max_sz); } ASSERT(x->max_sz >= AOFF_BLK_SZ(x)); ASSERT(x->max_sz == AOFF_BLK_SZ(x) @@ -1327,7 +1327,7 @@ check_tree(Carrier_t* within_crr, enum AOFFSortOrder order, AOFF_RBTree_t* root, x = x->parent; --depth; } - ASSERT(depth == 0 || (!root && depth==1)); + ASSERT(depth == 0 || (!root && depth==1)); ASSERT(curr_blacks == 0); ASSERT((1 << (max_depth/2)) <= node_cnt); @@ -1373,4 +1373,3 @@ print_tree(AOFF_RBTree_t* root) #endif /* PRINT_TREE */ #endif /* HARD_DEBUG */ - diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index 9cb1199c2a..3f981ca2bc 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -122,8 +122,8 @@ typedef struct { RBTree_t *next; } RBTreeList_t; -#define LIST_NEXT(N) (((RBTreeList_t *) (N))->next) -#define LIST_PREV(N) (((RBTreeList_t *) (N))->t.parent) +#define BF_LIST_NEXT(N) (((RBTreeList_t *) (N))->next) +#define BF_LIST_PREV(N) (((RBTreeList_t *) (N))->t.parent) #ifdef DEBUG @@ -593,7 +593,6 @@ aobf_link_free_block(Allctr_t *allctr, Block_t *block) RBTree_t *blk = (RBTree_t *) block; Uint blk_sz = BF_BLK_SZ(blk); - blk->flags = 0; blk->left = NULL; @@ -673,7 +672,7 @@ aobf_get_free_block(Allctr_t *allctr, Uint size, x = x->left; } } - + if (!blk) return NULL; @@ -729,11 +728,11 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block) if (blk_sz == size) { SET_LIST_ELEM(blk); - LIST_NEXT(blk) = LIST_NEXT(x); - LIST_PREV(blk) = x; - if (LIST_NEXT(x)) - LIST_PREV(LIST_NEXT(x)) = blk; - LIST_NEXT(x) = blk; + BF_LIST_NEXT(blk) = BF_LIST_NEXT(x); + BF_LIST_PREV(blk) = x; + if (BF_LIST_NEXT(x)) + BF_LIST_PREV(BF_LIST_NEXT(x)) = blk; + BF_LIST_NEXT(x) = blk; return; /* Finnished */ } @@ -764,7 +763,7 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block) } SET_TREE_NODE(blk); - LIST_NEXT(blk) = NULL; + BF_LIST_NEXT(blk) = NULL; #ifdef HARD_DEBUG check_tree(root, 0, 0); @@ -780,22 +779,22 @@ bf_unlink_free_block(Allctr_t *allctr, Block_t *block) if (IS_LIST_ELEM(x)) { /* Remove from list */ - ASSERT(LIST_PREV(x)); - LIST_NEXT(LIST_PREV(x)) = LIST_NEXT(x); - if (LIST_NEXT(x)) - LIST_PREV(LIST_NEXT(x)) = LIST_PREV(x); + ASSERT(BF_LIST_PREV(x)); + BF_LIST_NEXT(BF_LIST_PREV(x)) = BF_LIST_NEXT(x); + if (BF_LIST_NEXT(x)) + BF_LIST_PREV(BF_LIST_NEXT(x)) = BF_LIST_PREV(x); } - else if (LIST_NEXT(x)) { + else if (BF_LIST_NEXT(x)) { /* Replace tree node by next element in list... */ - ASSERT(BF_BLK_SZ(LIST_NEXT(x)) == BF_BLK_SZ(x)); + ASSERT(BF_BLK_SZ(BF_LIST_NEXT(x)) == BF_BLK_SZ(x)); ASSERT(IS_TREE_NODE(x)); - ASSERT(IS_LIST_ELEM(LIST_NEXT(x))); + ASSERT(IS_LIST_ELEM(BF_LIST_NEXT(x))); #ifdef HARD_DEBUG check_tree(root, 0, 0); #endif - replace(root, x, LIST_NEXT(x)); + replace(root, x, BF_LIST_NEXT(x)); #ifdef HARD_DEBUG check_tree(bfallctr, 0); @@ -834,7 +833,7 @@ bf_get_free_block(Allctr_t *allctr, Uint size, x = x->left; } } - + if (!blk) return NULL; @@ -853,7 +852,7 @@ bf_get_free_block(Allctr_t *allctr, Uint size, /* Use next block if it exist in order to avoid replacing the tree node */ - blk = LIST_NEXT(blk) ? LIST_NEXT(blk) : blk; + blk = BF_LIST_NEXT(blk) ? BF_LIST_NEXT(blk) : blk; bf_unlink_free_block(allctr, (Block_t *) blk); return (Block_t *) blk; @@ -938,7 +937,7 @@ info_options(Allctr_t *allctr, } if (hpp || szp) { - + if (!atoms_initialized) erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized", __FILE__, __LINE__);; @@ -969,12 +968,12 @@ erts_bfalc_test(UWord op, UWord a1, UWord a2) case 0x202: return (UWord) ((RBTree_t *) a1)->parent; case 0x203: return (UWord) ((RBTree_t *) a1)->left; case 0x204: return (UWord) ((RBTree_t *) a1)->right; - case 0x205: return (UWord) LIST_NEXT(a1); + case 0x205: return (UWord) BF_LIST_NEXT(a1); case 0x206: return (UWord) IS_BLACK((RBTree_t *) a1); case 0x207: return (UWord) IS_TREE_NODE((RBTree_t *) a1); case 0x208: return (UWord) 1; /* IS_BF_ALGO */ case 0x20a: return (UWord) !((BFAllctr_t *) a1)->address_order; /* IS_BF */ - case 0x20b: return (UWord) LIST_PREV(a1); + case 0x20b: return (UWord) BF_LIST_PREV(a1); default: ASSERT(0); return ~((UWord) 0); } } diff --git a/erts/emulator/beam/erl_bif_atomics.c b/erts/emulator/beam/erl_bif_atomics.c index 092dbb3bd3..029831bd95 100644 --- a/erts/emulator/beam/erl_bif_atomics.c +++ b/erts/emulator/beam/erl_bif_atomics.c @@ -133,7 +133,7 @@ static ERTS_INLINE Eterm bld_atomic(Process* proc, AtomicsRef* p, if ((Uint64)val <= MAX_SMALL) return make_small((Sint) val); else { - Uint hsz = ERTS_UINT64_HEAP_SIZE(val); + Uint hsz = ERTS_UINT64_HEAP_SIZE((Uint64)val); Eterm* hp = HAlloc(proc, hsz); return erts_uint64_to_big(val, &hp); } diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index a2610bf2e1..ae1bf6e652 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -2762,7 +2762,7 @@ static BIF_RETTYPE do_encode_unsigned(Process *p, Eterm uns, Eterm endianess) dsize_t num_parts = BIG_SIZE(bigp); Eterm res; byte *b; - ErtsDigit d; + ErtsDigit d = 0; if(BIG_SIGN(bigp)) { goto badarg; @@ -2778,26 +2778,22 @@ static BIF_RETTYPE do_encode_unsigned(Process *p, Eterm uns, Eterm endianess) if (endianess == am_big) { Sint i,j; j = 0; - d = BIG_DIGIT(bigp,0); for (i=n-1;i>=0;--i) { - b[i] = d & 0xFF; - if (!((++j) % sizeof(ErtsDigit))) { + if (!((j++) % sizeof(ErtsDigit))) { d = BIG_DIGIT(bigp,j / sizeof(ErtsDigit)); - } else { - d >>= 8; } + b[i] = d & 0xFF; + d >>= 8; } } else { Sint i,j; j = 0; - d = BIG_DIGIT(bigp,0); for (i=0;i<n;++i) { - b[i] = d & 0xFF; - if (!((++j) % sizeof(ErtsDigit))) { + if (!((j++) % sizeof(ErtsDigit))) { d = BIG_DIGIT(bigp,j / sizeof(ErtsDigit)); - } else { - d >>= 8; } + b[i] = d & 0xFF; + d >>= 8; } } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 7fada0d548..f6a98c85fd 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2967,7 +2967,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (ERTS_IS_ATOM_STR("context_reductions", BIF_ARG_1)) { BIF_RET(make_small(CONTEXT_REDS)); } else if (ERTS_IS_ATOM_STR("kernel_poll", BIF_ARG_1)) { -#ifdef ERTS_ENABLE_KERNEL_POLL +#if ERTS_ENABLE_KERNEL_POLL BIF_RET(am_true); #else BIF_RET(am_false); @@ -3139,6 +3139,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) DECL_AM(tag); BIF_RET(AM_tag); #endif + } else if (ERTS_IS_ATOM_STR("system_logger", BIF_ARG_1)) { + BIF_RET(erts_get_system_logger()); } BIF_ERROR(BIF_P, BADARG); @@ -4608,6 +4610,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } } else if (ERTS_IS_ATOM_STR("broken_halt", BIF_ARG_1)) { + erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); broken_halt_test(BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c index 9dca768a18..5a78a043ce 100644 --- a/erts/emulator/beam/erl_bif_persistent.c +++ b/erts/emulator/beam/erl_bif_persistent.c @@ -332,6 +332,23 @@ BIF_RETTYPE persistent_term_get_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2) +{ + Eterm key = BIF_ARG_1; + Eterm result = BIF_ARG_2; + HashTable* hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); + Uint entry_index; + Eterm term; + + entry_index = lookup(hash_table, key); + term = hash_table->term[entry_index]; + if (is_boxed(term)) { + ASSERT(is_tuple_arity(term, 2)); + result = tuple_val(term)[2]; + } + BIF_RET(result); +} + BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) { Eterm key = BIF_ARG_1; diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 7fe4e02782..ed825d3dda 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -891,10 +891,6 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) driver = &spawn_driver; } else if (*tp == am_fd) { /* An fd port */ - int n; - struct Sint_buf sbuf; - char* p; - if (arity != make_arityval(3)) { goto badarg; } @@ -904,15 +900,9 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) opts.ifd = unsigned_val(tp[1]); opts.ofd = unsigned_val(tp[2]); - /* Syntesize name from input and output descriptor. */ - name_buf = erts_alloc(ERTS_ALC_T_TMP, - 2*sizeof(struct Sint_buf) + 2); - p = Sint_to_buf(opts.ifd, &sbuf); - n = sys_strlen(p); - sys_strncpy(name_buf, p, n); - name_buf[n] = '/'; - p = Sint_to_buf(opts.ofd, &sbuf); - sys_strcpy(name_buf+n+1, p); + /* Syntesize name from input and output descriptor. */ + name_buf = erts_alloc(ERTS_ALC_T_TMP, 256); + erts_snprintf(name_buf, 256, "%i/%i", opts.ifd, opts.ofd); driver = &fd_driver; } else { diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index f1d47326b4..e2c029c244 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2470,7 +2470,7 @@ restart: case matchProcessDump: { erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0); ASSERT(c_p == self); - print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p); + print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p, ERTS_PROC_LOCK_MAIN); *esp++ = new_binary(build_proc, (byte *)dsbufp->str, dsbufp->str_len); erts_destroy_tmp_dsbuf(dsbufp); diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index d5dfb096b1..3a50b294d1 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2438,27 +2438,9 @@ erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, cpy_words: ASSERT(sz >= cpy_sz); sz -= cpy_sz; - while (cpy_sz >= 8) { - cpy_sz -= 8; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - } - switch (cpy_sz) { - case 7: *hp++ = *fhp++; - case 6: *hp++ = *fhp++; - case 5: *hp++ = *fhp++; - case 4: *hp++ = *fhp++; - case 3: *hp++ = *fhp++; - case 2: *hp++ = *fhp++; - case 1: *hp++ = *fhp++; - default: break; - } + sys_memcpy(hp, fhp, cpy_sz * sizeof(Eterm)); + hp += cpy_sz; + fhp += cpy_sz; if (oh) { /* Add to offheap list */ oh->next = off_heap->first; @@ -2477,7 +2459,7 @@ erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, *hpp = hp; for (i = 0; i < nrefs; i++) { - if (is_not_immed(refs[i]) && !erts_is_literal(refs[i],boxed_val(refs[i]))) + if (is_not_immed(refs[i]) && !erts_is_literal(refs[i],ptr_val(refs[i]))) refs[i] = offset_ptr(refs[i], offs); } bp->off_heap.first = NULL; diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index ef7a55fa38..75ad6de2c9 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -29,8 +29,6 @@ # include "config.h" #endif -/* #define ERTS_MAGIC_REF_BIF_TIMERS */ - #include "sys.h" #include "global.h" #include "bif.h" @@ -39,9 +37,6 @@ #include "erl_time.h" #include "erl_hl_timer.h" #include "erl_proc_sig_queue.h" -#ifdef ERTS_MAGIC_REF_BIF_TIMERS -#include "erl_binary.h" -#endif #define ERTS_TMR_CHECK_CANCEL_ON_CREATE 0 @@ -195,14 +190,9 @@ struct ErtsBifTimer_ { } type; struct { erts_atomic32_t state; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsMagicBinary *mbin; - ErtsHLTimerList proc_list; -#else Uint32 refn[ERTS_REF_NUMBERS]; ErtsBifTimerTree proc_tree; ErtsBifTimerTree tree; -#endif Eterm message; ErlHeapFragment *bp; } btm; @@ -220,11 +210,7 @@ typedef ErtsTimer *(*ErtsCreateTimerFunc)(ErtsSchedulerData *esdp, int short_time, ErtsTmrType type, void *rcvrp, Eterm rcvr, Eterm msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsMagicBinary *mbin, -#else Uint32 *refn, -#endif void (*callback)(void *), void *arg); #ifdef SMALL_MEMORY @@ -303,16 +289,12 @@ typedef struct { struct ErtsHLTimerService_ { ErtsHLTCncldTmrQ canceled_queue; ErtsHLTimer *time_tree; -#ifndef ERTS_MAGIC_REF_BIF_TIMERS ErtsBifTimer *btm_tree; -#endif ErtsHLTimer *next_timeout; ErtsYieldingTimeoutState yield; ErtsTWheelTimer service_timer; }; -#ifndef ERTS_MAGIC_REF_BIF_TIMERS - static ERTS_INLINE int refn_is_lt(Uint32 *x, Uint32 *y) { @@ -334,8 +316,6 @@ refn_is_eq(Uint32 *x, Uint32 *y) return (x[0] == y[0]) & (x[1] == y[1]) & (x[2] == y[2]); } -#endif - #define ERTS_RBT_PREFIX time #define ERTS_RBT_T ErtsHLTimer #define ERTS_RBT_KEY_T ErtsMonotonicTime @@ -525,13 +505,7 @@ same_time_list_lookup(ErtsHLTimer *root, ErtsHLTimer *x) #endif /* ERTS_HLT_HARD_DEBUG */ -#ifdef ERTS_MAGIC_REF_BIF_TIMERS -#define ERTS_BTM_HLT2REFN(T) ((T)->btm.mbin->refn) -#else #define ERTS_BTM_HLT2REFN(T) ((T)->btm.refn) -#endif - -#ifndef ERTS_MAGIC_REF_BIF_TIMERS #define ERTS_RBT_PREFIX btm #define ERTS_RBT_T ErtsBifTimer @@ -576,87 +550,12 @@ same_time_list_lookup(ErtsHLTimer *root, ErtsHLTimer *x) #define ERTS_RBT_IS_EQ(KX, KY) refn_is_eq((KX), (KY)) #define ERTS_RBT_WANT_DELETE #define ERTS_RBT_WANT_INSERT -#ifndef ERTS_MAGIC_REF_BIF_TIMERS #define ERTS_RBT_WANT_LOOKUP -#endif #define ERTS_RBT_WANT_FOREACH #define ERTS_RBT_UNDEF #include "erl_rbtree.h" -#endif /* !ERTS_MAGIC_REF_BIF_TIMERS */ - -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - -static ERTS_INLINE void -proc_btm_list_insert(ErtsBifTimer **list, ErtsBifTimer *x) -{ - ErtsBifTimer *y = *list; - if (!y) { - x->btm.proc_list.next = x; - x->btm.proc_list.prev = x; - *list = x; - } - else { - ERTS_HLT_ASSERT(y->btm.proc_list.prev->btm.proc_list.next == y); - x->btm.proc_list.next = y; - x->btm.proc_list.prev = y->btm.proc_list.prev; - y->btm.proc_list.prev->btm.proc_list.next = x; - y->btm.proc_list.prev = x; - } -} - -static ERTS_INLINE void -proc_btm_list_delete(ErtsBifTimer **list, ErtsBifTimer *x) -{ - ErtsBifTimer *y = *list; - if (y == x && x->btm.proc_list.next == x) { - ERTS_HLT_ASSERT(x->btm.proc_list.prev == x); - *list = NULL; - } - else { - if (y == x) - *list = x->btm.proc_list.next; - ERTS_HLT_ASSERT(x->btm.proc_list.prev->btm.proc_list.next == x); - ERTS_HLT_ASSERT(x->btm.proc_list.next->btm.proc_list.prev == x); - x->btm.proc_list.prev->btm.proc_list.next = x->btm.proc_list.next; - x->btm.proc_list.next->btm.proc_list.prev = x->btm.proc_list.prev; - } - x->btm.proc_list.next = NULL; -} - -static ERTS_INLINE int -proc_btm_list_foreach_destroy_yielding(ErtsBifTimer **list, - void (*destroy)(ErtsBifTimer *, void *), - void *arg, - int limit) -{ - int i; - ErtsBifTimer *first, *last; - - first = *list; - if (!first) - return 0; - - last = first->btm.proc_list.prev; - for (i = 0; i < limit; i++) { - ErtsBifTimer *x = last; - last = last->btm.proc_list.prev; - (*destroy)(x, arg); - x->btm.proc_list.next = NULL; - if (x == first) { - *list = NULL; - return 0; - } - } - - last->btm.proc_list.next = first; - first->btm.proc_list.prev = last; - return 1; -} - -#else /* !ERTS_MAGIC_REF_BIF_TIMERS */ - #define ERTS_RBT_PREFIX proc_btm #define ERTS_RBT_T ErtsBifTimer #define ERTS_RBT_KEY_T Uint32 * @@ -700,16 +599,12 @@ proc_btm_list_foreach_destroy_yielding(ErtsBifTimer **list, #define ERTS_RBT_IS_EQ(KX, KY) refn_is_eq((KX), (KY)) #define ERTS_RBT_WANT_DELETE #define ERTS_RBT_WANT_INSERT -#ifndef ERTS_MAGIC_REF_BIF_TIMERS #define ERTS_RBT_WANT_LOOKUP -#endif #define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING #define ERTS_RBT_UNDEF #include "erl_rbtree.h" -#endif /* !ERTS_MAGIC_REF_BIF_TIMERS */ - static void init_canceled_queue(ErtsHLTCncldTmrQ *cq); void @@ -728,9 +623,7 @@ erts_create_timer_service(void) srv = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_TIMER_SERVICE, sizeof(ErtsHLTimerService)); srv->time_tree = NULL; -#ifndef ERTS_MAGIC_REF_BIF_TIMERS srv->btm_tree = NULL; -#endif srv->next_timeout = NULL; srv->yield = init_yield; erts_twheel_init_timer(&srv->service_timer); @@ -805,40 +698,10 @@ port_timeout_common(Port *port, void *tmr) return 0; } -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - -static erts_atomic_t * -mbin_to_btmref__(ErtsMagicBinary *mbin) -{ - return erts_binary_to_magic_indirection((Binary *) mbin); -} - -static ERTS_INLINE void -magic_binary_init(ErtsMagicBinary *mbin, ErtsBifTimer *tmr) -{ - erts_atomic_t *aptr = mbin_to_btmref__(mbin); - erts_atomic_init_nob(aptr, (erts_aint_t) tmr); -} - -static ERTS_INLINE ErtsBifTimer * -magic_binary_to_btm(ErtsMagicBinary *mbin) -{ - erts_atomic_t *aptr = mbin_to_btmref__(mbin); - ErtsBifTimer *tmr = (ErtsBifTimer *) erts_atomic_read_nob(aptr); - ERTS_HLT_ASSERT(!tmr || tmr->btm.mbin == mbin); - return tmr; -} - -#endif /* ERTS_MAGIC_REF_BIF_TIMERS */ - static ERTS_INLINE erts_aint_t init_btm_specifics(ErtsSchedulerData *esdp, ErtsBifTimer *tmr, Eterm msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsMagicBinary *mbin -#else Uint32 *refn -#endif ) { Uint hsz = is_immed(msg) ? ((Uint) 0) : size_object(msg); @@ -853,13 +716,6 @@ init_btm_specifics(ErtsSchedulerData *esdp, tmr->btm.message = copy_struct(msg, hsz, &hp, &bp->off_heap); tmr->btm.bp = bp; } -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - refc = 1; - tmr->btm.mbin = mbin; - erts_refc_inc(&mbin->refc, 1); - magic_binary_init(mbin, tmr); - tmr->btm.proc_list.next = NULL; -#else refc = 0; tmr->btm.refn[0] = refn[0]; tmr->btm.refn[1] = refn[1]; @@ -868,7 +724,6 @@ init_btm_specifics(ErtsSchedulerData *esdp, tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; btm_rbt_insert(&esdp->timer_service->btm_tree, tmr); -#endif erts_atomic32_init_nob(&tmr->btm.state, ERTS_TMR_STATE_ACTIVE); return refc; /* refc from magic binary... */ @@ -886,11 +741,6 @@ timer_destroy(ErtsTimer *tmr, int twt, int btm) erts_free(ERTS_ALC_T_HL_PTIMER, tmr); } else { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - Binary *bp = (Binary *) tmr->btm.btm.mbin; - if (erts_refc_dectest(&bp->refc, 0) == 0) - erts_bin_free(bp); -#endif if (tmr->head.roflgs & ERTS_TMR_ROFLG_PRE_ALC) bif_timer_pre_free(&tmr->btm); else @@ -940,9 +790,6 @@ schedule_tw_timer_destroy(ErtsTWTimer *tmr) else { /* Message buffer already dropped... */ size = sizeof(ErtsBifTimer); -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - size += sizeof(ErtsMagicIndirectionWord); -#endif } erts_schedule_thr_prgr_later_cleanup_op( @@ -1006,11 +853,7 @@ create_tw_timer(ErtsSchedulerData *esdp, int short_time, ErtsTmrType type, void *rcvrp, Eterm rcvr, Eterm msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsMagicBinary *mbin, -#else Uint32 *refn, -#endif void (*callback)(void *), void *arg) { ErtsTWTimer *tmr; @@ -1087,11 +930,7 @@ create_tw_timer(ErtsSchedulerData *esdp, refc += init_btm_specifics(esdp, (ErtsBifTimer *) tmr, msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - mbin -#else refn -#endif ); break; @@ -1152,9 +991,6 @@ schedule_hl_timer_destroy(ErtsHLTimer *tmr, Uint32 roflgs) else { /* Message buffer already dropped... */ size = sizeof(ErtsBifTimer); -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - size += sizeof(ErtsMagicIndirectionWord); -#endif } erts_schedule_thr_prgr_later_cleanup_op( @@ -1192,34 +1028,6 @@ check_canceled_queue(ErtsSchedulerData *esdp, ErtsHLTimerService *srv) #endif } -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - -static int -bif_timer_ref_destructor(Binary *unused) -{ - return 1; -} - -static ERTS_INLINE void -btm_clear_magic_binary(ErtsBifTimer *tmr) -{ - erts_atomic_t *aptr = mbin_to_btmref__(tmr->btm.mbin); - Uint32 roflgs = tmr->type.head.roflgs; -#ifdef ERTS_HLT_DEBUG - erts_aint_t tval = erts_atomic_xchg_nob(aptr, - (erts_aint_t) NULL); - ERTS_HLT_ASSERT(tval == (erts_aint_t) tmr); -#else - erts_atomic_set_nob(aptr, (erts_aint_t) NULL); -#endif - if (roflgs & ERTS_TMR_ROFLG_HLT) - hl_timer_dec_refc(&tmr->type.hlt, roflgs); - else - tw_timer_dec_refc(&tmr->type.twt); -} - -#endif /* ERTS_MAGIC_REF_BIF_TIMERS */ - static ERTS_INLINE void bif_timer_timeout(ErtsHLTimerService *srv, ErtsBifTimer *tmr, @@ -1240,10 +1048,6 @@ bif_timer_timeout(ErtsHLTimerService *srv, if (state == ERTS_TMR_STATE_ACTIVE) { Process *proc; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - btm_clear_magic_binary(tmr); -#endif - if (roflgs & ERTS_TMR_ROFLG_REG_NAME) { Eterm term; term = tmr->type.head.receiver.name; @@ -1266,18 +1070,11 @@ bif_timer_timeout(ErtsHLTimerService *srv, erts_proc_lock(proc, ERTS_PROC_LOCK_BTM); /* If the process is exiting do not disturb the cleanup... */ if (!ERTS_PROC_IS_EXITING(proc)) { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - if (tmr->btm.proc_list.next) { - proc_btm_list_delete(&proc->bif_timers, tmr); - dec_refc = 1; - } -#else if (tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { proc_btm_rbt_delete(&proc->bif_timers, tmr); tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; dec_refc = 1; } -#endif } erts_proc_unlock(proc, ERTS_PROC_LOCK_BTM); if (dec_refc) @@ -1287,25 +1084,18 @@ bif_timer_timeout(ErtsHLTimerService *srv, free_message_buffer(tmr->btm.bp); } -#ifndef ERTS_MAGIC_REF_BIF_TIMERS if (tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { btm_rbt_delete(&srv->btm_tree, tmr); tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; } -#endif - } static void tw_bif_timer_timeout(void *vbtmp) { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsHLTimerService *srv = NULL; -#else ErtsSchedulerData *esdp = erts_get_scheduler_data(); ErtsHLTimerService *srv = esdp->timer_service; -#endif ErtsBifTimer *btmp = (ErtsBifTimer *) vbtmp; bif_timer_timeout(srv, btmp, btmp->type.head.roflgs); tw_timer_dec_refc(&btmp->type.twt); @@ -1317,11 +1107,7 @@ create_hl_timer(ErtsSchedulerData *esdp, int short_time, ErtsTmrType type, void *rcvrp, Eterm rcvr, Eterm msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsMagicBinary *mbin, -#else Uint32 *refn, -#endif void (*callback)(void *), void *arg) { ErtsHLTimerService *srv = esdp->timer_service; @@ -1407,11 +1193,7 @@ create_hl_timer(ErtsSchedulerData *esdp, refc += init_btm_specifics(esdp, (ErtsBifTimer *) tmr, msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - mbin -#else refn -#endif ); } @@ -1628,7 +1410,6 @@ cleanup_sched_local_canceled_timer(ErtsSchedulerData *esdp, ERTS_HLT_ASSERT((tmr->head.roflgs & ERTS_TMR_ROFLG_SID_MASK) == (Uint32) esdp->no); -#ifndef ERTS_MAGIC_REF_BIF_TIMERS if (roflgs & ERTS_TMR_ROFLG_BIF_TMR) { ErtsBifTimer *btm = (ErtsBifTimer *) tmr; if (btm->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { @@ -1636,7 +1417,6 @@ cleanup_sched_local_canceled_timer(ErtsSchedulerData *esdp, btm->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; } } -#endif if (roflgs & ERTS_TMR_ROFLG_HLT) { hlt_delete_timer(esdp, &tmr->hlt); @@ -1909,9 +1689,6 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos, Eterm ref, tmo_msg, *hp; ErtsBifTimer *tmr; ErtsSchedulerData *esdp; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - Binary *mbin; -#endif Eterm tmp_hp[4]; ErtsCreateTimerFunc create_timer; @@ -1920,18 +1697,10 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos, esdp = erts_proc_sched_data(c_p); -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - mbin = erts_create_magic_indirection(bif_timer_ref_destructor); - hp = HAlloc(c_p, ERTS_MAGIC_REF_THING_SIZE); - ref = erts_mk_magic_ref(&hp, &c_p->off_heap, mbin); - ASSERT(erts_get_ref_numbers_thr_id(((ErtsMagicBinary *)mbin)->refn) - == (Uint32) esdp->no); -#else hp = HAlloc(c_p, ERTS_REF_THING_SIZE); ref = erts_sched_make_ref_in_buffer(esdp, hp); ASSERT(erts_get_ref_numbers_thr_id(internal_ordinary_ref_numbers(ref)) == (Uint32) esdp->no); -#endif tmo_msg = wrap ? TUPLE3(tmp_hp, am_timeout, ref, msg) : msg; @@ -1939,11 +1708,7 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos, tmr = (ErtsBifTimer *) create_timer(esdp, timeout_pos, short_time, ERTS_TMR_BIF, NULL, rcvr, tmo_msg, -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - (ErtsMagicBinary *) mbin, -#else internal_ordinary_ref_numbers(ref), -#endif NULL, NULL); if (is_internal_pid(rcvr)) { @@ -1951,14 +1716,10 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos, rcvr, ERTS_PROC_LOCK_BTM, ERTS_P2P_FLG_INC_REFC); if (!proc) { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - btm_clear_magic_binary(tmr); -#else if (tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { btm_rbt_delete(&esdp->timer_service->btm_tree, tmr); tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; } -#endif if (tmr->btm.bp) free_message_buffer(tmr->btm.bp); if (twheel) @@ -1968,11 +1729,7 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos, timer_destroy((ErtsTimer *) tmr, twheel, 1); } else { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - proc_btm_list_insert(&proc->bif_timers, tmr); -#else proc_btm_rbt_insert(&proc->bif_timers, tmr); -#endif erts_proc_unlock(proc, ERTS_PROC_LOCK_BTM); tmr->type.head.receiver.proc = proc; } @@ -2000,10 +1757,6 @@ cancel_bif_timer(ErtsBifTimer *tmr) if (state != ERTS_TMR_STATE_ACTIVE) return 0; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - btm_clear_magic_binary(tmr); -#endif - if (tmr->btm.bp) free_message_buffer(tmr->btm.bp); @@ -2022,19 +1775,12 @@ cancel_bif_timer(ErtsBifTimer *tmr) * the btm tree by itself (it may be in * the middle of tree destruction). */ -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - if (!ERTS_PROC_IS_EXITING(proc) && tmr->btm.proc_list.next) { - proc_btm_list_delete(&proc->bif_timers, tmr); - res = 1; - } -#else if (!ERTS_PROC_IS_EXITING(proc) && tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { proc_btm_rbt_delete(&proc->bif_timers, tmr); tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; res = 1; } -#endif erts_proc_unlock(proc, ERTS_PROC_LOCK_BTM); } @@ -2075,12 +1821,10 @@ access_btm(ErtsBifTimer *tmr, Uint32 sid, ErtsSchedulerData *esdp, int cancel) queue_canceled_timer(esdp, sid, (ErtsTimer *) tmr); } else { -#ifndef ERTS_MAGIC_REF_BIF_TIMERS if (tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { btm_rbt_delete(&esdp->timer_service->btm_tree, tmr); tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; } -#endif if (is_hlt) { if (cncl_res > 0) hl_timer_dec_refc(&tmr->type.hlt, tmr->type.hlt.head.roflgs); @@ -2157,52 +1901,6 @@ send_async_info(Process *proc, ErtsProcLocks initial_locks, return am_ok; } -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - -static BIF_RETTYPE -access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info) -{ - BIF_RETTYPE ret; - Eterm res; - Sint64 time_left; - - if (!is_internal_magic_ref(tref)) { - if (is_not_ref(tref)) { - ERTS_BIF_PREP_ERROR(ret, c_p, BADARG); - return ret; - } - time_left = -1; - } - else { - ErtsMagicBinary *mbin; - mbin = (ErtsMagicBinary *) erts_magic_ref2bin(tref); - if (mbin->destructor != bif_timer_ref_destructor) - time_left = -1; - else { - ErtsBifTimer *tmr; - Uint32 sid; - tmr = magic_binary_to_btm(mbin); - sid = erts_get_ref_numbers_thr_id(internal_magic_ref_numbers(tref)); - ASSERT(1 <= sid && sid <= erts_no_schedulers); - time_left = access_btm(tmr, sid, erts_proc_sched_data(c_p), cancel); - } - } - - if (!info) - res = am_ok; - else if (!async) - res = return_info(c_p, time_left); - else - res = send_async_info(c_p, ERTS_PROC_LOCK_MAIN, - tref, cancel, time_left); - - ERTS_BIF_PREP_RET(ret, res); - - return ret; -} - -#else /* !ERTS_MAGIC_REF_BIF_TIMERS */ - static ERTS_INLINE Eterm send_sync_info(Process *proc, ErtsProcLocks initial_locks, Uint32 *refn, int cancel, Sint64 time_left) @@ -2505,8 +2203,6 @@ no_timer: return no_timer_result(c_p, tref, cancel, async, info); } -#endif /* !ERTS_MAGIC_REF_BIF_TIMERS */ - static ERTS_INLINE int bool_arg(Eterm val, int *argp) { @@ -2584,18 +2280,11 @@ exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp) is_hlt = !!(roflgs & ERTS_TMR_ROFLG_HLT); ERTS_HLT_ASSERT(sid == erts_get_ref_numbers_thr_id(ERTS_BTM_HLT2REFN(tmr))); -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ERTS_HLT_ASSERT(tmr->btm.proc_list.next); -#else ERTS_HLT_ASSERT(tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE); tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; -#endif if (state == ERTS_TMR_STATE_ACTIVE) { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - btm_clear_magic_binary(tmr); -#endif if (tmr->btm.bp) free_message_buffer(tmr->btm.bp); @@ -2604,12 +2293,10 @@ exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp) return; } -#ifndef ERTS_MAGIC_REF_BIF_TIMERS if (tmr->btm.tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) { btm_rbt_delete(&esdp->timer_service->btm_tree, tmr); tmr->btm.tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE; } -#endif if (is_hlt) hlt_delete_timer(esdp, &tmr->type.hlt); else @@ -2627,28 +2314,17 @@ exit_cancel_bif_timer(ErtsBifTimer *tmr, void *vesdp) # define ERTS_BTM_MAX_DESTROY_LIMIT 50 #endif -#ifndef ERTS_MAGIC_REF_BIF_TIMERS typedef struct { ErtsBifTimers *bif_timers; union { proc_btm_rbt_yield_state_t proc_btm_yield_state; } u; } ErtsBifTimerYieldState; -#endif int erts_cancel_bif_timers(Process *p, ErtsBifTimers **btm, void **vyspp) { ErtsSchedulerData *esdp = erts_proc_sched_data(p); -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - - return proc_btm_list_foreach_destroy_yielding(btm, - exit_cancel_bif_timer, - (void *) esdp, - ERTS_BTM_MAX_DESTROY_LIMIT); - -#else /* !ERTS_MAGIC_REF_BIF_TIMERS */ - ErtsBifTimerYieldState ys = {*btm, {ERTS_RBT_YIELD_STAT_INITER}}; ErtsBifTimerYieldState *ysp; int res; @@ -2682,7 +2358,6 @@ int erts_cancel_bif_timers(Process *p, ErtsBifTimers **btm, void **vyspp) return res; -#endif /* !ERTS_MAGIC_REF_BIF_TIMERS */ } static ERTS_INLINE int @@ -3116,11 +2791,6 @@ btm_print(ErtsBifTimer *tmr, void *vbtmp, ErtsMonotonicTime tpos, int is_hlt) ErtsMonotonicTime left; Eterm receiver; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - if (!(tmr->type.head.roflgs & ERTS_TMR_ROFLG_BIF_TMR)) - return; -#endif - if (is_hlt) { ERTS_HLT_ASSERT(tmr->type.head.roflgs & ERTS_TMR_ROFLG_HLT); if (tmr->type.hlt.timeout <= btmp->now) @@ -3149,22 +2819,6 @@ btm_print(ErtsBifTimer *tmr, void *vbtmp, ErtsMonotonicTime tpos, int is_hlt) (Sint64) left); } -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - -static void -hlt_btm_print(ErtsHLTimer *tmr, void *vbtmp) -{ - btm_print((ErtsBifTimer *) tmr, vbtmp, 0, 1); -} - -static void -twt_btm_print(void *vbtmp, ErtsMonotonicTime tpos, void *vtwtp) -{ - btm_print((ErtsBifTimer *) vtwtp, vbtmp, tpos, 0); -} - -#else - static void btm_tree_print(ErtsBifTimer *tmr, void *vbtmp) { @@ -3177,8 +2831,6 @@ btm_tree_print(ErtsBifTimer *tmr, void *vbtmp) btm_print(tmr, vbtmp, tpos, is_hlt); } -#endif - void erts_print_bif_timer_info(fmtfn_t to, void *to_arg) { @@ -3196,15 +2848,7 @@ erts_print_bif_timer_info(fmtfn_t to, void *to_arg) for (six = 0; six < erts_no_schedulers; six++) { ErtsHLTimerService *srv = erts_aligned_scheduler_data[six].esd.timer_service; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsTimerWheel *twheel = - erts_aligned_scheduler_data[six].esd.timer_wheel; - erts_twheel_debug_foreach(twheel, tw_bif_timer_timeout, - twt_btm_print, (void *) &btmp); - time_rbt_foreach(srv->time_tree, hlt_btm_print, (void *) &btmp); -#else btm_rbt_foreach(srv->btm_tree, btm_tree_print, (void *) &btmp); -#endif } } @@ -3219,10 +2863,6 @@ typedef struct { static void debug_btm_foreach(ErtsBifTimer *tmr, void *vbtmfd) { -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - if (!(tmr->type.head.roflgs & ERTS_TMR_ROFLG_BIF_TMR)) - return; -#endif if (erts_atomic32_read_nob(&tmr->btm.state) == ERTS_TMR_STATE_ACTIVE) { ErtsBTMForeachDebug *btmfd = (ErtsBTMForeachDebug *) vbtmfd; Eterm id = ((tmr->type.head.roflgs & ERTS_TMR_ROFLG_REG_NAME) @@ -3232,22 +2872,6 @@ debug_btm_foreach(ErtsBifTimer *tmr, void *vbtmfd) } } -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - -static void -hlt_debug_btm_foreach(ErtsHLTimer *tmr, void *vbtmfd) -{ - debug_btm_foreach((ErtsBifTimer *) tmr, vbtmfd); -} - -static void -twt_debug_btm_foreach(void *vbtmfd, ErtsMonotonicTime tpos, void *vtwtp) -{ - debug_btm_foreach((ErtsBifTimer *) vtwtp, vbtmfd); -} - -#endif - void erts_debug_bif_timer_foreach(void (*func)(Eterm, Eterm, @@ -3267,20 +2891,9 @@ erts_debug_bif_timer_foreach(void (*func)(Eterm, for (six = 0; six < erts_no_schedulers; six++) { ErtsHLTimerService *srv = erts_aligned_scheduler_data[six].esd.timer_service; -#ifdef ERTS_MAGIC_REF_BIF_TIMERS - ErtsTimerWheel *twheel = - erts_aligned_scheduler_data[six].esd.timer_wheel; - erts_twheel_debug_foreach(twheel, tw_bif_timer_timeout, - twt_debug_btm_foreach, - (void *) &btmfd); - time_rbt_foreach(srv->time_tree, - hlt_debug_btm_foreach, - (void *) &btmfd); -#else btm_rbt_foreach(srv->btm_tree, debug_btm_foreach, (void *) &btmfd); -#endif } } @@ -3403,9 +3016,7 @@ st_hdbg_func(ErtsHLTimer *tmr, void *vhdbg) } ERTS_HLT_ASSERT(tmr->time.tree.u.l.next->time.tree.u.l.prev == tmr); ERTS_HLT_ASSERT(tmr->time.tree.u.l.prev->time.tree.u.l.next == tmr); -#ifndef ERTS_MAGIC_REF_BIF_TIMERS ERTS_HLT_ASSERT(btm_rbt_lookup(hdbg->srv->btm_tree, ERTS_BTM_HLT2REFN(tmr)) == tmr); -#endif } static void @@ -3434,10 +3045,8 @@ tt_hdbg_func(ErtsHLTimer *tmr, void *vhdbg) & ~ERTS_HLT_PFLGS_MASK); ERTS_HLT_ASSERT(tmr == prnt); } -#ifndef ERTS_MAGIC_REF_BIF_TIMERS if (tmr->head.roflgs & ERTS_TMR_ROFLG_BIF_TMR) ERTS_HLT_ASSERT(btm_rbt_lookup(hdbg->srv->btm_tree, ERTS_BTM_HLT2REFN(tmr)) == tmr); -#endif if (tmr->time.tree.same_time) { ErtsHdbgHLT st_hdbg; st_hdbg.srv = hdbg->srv; @@ -3503,7 +3112,6 @@ hdbg_chk_srv(ErtsHLTimerService *srv) time_rbt_foreach(srv->time_tree, tt_hdbg_func, (void *) &hdbg); ERTS_HLT_ASSERT(hdbg.found_root); } -#ifndef ERTS_MAGIC_REF_BIF_TIMERS if (srv->btm_tree) { ErtsHdbgHLT hdbg; hdbg.srv = srv; @@ -3512,7 +3120,6 @@ hdbg_chk_srv(ErtsHLTimerService *srv) btm_rbt_foreach(srv->btm_tree, bt_hdbg_func, (void *) &hdbg); ERTS_HLT_ASSERT(hdbg.found_root); } -#endif } #endif /* ERTS_HLT_HARD_DEBUG */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 99e788c718..163724ed3c 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -78,7 +78,7 @@ const char etp_erts_version[] = ERLANG_VERSION; const char etp_otp_release[] = ERLANG_OTP_RELEASE; const char etp_compile_date[] = ERLANG_COMPILE_DATE; const char etp_arch[] = ERLANG_ARCHITECTURE; -#ifdef ERTS_ENABLE_KERNEL_POLL +#if ERTS_ENABLE_KERNEL_POLL const int erts_use_kernel_poll = 1; const int etp_kernel_poll_support = 1; #else @@ -376,6 +376,27 @@ erl_init(int ncpu, } static Eterm +erl_spawn_system_process(Process* parent, Eterm mod, Eterm func, Eterm args, + ErlSpawnOpts *so) +{ + Eterm res; + int arity; + + ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(parent)); + arity = erts_list_length(args); + + if (erts_find_function(mod, func, arity, erts_active_code_ix()) == NULL) { + erts_exit(ERTS_ERROR_EXIT, "No function %T:%T/%i\n", mod, func, arity); + } + + so->flags |= SPO_SYSTEM_PROC; + + res = erl_create_process(parent, mod, func, args, so); + + return res; +} + +static Eterm erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** argv) { int i; @@ -386,12 +407,6 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** Process parent; ErlSpawnOpts so; Eterm env; - - start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1); - if (erts_find_function(start_mod, am_start, 2, - erts_active_code_ix()) == NULL) { - erts_exit(ERTS_ERROR_EXIT, "No function %s:start/2\n", modname); - } /* * We need a dummy parent process to be able to call erl_create_process(). @@ -399,6 +414,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** erts_init_empty_process(&parent); erts_proc_lock(&parent, ERTS_PROC_LOCK_MAIN); + hp = HAlloc(&parent, argc*2 + 4); args = NIL; for (i = argc-1; i >= 0; i--) { @@ -411,44 +427,73 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** hp += 2; args = CONS(hp, env, args); - so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC; - res = erl_create_process(&parent, start_mod, am_start, args, &so); + start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), + ERTS_ATOM_ENC_LATIN1, 1); + + so.flags = erts_default_spo_flags; + res = erl_spawn_system_process(&parent, start_mod, am_start, args, &so); + ASSERT(is_internal_pid(res)); + erts_proc_unlock(&parent, ERTS_PROC_LOCK_MAIN); erts_cleanup_empty_process(&parent); + return res; } static Eterm erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int prio) -{ - Eterm start_mod; - Process* parent; +{ + Process *parent; ErlSpawnOpts so; - Eterm res; - - start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1); - if (erts_find_function(start_mod, am_start, 0, - erts_active_code_ix()) == NULL) { - erts_exit(ERTS_ERROR_EXIT, "No function %s:start/0\n", modname); - } + Eterm mod, res; parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN); + mod = erts_atom_put((byte *) modname, sys_strlen(modname), + ERTS_ATOM_ENC_LATIN1, 1); + + so.flags = erts_default_spo_flags|SPO_USE_ARGS; - so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC|SPO_USE_ARGS; - if (off_heap_msgq) + if (off_heap_msgq) { so.flags |= SPO_OFF_HEAP_MSGQ; - so.min_heap_size = H_MIN_SIZE; - so.min_vheap_size = BIN_VH_MIN_SIZE; - so.max_heap_size = H_MAX_SIZE; - so.max_heap_flags = H_MAX_FLAGS; - so.priority = prio; - so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs); - so.scheduler = 0; - res = erl_create_process(parent, start_mod, am_start, NIL, &so); + } + + so.min_heap_size = H_MIN_SIZE; + so.min_vheap_size = BIN_VH_MIN_SIZE; + so.max_heap_size = H_MAX_SIZE; + so.max_heap_flags = H_MAX_FLAGS; + so.priority = prio; + so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs); + so.scheduler = 0; + + res = erl_spawn_system_process(parent, mod, am_start, NIL, &so); + ASSERT(is_internal_pid(res)); + erts_proc_unlock(parent, ERTS_PROC_LOCK_MAIN); + return res; } +Eterm erts_internal_spawn_system_process_3(BIF_ALIST_3) { + Eterm mod, func, args, res; + ErlSpawnOpts so; + + mod = BIF_ARG_1; + func = BIF_ARG_2; + args = BIF_ARG_3; + + ASSERT(is_atom(mod)); + ASSERT(is_atom(func)); + ASSERT(erts_list_length(args) >= 0); + + so.flags = erts_default_spo_flags; + res = erl_spawn_system_process(BIF_P, mod, func, args, &so); + + if (is_non_value(res)) { + BIF_ERROR(BIF_P, so.error_code); + } + + BIF_RET(res); +} Eterm erts_preloaded(Process* p) @@ -684,6 +729,9 @@ void erts_usage(void) erts_fprintf(stderr, "-zebwt val set ets busy wait threshold, valid values are:\n"); erts_fprintf(stderr, " none|very_short|short|medium|long|very_long|extremely_long\n"); #endif + erts_fprintf(stderr, "-ztma bool enable/disable tuple module apply support in emulator\n"); + erts_fprintf(stderr, " (transitional flag for parameterized modules; recompile\n"); + erts_fprintf(stderr, " with +tuple_calls for compatibility with future versions)\n"); erts_fprintf(stderr, "\n"); erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); @@ -2167,6 +2215,17 @@ erl_start(int argc, char **argv) erts_usage(); } } + else if (has_prefix("tma", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if (sys_strcmp(arg,"true") == 0) { + tuple_module_apply = 1; + } else if (sys_strcmp(arg,"false") == 0) { + tuple_module_apply = 0; + } else { + erts_fprintf(stderr, "bad tuple module apply %s\n", arg); + erts_usage(); + } + } else { erts_fprintf(stderr, "bad -z option %s\n", argv[i]); erts_usage(); diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index cba17d3e6a..8f96dc3d23 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -475,7 +475,7 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n, Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n) { - if (n < MAP_SMALL_MAP_LIMIT) { + if (n <= MAP_SMALL_MAP_LIMIT) { Eterm *ks, *vs, *hp; flatmap_t *mp; Eterm keys; diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ee6e6085b6..ebef485b04 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2354,6 +2354,13 @@ static void dtor_demonitor(ErtsMonitor* mon, void* context) erts_proc_sig_send_demonitor(mon); } +#ifdef DEBUG +int erts_dbg_is_resource_dying(ErtsResource* resource) +{ + return resource->monitors && rmon_is_dying(resource->monitors); +} +#endif + # define NIF_RESOURCE_DTOR &nif_resource_dtor static int nif_resource_dtor(Binary* bin) @@ -2694,8 +2701,12 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) { Process *proc; Sint reds; + int sched; - execution_state(env, &proc, NULL); + execution_state(env, &proc, &sched); + + if (sched < 0) + return 0; /* no-op on dirty scheduler */ ASSERT(is_proc_bound(env) && percent >= 1 && percent <= 100); if (percent < 1) percent = 1; diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 25976d38cc..039d8cf67a 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -1018,6 +1018,6 @@ int erts_port_output_async(Port *, Eterm, Eterm); /* * Signals from ports to ports. Used by sys drivers. */ -int erl_drv_port_control(Eterm, char, char*, ErlDrvSizeT); +int erl_drv_port_control(Eterm, unsigned int, char*, ErlDrvSizeT); #endif diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 990a01b96f..9cfb7fc681 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -364,13 +364,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) { int print_res; char def_buf[64]; char *buf, *big_str; - Uint sz = (Uint) big_decimal_estimate(wobj); + Uint sz = (Uint) big_integer_estimate(wobj, 10); sz++; if (sz <= 64) buf = &def_buf[0]; else buf = erts_alloc(ERTS_ALC_T_TMP, sz); - big_str = erts_big_to_string(wobj, buf, sz); + big_str = erts_big_to_string(wobj, 10, buf, sz); print_res = erts_printf_string(fn, arg, big_str); if (buf != &def_buf[0]) erts_free(ERTS_ALC_T_TMP, (void *) buf); diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index f343e984f7..18418a76e1 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -3812,7 +3812,6 @@ clear_seq_trace_token(ErtsMessage *sig) void erts_proc_sig_clear_seq_trace_tokens(Process *c_p) { - ASSERT(erts_thr_progress_is_blocking()); erts_proc_sig_fetch(c_p); ERTS_FOREACH_SIG_PRIVQS(c_p, sig, clear_seq_trace_token(sig)); } diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h index 3fc2d06b2d..6b065a7add 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.h +++ b/erts/emulator/beam/erl_proc_sig_queue.h @@ -989,8 +989,7 @@ erts_proc_sig_fetch(Process *proc) Sint res = 0; ErtsSignal *sig; - ERTS_LC_ASSERT(erts_thr_progress_is_blocking() - || ERTS_PROC_IS_EXITING(proc) + ERTS_LC_ASSERT(ERTS_PROC_IS_EXITING(proc) || ((erts_proc_lc_my_proc_locks(proc) & (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_MSGQ)) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 2427d87f66..cc02fbad1e 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -92,10 +92,6 @@ #undef HARDDEBUG #endif -#ifdef HARDDEBUG -#define HARDDEBUG_RUNQS -#endif - #ifdef HIPE #include "hipe_mode_switch.h" /* for hipe_init_process() */ #include "hipe_signal.h" /* for hipe_thread_signal_init() */ @@ -344,6 +340,7 @@ erts_sched_stat_t erts_sched_stat; static erts_tsd_key_t ERTS_WRITE_UNLIKELY(sched_data_key); #if ERTS_POLL_USE_SCHEDULER_POLLING +static erts_atomic32_t function_calls; static erts_atomic32_t doing_sys_schedule; #endif static erts_atomic32_t no_empty_run_queues; @@ -2465,6 +2462,13 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) erts_port_lock(prt); + if (prt->common.u.alive.reg && + prt->common.u.alive.reg->name == am_heart_port) { + /* Leave heart port to not get killed before flushing is done*/ + erts_port_release(prt); + continue; + } + state = erts_atomic32_read_nob(&prt->state); if (!(state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP | ERTS_PORT_SFLG_HALT))) { @@ -3251,6 +3255,7 @@ poll_thread(void *arg) static ERTS_INLINE void clear_sys_scheduling(void) { + erts_atomic32_set_relb(&function_calls, 0); erts_atomic32_set_mb(&doing_sys_schedule, 0); } @@ -3273,31 +3278,49 @@ prepare_for_sys_schedule(void) return 0; } -static void -check_io_timer(void *null) +#else +#define clear_sys_scheduling() +#define prepare_for_sys_schedule() 0 +#endif + +#ifdef HARDDEBUG +#define ERTS_HDBG_CHK_SLEEP_LIST(SL, L, F, FN) \ + check_sleepers_list((SL), (L), (F), (FN)) +static void check_sleepers_list(ErtsSchedulerSleepList *sl, + int lock, + ErtsSchedulerSleepInfo *find, + ErtsSchedulerSleepInfo *find_not) { - ErtsSchedulerData *esdp = erts_get_scheduler_data(); - if (prepare_for_sys_schedule()) { - erts_check_io(esdp->ssi->psi, ERTS_POLL_NO_TIMEOUT); - clear_sys_scheduling(); - } + ErtsSchedulerSleepInfo *last_out; + int found = 0; - /* The timer is cleared if this schedulers run-queue became empty - or if the CHECKIO flag was cleared. The CHECKIO flags is cleared - when a check_balance assigns another scheduler to be the poller in - the overload scenario. */ - if ((ERTS_RUNQ_FLGS_GET_NOB(esdp->run_queue) & (ERTS_RUNQ_FLG_OUT_OF_WORK|ERTS_RUNQ_FLG_CHECKIO)) - == ERTS_RUNQ_FLG_CHECKIO) { - erts_start_timer_callback(ERTS_POLL_SCHEDULER_POLLING_TIMEOUT, - check_io_timer, NULL); - } else { - ERTS_RUNQ_FLGS_UNSET(esdp->run_queue, ERTS_RUNQ_FLG_CHECKIO); - } -} + if (lock) + erts_spin_lock(&sl->lock); + ERTS_ASSERT(!find_not || (!find_not->next && !find_not->prev)); + + last_out = sl->list; + if (last_out) { + ErtsSchedulerSleepInfo *tmp = last_out; + do { + ERTS_ASSERT(tmp->next); + ERTS_ASSERT(tmp->prev); + ERTS_ASSERT(tmp->next->prev == tmp); + ERTS_ASSERT(tmp->prev->next == tmp); + ERTS_ASSERT(tmp != find_not); + if (tmp == find) + found = !0; + tmp = tmp->next; + + } while (tmp != last_out); + } + ERTS_ASSERT(!find || found); + + if (lock) + erts_spin_unlock(&sl->lock); +} #else -#define clear_sys_scheduling() -#define prepare_for_sys_schedule() 0 +#define ERTS_HDBG_CHK_SLEEP_LIST(SL, L, F, FN) ((void) 0) #endif static void @@ -3313,23 +3336,29 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq)); - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - erts_spin_lock(&rq->sleepers.lock); flgs = sched_prep_spin_wait(ssi); if (flgs & ERTS_SSI_FLG_SUSPENDED) { /* Go suspend instead... */ - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - erts_spin_unlock(&rq->sleepers.lock); return; } if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { - ssi->prev = NULL; - ssi->next = rq->sleepers.list; - if (rq->sleepers.list) - rq->sleepers.list->prev = ssi; - rq->sleepers.list = ssi; - erts_spin_unlock(&rq->sleepers.lock); + erts_spin_lock(&rq->sleepers.lock); + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, ssi); + ASSERT(!ssi->next); /* Not in sleepers list */ + ASSERT(!ssi->prev); + if (!rq->sleepers.list) { + ssi->next = ssi->prev = ssi; + rq->sleepers.list = ssi; + } + else { + ssi->prev = rq->sleepers.list; + ssi->next = rq->sleepers.list->next; + ssi->prev->next = ssi; + ssi->next->prev = ssi; + } + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, ssi, NULL); + erts_spin_unlock(&rq->sleepers.lock); dirty_active(esdp, -1); } @@ -3406,6 +3435,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) current_time = erts_get_monotonic_time(esdp); } } + *fcalls = 0; clear_sys_scheduling(); } else { if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { @@ -3470,8 +3500,28 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) (ERTS_SSI_FLG_SUSPENDED | ERTS_SSI_FLG_MSB_EXEC)); - if (ERTS_SCHEDULER_IS_DIRTY(esdp)) + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { dirty_sched_wall_time_change(esdp, working = 1); + erts_spin_lock(&rq->sleepers.lock); + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, ssi->next ? ssi : NULL, NULL); + if (ssi->next) { /* Still in list... */ + if (ssi->next == ssi) { + ASSERT(rq->sleepers.list == ssi); + ASSERT(ssi->prev == ssi); + rq->sleepers.list = NULL; + } + else { + ASSERT(ssi->prev != ssi); + if (rq->sleepers.list == ssi) + rq->sleepers.list = ssi->next; + ssi->prev->next = ssi->next; + ssi->next->prev = ssi->prev; + } + ssi->next = ssi->prev = NULL; + } + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, ssi); + erts_spin_unlock(&rq->sleepers.lock); + } else if (!thr_prgr_active) { erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); sched_wall_time_change(esdp, 1); @@ -3559,56 +3609,44 @@ wake_scheduler(ErtsRunQueue *rq) } static void -wake_dirty_schedulers(ErtsRunQueue *rq, int one) +wake_dirty_scheduler(ErtsRunQueue *rq) { - ErtsSchedulerSleepInfo *ssi; + ErtsSchedulerSleepInfo *lo_ssi, *fo_ssi; ErtsSchedulerSleepList *sl; ASSERT(ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); sl = &rq->sleepers; erts_spin_lock(&sl->lock); - ssi = sl->list; - if (!ssi) { + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, NULL); + lo_ssi = sl->list; + if (!lo_ssi) { erts_spin_unlock(&sl->lock); - if (one) - wake_scheduler(rq); - } else if (one) { + wake_scheduler(rq); + } + else { erts_aint32_t flgs; - if (ssi->prev) - ssi->prev->next = ssi->next; - else { - ASSERT(sl->list == ssi); - sl->list = ssi->next; + fo_ssi = lo_ssi->next; + ASSERT(fo_ssi->prev == lo_ssi); + if (fo_ssi == lo_ssi) { + ASSERT(lo_ssi->prev == lo_ssi); + sl->list = NULL; + } + else { + ASSERT(lo_ssi->prev != lo_ssi); + lo_ssi->next = fo_ssi->next; + fo_ssi->next->prev = fo_ssi->prev; } - if (ssi->next) - ssi->next->prev = ssi->prev; - - erts_spin_unlock(&sl->lock); - - ERTS_THR_MEMORY_BARRIER; - flgs = ssi_flags_set_wake(ssi); - erts_sched_finish_poke(ssi, flgs); - } else { - sl->list = NULL; + fo_ssi->next = fo_ssi->prev = NULL; + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, fo_ssi); erts_spin_unlock(&sl->lock); ERTS_THR_MEMORY_BARRIER; - do { - ErtsSchedulerSleepInfo *wake_ssi = ssi; - ssi = ssi->next; - erts_sched_finish_poke(wake_ssi, ssi_flags_set_wake(wake_ssi)); - } while (ssi); + flgs = ssi_flags_set_wake(fo_ssi); + erts_sched_finish_poke(fo_ssi, flgs); } } -static void -wake_dirty_scheduler(ErtsRunQueue *rq) -{ - wake_dirty_schedulers(rq, 1); -} - - #define ERTS_NO_USED_RUNQS_SHIFT 16 #define ERTS_NO_RUNQS_MASK 0xffffU @@ -4661,15 +4699,6 @@ check_balance(ErtsRunQueue *c_rq) if (blnc_no_rqs == 1) { c_rq->check_balance_reds = INT_MAX; erts_atomic32_set_nob(&balance_info.checking_balance, 0); -#if ERTS_POLL_USE_SCHEDULER_POLLING - c_rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; - if ((ERTS_RUNQ_FLGS_GET_NOB(c_rq) & (ERTS_RUNQ_FLG_OUT_OF_WORK|ERTS_RUNQ_FLG_CHECKIO)) - == 0) { - ERTS_RUNQ_FLGS_SET(c_rq, ERTS_RUNQ_FLG_CHECKIO); - erts_start_timer_callback(ERTS_POLL_SCHEDULER_POLLING_TIMEOUT, check_io_timer, NULL); - } - ERTS_RUNQ_FLGS_UNSET(c_rq, ERTS_RUNQ_FLGS_MIGRATION_INFO); -#endif return; } @@ -5189,19 +5218,6 @@ erts_fprintf(stderr, "--------------------------------\n"); /* Publish new migration paths... */ erts_atomic_set_wb(&erts_migration_paths, (erts_aint_t) new_mpaths); -#if ERTS_POLL_USE_SCHEDULER_POLLING - if (full_scheds == current_active) { - ERTS_ASSERT(full_scheds <= current_active); - /* All active schedulers ran for full, we need to do active polling, - so we setup a timer that does active polling */ - if (!(ERTS_RUNQ_FLGS_GET_NOB(c_rq) & ERTS_RUNQ_FLG_CHECKIO)) { - /* Active polling is not running, start it */ - erts_start_timer_callback(ERTS_POLL_SCHEDULER_POLLING_TIMEOUT, check_io_timer, NULL); - } - run_queue_info[c_rq->ix].flags |= ERTS_RUNQ_FLG_CHECKIO; - } -#endif - /* Reset balance statistics in all online queues */ for (qix = 0; qix < blnc_no_rqs; qix++) { Uint32 flags = run_queue_info[qix].flags; @@ -5211,8 +5227,6 @@ erts_fprintf(stderr, "--------------------------------\n"); ASSERT(!(flags & ERTS_RUNQ_FLG_OUT_OF_WORK)); if (rq->waiting) flags |= ERTS_RUNQ_FLG_OUT_OF_WORK; - if (rq != c_rq) - flags &= ~ERTS_RUNQ_FLG_CHECKIO; rq->full_reds_history_sum = run_queue_info[qix].full_reds_history_sum; @@ -5222,7 +5236,7 @@ erts_fprintf(stderr, "--------------------------------\n"); ERTS_DBG_CHK_FULL_REDS_HISTORY(rq); rq->out_of_work_count = 0; - (void) ERTS_RUNQ_FLGS_READ_BSET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO|ERTS_RUNQ_FLG_CHECKIO, flags); + (void) ERTS_RUNQ_FLGS_READ_BSET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO, flags); rq->max_len = erts_atomic32_read_dirty(&rq->len); for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) { ErtsRunQueueInfo *rqi; @@ -5873,6 +5887,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th erts_alloc_permanent_cache_aligned(ERTS_ALC_T_RUNQS, size_runqs); #if ERTS_POLL_USE_SCHEDULER_POLLING erts_atomic32_init_nob(&doing_sys_schedule, 0); + erts_atomic32_init_nob(&function_calls, 0); #endif erts_atomic32_init_nob(&no_empty_run_queues, 0); @@ -5988,6 +6003,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_cpu_sched_sleep_info[ix].ssi; erts_atomic32_init_nob(&ssi->flags, 0); + ssi->next = NULL; + ssi->prev = NULL; ssi->event = NULL; /* initialized in sched_dirty_cpu_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } @@ -5998,6 +6015,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th for (ix = 0; ix < no_dirty_io_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_io_sched_sleep_info[ix].ssi; erts_atomic32_init_nob(&ssi->flags, 0); + ssi->next = NULL; + ssi->prev = NULL; ssi->event = NULL; /* initialized in sched_dirty_io_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } @@ -7517,7 +7536,13 @@ suspend_scheduler(ErtsSchedulerData *esdp) return; } +#ifdef HARDDEBUG + if (sched_type != ERTS_SCHED_NORMAL) + ERTS_HDBG_CHK_SLEEP_LIST(&esdp->run_queue->sleepers, !0, NULL, ssi); +#endif + if (erts_atomic32_read_nob(&ssi->flags) & ERTS_SSI_FLG_MSB_EXEC) { + ASSERT(no == 1); if (!msb_scheduler_type_switch(sched_type, esdp, no)) return; @@ -9217,7 +9242,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) Process *proxy_p = NULL; ErtsRunQueue *rq; int context_reds; - int fcalls; + int fcalls = 0; int actual_reds; int reds; Uint32 flags; @@ -9291,6 +9316,10 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST; esdp->virtual_reds = 0; +#if ERTS_POLL_USE_SCHEDULER_POLLING + fcalls = (int) erts_atomic32_add_read_acqb(&function_calls, reds); +#endif + ASSERT(esdp && esdp == erts_get_scheduler_data()); rq = erts_get_runq_current(esdp); @@ -9507,7 +9536,33 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) non_empty_runq(rq); goto check_activities_to_run; - } + } else if (is_normal_sched && + fcalls > (2 * context_reds) && + prepare_for_sys_schedule()) { + ErtsMonotonicTime current_time; + /* + * Schedule system-level activities. + */ + + ERTS_MSACC_PUSH_STATE_CACHED_M(); + + erts_runq_unlock(rq); + + ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_CHECK_IO); + LTTNG2(scheduler_poll, esdp->no, 1); + + erts_check_io(esdp->ssi->psi, ERTS_POLL_NO_TIMEOUT); + ERTS_MSACC_POP_STATE_M(); + + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) + erts_bump_timers(esdp->timer_wheel, current_time); + + erts_runq_lock(rq); + fcalls = 0; + clear_sys_scheduling(); + goto continue_check_activities_to_run; + } if (flags & ERTS_RUNQ_FLG_MISC_OP) exec_misc_ops(rq); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index a1b029adbe..43937f216c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -173,8 +173,6 @@ extern int erts_dio_sched_thread_suggested_stack_size; (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 9)) #define ERTS_RUNQ_FLG_HALTING \ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 10)) -#define ERTS_RUNQ_FLG_CHECKIO \ - (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 11)) #define ERTS_RUNQ_FLG_MAX (ERTS_RUNQ_FLG_BASE2 + 12) @@ -359,7 +357,7 @@ typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; typedef struct { erts_spinlock_t lock; - ErtsSchedulerSleepInfo *list; + ErtsSchedulerSleepInfo *list; /* circular lifo list; points to last out */ } ErtsSchedulerSleepList; struct ErtsSchedulerSleepInfo_ { diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 2350d4c02f..ae7084b7f4 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -72,6 +72,7 @@ static ErtsTracer default_port_tracer; static Eterm system_monitor; static Eterm system_profile; +static erts_atomic_t system_logger; #ifdef HAVE_ERTS_NOW_CPU int erts_cpu_timestamp; @@ -340,6 +341,7 @@ void erts_init_trace(void) { default_port_trace_flags = F_INITIAL_TRACE_FLAGS; default_port_tracer = erts_tracer_nil; system_seq_tracer = erts_tracer_nil; + erts_atomic_init_nob(&system_logger, am_logger); init_sys_msg_dispatcher(); init_tracer_nif(); } @@ -2027,10 +2029,24 @@ enqueue_sys_msg(enum ErtsSysMsgType type, erts_mtx_unlock(&smq_mtx); } +Eterm +erts_get_system_logger(void) +{ + return (Eterm)erts_atomic_read_nob(&system_logger); +} + +Eterm +erts_set_system_logger(Eterm logger) +{ + if (logger != am_logger && logger != am_undefined && !is_internal_pid(logger)) + return THE_NON_VALUE; + return (Eterm)erts_atomic_xchg_nob(&system_logger, logger); +} + void erts_queue_error_logger_message(Eterm from, Eterm msg, ErlHeapFragment *bp) { - enqueue_sys_msg(SYS_MSG_TYPE_ERRLGR, from, am_logger, msg, bp); + enqueue_sys_msg(SYS_MSG_TYPE_ERRLGR, from, erts_get_system_logger(), msg, bp); } void @@ -2271,7 +2287,7 @@ sys_msg_dispatcher_func(void *unused) } break; case SYS_MSG_TYPE_ERRLGR: - receiver = am_logger; + receiver = smqp->to; break; default: receiver = NIL; @@ -2285,8 +2301,15 @@ sys_msg_dispatcher_func(void *unused) if (is_internal_pid(receiver)) { proc = erts_pid2proc(NULL, 0, receiver, proc_locks); if (!proc) { - /* Bad tracer */ - goto failure; + if (smqp->type == SYS_MSG_TYPE_ERRLGR) { + /* Bad logger process, send to kernel 'logger' process */ + erts_set_system_logger(am_logger); + receiver = erts_get_system_logger(); + goto logger; + } else { + /* Bad tracer */ + goto failure; + } } else { ErtsMessage *mp; @@ -2299,9 +2322,9 @@ sys_msg_dispatcher_func(void *unused) #endif erts_proc_unlock(proc, proc_locks); } - } - else if (receiver == am_logger) { - proc = erts_whereis_process(NULL,0,receiver,proc_locks,0); + } else if (receiver == am_logger) { + logger: + proc = erts_whereis_process(NULL,0,am_logger,proc_locks,0); if (!proc) goto failure; else if (smqp->from == proc->common.id) @@ -2309,7 +2332,10 @@ sys_msg_dispatcher_func(void *unused) else goto queue_proc_msg; } - else if (is_internal_port(receiver)) { + else if (receiver == am_undefined) { + goto drop_sys_msg; + } + else if (is_internal_port(receiver)) { port = erts_thr_id2port_sflgs(receiver, ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); if (!port) @@ -2366,7 +2392,7 @@ erts_foreach_sys_msg_in_q(void (*func)(Eterm, to = erts_get_system_profile(); break; case SYS_MSG_TYPE_ERRLGR: - to = am_logger; + to = erts_get_system_logger(); break; default: to = NIL; diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index bccf31606e..b7844d1cb0 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -94,6 +94,8 @@ void erts_foreach_sys_msg_in_q(void (*func)(Eterm, Eterm, Eterm, ErlHeapFragment *)); +Eterm erts_set_system_logger(Eterm); +Eterm erts_get_system_logger(void); void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *); void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 4089fac48e..d37c2940c4 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -167,6 +167,8 @@ extern const int num_instructions; /* Number of instruction in opc[]. */ extern Uint erts_instr_count[]; +extern int tuple_module_apply; + /* some constants for various table sizes etc */ #define ATOM_TEXT_SIZE 32768 /* Increment for allocating atom text space */ diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 9a66e491f3..1ded5f031c 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -102,7 +102,7 @@ static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint32, struct erl_off_h struct TTBEncodeContext_; static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags, struct erl_off_heap_header** off_heap, Sint *reds, byte **res); -static Uint is_external_string(Eterm obj, int* p_is_string); +static int is_external_string(Eterm obj, Uint* lenp); static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32); static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32); struct B2TContext_t; @@ -2481,11 +2481,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, { Eterm* cons = list_val(obj); Eterm tl; + Uint len_cnt = WSTACK_POP(s); obj = CAR(cons); tl = CDR(cons); - WSTACK_PUSH2(s, (is_list(tl) ? ENC_ONE_CONS : ENC_TERM), - tl); + if (is_list(tl)) { + len_cnt++; + WSTACK_PUSH3(s, len_cnt, ENC_ONE_CONS, tl); + } + else { + byte* list_lenp = (byte*) WSTACK_POP(s); + ASSERT(list_lenp[-1] == LIST_EXT); + put_int32(len_cnt, list_lenp); + + WSTACK_PUSH2(s, ENC_TERM, tl); + } } break; case ENC_PATCH_FUN_SIZE: @@ -2689,10 +2699,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } case LIST_DEF: { - int is_str; - - i = is_external_string(obj, &is_str); - if (is_str) { + if (is_external_string(obj, &i)) { *ep++ = STRING_EXT; put_int16(i, ep); ep += 2; @@ -2701,9 +2708,12 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, *ep++ = unsigned_val(CAR(cons)); obj = CDR(cons); } + r -= i; } else { + r -= i/2; *ep++ = LIST_EXT; - put_int32(i, ep); + /* Patch list length when we find end of list */ + WSTACK_PUSH2(s, (UWord)ep, 1); ep += 4; goto encode_one_cons; } @@ -2961,9 +2971,13 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, return 0; } +/** @brief Is it a list of bytes not longer than MAX_STRING_LEN? + * @param lenp out: string length or number of list cells traversed + * @return true/false + */ static -Uint -is_external_string(Eterm list, int* p_is_string) +int +is_external_string(Eterm list, Uint* lenp) { Uint len = 0; @@ -2975,29 +2989,15 @@ is_external_string(Eterm list, int* p_is_string) Eterm* consp = list_val(list); Eterm hd = CAR(consp); - if (!is_byte(hd)) { - break; + if (!is_byte(hd) || ++len > MAX_STRING_LEN) { + *lenp = len; + return 0; } - len++; list = CDR(consp); } - /* - * If we have reached the end of the list, and we have - * not exceeded the maximum length of a string, this - * is a string. - */ - *p_is_string = is_nil(list) && len < MAX_STRING_LEN; - - /* - * Continue to calculate the length. - */ - while (is_list(list)) { - Eterm* consp = list_val(list); - len++; - list = CDR(consp); - } - return len; + *lenp = len; + return is_nil(list); } @@ -4075,8 +4075,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, for (;;) { ASSERT(!is_header(obj)); - if (ctx && --r == 0) { - *reds = r; + if (ctx && --r <= 0) { + *reds = 0; ctx->obj = obj; ctx->result = result; WSTACK_SAVE(s, &ctx->wstack); @@ -4166,8 +4166,10 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) + 4 + 1); break; - case LIST_DEF: - if ((m = is_string(obj)) && (m < MAX_STRING_LEN)) { + case LIST_DEF: { + int is_str = is_external_string(obj, &m); + r -= m/2; + if (is_str) { result += m + 2 + 1; } else { result += 5; @@ -4176,6 +4178,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, continue; /* big loop */ } break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(obj); @@ -4317,7 +4320,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, if (is_header(obj)) { switch (obj) { - case LIST_TAIL_OP: + case LIST_TAIL_OP: obj = (Eterm) WSTACK_POP(s); if (is_list(obj)) { Eterm* cons = list_val(obj); @@ -4343,7 +4346,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, WSTACK_DESTROY(s); if (ctx) { ASSERT(ctx->wstack.wstart == NULL); - *reds = r; + *reds = r < 0 ? 0 : r; } *res = result; return 0; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 0631404599..f564472081 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -113,6 +113,9 @@ extern Eterm erts_bld_resource_ref(Eterm** hp, ErlOffHeap*, ErtsResource*); extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); +#ifdef DEBUG +int erts_dbg_is_resource_dying(ErtsResource*); +#endif extern void erts_resource_stop(ErtsResource*, ErlNifEvent, int is_direct_call); void erts_fire_nif_monitor(ErtsMonitor *tmon); void erts_nif_demonitored(ErtsResource* resource); @@ -962,7 +965,7 @@ void init_break_handler(void); void erts_set_ignore_break(void); void erts_replace_intr(void); void process_info(fmtfn_t, void *); -void print_process_info(fmtfn_t, void *, Process*); +void print_process_info(fmtfn_t, void *, Process*, ErtsProcLocks); void info(fmtfn_t, void *); void loaded(fmtfn_t, void *); void erts_print_base64(fmtfn_t to, void *to_arg, byte* src, Uint size); @@ -1303,14 +1306,7 @@ Sint intlist_to_buf(Eterm, char*, Sint); /* most callers pass plain char*'s */ int erts_unicode_list_to_buf(Eterm list, byte *buf, Sint len, Sint* written); Sint erts_unicode_list_to_buf_len(Eterm list); -struct Sint_buf { -#if defined(ARCH_64) - char s[22]; -#else - char s[12]; -#endif -}; -char* Sint_to_buf(Sint, struct Sint_buf*); +int Sint_to_buf(Sint num, int base, char **buf_p, size_t buf_size); #define ERTS_IOLIST_STATE_INITER(C_P, OBJ) \ {(C_P), 0, 0, (OBJ), {NULL, NULL, NULL, ERTS_ALC_T_INVALID}, 0, 0} diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 5325480901..7322239a73 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4073,7 +4073,7 @@ done: * to the caller. */ int -erl_drv_port_control(Eterm port_num, char cmd, char* buff, ErlDrvSizeT size) +erl_drv_port_control(Eterm port_num, unsigned int cmd, char* buff, ErlDrvSizeT size) { ErtsProc2PortSigData *sigdp = erts_port_task_alloc_p2p_sig_data(); diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 869a575cb4..a69da4d762 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -1291,4 +1291,13 @@ erts_raw_env_next_char(byte *p, int encoding) #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ +/* + * Magic numbers for our driver port_control callbacks. + * Kept them below 1<<27 to not inflict extra bignum garbage on 32-bit. + */ +#define ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER 0x018b0900U +#define ERTS_INET_DRV_CONTROL_MAGIC_NUMBER 0x03f1a300U +#define ERTS_SPAWN_DRV_CONTROL_MAGIC_NUMBER 0x04c76a00U +#define ERTS_FORKER_DRV_CONTROL_MAGIC_NUMBER 0x050a7800U + #endif diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index d81bd89a48..c5deed38ad 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1946,7 +1946,7 @@ do_allocate_logger_message(Eterm gleader, ErtsMonotonicTime *ts, Eterm *pid, else sz += MAP4_SZ /* metadata map w gl w pid*/; - *ts = ERTS_MONOTONIC_TO_USEC(erts_get_monotonic_time(NULL) + erts_get_time_offset()); + *ts = ERTS_MONOTONIC_TO_USEC(erts_os_system_time()); erts_bld_sint64(NULL, &sz, *ts); *bp = new_message_buffer(sz); @@ -3681,30 +3681,47 @@ erts_unicode_list_to_buf_len(Eterm list) } } -/* -** Convert an integer to a byte list -** return pointer to converted stuff (need not to be at start of buf!) -*/ -char* Sint_to_buf(Sint n, struct Sint_buf *buf) +/* Prints an integer in the given base, returning the number of digits printed. + * + * (*buf) is a pointer to the buffer, and is set to the start of the string + * when returning. */ +int Sint_to_buf(Sint n, int base, char **buf, size_t buf_size) { - char* p = &buf->s[sizeof(buf->s)-1]; - int sign = 0; - - *p-- = '\0'; /* null terminate */ - if (n == 0) - *p-- = '0'; - else if (n < 0) { - sign = 1; - n = -n; + char *p = &(*buf)[buf_size - 1]; + int sign = 0, size = 0; + + ASSERT(base >= 2 && base <= 36); + + if (n == 0) { + *p-- = '0'; + size++; + } else if (n < 0) { + sign = 1; + n = -n; } while (n != 0) { - *p-- = (n % 10) + '0'; - n /= 10; + int digit = n % base; + + if (digit < 10) { + *p-- = '0' + digit; + } else { + *p-- = 'A' + (digit - 10); + } + + size++; + + n /= base; } - if (sign) - *p-- = '-'; - return p+1; + + if (sign) { + *p-- = '-'; + size++; + } + + *buf = p + 1; + + return size; } /* Build a list of integers in some safe memory area @@ -4771,58 +4788,3 @@ erts_ptr_id(void *ptr) return ptr; } -#ifdef DEBUG -/* - * Handy functions when using a debugger - don't use in the code! - */ - -void upp(byte *buf, size_t sz) -{ - bin_write(ERTS_PRINT_STDERR, NULL, buf, sz); -} - -void pat(Eterm atom) -{ - upp(atom_tab(atom_val(atom))->name, - atom_tab(atom_val(atom))->len); -} - - -void pinfo() -{ - process_info(ERTS_PRINT_STDOUT, NULL); -} - - -void pp(p) -Process *p; -{ - if(p) - print_process_info(ERTS_PRINT_STDERR, NULL, p); -} - -void ppi(Eterm pid) -{ - pp(erts_proc_lookup(pid)); -} - -void td(Eterm x) -{ - erts_fprintf(stderr, "%T\n", x); -} - -void -ps(Process* p, Eterm* stop) -{ - Eterm* sp = STACK_START(p) - 1; - - if (stop <= STACK_END(p)) { - stop = STACK_END(p) + 1; - } - - while(sp >= stop) { - erts_printf("%p: %.75T\n", sp, *sp); - sp--; - } -} -#endif diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 47eb5df7dd..a6a5c20266 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -634,9 +634,13 @@ static size_t my_strnlen(const char *s, size_t maxlen) * header length. To get the header length we use * the pointer difference from the cmsg start pointer * to the CMSG_DATA(cmsg) pointer. + * + * Some platforms (seen on ppc Linux 2.6.29-3.ydl61.3) + * may return 0 as the cmsg_len if the cmsg is to be ignored. */ #define LEN_CMSG_DATA(cmsg) \ - ((cmsg)->cmsg_len - ((char*)CMSG_DATA(cmsg) - (char*)(cmsg))) + ((cmsg)->cmsg_len < sizeof (struct cmsghdr) ? 0 : \ + (cmsg)->cmsg_len - ((char*)CMSG_DATA(cmsg) - (char*)(cmsg))) #define NXT_CMSG_HDR(cmsg) \ ((struct cmsghdr*)(((char*)(cmsg)) + CMSG_SPACE(LEN_CMSG_DATA(cmsg)))) #endif @@ -9951,6 +9955,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, { tcp_descriptor* desc = (tcp_descriptor*)e; + cmd -= ERTS_INET_DRV_CONTROL_MAGIC_NUMBER; switch(cmd) { case INET_REQ_OPEN: { /* open socket and return internal index */ int domain; @@ -12180,6 +12185,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int type = SOCK_DGRAM; int af = AF_INET; + cmd -= ERTS_INET_DRV_CONTROL_MAGIC_NUMBER; switch(cmd) { case INET_REQ_OPEN: /* open socket and return internal index */ DEBUGF(("packet_inet_ctl(%ld): OPEN\r\n", (long)desc->port)); @@ -12956,38 +12962,40 @@ make_noninheritable_handle(SOCKET s) static void fire_multi_timers(tcp_descriptor *desc, ErlDrvPort port, ErlDrvData data) { - ErlDrvTime next_timeout; - MultiTimerData *curr = desc->mtd; - if (!curr) { - ASSERT(0); - return; + ErlDrvTime next_timeout = 0; + if (!desc->mtd) { + ASSERT(0); + return; } #ifdef DEBUG { ErlDrvTime chk = erl_drv_monotonic_time(ERL_DRV_MSEC); - ASSERT(chk >= curr->when); + ASSERT(chk >= desc->mtd->when); } #endif do { - MultiTimerData *save = curr; + MultiTimerData save = *desc->mtd; - (*(save->timeout_function))(data,save->caller); + /* We first remove the timer so that the timeout_functions has + can call clean_multi_timers without breaking anything */ + if (desc->mtd_cache == NULL) { + desc->mtd_cache = desc->mtd; + } else { + FREE(desc->mtd); + } - curr = curr->next; + desc->mtd = save.next; + if (desc->mtd != NULL) + desc->mtd->prev = NULL; - if (desc->mtd_cache == NULL) - desc->mtd_cache = save; - else - FREE(save); + (*(save.timeout_function))(data,save.caller); - if (curr == NULL) { - desc->mtd = NULL; + if (desc->mtd == NULL) return; - } - curr->prev = NULL; - next_timeout = curr->when - erl_drv_monotonic_time(ERL_DRV_MSEC); + + next_timeout = desc->mtd->when - erl_drv_monotonic_time(ERL_DRV_MSEC); } while (next_timeout <= 0); - desc->mtd = curr; + driver_set_timer(port, (unsigned long) next_timeout); } diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 28c6cc0f94..d2a524cb6c 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -394,6 +394,8 @@ static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data, { char resbuff[2*sizeof(Uint32)]; ErlDrvSizeT res_size; + + command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER; switch (command) { case CTRL_OP_GET_WINSIZE: { @@ -419,7 +421,7 @@ static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data, } break; default: - return 0; + return -1; } if (rlen < res_size) { *rbuf = driver_alloc(res_size); diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 99e7fb25a4..d19bfa3079 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -176,6 +176,8 @@ static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data, { char resbuff[2*sizeof(Uint32)]; ErlDrvSizeT res_size; + + command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER; switch (command) { case CTRL_OP_GET_WINSIZE: { @@ -201,7 +203,7 @@ static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data, } break; default: - return 0; + return -1; } if (rlen < res_size) { *rbuf = driver_alloc(res_size); diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c index b34bf11205..a78ee62677 100644 --- a/erts/emulator/nifs/common/prim_file_nif.c +++ b/erts/emulator/nifs/common/prim_file_nif.c @@ -38,7 +38,6 @@ static void unload(ErlNifEnv *env, void* priv_data); static ErlNifResourceType *efile_resource_type; -static ERL_NIF_TERM am_erts_prim_file; static ERL_NIF_TERM am_close; static ERL_NIF_TERM am_ok; @@ -220,7 +219,6 @@ static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM prim_file_pid) ASSERT(!"bad pid passed to prim_file_nif"); } - am_erts_prim_file = enif_make_atom(env, "erts_prim_file"); am_close = enif_make_atom(env, "close"); am_ok = enif_make_atom(env, "ok"); @@ -933,7 +931,7 @@ static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_ posix_errno_t posix_errno; efile_path_t path; - Uint permissions; + unsigned int permissions; if(argc != 2 || !enif_get_uint(env, argv[1], &permissions)) { return enif_make_badarg(env); @@ -952,7 +950,7 @@ static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a posix_errno_t posix_errno; efile_path_t path; - Sint uid, gid; + int uid, gid; if(argc != 3 || !enif_get_int(env, argv[1], &uid) || !enif_get_int(env, argv[2], &gid)) { diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index c681fa481f..c39cd01e1c 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -962,7 +962,7 @@ enif_select(ErlNifEnv* env, ErtsDrvSelectDataState *free_select = NULL; ErtsNifSelectDataState *free_nif = NULL; - ASSERT(!resource->monitors); + ASSERT(!erts_dbg_is_resource_dying(resource)); #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS if (!grow_drv_ev_state(fd)) { @@ -2262,14 +2262,14 @@ erts_check_io_info(void *proc) #if ERTS_POLL_USE_FALLBACK erts_poll_info_flbk(get_fallback_pollset(), &piv[0]); - piv[0].poll_threads = 1; + piv[0].poll_threads = 0; piv[0].active_fds = 0; piv++; #endif #if ERTS_POLL_USE_SCHEDULER_POLLING erts_poll_info(get_scheduler_pollset(0), &piv[0]); - piv[0].poll_threads = 1; + piv[0].poll_threads = 0; piv[0].active_fds = 0; piv++; #endif diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 51d50933ff..c71d23f58c 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -872,8 +872,8 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) } } -#if defined(EV_DISPATCH) && !defined(__OpenBSD__) - /* If we have EV_DISPATCH we use it, unless we are on OpenBSD as the +#if defined(EV_DISPATCH) && !(defined(__OpenBSD__) || defined(__NetBSD__)) + /* If we have EV_DISPATCH we use it, unless we are on OpenBSD/NetBSD as the behavior of EV_EOF seems to be edge triggered there and we need it to be level triggered. @@ -2326,6 +2326,7 @@ uint32_t epoll_events(int kp_fd, int fd) { /* For epoll we read the information about what is selected upon from the proc fs.*/ char fname[30]; + char s[256]; FILE *f; unsigned int pos, flags, mnt_id; int line = 0; @@ -2343,12 +2344,12 @@ uint32_t epoll_events(int kp_fd, int fd) } if (fscanf(f,"\nmnt_id:\t%x\n", &mnt_id)); line += 3; - while (!feof(f)) { + while (fgets(s, sizeof(s) / sizeof(*s), f)) { /* tfd: 10 events: 40000019 data: 180000000a */ int ev_fd; uint32_t events; uint64_t data; - if (fscanf(f,"tfd:%d events:%x data:%llx\n", &ev_fd, &events, + if (sscanf(s,"tfd:%d events:%x data:%llx", &ev_fd, &events, (unsigned long long*)&data) != 3) { fprintf(stderr,"failed to parse file %s on line %d, errno = %d\n", fname, line, @@ -2392,6 +2393,7 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps, /* For epoll we read the information about what is selected upon from the proc fs.*/ char fname[30]; + char s[256]; FILE *f; unsigned int pos, flags, mnt_id; int line = 0; @@ -2410,12 +2412,12 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps, } if (fscanf(f,"\nmnt_id:\t%x\n", &mnt_id)); line += 3; - while (!feof(f)) { + while (fgets(s, sizeof(s) / sizeof(*s), f)) { /* tfd: 10 events: 40000019 data: 180000000a */ int fd; uint32_t events; uint64_t data; - if (fscanf(f,"tfd:%d events:%x data:%llx\n", &fd, &events, + if (sscanf(s,"tfd:%d events:%x data:%llx", &fd, &events, (unsigned long long*)&data) != 3) { fprintf(stderr,"failed to parse file %s on line %d, errno = %d\n", fname, line, errno); diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 816bdea9c5..042a091db1 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -732,7 +732,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, proto->u.start.fds[1] = ifd[1]; proto->u.start.fds[2] = stderrfd; proto->u.start.port_id = opts->exit_status ? erts_drvport2id(port_num) : THE_NON_VALUE; - if (erl_drv_port_control(forker_port, 'S', (char*)proto, sizeof(*proto))) { + if (erl_drv_port_control(forker_port, ERTS_FORKER_DRV_CONTROL_MAGIC_NUMBER, + (char*)proto, sizeof(*proto))) { /* The forker port has been killed, we close both fd's which will make open_port throw an epipe error */ close(ofd[0]); @@ -759,6 +760,9 @@ static ErlDrvSSizeT spawn_control(ErlDrvData e, unsigned int cmd, char *buf, ErtsSysDriverData *dd = (ErtsSysDriverData*)e; ErtsSysForkerProto *proto = (ErtsSysForkerProto *)buf; + if (cmd != ERTS_SPAWN_DRV_CONTROL_MAGIC_NUMBER) + return -1; + ASSERT(len == sizeof(*proto)); ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld); @@ -799,6 +803,8 @@ static ErlDrvSSizeT fd_control(ErlDrvData drv_data, { int fd = (int)(long)drv_data; char resbuff[2*sizeof(Uint32)]; + + command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER; switch (command) { case FD_CTRL_OP_GET_WINSIZE: { @@ -810,7 +816,7 @@ static ErlDrvSSizeT fd_control(ErlDrvData drv_data, } break; default: - return 0; + return -1; } if (rlen < 2*sizeof(Uint32)) { *rbuf = driver_alloc(2*sizeof(Uint32)); @@ -998,9 +1004,9 @@ static void clear_fd_data(ErtsSysFdData *fdd) fdd->psz = 0; } -static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd) +static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd, int use) { - driver_select(prt, abs(fdd->fd), ERL_DRV_USE_NO_CALLBACK|DO_READ|DO_WRITE, 0); + driver_select(prt, abs(fdd->fd), use ? ERL_DRV_USE_NO_CALLBACK : 0|DO_READ|DO_WRITE, 0); clear_fd_data(fdd); SET_BLOCKING(abs(fdd->fd)); @@ -1020,11 +1026,11 @@ static void fd_stop(ErlDrvData ev) /* Does not close the fds */ if (dd->ifd) { sz += sizeof(ErtsSysFdData); - nbio_stop_fd(prt, dd->ifd); + nbio_stop_fd(prt, dd->ifd, 1); } if (dd->ofd && dd->ofd != dd->ifd) { sz += sizeof(ErtsSysFdData); - nbio_stop_fd(prt, dd->ofd); + nbio_stop_fd(prt, dd->ofd, 1); } erts_free(ERTS_ALC_T_DRV_TAB, dd); @@ -1070,12 +1076,12 @@ static void stop(ErlDrvData ev) ErlDrvPort prt = dd->port_num; if (dd->ifd) { - nbio_stop_fd(prt, dd->ifd); + nbio_stop_fd(prt, dd->ifd, 0); driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE, 0); /* close(ifd); */ } if (dd->ofd && dd->ofd != dd->ifd) { - nbio_stop_fd(prt, dd->ofd); + nbio_stop_fd(prt, dd->ofd, 0); driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE, 0); /* close(ofd); */ } @@ -1693,7 +1699,8 @@ static void forker_sigchld(Eterm port_id, int error) already used by the spawn_driver, we use control instead. Note that when using erl_drv_port_control it is an asynchronous control. */ - erl_drv_port_control(port_id, 'S', (char*)proto, sizeof(*proto)); + erl_drv_port_control(port_id, ERTS_SPAWN_DRV_CONTROL_MAGIC_NUMBER, + (char*)proto, sizeof(*proto)); } static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd) @@ -1778,6 +1785,9 @@ static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf, ErlDrvPort port_num = (ErlDrvPort)e; int res; + if (cmd != ERTS_FORKER_DRV_CONTROL_MAGIC_NUMBER) + return -1; + if (first_call) { /* * Do driver_select here when schedulers and their pollsets have started. diff --git a/erts/emulator/test/atomics_SUITE.erl b/erts/emulator/test/atomics_SUITE.erl index 8c42354770..a5407c42ee 100644 --- a/erts/emulator/test/atomics_SUITE.erl +++ b/erts/emulator/test/atomics_SUITE.erl @@ -126,6 +126,9 @@ unsigned_limits(Config) when is_list(Config) -> Min = atomics:add_get(Ref, 1, 1), Max = atomics:sub_get(Ref, 1, 1), + atomics:put(Ref, 1, Max), + io:format("Max=~p~n", [atomics:get(Ref, 1)]), + {'EXIT',{badarg,_}} = (catch atomics:add(Ref, 1, Max+1)), IncrMin = -(1 bsl (Bits-1)), ok = atomics:put(Ref, 1, -IncrMin), diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 0a42b09903..3b9b9e5989 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -24,7 +24,7 @@ -export([t_div/1, eq_28/1, eq_32/1, eq_big/1, eq_math/1, big_literals/1, borders/1, negative/1, big_float_1/1, big_float_2/1, - bxor_2pow/1, + bxor_2pow/1, band_2pow/1, shift_limit_1/1, powmod/1, system_limit/1, toobig/1, otp_6692/1]). %% Internal exports. @@ -43,7 +43,7 @@ suite() -> all() -> [t_div, eq_28, eq_32, eq_big, eq_math, big_literals, borders, negative, {group, big_float}, shift_limit_1, - bxor_2pow, + bxor_2pow, band_2pow, powmod, system_limit, toobig, otp_6692]. groups() -> @@ -168,7 +168,11 @@ eval({op,_,Op,A0,B0}, LFH) -> Res = eval_op(Op, A, B), erlang:garbage_collect(), Res; -eval({integer,_,I}, _) -> I; +eval({integer,_,I}, _) -> + %% "Parasitic" ("symbiotic"?) test of squaring all numbers + %% found in the test data. + test_squaring(I), + I; eval({call,_,{atom,_,Local},Args0}, LFH) -> Args = eval_list(Args0, LFH), LFH(Local, Args). @@ -192,6 +196,18 @@ eval_op('bxor', A, B) -> A bxor B; eval_op('bsl', A, B) -> A bsl B; eval_op('bsr', A, B) -> A bsr B. +test_squaring(I) -> + %% Multiplying an integer by itself is specially optimized, so we + %% should take special care to test squaring. The optimization + %% will kick in when the two operands have the same address. + Sqr = I * I, + + %% This expression will be multiplied in the usual way, because + %% the the two operands for '*' are stored at different addresses. + Sqr = I * ((I + id(1)) - id(1)), + + ok. + %% Built in test functions fac(0) -> 1; @@ -456,3 +472,38 @@ my_bxor(A, B, N, Acc0) -> false -> Acc0 bor (1 bsl N) end, my_bxor(A bsr 1, B bsr 1, N+1, Acc1). + + +%% ERL-804 +band_2pow(_Config) -> + IL = lists:seq(8*3, 8*16, 4), + JL = lists:seq(0, 64), + [band_2pow_1((1 bsl I), (1 bsl J)) + || I <- IL, J <- JL], + ok. + +band_2pow_1(A, B) -> + for(-1,1, fun(Ad) -> + for(-1,1, fun(Bd) -> + band_2pow_2(A+Ad, B+Bd), + band_2pow_2(-A+Ad, B+Bd), + band_2pow_2(A+Ad, -B+Bd), + band_2pow_2(-A+Ad, -B+Bd) + end) + end). + +band_2pow_2(A, B) -> + Correct = my_band(A, B), + case A band B of + Correct -> ok; + Wrong -> + io:format("~.16# band ~.16#\n", [A,B]), + io:format("Expected ~.16#\n", [Correct]), + io:format("Got ~.16#\n", [Wrong]), + ct:fail({failed, 'band'}) + + end. + +%% Implement band without band +my_band(A, B) -> + bnot ((bnot A) bor (bnot B)). diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index bd62708aa7..1d2ae4fb51 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1069,14 +1069,19 @@ get_stable_check_io_info(N) -> get_check_io_total(ChkIo) -> ct:log("ChkIo = ~p~n",[ChkIo]), {Fallback, Rest} = get_fallback(ChkIo), + OnlyPollThreads = [PS || PS <- Rest, not is_scheduler_pollset(PS)], add_fallback_infos(Fallback, - lists:foldl(fun(Pollset, Acc) -> - lists:zipwith(fun(A, B) -> - add_pollset_infos(A,B) - end, - Pollset, Acc) - end, - hd(Rest), tl(Rest))). + lists:foldl( + fun(Pollset, Acc) -> + lists:zipwith(fun(A, B) -> + add_pollset_infos(A,B) + end, + Pollset, Acc) + end, + hd(OnlyPollThreads), tl(OnlyPollThreads))). + +is_scheduler_pollset(Pollset) -> + proplists:get_value(poll_threads, Pollset) == 0. add_pollset_infos({Tag, A}=TA , {Tag, B}=TB) -> case tag_type(Tag) of diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a2f3489943..ca5f90621f 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1175,6 +1175,15 @@ maps(Config) when is_list(Config) -> M2 = maps_from_list_nif(maps:to_list(M2)), M3 = maps_from_list_nif(maps:to_list(M3)), + %% Test different map sizes (OTP-15567) + repeat_while(fun({35,_}) -> false; + ({K,Map}) -> + Map = maps_from_list_nif(maps:to_list(Map)), + Map = maps:filter(fun(K,V) -> V =:= K*100 end, Map), + {K+1, maps:put(K,K*100,Map)} + end, + {1,#{}}), + has_duplicate_keys = maps_from_list_nif([{1,1},{1,1}]), verify_tmpmem(TmpMem), @@ -2471,6 +2480,13 @@ repeat(0, _, Arg) -> repeat(N, Fun, Arg0) -> repeat(N-1, Fun, Fun(Arg0)). +repeat_while(Fun, Acc0) -> + case Fun(Acc0) of + false -> ok; + Acc1 -> + repeat_while(Fun, Acc1) + end. + check(Exp,Got,Line) -> case Got of Exp -> Exp; diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index f15217814a..6b834705cf 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -504,6 +504,10 @@ t_integer_to_string(Config) when is_list(Config) -> test_its("A", 10, 16), test_its("D4BE", 54462, 16), test_its("-D4BE", -54462, 16), + test_its("FFFFFFFFFF", 1099511627775, 16), + test_its("123456789ABCDEF123456789ABCDEF123456789ABCDEF", + 108977460683796539709587792812439445667270661579197935, + 16), lists:foreach(fun(Value) -> {'EXIT', {badarg, _}} = @@ -515,12 +519,14 @@ t_integer_to_string(Config) when is_list(Config) -> ok. test_its(List,Int) -> - Int = list_to_integer(List), - Int = binary_to_integer(list_to_binary(List)). + List = integer_to_list(Int), + Binary = list_to_binary(List), + Binary = integer_to_binary(Int). test_its(List,Int,Base) -> - Int = list_to_integer(List, Base), - Int = binary_to_integer(list_to_binary(List), Base). + List = integer_to_list(Int, Base), + Binary = list_to_binary(List), + Binary = integer_to_binary(Int, Base). %% Tests binary_to_integer/1. diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl index 58cd3276b0..93eb026ced 100644 --- a/erts/emulator/test/persistent_term_SUITE.erl +++ b/erts/emulator/test/persistent_term_SUITE.erl @@ -6,7 +6,7 @@ %% 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 -%5 +%% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software @@ -21,7 +21,7 @@ -module(persistent_term_SUITE). -include_lib("common_test/include/ct.hrl"). --export([all/0,suite/0, +-export([all/0,suite/0,init_per_suite/1,end_per_suite/1, basic/1,purging/1,sharing/1,get_trapping/1, info/1,info_trapping/1,killed_while_trapping/1, off_heap_values/1,keys/1,collisions/1, @@ -39,39 +39,50 @@ all() -> killed_while_trapping,off_heap_values,keys,collisions, init_restart]. +init_per_suite(Config) -> + %% Put a term in the dict so that we know that the testcases handle + %% stray terms left by stdlib or other test suites. + persistent_term:put(init_per_suite, {?MODULE}), + Config. + +end_per_suite(Config) -> + persistent_term:erase(init_per_suite), + Config. + basic(_Config) -> Chk = chk(), N = 777, Seq = lists:seq(1, N), - par(2, N, Seq), - seq(3, Seq), - seq(3, Seq), %Same values. + par(2, N, Seq, Chk), + seq(3, Seq, Chk), + seq(3, Seq, Chk), %Same values. _ = [begin Key = {?MODULE,{key,I}}, true = persistent_term:erase(Key), false = persistent_term:erase(Key), - {'EXIT',{badarg,_}} = (catch persistent_term:get(Key)) + {'EXIT',{badarg,_}} = (catch persistent_term:get(Key)), + {not_present,Key} = persistent_term:get(Key, {not_present,Key}) end || I <- Seq], - [] = [P || {{?MODULE,_},_}=P <- persistent_term:get()], + [] = [P || {{?MODULE,_},_}=P <- pget(Chk)], chk(Chk). -par(C, N, Seq) -> +par(C, N, Seq, Chk) -> _ = [spawn_link(fun() -> ok = persistent_term:put({?MODULE,{key,I}}, {value,C*I}) end) || I <- Seq], - Result = wait(N), + Result = wait(N, Chk), _ = [begin Double = C*I, {{?MODULE,{key,I}},{value,Double}} = Res end || {I,Res} <- lists:zip(Seq, Result)], ok. -seq(C, Seq) -> +seq(C, Seq, Chk) -> _ = [ok = persistent_term:put({?MODULE,{key,I}}, {value,C*I}) || I <- Seq], - All = persistent_term:get(), - All = [P || {{?MODULE,_},_}=P <- persistent_term:get()], + All = pget(Chk), + All = [P || {{?MODULE,_},_}=P <- All], All = [{Key,persistent_term:get(Key)} || {Key,_} <- All], Result = lists:sort(All), _ = [begin @@ -80,15 +91,15 @@ seq(C, Seq) -> end || {I,Res} <- lists:zip(Seq, Result)], ok. -wait(N) -> - All = [P || {{?MODULE,_},_}=P <- persistent_term:get()], +wait(N, Chk) -> + All = [P || {{?MODULE,_},_}=P <- pget(Chk)], case length(All) of N -> All = [{Key,persistent_term:get(Key)} || {Key,_} <- All], lists:sort(All); _ -> receive after 10 -> ok end, - wait(N) + wait(N, Chk) end. %% Make sure that terms that have been erased are copied into all @@ -200,23 +211,23 @@ get_trapping(_Config) -> _ -> 1000 end, spawn_link(fun() -> get_trapping_create(N) end), - All = do_get_trapping(N, []), + All = do_get_trapping(N, [], Chk), N = get_trapping_check_result(lists:sort(All), 1), erlang:garbage_collect(), get_trapping_erase(N), chk(Chk). -do_get_trapping(N, Prev) -> - case persistent_term:get() of +do_get_trapping(N, Prev, Chk) -> + case pget(Chk) of Prev when length(Prev) >= N -> All = [P || {{?MODULE,{get_trapping,_}},_}=P <- Prev], case length(All) of N -> All; - _ -> do_get_trapping(N, Prev) + _ -> do_get_trapping(N, Prev, Chk) end; New -> receive after 1 -> ok end, - do_get_trapping(N, New) + do_get_trapping(N, New, Chk) end. get_trapping_create(0) -> @@ -331,25 +342,25 @@ info_trapping(_Config) -> _ -> 1000 end, spawn_link(fun() -> info_trapping_create(N) end), - All = do_info_trapping(N, 0), + All = do_info_trapping(N, 0, Chk), N = info_trapping_check_result(lists:sort(All), 1), erlang:garbage_collect(), info_trapping_erase(N), chk(Chk). -do_info_trapping(N, PrevMem) -> +do_info_trapping(N, PrevMem, Chk) -> case info_info() of - {N,Mem} -> + {M,Mem} when M >= N -> true = Mem >= PrevMem, - All = [P || {{?MODULE,{info_trapping,_}},_}=P <- persistent_term:get()], + All = [P || {{?MODULE,{info_trapping,_}},_}=P <- pget(Chk)], case length(All) of N -> All; - _ -> do_info_trapping(N, PrevMem) + _ -> do_info_trapping(N, PrevMem, Chk) end; {_,Mem} -> true = Mem >= PrevMem, receive after 1 -> ok end, - do_info_trapping(N, Mem) + do_info_trapping(N, Mem, Chk) end. info_trapping_create(0) -> @@ -462,17 +473,17 @@ collisions(_Config) -> _ = [V = persistent_term:get(K) || {K,V} <- Kvs], %% Now delete the persistent terms in random order. - collisions_delete(lists:keysort(2, Kvs)), + collisions_delete(lists:keysort(2, Kvs), Chk), chk(Chk). -collisions_delete([{Key,Val}|Kvs]) -> +collisions_delete([{Key,Val}|Kvs], Chk) -> Val = persistent_term:get(Key), true = persistent_term:erase(Key), - true = lists:sort(persistent_term:get()) =:= lists:sort(Kvs), + true = lists:sort(pget(Chk)) =:= lists:sort(Kvs), _ = [V = persistent_term:get(K) || {K,V} <- Kvs], - collisions_delete(Kvs); -collisions_delete([]) -> + collisions_delete(Kvs, Chk); +collisions_delete([], _) -> ok. colliding_keys() -> @@ -589,15 +600,16 @@ do_test_init_restart_cmd(File) -> %% and after each test case. chk() -> - persistent_term:info(). + {persistent_term:info(), persistent_term:get()}. -chk(Chk) -> - Chk = persistent_term:info(), +chk({Info, _Initial} = Chk) -> + Info = persistent_term:info(), Key = {?MODULE,?FUNCTION_NAME}, - ok = persistent_term:put(Key, {term,Chk}), + ok = persistent_term:put(Key, {term,Info}), Term = persistent_term:get(Key), true = persistent_term:erase(Key), chk_not_stuck(Term), + [persistent_term:erase(K) || {K, _} <- pget(Chk)], ok. chk_not_stuck(Term) -> @@ -612,3 +624,6 @@ chk_not_stuck(Term) -> _ -> ok end. + +pget({_, Initial}) -> + persistent_term:get() -- Initial. diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 57eb082d64..b23f77a0b2 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -2104,6 +2104,13 @@ spawn_opt_max_heap_size(_Config) -> error_logger:add_report_handler(?MODULE, self()), + %% flush any prior messages in error_logger + Pid = spawn(fun() -> ok = nok end), + receive + {error, _, {emulator, _, [Pid|_]}} -> + flush() + end, + %% Test that numerical limit works max_heap_size_test(1024, 1024, true, true), @@ -2208,6 +2215,13 @@ receive_unexpected() -> ok end. +flush() -> + receive + _M -> flush() + after 0 -> + ok + end. + %% error_logger report handler proxy init(Pid) -> {ok, Pid}. diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index 2e0dfa42f3..f61949c75b 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1450,28 +1450,26 @@ poll_threads(Config) when is_list(Config) -> {Conc, PollType, KP} = get_ioconfig(Config), {Sched, SchedOnln, _} = get_sstate(Config, ""), + [1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), + [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), + [1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), + if Conc -> - [1, 1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), - [1, 1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), - [1, 1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), - [5, 1] = get_ionum(Config,"+IOt 5 +IOp 1"), - [3, 2, 1] = get_ionum(Config,"+IOt 5 +IOp 2"), - [2, 2, 2, 2, 2, 1] = get_ionum(Config,"+IOt 10 +IOPp 50"), + [5] = get_ionum(Config,"+IOt 5 +IOp 1"), + [3, 2] = get_ionum(Config,"+IOt 5 +IOp 2"), + [2, 2, 2, 2, 2] = get_ionum(Config,"+IOt 10 +IOPp 50"), - [2, 1] = get_ionum(Config, "+S 2 +IOPt 100"), - [4, 1] = get_ionum(Config, "+S 4 +IOPt 100"), - [4, 1] = get_ionum(Config, "+S 4:2 +IOPt 100"), - [4, 4, 1] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"), + [2] = get_ionum(Config, "+S 2 +IOPt 100"), + [4] = get_ionum(Config, "+S 4 +IOPt 100"), + [4] = get_ionum(Config, "+S 4:2 +IOPt 100"), + [4, 4] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"), fail = get_ionum(Config, "+IOt 1 +IOp 2"), ok; not Conc -> - [1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), - [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), - [1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 1"), [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 2"), @@ -1515,7 +1513,8 @@ get_iostate(Config, Cmd)-> erlang:system_info(check_io) end]), IO = [IOState || IOState <- IOStates, - proplists:get_value(fallback, IOState) == false], + proplists:get_value(fallback, IOState) == false, + proplists:get_value(poll_threads, IOState) /= 0], stop_node(Node), IO; {error,timeout} -> diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 21ab6b378a..4e663fed7f 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -37,8 +37,9 @@ -export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1, - ets_count/1, - atom_count/1]). + ets_count/1, atom_count/1, system_logger/1]). + +-export([init/1, handle_event/2, handle_call/2]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -46,8 +47,8 @@ suite() -> all() -> [process_count, system_version, misc_smoke_tests, - ets_count, - heap_size, wordsize, memory, ets_limit, atom_limit, atom_count]. + ets_count, heap_size, wordsize, memory, ets_limit, atom_limit, atom_count, + system_logger]. %%% %%% The test cases ------------------------------------------------------------- @@ -573,3 +574,78 @@ atom_count(Config) when is_list(Config) -> true = Limit >= Count2, true = Count2 > Count1, ok. + + +system_logger(Config) when is_list(Config) -> + + TC = self(), + + ok = error_logger:add_report_handler(?MODULE, [TC]), + + generate_log_event(), + + flush(1, report_handler), + + Initial = erlang:system_info(system_logger), + + {Logger,_} = spawn_monitor(fun F() -> receive M -> TC ! {system_logger,M}, F() end end), + + Initial = erlang:system_flag(system_logger, Logger), + Logger = erlang:system_info(system_logger), + + generate_log_event(), + flush(1, system_logger), + + Logger = erlang:system_flag(system_logger, Logger), + + generate_log_event(), + flush(1, system_logger), + + exit(Logger, die), + receive {'DOWN',_,_,_,_} -> ok end, + + generate_log_event(), + flush(1, report_handler), + + logger = erlang:system_info(system_logger), + + logger = erlang:system_flag(system_logger, undefined), + generate_log_event(), + flush(), + + undefined = erlang:system_flag(system_logger, Initial), + + ok. + +flush() -> + receive + M -> + ct:fail({unexpected_message, M}) + after 0 -> + ok + end. + +flush(0, _Pat) -> + flush(); +flush(Cnt, Pat) -> + receive + M when element(1,M) =:= Pat -> + ct:log("~p",[M]), + flush(Cnt-1, Pat) + after 500 -> + ct:fail({missing, Cnt, Pat}) + end. + +generate_log_event() -> + {_Pid, Ref} = spawn_monitor(fun() -> ok = nok end), + receive {'DOWN', Ref, _, _, _} -> ok end. + +init([To]) -> + {ok, To}. + +handle_call(Msg, State) -> + {ok, Msg, State}. + +handle_event(Event, State) -> + State ! {report_handler, Event}, + {ok, State}. diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index 1c52e1a934..6549108126 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -251,7 +251,7 @@ pollset_size(Config) when is_list(Config) -> end. check_io_debug(Config) when is_list(Config) -> - case lists:keysearch(name, 1, erlang:system_info(check_io)) of + case lists:keysearch(name, 1, hd(erlang:system_info(check_io))) of {value, {name, erts_poll}} -> check_io_debug_test(); _ -> {skipped, "Not implemented in this emulator"} end. diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 0cb01fd4ef..23bbb86333 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -174,6 +174,7 @@ static char *plusz_val_switches[] = { "dbbl", "dntgc", "ebwt", + "tma", NULL }; diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index bd218ff725..bb843a616b 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -500,7 +500,7 @@ message_loop(erlin_fd, erlout_fd) #if defined(__WIN32__) static void -kill_old_erlang(void){ +kill_old_erlang(int reason){ HANDLE erlh; DWORD exit_code; char* envvar = NULL; @@ -536,7 +536,8 @@ kill_old_erlang(void){ } #else static void -kill_old_erlang(void){ +kill_old_erlang(int reason) +{ pid_t pid; int i, res; int sig = SIGKILL; @@ -546,14 +547,25 @@ kill_old_erlang(void){ if (envvar && strcmp(envvar, "TRUE") == 0) return; - envvar = get_env(HEART_KILL_SIGNAL); - if (envvar && strcmp(envvar, "SIGABRT") == 0) { - print_error("kill signal SIGABRT requested"); - sig = SIGABRT; - } - if(heart_beat_kill_pid != 0){ - pid = (pid_t) heart_beat_kill_pid; + pid = (pid_t) heart_beat_kill_pid; + if (reason == R_CLOSED) { + print_error("Wait 5 seconds for Erlang to terminate nicely"); + for (i=0; i < 5; ++i) { + res = kill(pid, 0); /* check if alive */ + if (res < 0 && errno == ESRCH) + return; + sleep(1); + } + print_error("Erlang still alive, kill it"); + } + + envvar = get_env(HEART_KILL_SIGNAL); + if (envvar && strcmp(envvar, "SIGABRT") == 0) { + print_error("kill signal SIGABRT requested"); + sig = SIGABRT; + } + res = kill(pid,sig); for(i=0; i < 5 && res == 0; ++i){ sleep(1); @@ -677,7 +689,7 @@ do_terminate(int erlin_fd, int reason) { if(!command) print_error("Would reboot. Terminating."); else { - kill_old_erlang(); + kill_old_erlang(reason); /* High prio combined with system() works badly indeed... */ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); win_system(command); @@ -685,7 +697,7 @@ do_terminate(int erlin_fd, int reason) { } free_env_val(command); } else { - kill_old_erlang(); + kill_old_erlang(reason); /* High prio combined with system() works badly indeed... */ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); win_system(&cmd[0]); @@ -697,13 +709,13 @@ do_terminate(int erlin_fd, int reason) { if(!command) print_error("Would reboot. Terminating."); else { - kill_old_erlang(); + kill_old_erlang(reason); ret = system(command); print_error("Executed \"%s\" -> %d. Terminating.",command, ret); } free_env_val(command); } else { - kill_old_erlang(); + kill_old_erlang(reason); ret = system((char*)&cmd[0]); print_error("Executed \"%s\" -> %d. Terminating.",cmd, ret); } diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile index 83c64d35fd..21a725cb88 100644 --- a/erts/etc/unix/Makefile +++ b/erts/etc/unix/Makefile @@ -30,7 +30,8 @@ opt debug lcnt: etc etc: etp-commands etp-commands: etp-commands.in - $(gen_verbose)sed 's:@ERL_TOP@:${ERL_TOP}:g' etp-commands.in > etp-commands + $(gen_verbose)sed -e 's:@ERL_TOP@:${ERL_TOP}:g' \ + etp-commands.in > etp-commands .PHONY: docs docs: diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 2e034513b0..bcd64d242e 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -224,7 +224,13 @@ while [ $# -gt 0 ]; do shift cargs="$cargs -rr" run_rr=yes - skip_erlexec=yes + case "$1" in + "replay"|"ps") + ;; + *) + skip_erlexec=yes + ;; + esac ;; *) break @@ -307,7 +313,26 @@ if [ "x$GDB" = "x" ]; then exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" elif [ $run_rr = yes ]; then - exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@" + if [ $1 = replay ]; then + shift + cmdfile="/tmp/.cerlgdb.$$" + echo "set \$etp_beam_executable = \"$BINDIR/$EMU_NAME\"" > $cmdfile + if [ "$1" = "-p" ]; then + echo 'set $etp_rr_run_until_beam = 1' >> $cmdfile + fi + cat $ROOTDIR/erts/etc/unix/etp-commands.in >> $cmdfile + exec rr replay -x $cmdfile $* + elif [ $1 = ps ]; then + shift + rr ps $* | head -1 + ChildSetup=`rr ps $* | grep 'erl_child_setup' | awk '{ print $2 }'` + for CS in $ChildSetup; do + rr ps $* | grep -E "^$CS" + done + exit 0 + else + exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@" + fi else exec $EXEC $xargs ${1+"$@"} fi diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index b12a205ba7..bb7b1a73f5 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -149,7 +149,7 @@ define etp-1 else # (($arg0) & 0x3) == 0 if (($arg0) == etp_the_non_value) - printf "<the non-value>" + printf "<the-non-value>" else etp-cp-1 ($arg0) end @@ -1241,7 +1241,7 @@ define etp-sig-int if $etp_sig_tag != etp_the_non_value etp-1 $etp_sig_tag 0 else - print "!ENCODED-DIST-MSG" + printf "!ENCODED-DIST-MSG" end if ($arg0)->m[1] != $etp_nil printf " @token= " @@ -1251,7 +1251,7 @@ define etp-sig-int etp-1 ($arg0)->m[2] 0 else if ($etp_sig_tag & 0x3f) != 0x30 - print "!INVALID-SIGNAL" + printf "!INVALID-SIGNAL" else set $etp_sig_op = (($etp_sig_tag >> 6) & 0xff) set $etp_sig_type = (($etp_sig_tag >> 14) & 0xff) @@ -4326,6 +4326,20 @@ document etp-show %--------------------------------------------------------------------------- end +define etp-rr-run-until-beam + source @ERL_TOP@/erts/etc/unix/etp-rr-run-until-beam.py +end + +document etp-rr-run-until-beam +%--------------------------------------------------------------------------- +% etp-rr-run-until-beam +% +% Use this gdb macro to make cerl -rr replay -p PID walk until +% the correct execute has been made. You may have to change the +% file that is used to debug with. +%--------------------------------------------------------------------------- +end + ############################################################################ # Init # @@ -4363,7 +4377,13 @@ define hook-run set $_exitsignal = -1 end +handle SIGPIPE nostop + etp-init help etp-init -etp-show -etp-system-info +if $etp_rr_run_until_beam + help etp-rr-run-until-beam +else + etp-show + etp-system-info +end diff --git a/erts/etc/unix/etp-rr-run-until-beam.py b/erts/etc/unix/etp-rr-run-until-beam.py new file mode 100644 index 0000000000..078998b910 --- /dev/null +++ b/erts/etc/unix/etp-rr-run-until-beam.py @@ -0,0 +1,45 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2013-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% +# + +has_exited = False + +def stop_handler (event): + global has_exited + if isinstance(event, gdb.SignalEvent): + print("exit code: %s" % (event.stop_signal)) + has_exited = True + +gdb.events.stop.connect (stop_handler) + +gdb.execute('continue') + +while not has_exited: + r = gdb.execute('when', to_string=True) + m = re.match("[^0-9]*([0-9]+)", r) + if m: + event = int(m.group(1)); + gdb.execute('start ' + str(event + 1)); + gdb.execute('continue') + +gdb.events.stop.disconnect (stop_handler) + +gdb.execute('file ' + str(gdb.parse_and_eval("$etp_beam_executable"))) +gdb.execute('break main') +gdb.execute('reverse-continue') diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differindex a5ac24f0b8..f8fb26b728 100644 --- a/erts/preloaded/ebin/atomics.beam +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differindex a1aa34a415..54fe86eb18 100644 --- a/erts/preloaded/ebin/counters.beam +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 37903d24b6..6f5a75a2db 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex 2509f238bf..ec4d6153d1 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 7563663807..ee96878ff7 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex bc697d11d7..ce4a749d38 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex 5b788368af..c22aeb8949 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex b8415a9833..bc53a2e431 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex e2a8c65f38..0845a8405a 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex fee2da33a6..d8c3a26674 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam Binary files differindex 324e111ad1..57ad5c7fdd 100644 --- a/erts/preloaded/ebin/otp_ring0.beam +++ b/erts/preloaded/ebin/otp_ring0.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differindex c73da80a98..c882e4fad4 100644 --- a/erts/preloaded/ebin/persistent_term.beam +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex 75e5b7c9cb..ac5a232dd4 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex ddda4764e1..24911123f9 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 2d1ce7d631..d3d4a75a11 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 558968b58a..d3614d5f16 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex 51721a27a8..45142bd5d2 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 4519b540c4..5465917179 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index ae5f86e017..1605c20f2c 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -297,12 +297,13 @@ check_file_result(Func, Target, {error,Reason}) -> "Target: " ++ TargetStr ++ ". " ++ "Function: " ++ atom_to_list(Func) ++ ". " ++ Process end, - %% this is equal to calling error_logger:error_report/1 which - %% we don't want to do from code_server during system boot + %% This is equal to calling logger:error/2 which + %% we don't want to do from code_server during system boot. + %% We don't want to call logger:timestamp() either. logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report}, #{pid=>self(), gl=>group_leader(), - time=>erlang:monotonic_time(microsecond), + time=>os:system_time(microsecond), error_logger=>#{tag=>error_report, type=>std_error}}}, error diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 1ed6b6b284..5730e999cb 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2526,6 +2526,9 @@ subtract(_,_) -> OldSchedulersOnline when SchedulersOnline :: pos_integer(), OldSchedulersOnline :: pos_integer(); + (system_logger, Logger) -> PrevLogger when + Logger :: logger | undefined | pid(), + PrevLogger :: logger | undefined | pid(); (trace_control_word, TCW) -> OldTCW when TCW :: non_neg_integer(), OldTCW :: non_neg_integer(); @@ -2534,7 +2537,7 @@ subtract(_,_) -> %% These are deliberately not documented (internal_cpu_topology, term()) -> term(); (sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false; - (1,0) -> true. + (reset_seq_trace,true) -> true. system_flag(_Flag, _Value) -> erlang:nif_error(undefined). @@ -2731,8 +2734,9 @@ tuple_to_list(_Tuple) -> (schedulers | schedulers_online) -> pos_integer(); (smp_support) -> boolean(); (start_time) -> integer(); - (system_version) -> string(); (system_architecture) -> string(); + (system_logger) -> logger | undefined | pid(); + (system_version) -> string(); (threads) -> boolean(); (thread_pool_size) -> non_neg_integer(); (time_correction) -> true | false; @@ -3395,60 +3399,15 @@ get_cookie() -> -spec integer_to_list(Integer, Base) -> string() when Integer :: integer(), Base :: 2..36. -integer_to_list(I, 10) -> - erlang:integer_to_list(I); -integer_to_list(I, Base) - when erlang:is_integer(I), erlang:is_integer(Base), - Base >= 2, Base =< 1+$Z-$A+10 -> - if I < 0 -> - [$-|integer_to_list(-I, Base, [])]; - true -> - integer_to_list(I, Base, []) - end; -integer_to_list(I, Base) -> - erlang:error(badarg, [I, Base]). - -integer_to_list(I0, Base, R0) -> - D = I0 rem Base, - I1 = I0 div Base, - R1 = if D >= 10 -> - [D-10+$A|R0]; - true -> - [D+$0|R0] - end, - if I1 =:= 0 -> - R1; - true -> - integer_to_list(I1, Base, R1) - end. +integer_to_list(_I, _Base) -> + erlang:nif_error(undefined). -spec integer_to_binary(Integer, Base) -> binary() when Integer :: integer(), Base :: 2..36. -integer_to_binary(I, 10) -> - erlang:integer_to_binary(I); -integer_to_binary(I, Base) - when erlang:is_integer(I), erlang:is_integer(Base), - Base >= 2, Base =< 1+$Z-$A+10 -> - if I < 0 -> - <<$-,(integer_to_binary(-I, Base, <<>>))/binary>>; - true -> - integer_to_binary(I, Base, <<>>) - end; -integer_to_binary(I, Base) -> - erlang:error(badarg, [I, Base]). - -integer_to_binary(I0, Base, R0) -> - D = I0 rem Base, - I1 = I0 div Base, - R1 = if - D >= 10 -> <<(D-10+$A),R0/binary>>; - true -> <<(D+$0),R0/binary>> - end, - if - I1 =:= 0 -> R1; - true -> integer_to_binary(I1, Base, R1) - end. +integer_to_binary(_I, _Base) -> + erlang:nif_error(undefined). + -record(cpu, {node = -1, processor = -1, diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 8f29a569f2..305b524438 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -97,6 +97,8 @@ -export([counters_new/1, counters_get/2, counters_add/3, counters_put/3, counters_info/1]). +-export([spawn_system_process/3]). + %% %% Await result of send to port %% @@ -726,3 +728,10 @@ counters_put(_Ref, _Ix, _Value) -> -spec counters_info(reference()) -> #{}. counters_info(_Ref) -> erlang:nif_error(undef). + +-spec spawn_system_process(Mod, Func, Args) -> pid() when + Mod :: atom(), + Func :: atom(), + Args :: list(). +spawn_system_process(_Mod, _Func, _Args) -> + erlang:nif_error(undefined). diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index b4b8b3bf9b..86b4f35ae5 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -483,13 +483,16 @@ do_handle_msg(Msg,State) -> {From, {ensure_loaded, _}} -> From ! {init, not_allowed}; X -> + %% This is equal to calling logger:info/3 which we don't + %% want to do from the init process, at least not during + %% system boot. We don't want to call logger:timestamp() + %% either. case whereis(user) of undefined -> - Time = erlang:monotonic_time(microsecond), catch logger ! {log, info, "init got unexpected: ~p", [X], #{pid=>self(), gl=>self(), - time=>Time, + time=>os:system_time(microsecond), error_logger=>#{tag=>info_msg}}}; User -> User ! X, diff --git a/erts/preloaded/src/persistent_term.erl b/erts/preloaded/src/persistent_term.erl index 5d0c266127..ee7e49b6cb 100644 --- a/erts/preloaded/src/persistent_term.erl +++ b/erts/preloaded/src/persistent_term.erl @@ -19,7 +19,7 @@ %% -module(persistent_term). --export([erase/1,get/0,get/1,info/0,put/2]). +-export([erase/1,get/0,get/1,get/2,info/0,put/2]). -type key() :: term(). -type value() :: term(). @@ -41,6 +41,13 @@ get() -> get(_Key) -> erlang:nif_error(undef). +-spec get(Key, Default) -> Value when + Key :: key(), + Default :: value(), + Value :: value(). +get(_Key, _Default) -> + erlang:nif_error(undef). + -spec info() -> Info when Info :: #{'count':=Count,'memory':=Memory}, Count :: non_neg_integer(), diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 5fc22bc582..1aa5d85c64 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -46,7 +46,7 @@ -define(MIN_READLINE_SIZE, 256). -define(LARGEFILESIZE, (1 bsl 63)). --export([copy/3]). +-export([copy/3, start/0]). -include("file_int.hrl"). @@ -85,14 +85,21 @@ is_translatable(_) -> %% This is a janitor process used to close files whose controlling process has %% died. The emulator will be torn down if this is killed. -delayed_close_loop() -> +start() -> + helper_loop(). + +helper_loop() -> receive {close, FRef} when is_reference(FRef) -> delayed_close_nif(FRef); _ -> ok end, - delayed_close_loop(). + helper_loop(). -%% +on_load() -> + %% This is spawned as a system process to prevent init:restart/0 from + %% killing it. + Pid = erts_internal:spawn_system_process(?MODULE, start, []), + ok = erlang:load_nif(atom_to_list(?MODULE), Pid). %% Returns {error, Reason} | {ok, BytesCopied} copy(#file_descriptor{module = ?MODULE} = Source, @@ -103,14 +110,6 @@ copy(#file_descriptor{module = ?MODULE} = Source, %% XXX Should be moved down to the driver for optimization. file:copy_opened(Source, Dest, Length). -on_load() -> - Pid = spawn(fun() -> - process_flag(trap_exit, true), - delayed_close_loop() - end), - true = register(erts_prim_file, Pid), - ok = erlang:load_nif(atom_to_list(?MODULE), Pid). - open(Name, Modes) -> %% The try/catch pattern seen here is used throughout the file to adhere to %% the public file interface, which has leaked through for ages because of @@ -573,12 +572,13 @@ list_dir_convert([RawName | Rest], SkipInvalid, Result) -> {error, ignore} -> list_dir_convert(Rest, SkipInvalid, Result); {error, warning} -> - %% this is equal to calling error_logger:warning_msg/2 which - %% we don't want to do from code_server during system boot + %% This is equal to calling logger:warning/3 which + %% we don't want to do from code_server during system boot. + %% We don't want to call logger:timestamp() either. logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName], #{pid=>self(), gl=>group_leader(), - time=>erlang:system_time(microsecond), + time=>os:system_time(microsecond), error_logger=>#{tag=>warning_msg}}}, list_dir_convert(Rest, SkipInvalid, Result); {error, _} -> diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index f1d938c9a4..2820a5bef4 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2018. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -172,8 +172,18 @@ close(S) when is_port(S) -> %% and is a contradiction in itself. %% We have hereby done our best... %% - Tref = erlang:start_timer(T * 1000, self(), close_port), - close_pend_loop(S, Tref, undefined); + case subscribe(S, [subs_empty_out_q]) of + {ok, [{subs_empty_out_q,0}]} -> + close_port(S); + {ok, [{subs_empty_out_q,N}]} when N > 0 -> + %% Wait for pending output to be sent + Tref = erlang:start_timer(T * 1000, self(), close_port), + close_pend_loop(S, Tref, N); + _ -> + %% Subscribe failed - wait full time + Tref = erlang:start_timer(T * 1000, self(), close_port), + close_pend_loop(S, Tref, undefined) + end; _ -> % Regard this as {ok,{false,_}} case subscribe(S, [subs_empty_out_q]) of {ok, [{subs_empty_out_q,N}]} when N > 0 -> @@ -2679,12 +2689,13 @@ get_ip6([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16 | T]) -> ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, T }. +-define(ERTS_INET_DRV_CONTROL_MAGIC_NUMBER, 16#03f1a300). %% Control command ctl_cmd(Port, Cmd, Args) -> ?DBG_FORMAT("prim_inet:ctl_cmd(~p, ~p, ~p)~n", [Port,Cmd,Args]), Result = - try erlang:port_control(Port, Cmd, Args) of + try erlang:port_control(Port, Cmd+?ERTS_INET_DRV_CONTROL_MAGIC_NUMBER, Args) of [?INET_REP_OK|Reply] -> {ok,Reply}; [?INET_REP] -> inet_reply; [?INET_REP_ERROR|Err] -> {error,list_to_atom(Err)} diff --git a/erts/vsn.mk b/erts/vsn.mk index 0adee946cc..e4bdb1a8eb 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.2 +VSN = 10.2.4 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml index ccf07a9cc1..e86dbd9f5e 100644 --- a/lib/asn1/doc/src/asn1ct.xml +++ b/lib/asn1/doc/src/asn1ct.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>asn1.sgml</file> </header> - <module>asn1ct</module> + <module since="">asn1ct</module> <modulesummary>ASN.1 compiler and compile-time support functions</modulesummary> <description> <p>The ASN.1 compiler takes an ASN.1 module as input and generates a @@ -72,8 +72,8 @@ <funcs> <func> - <name>compile(Asn1module) -> ok | {error, Reason}</name> - <name>compile(Asn1module, Options) -> ok | {error, Reason}</name> + <name since="">compile(Asn1module) -> ok | {error, Reason}</name> + <name since="">compile(Asn1module, Options) -> ok | {error, Reason}</name> <fsummary>Compiles an ASN.1 module and generates encode/decode functions according to encoding rules BER or PER.</fsummary> <type> <v>Asn1module = atom() | string()</v> @@ -336,7 +336,7 @@ File3.asn</pre> </func> <func> - <name>value(Module, Type) -> {ok, Value} | {error, Reason}</name> + <name since="">value(Module, Type) -> {ok, Value} | {error, Reason}</name> <fsummary>Creates an ASN.1 value for test purposes.</fsummary> <type> <v>Module = Type = atom()</v> @@ -361,9 +361,9 @@ File3.asn</pre> </func> <func> - <name>test(Module) -> ok | {error, Reason}</name> - <name>test(Module, Type | Options) -> ok | {error, Reason}</name> - <name>test(Module, Type, Value | Options) -> ok | {error, Reason}</name> + <name since="">test(Module) -> ok | {error, Reason}</name> + <name since="">test(Module, Type | Options) -> ok | {error, Reason}</name> + <name since="">test(Module, Type, Value | Options) -> ok | {error, Reason}</name> <fsummary>Performs a test of encode and decode for types in an ASN.1 module.</fsummary> <type> <v>Module = Type = atom()</v> diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index a3b3f927eb..7887a2c3ea 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>common_test_app.sgml</file> </header> - <module>common_test</module> + <module since="">common_test</module> <modulesummary>A framework for automated testing of any target nodes.</modulesummary> <description> @@ -68,7 +68,7 @@ <funcs> <func> - <name>Module:all() -> Tests | {skip,Reason} </name> + <name since="">Module:all() -> Tests | {skip,Reason} </name> <fsummary>Returns the list of all test case groups and test cases in the module.</fsummary> <type> @@ -115,7 +115,7 @@ </func> <func> - <name>Module:groups() -> GroupDefs</name> + <name since="">Module:groups() -> GroupDefs</name> <fsummary>Returns a list of test case group definitions.</fsummary> <type> <v>GroupDefs = [Group]</v> @@ -140,7 +140,7 @@ </func> <func> - <name>Module:suite() -> [Info] </name> + <name since="">Module:suite() -> [Info] </name> <fsummary>Test suite info function (providing default data for the suite).</fsummary> <type> @@ -213,7 +213,7 @@ </func> <func> - <name>Module:init_per_suite(Config) -> NewConfig | {skip,Reason} | + <name since="">Module:init_per_suite(Config) -> NewConfig | {skip,Reason} | {skip_and_save,Reason,SaveConfig}</name> <fsummary>Test suite initializations.</fsummary> <type> @@ -248,7 +248,7 @@ </func> <func> - <name>Module:end_per_suite(Config) -> term() | + <name since="">Module:end_per_suite(Config) -> term() | {save_config,SaveConfig}</name> <fsummary>Test suite finalization.</fsummary> <type> @@ -272,7 +272,7 @@ </func> <func> - <name>Module:group(GroupName) -> [Info] </name> + <name since="OTP R15B">Module:group(GroupName) -> [Info] </name> <fsummary>Test case group information function (providing default data for a test case group, that is, its test cases and subgroups).</fsummary> @@ -352,7 +352,7 @@ </func> <func> - <name>Module:init_per_group(GroupName, Config) -> NewConfig | + <name since="">Module:init_per_group(GroupName, Config) -> NewConfig | {skip,Reason}</name> <fsummary>Test case group initializations.</fsummary> <type> @@ -390,7 +390,7 @@ </func> <func> - <name>Module:end_per_group(GroupName, Config) -> term() | + <name since="">Module:end_per_group(GroupName, Config) -> term() | {return_group_result,Status}</name> <fsummary>Test case group finalization.</fsummary> <type> @@ -424,7 +424,7 @@ </func> <func> - <name>Module:init_per_testcase(TestCase, Config) -> NewConfig | {fail,Reason} | {skip,Reason}</name> + <name since="">Module:init_per_testcase(TestCase, Config) -> NewConfig | {fail,Reason} | {skip,Reason}</name> <fsummary>Test case initializations.</fsummary> <type> <v> TestCase = atom()</v> @@ -454,7 +454,7 @@ </func> <func> - <name>Module:end_per_testcase(TestCase, Config) -> term() | {fail,Reason} | {save_config,SaveConfig}</name> + <name since="">Module:end_per_testcase(TestCase, Config) -> term() | {fail,Reason} | {save_config,SaveConfig}</name> <fsummary>Test case finalization.</fsummary> <type> <v>TestCase = atom()</v> @@ -486,7 +486,7 @@ </func> <func> - <name>Module:Testcase() -> [Info] </name> + <name since="OTP R14B">Module:Testcase() -> [Info] </name> <fsummary>Test case information function.</fsummary> <type> <v>Info = {timetrap,Time} | {require,Required} | {require,Name,Required} | {userdata,UserData} | {silent_connections,Conns}</v> @@ -560,7 +560,7 @@ </func> <func> - <name>Module:Testcase(Config) -> term() | {skip,Reason} | {comment,Comment} | {save_config,SaveConfig} | {skip_and_save,Reason,SaveConfig} | exit() </name> + <name since="OTP R14B">Module:Testcase(Config) -> term() | {skip,Reason} | {comment,Comment} | {save_config,SaveConfig} | {skip_and_save,Reason,SaveConfig} | exit() </name> <fsummary>A test case.</fsummary> <type> <v>Config = SaveConfig = [{Key,Value}]</v> diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml index c0380c4142..83c0ecb309 100644 --- a/lib/common_test/doc/src/ct.xml +++ b/lib/common_test/doc/src/ct.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct.xml</file> </header> - <module>ct</module> + <module since="">ct</module> <modulesummary>Main user interface for the Common Test framework.</modulesummary> <description> @@ -139,7 +139,7 @@ <funcs> <func> - <name>abort_current_testcase(Reason) -> ok | {error, ErrorReason}</name> + <name since="">abort_current_testcase(Reason) -> ok | {error, ErrorReason}</name> <fsummary>Aborts the currently executing test case.</fsummary> <type> <v>Reason = term()</v> @@ -157,7 +157,7 @@ </func> <func> - <name>add_config(Callback, Config) -> ok | {error, Reason}</name> + <name since="OTP R14B">add_config(Callback, Config) -> ok | {error, Reason}</name> <fsummary>Loads configuration variables using the specified callback module and configuration string.</fsummary> <type> @@ -176,7 +176,7 @@ </func> <func> - <name>break(Comment) -> ok | {error, Reason}</name> + <name since="OTP R15B02">break(Comment) -> ok | {error, Reason}</name> <fsummary>Cancels any active timetrap and pause the execution of the current test case until the user calls function continue/0.</fsummary> <type> @@ -206,7 +206,7 @@ </func> <func> - <name>break(TestCase, Comment) -> ok | {error, Reason}</name> + <name since="OTP R15B02">break(TestCase, Comment) -> ok | {error, Reason}</name> <fsummary>Works the same way as break/1, only argument TestCase makes it possible to pause a test case executing in a parallel group.</fsummary> <type> @@ -228,7 +228,7 @@ </func> <func> - <name>capture_get() -> ListOfStrings</name> + <name since="OTP R15B">capture_get() -> ListOfStrings</name> <fsummary>Equivalent to capture_get([default]).</fsummary> <type> <v>ListOfStrings = [string()]</v> @@ -240,7 +240,7 @@ </func> <func> - <name>capture_get(ExclCategories) -> ListOfStrings</name> + <name since="OTP R15B">capture_get(ExclCategories) -> ListOfStrings</name> <fsummary>Returns and purges the list of text strings buffered during the latest session of capturing printouts to stdout.</fsummary> <type> @@ -262,7 +262,7 @@ </func> <func> - <name>capture_start() -> ok</name> + <name since="OTP R15B">capture_start() -> ok</name> <fsummary>Starts capturing all text strings printed to stdout during execution of the test case.</fsummary> <desc><marker id="capture_start-0"/> @@ -276,7 +276,7 @@ </func> <func> - <name>capture_stop() -> ok</name> + <name since="OTP R15B">capture_stop() -> ok</name> <fsummary>Stops capturing text strings (a session started with capture_start/0).</fsummary> <desc><marker id="capture_stop-0"/> @@ -290,7 +290,7 @@ </func> <func> - <name>comment(Comment) -> ok</name> + <name since="">comment(Comment) -> ok</name> <fsummary>Prints the specified Comment in the comment field in the table on the test suite result page.</fsummary> <type> @@ -307,7 +307,7 @@ </func> <func> - <name>comment(Format, Args) -> ok</name> + <name since="OTP R15B">comment(Format, Args) -> ok</name> <fsummary>Prints the formatted string in the comment field in the table on the test suite result page.</fsummary> <type> @@ -326,7 +326,7 @@ </func> <func> - <name>continue() -> ok</name> + <name since="OTP R15B02">continue() -> ok</name> <fsummary>This function must be called to continue after a test case (not executing in a parallel group) has called break/1.</fsummary> <desc><marker id="continue-0"/> @@ -337,7 +337,7 @@ </func> <func> - <name>continue(TestCase) -> ok</name> + <name since="OTP R15B02">continue(TestCase) -> ok</name> <fsummary>This function must be called to continue after a test case has called break/2.</fsummary> <type> @@ -353,7 +353,7 @@ </func> <func> - <name>decrypt_config_file(EncryptFileName, TargetFileName) -> ok | {error, Reason}</name> + <name since="">decrypt_config_file(EncryptFileName, TargetFileName) -> ok | {error, Reason}</name> <fsummary>Decrypts EncryptFileName, previously generated with encrypt_config_file/2,3.</fsummary> <type> @@ -372,7 +372,7 @@ </func> <func> - <name>decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) -> ok | {error, Reason}</name> + <name since="">decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) -> ok | {error, Reason}</name> <fsummary>Decrypts EncryptFileName, previously generated with encrypt_config_file/2,3.</fsummary> <type> @@ -390,7 +390,7 @@ </func> <func> - <name>encrypt_config_file(SrcFileName, EncryptFileName) -> ok | {error, Reason}</name> + <name since="">encrypt_config_file(SrcFileName, EncryptFileName) -> ok | {error, Reason}</name> <fsummary>Encrypts the source configuration file with DES3 and saves the result in file EncryptFileName.</fsummary> <type> @@ -416,7 +416,7 @@ </func> <func> - <name>encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) -> ok | {error, Reason}</name> + <name since="">encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) -> ok | {error, Reason}</name> <fsummary>Encrypts the source configuration file with DES3 and saves the result in the target file EncryptFileName.</fsummary> <type> @@ -442,7 +442,7 @@ </func> <func> - <name>fail(Reason) -> ok</name> + <name since="">fail(Reason) -> ok</name> <fsummary>Terminates a test case with the specified error Reason.</fsummary> <type> @@ -454,7 +454,7 @@ </func> <func> - <name>fail(Format, Args) -> ok</name> + <name since="OTP R15B">fail(Format, Args) -> ok</name> <fsummary>Terminates a test case with an error message specified by a format string and a list of values (used as arguments to io_lib:format/2).</fsummary> @@ -470,7 +470,7 @@ </func> <func> - <name>get_config(Required) -> Value</name> + <name since="">get_config(Required) -> Value</name> <fsummary>Equivalent to get_config(Required, undefined, []).</fsummary> <desc><marker id="get_config-1"/> <p>Equivalent to <seealso marker="#get_config-3"><c>ct:get_config(Required, @@ -479,7 +479,7 @@ </func> <func> - <name>get_config(Required, Default) -> Value</name> + <name since="">get_config(Required, Default) -> Value</name> <fsummary>Equivalent to get_config(Required, Default, []).</fsummary> <desc><marker id="get_config-2"/> <p>Equivalent to <seealso marker="#get_config-3"><c>ct:get_config(Required, @@ -488,7 +488,7 @@ </func> <func> - <name>get_config(Required, Default, Opts) -> ValueOrElement</name> + <name since="">get_config(Required, Default, Opts) -> ValueOrElement</name> <fsummary>Reads configuration data values.</fsummary> <type> <v>Required = KeyOrName | {KeyOrName, SubKey} | {KeyOrName, SubKey, SubKey}</v> @@ -554,7 +554,7 @@ </func> <func> - <name>get_event_mgr_ref() -> EvMgrRef</name> + <name since="OTP 17.5">get_event_mgr_ref() -> EvMgrRef</name> <fsummary>Gets a reference to the <c>Common Test</c> event manager.</fsummary> <type> <v>EvMgrRef = atom()</v> @@ -572,7 +572,7 @@ </func> <func> - <name>get_progname() -> string()</name> + <name since="OTP 21.0">get_progname() -> string()</name> <fsummary>Returns the command used to start this Erlang instance.</fsummary> <desc><marker id="get_progname-0"/> <p>Returns the command used to start this Erlang instance. @@ -582,7 +582,7 @@ </func> <func> - <name>get_status() -> TestStatus | {error, Reason} | no_tests_running</name> + <name since="">get_status() -> TestStatus | {error, Reason} | no_tests_running</name> <fsummary>Returns status of ongoing test.</fsummary> <type> <v>TestStatus = [StatusElem]</v> @@ -608,7 +608,7 @@ </func> <func> - <name>get_target_name(Handle) -> {ok, TargetName} | {error, Reason}</name> + <name since="">get_target_name(Handle) -> {ok, TargetName} | {error, Reason}</name> <fsummary>Returns the name of the target that the specified connection belongs to.</fsummary> <type> @@ -622,7 +622,7 @@ </func> <func> - <name>get_testspec_terms() -> TestSpecTerms | undefined</name> + <name since="OTP 18.0">get_testspec_terms() -> TestSpecTerms | undefined</name> <fsummary>Gets a list of all test specification terms used to configure and run this test.</fsummary> <type> @@ -636,7 +636,7 @@ </func> <func> - <name>get_testspec_terms(Tags) -> TestSpecTerms | undefined</name> + <name since="OTP 18.0">get_testspec_terms(Tags) -> TestSpecTerms | undefined</name> <fsummary>Reads one or more terms from the test specification used to configure and run this test.</fsummary> <type> @@ -663,7 +663,7 @@ </func> <func> - <name>get_timetrap_info() -> {Time, {Scaling,ScaleVal}}</name> + <name since="OTP R15B">get_timetrap_info() -> {Time, {Scaling,ScaleVal}}</name> <fsummary>Reads information about the timetrap set for the current test case.</fsummary> <type> @@ -682,7 +682,7 @@ </func> <func> - <name>get_verbosity(Category) -> Level | undefined</name> + <name since="OTP 19.1">get_verbosity(Category) -> Level | undefined</name> <fsummary>Read the verbosity level for a logging category.</fsummary> <type> <v>Category = default | atom()</v> @@ -697,7 +697,7 @@ </func> <func> - <name>install(Opts) -> ok | {error, Reason}</name> + <name since="">install(Opts) -> ok | {error, Reason}</name> <fsummary>Installs configuration files and event handlers.</fsummary> <type> <v>Opts = [Opt]</v> @@ -724,7 +724,7 @@ </func> <func> - <name>listenv(Telnet) -> [Env]</name> + <name since="">listenv(Telnet) -> [Env]</name> <fsummary>Performs command listenv on the specified Telnet connection and returns the result as a list of key-value pairs.</fsummary> <type> @@ -740,7 +740,7 @@ </func> <func> - <name>log(Format) -> ok</name> + <name since="">log(Format) -> ok</name> <fsummary>Equivalent to log(default, 50, Format, [], []).</fsummary> <desc><marker id="log-1"/> <p>Equivalent to @@ -749,7 +749,7 @@ </func> <func> - <name>log(X1, X2) -> ok</name> + <name since="">log(X1, X2) -> ok</name> <fsummary>Equivalent to log(Category, Importance, Format, FormatArgs, []).</fsummary> <type> @@ -763,7 +763,7 @@ </func> <func> - <name>log(X1, X2, X3) -> ok</name> + <name since="">log(X1, X2, X3) -> ok</name> <fsummary>Equivalent to log(Category, Importance, Format, FormatArgs, Opts).</fsummary> <type> @@ -778,7 +778,7 @@ </func> <func> - <name>log(X1, X2, X3, X4) -> ok</name> + <name since="OTP R15B02">log(X1, X2, X3, X4) -> ok</name> <fsummary>Equivalent to log(Category, Importance, Format, FormatArgs, Opts).</fsummary> <type> @@ -794,7 +794,7 @@ </func> <func> - <name>log(Category, Importance, Format, FormatArgs, Opts) -> ok</name> + <name since="OTP 18.3">log(Category, Importance, Format, FormatArgs, Opts) -> ok</name> <fsummary>Prints from a test case to the log file.</fsummary> <type> <v>Category = atom()</v> @@ -825,7 +825,7 @@ </func> <func> - <name>make_priv_dir() -> ok | {error, Reason}</name> + <name since="OTP R15B01">make_priv_dir() -> ok | {error, Reason}</name> <fsummary>If the test has been started with option create_priv_dir set to manual_per_tc, in order for the test case to use the private directory, it must first create it by calling this function.</fsummary> @@ -841,7 +841,7 @@ </func> <func> - <name>notify(Name, Data) -> ok</name> + <name since="OTP R15B02">notify(Name, Data) -> ok</name> <fsummary>Sends an asynchronous notification of type Name with Data to the <c>Common Test</c> event manager.</fsummary> <type> @@ -859,7 +859,7 @@ </func> <func> - <name>pal(Format) -> ok</name> + <name since="">pal(Format) -> ok</name> <fsummary>Equivalent to pal(default, 50, Format, [], []).</fsummary> <desc><marker id="pal-1"/> <p>Equivalent to @@ -869,7 +869,7 @@ </func> <func> - <name>pal(X1, X2) -> ok</name> + <name since="">pal(X1, X2) -> ok</name> <fsummary>Equivalent to pal(Category, Importance, Format, FormatArgs, []).</fsummary> <type> @@ -883,7 +883,7 @@ </func> <func> - <name>pal(X1, X2, X3) -> ok</name> + <name since="">pal(X1, X2, X3) -> ok</name> <fsummary>Equivalent to pal(Category, Importance, Format, FormatArgs, Opts).</fsummary> <type> @@ -898,7 +898,7 @@ </func> <func> - <name>pal(X1, X2, X3, X4) -> ok</name> + <name since="OTP R15B02">pal(X1, X2, X3, X4) -> ok</name> <fsummary>Equivalent to pal(Category, Importance, Format, FormatArgs, Opts).</fsummary> <type> @@ -914,7 +914,7 @@ </func> <func> - <name>pal(Category, Importance, Format, FormatArgs, Opts) -> ok</name> + <name since="OTP 19.2">pal(Category, Importance, Format, FormatArgs, Opts) -> ok</name> <fsummary>Prints and logs from a test case.</fsummary> <type> <v>Category = atom()</v> @@ -945,7 +945,7 @@ </func> <func> - <name>parse_table(Data) -> {Heading, Table}</name> + <name since="">parse_table(Data) -> {Heading, Table}</name> <fsummary>Parses the printout from an SQL table and returns a list of tuples.</fsummary> <type> @@ -967,7 +967,7 @@ </func> <func> - <name>print(Format) -> ok</name> + <name since="">print(Format) -> ok</name> <fsummary>Equivalent to print(default, 50, Format, [], []).</fsummary> <desc><marker id="print-1"/> <p>Equivalent to <seealso marker="#print-5"><c>ct:print(default, @@ -976,7 +976,7 @@ </func> <func> - <name>print(X1, X2) -> ok</name> + <name since="OTP R15B02">print(X1, X2) -> ok</name> <fsummary>Equivalent to print(Category, Importance, Format, FormatArgs, []).</fsummary> <type> @@ -990,7 +990,7 @@ </func> <func> - <name>print(X1, X2, X3) -> ok</name> + <name since="">print(X1, X2, X3) -> ok</name> <fsummary>Equivalent to print(Category, Importance, Format, FormatArgs, Opts).</fsummary> <type> @@ -1005,7 +1005,7 @@ </func> <func> - <name>print(X1, X2, X3, X4) -> ok</name> + <name since="OTP R15B02">print(X1, X2, X3, X4) -> ok</name> <fsummary>Equivalent to print(Category, Importance, Format, FormatArgs, Opts).</fsummary> <type> @@ -1021,7 +1021,7 @@ </func> <func> - <name>print(Category, Importance, Format, FormatArgs, Opts) -> ok</name> + <name since="OTP 19.2">print(Category, Importance, Format, FormatArgs, Opts) -> ok</name> <fsummary>Prints from a test case to the console.</fsummary> <type> <v>Category = atom()</v> @@ -1048,7 +1048,7 @@ </func> <func> - <name>reload_config(Required) -> ValueOrElement | {error, Reason}</name> + <name since="OTP R14B">reload_config(Required) -> ValueOrElement | {error, Reason}</name> <fsummary>Reloads configuration file containing specified configuration key.</fsummary> <type> @@ -1071,7 +1071,7 @@ </func> <func> - <name>remaining_test_procs() -> {TestProcs,SharedGL,OtherGLs}</name> + <name since="OTP 20.2">remaining_test_procs() -> {TestProcs,SharedGL,OtherGLs}</name> <fsummary>>This function will return the identity of test- and group leader processes that are still running at the time of this call.</fsummary> <type> @@ -1107,7 +1107,7 @@ </func> <func> - <name>remove_config(Callback, Config) -> ok</name> + <name since="OTP R14B">remove_config(Callback, Config) -> ok</name> <fsummary>Removes configuration variables (together with their aliases) that were loaded with specified callback module and configuration string.</fsummary> @@ -1124,7 +1124,7 @@ </func> <func> - <name>require(Required) -> ok | {error, Reason}</name> + <name since="">require(Required) -> ok | {error, Reason}</name> <fsummary>Checks if the required configuration is available.</fsummary> <type> <v>Required = Key | {Key, SubKeys} | {Key, SubKey, SubKeys}</v> @@ -1178,7 +1178,7 @@ </func> <func> - <name>require(Name, Required) -> ok | {error, Reason}</name> + <name since="">require(Name, Required) -> ok | {error, Reason}</name> <fsummary>Checks if the required configuration is available and gives it a name.</fsummary> <type> @@ -1237,7 +1237,7 @@ </func> <func> - <name>run(TestDirs) -> Result</name> + <name since="">run(TestDirs) -> Result</name> <fsummary>Runs all test cases in all suites in the specified directories.</fsummary> <type> @@ -1251,7 +1251,7 @@ </func> <func> - <name>run(TestDir, Suite) -> Result</name> + <name since="">run(TestDir, Suite) -> Result</name> <fsummary>Runs all test cases in the specified suite.</fsummary> <desc><marker id="run-2"/> <p>Runs all test cases in the specified suite.</p> @@ -1261,7 +1261,7 @@ </func> <func> - <name>run(TestDir, Suite, Cases) -> Result</name> + <name since="">run(TestDir, Suite, Cases) -> Result</name> <fsummary>Runs the specified test cases.</fsummary> <type> <v>TestDir = string()</v> @@ -1283,7 +1283,7 @@ </func> <func> - <name>run_test(Opts) -> Result</name> + <name since="">run_test(Opts) -> Result</name> <fsummary>Runs tests as specified by the combination of options in Opts.</fsummary> <type> @@ -1355,7 +1355,7 @@ </func> <func> - <name>run_testspec(TestSpec) -> Result</name> + <name since="">run_testspec(TestSpec) -> Result</name> <fsummary>Runs a test specified by TestSpec.</fsummary> <type> <v>TestSpec = [term()]</v> @@ -1375,7 +1375,7 @@ </func> <func> - <name>set_verbosity(Category, Level) -> ok</name> + <name since="OTP 19.1">set_verbosity(Category, Level) -> ok</name> <fsummary>Set the verbosity level for a logging category.</fsummary> <type> <v>Category = default | atom()</v> @@ -1390,7 +1390,7 @@ </func> <func> - <name>sleep(Time) -> ok</name> + <name since="OTP R14B">sleep(Time) -> ok</name> <fsummary>This function, similar to timer:sleep/1, suspends the test case for a specified time.</fsummary> <type> @@ -1412,7 +1412,7 @@ </func> <func> - <name>start_interactive() -> ok</name> + <name since="">start_interactive() -> ok</name> <fsummary>Starts <c>Common Test</c> in interactive mode.</fsummary> <desc><marker id="start_interactive-0"/> <p>Starts <c>Common Test</c> in interactive mode.</p> @@ -1440,7 +1440,7 @@ </func> <func> - <name>step(TestDir, Suite, Case) -> Result</name> + <name since="">step(TestDir, Suite, Case) -> Result</name> <fsummary>Steps through a test case with the debugger.</fsummary> <type> <v>Case = atom()</v> @@ -1453,7 +1453,7 @@ </func> <func> - <name>step(TestDir, Suite, Case, Opts) -> Result</name> + <name since="">step(TestDir, Suite, Case, Opts) -> Result</name> <fsummary>Steps through a test case with the debugger.</fsummary> <type> <v>Case = atom()</v> @@ -1470,7 +1470,7 @@ </func> <func> - <name>stop_interactive() -> ok</name> + <name since="">stop_interactive() -> ok</name> <fsummary>Exits the interactive mode.</fsummary> <desc><marker id="stop_interactive-0"/> <p>Exits the interactive mode.</p> @@ -1482,7 +1482,7 @@ </func> <func> - <name>sync_notify(Name, Data) -> ok</name> + <name since="OTP R15B02">sync_notify(Name, Data) -> ok</name> <fsummary>Sends a synchronous notification of type Name with Data to the <c>Common Test</c> event manager.</fsummary> <type> @@ -1501,7 +1501,7 @@ </func> <func> - <name>testcases(TestDir, Suite) -> Testcases | {error, Reason}</name> + <name since="">testcases(TestDir, Suite) -> Testcases | {error, Reason}</name> <fsummary>Returns all test cases in the specified suite.</fsummary> <type> <v>TestDir = string()</v> @@ -1515,7 +1515,7 @@ </func> <func> - <name>timetrap(Time) -> ok</name> + <name since="OTP R14B">timetrap(Time) -> ok</name> <fsummary>Sets a new timetrap for the running test case.</fsummary> <type> <v>Time = {hours, Hours} | {minutes, Mins} | {seconds, Secs} | Millisecs | infinity | Func</v> @@ -1539,7 +1539,7 @@ </func> <func> - <name>userdata(TestDir, Suite) -> SuiteUserData | {error, Reason}</name> + <name since="">userdata(TestDir, Suite) -> SuiteUserData | {error, Reason}</name> <fsummary>Returns any data specified with tag userdata in the list of tuples returned from Suite:suite/0.</fsummary> <type> @@ -1556,7 +1556,7 @@ </func> <func> - <name>userdata(TestDir, Suite, Case::GroupOrCase) -> TCUserData | {error, Reason}</name> + <name since="">userdata(TestDir, Suite, Case::GroupOrCase) -> TCUserData | {error, Reason}</name> <fsummary>Returns any data specified with tag userdata in the list of tuples returned from Suite:group(GroupName) or Suite:Case().</fsummary> <type> diff --git a/lib/common_test/doc/src/ct_cover.xml b/lib/common_test/doc/src/ct_cover.xml index 89d944acbe..61365d3522 100644 --- a/lib/common_test/doc/src/ct_cover.xml +++ b/lib/common_test/doc/src/ct_cover.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_cover.xml</file> </header> - <module>ct_cover</module> + <module since="">ct_cover</module> <modulesummary>Common Test framework code coverage support module. </modulesummary> @@ -47,7 +47,7 @@ <funcs> <func> - <name>add_nodes(Nodes) -> {ok, StartedNodes} | {error, Reason}</name> + <name since="">add_nodes(Nodes) -> {ok, StartedNodes} | {error, Reason}</name> <fsummary>Adds nodes to current cover test (only works if cover support is active).</fsummary> <type> @@ -67,7 +67,7 @@ </func> <func> - <name>cross_cover_analyse(Level, Tests) -> ok</name> + <name since="OTP R16B">cross_cover_analyse(Level, Tests) -> ok</name> <fsummary>Accumulates cover results over multiple tests.</fsummary> <type> <v>Level = overview | details</v> @@ -83,7 +83,7 @@ </func> <func> - <name>remove_nodes(Nodes) -> ok | {error, Reason}</name> + <name since="">remove_nodes(Nodes) -> ok | {error, Reason}</name> <fsummary>Removes nodes from the current cover test.</fsummary> <type> <v>Nodes = [atom()]</v> diff --git a/lib/common_test/doc/src/ct_ftp.xml b/lib/common_test/doc/src/ct_ftp.xml index 592c5eb05d..7ee6049486 100644 --- a/lib/common_test/doc/src/ct_ftp.xml +++ b/lib/common_test/doc/src/ct_ftp.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_ftp.xml</file> </header> - <module>ct_ftp</module> + <module since="">ct_ftp</module> <modulesummary>FTP client module (based on the FTP application).</modulesummary> <description> @@ -59,7 +59,7 @@ <funcs> <func> - <name>cd(Connection, Dir) -> ok | {error, Reason}</name> + <name since="">cd(Connection, Dir) -> ok | {error, Reason}</name> <fsummary>Changes directory on remote host.</fsummary> <type> <v>Connection = connection()</v> @@ -71,7 +71,7 @@ </func> <func> - <name>close(Connection) -> ok | {error, Reason}</name> + <name since="">close(Connection) -> ok | {error, Reason}</name> <fsummary>Closes the FTP connection.</fsummary> <type> <v>Connection = connection()</v> @@ -82,7 +82,7 @@ </func> <func> - <name>delete(Connection, File) -> ok | {error, Reason}</name> + <name since="">delete(Connection, File) -> ok | {error, Reason}</name> <fsummary>Deletes a file on remote host.</fsummary> <type> <v>Connection = connection()</v> @@ -94,7 +94,7 @@ </func> <func> - <name>get(KeyOrName, RemoteFile, LocalFile) -> ok | {error, Reason}</name> + <name since="">get(KeyOrName, RemoteFile, LocalFile) -> ok | {error, Reason}</name> <fsummary>Opens an FTP connection and fetches a file from the remote host.</fsummary> <type> @@ -122,7 +122,7 @@ </func> <func> - <name>ls(Connection, Dir) -> {ok, Listing} | {error, Reason}</name> + <name since="">ls(Connection, Dir) -> {ok, Listing} | {error, Reason}</name> <fsummary>Lists directory Dir.</fsummary> <type> <v>Connection = connection()</v> @@ -135,7 +135,7 @@ </func> <func> - <name>open(KeyOrName) -> {ok, Handle} | {error, Reason}</name> + <name since="">open(KeyOrName) -> {ok, Handle} | {error, Reason}</name> <fsummary>Opens an FTP connection to the specified node.</fsummary> <type> <v>KeyOrName = Key | Name</v> @@ -164,7 +164,7 @@ </func> <func> - <name>put(KeyOrName, LocalFile, RemoteFile) -> ok | {error, Reason}</name> + <name since="">put(KeyOrName, LocalFile, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Opens an FTP connection and sends a file to the remote host.</fsummary> <type> @@ -203,7 +203,7 @@ </func> <func> - <name>recv(Connection, RemoteFile) -> ok | {error, Reason}</name> + <name since="">recv(Connection, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Fetches a file over FTP.</fsummary> <desc><marker id="recv-2"/> <p>Fetches a file over FTP.</p> @@ -215,7 +215,7 @@ </func> <func> - <name>recv(Connection, RemoteFile, LocalFile) -> ok | {error, Reason}</name> + <name since="">recv(Connection, RemoteFile, LocalFile) -> ok | {error, Reason}</name> <fsummary>Fetches a file over FTP.</fsummary> <type> <v>Connection = connection()</v> @@ -230,7 +230,7 @@ </func> <func> - <name>send(Connection, LocalFile) -> ok | {error, Reason}</name> + <name since="">send(Connection, LocalFile) -> ok | {error, Reason}</name> <fsummary>Sends a file over FTP.</fsummary> <desc><marker id="send-2"/> <p>Sends a file over FTP.</p> @@ -243,7 +243,7 @@ </func> <func> - <name>send(Connection, LocalFile, RemoteFile) -> ok | {error, Reason}</name> + <name since="">send(Connection, LocalFile, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Sends a file over FTP.</fsummary> <type> <v>Connection = connection()</v> @@ -258,7 +258,7 @@ </func> <func> - <name>type(Connection, Type) -> ok | {error, Reason}</name> + <name since="">type(Connection, Type) -> ok | {error, Reason}</name> <fsummary>Changes the file transfer type.</fsummary> <type> <v>Connection = connection()</v> diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml index 954be0ffba..048552e4bb 100644 --- a/lib/common_test/doc/src/ct_hooks.xml +++ b/lib/common_test/doc/src/ct_hooks.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>ct_hooks.sgml</file> </header> - <module>ct_hooks</module> + <module since="OTP R14B02">ct_hooks</module> <modulesummary>A callback interface on top of Common Test.</modulesummary> <description> @@ -75,7 +75,7 @@ <funcs> <func> - <name>Module:init(Id, Opts) -> {ok, State} | {ok, State, Priority}</name> + <name since="OTP R14B02">Module:init(Id, Opts) -> {ok, State} | {ok, State, Priority}</name> <fsummary>Initiates the Common Test Hook.</fsummary> <type> <v>Id = reference() | term()</v> @@ -109,7 +109,7 @@ </func> <func> - <name>Module:pre_init_per_suite(SuiteName, InitData, CTHState) -> Result</name> + <name since="OTP R14B02">Module:pre_init_per_suite(SuiteName, InitData, CTHState) -> Result</name> <fsummary>Called before init_per_suite.</fsummary> <type> <v>SuiteName = atom()</v> @@ -161,7 +161,7 @@ </func> <func> - <name>Module:post_init_per_suite(SuiteName, Config, Return, CTHState) -> Result</name> + <name since="OTP R14B02">Module:post_init_per_suite(SuiteName, Config, Return, CTHState) -> Result</name> <fsummary>Called after init_per_suite.</fsummary> <type> <v>SuiteName = atom()</v> @@ -208,7 +208,7 @@ </func> <func> - <name>Module:pre_init_per_group(SuiteName, GroupName, InitData, CTHState) -> Result</name> + <name since="OTP 19.3">Module:pre_init_per_group(SuiteName, GroupName, InitData, CTHState) -> Result</name> <fsummary>Called before init_per_group.</fsummary> <type> <v>SuiteName = atom()</v> @@ -241,7 +241,7 @@ </func> <func> - <name>Module:post_init_per_group(SuiteName, GroupName, Config, Return, CTHState) -> Result</name> + <name since="OTP 19.3">Module:post_init_per_group(SuiteName, GroupName, Config, Return, CTHState) -> Result</name> <fsummary>Called after init_per_group.</fsummary> <type> <v>SuiteName = atom()</v> @@ -274,7 +274,7 @@ </func> <func> - <name>Module:pre_init_per_testcase(SuiteName, TestcaseName, InitData, CTHState) -> Result</name> + <name since="OTP 19.3">Module:pre_init_per_testcase(SuiteName, TestcaseName, InitData, CTHState) -> Result</name> <fsummary>Called before init_per_testcase.</fsummary> <type> <v>SuiteName = atom()</v> @@ -311,7 +311,7 @@ </func> <func> - <name>Module:post_init_per_testcase(SuiteName, TestcaseName, Config, Return, CTHState) -> Result</name> + <name since="OTP 19.3">Module:post_init_per_testcase(SuiteName, TestcaseName, Config, Return, CTHState) -> Result</name> <fsummary>Called after init_per_testcase.</fsummary> <type> <v>SuiteName = atom()</v> @@ -344,7 +344,7 @@ </func> <func> - <name>Module:pre_end_per_testcase(SuiteName, TestcaseName, EndData, CTHState) -> Result</name> + <name since="OTP 19.3">Module:pre_end_per_testcase(SuiteName, TestcaseName, EndData, CTHState) -> Result</name> <fsummary>Called before end_per_testcase.</fsummary> <type> <v>SuiteName = atom()</v> @@ -380,7 +380,7 @@ </func> <func> - <name>Module:post_end_per_testcase(SuiteName, TestcaseName, Config, Return, CTHState) -> Result</name> + <name since="OTP 19.3">Module:post_end_per_testcase(SuiteName, TestcaseName, Config, Return, CTHState) -> Result</name> <fsummary>Called after end_per_testcase.</fsummary> <type> <v>SuiteName = atom()</v> @@ -413,7 +413,7 @@ </func> <func> - <name>Module:pre_end_per_group(SuiteName, GroupName, EndData, CTHState) -> Result</name> + <name since="OTP 19.3">Module:pre_end_per_group(SuiteName, GroupName, EndData, CTHState) -> Result</name> <fsummary>Called before end_per_group.</fsummary> <type> <v>SuiteName = atom()</v> @@ -446,7 +446,7 @@ </func> <func> - <name>Module:post_end_per_group(SuiteName, GroupName, Config, Return, CTHState) -> Result</name> + <name since="OTP 19.3">Module:post_end_per_group(SuiteName, GroupName, Config, Return, CTHState) -> Result</name> <fsummary>Called after end_per_group.</fsummary> <type> <v>SuiteName = atom()</v> @@ -479,7 +479,7 @@ </func> <func> - <name>Module:pre_end_per_suite(SuiteName, EndData, CTHState) -> Result</name> + <name since="OTP R14B02">Module:pre_end_per_suite(SuiteName, EndData, CTHState) -> Result</name> <fsummary>Called before end_per_suite.</fsummary> <type> <v>SuiteName = atom()</v> @@ -506,7 +506,7 @@ </func> <func> - <name>Module:post_end_per_suite(SuiteName, Config, Return, CTHState) -> Result</name> + <name since="OTP R14B02">Module:post_end_per_suite(SuiteName, Config, Return, CTHState) -> Result</name> <fsummary>Called after end_per_suite.</fsummary> <type> <v>SuiteName = atom()</v> @@ -533,7 +533,7 @@ </func> <func> - <name>Module:on_tc_fail(SuiteName, TestName, Reason, CTHState) -> NewCTHState</name> + <name since="OTP 19.3">Module:on_tc_fail(SuiteName, TestName, Reason, CTHState) -> NewCTHState</name> <fsummary>Called after the CTH scope ends.</fsummary> <type> <v>SuiteName = atom()</v> @@ -577,7 +577,7 @@ </func> <func> - <name>Module:on_tc_skip(SuiteName, TestName, Reason, CTHState) -> NewCTHState</name> + <name since="OTP 19.3">Module:on_tc_skip(SuiteName, TestName, Reason, CTHState) -> NewCTHState</name> <fsummary>Called after the CTH scope ends.</fsummary> <type> <v>SuiteName = atom()</v> @@ -623,7 +623,7 @@ </func> <func> - <name>Module:terminate(CTHState)</name> + <name since="OTP R14B02">Module:terminate(CTHState)</name> <fsummary>Called after the CTH scope ends.</fsummary> <type> <v>CTHState = term()</v> @@ -637,7 +637,7 @@ </func> <func> - <name>Module:id(Opts) -> Id</name> + <name since="OTP R14B02">Module:id(Opts) -> Id</name> <fsummary>Called before the init function of a CTH.</fsummary> <type> <v>Opts = term()</v> diff --git a/lib/common_test/doc/src/ct_master.xml b/lib/common_test/doc/src/ct_master.xml index 6bde4644c6..2ab421fe9e 100644 --- a/lib/common_test/doc/src/ct_master.xml +++ b/lib/common_test/doc/src/ct_master.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_master.xml</file> </header> - <module>ct_master</module> + <module since="">ct_master</module> <modulesummary>Distributed test execution control for Common Test.</modulesummary> <description> @@ -46,7 +46,7 @@ <funcs> <func> - <name>abort() -> ok</name> + <name since="">abort() -> ok</name> <fsummary>Stops all running tests.</fsummary> <desc><marker id="abort-0"/> <p>Stops all running tests.</p> @@ -54,7 +54,7 @@ </func> <func> - <name>abort(Nodes) -> ok</name> + <name since="">abort(Nodes) -> ok</name> <fsummary>Stops tests on specified nodes.</fsummary> <type> <v>Nodes = atom() | [atom()]</v> @@ -65,7 +65,7 @@ </func> <func> - <name>basic_html(Bool) -> ok</name> + <name since="OTP R15B01">basic_html(Bool) -> ok</name> <fsummary>If set to true, the ct_master logs are written on a primitive HTML format, not using the <c>Common Test</c> CSS style sheet.</fsummary> <type> @@ -79,7 +79,7 @@ </func> <func> - <name>get_event_mgr_ref() -> MasterEvMgrRef</name> + <name since="OTP 17.5">get_event_mgr_ref() -> MasterEvMgrRef</name> <fsummary>Gets a reference to the <c>Common Test</c> master event manager.</fsummary> <type> @@ -98,7 +98,7 @@ </func> <func> - <name>progress() -> [{Node, Status}]</name> + <name since="">progress() -> [{Node, Status}]</name> <fsummary>Returns test progress.</fsummary> <type> <v>Node = atom()</v> @@ -112,7 +112,7 @@ </func> <func> - <name>run(TestSpecs) -> ok</name> + <name since="">run(TestSpecs) -> ok</name> <fsummary>Equivalent to run(TestSpecs, false, [], []).</fsummary> <type> <v>TestSpecs = string() | [SeparateOrMerged]</v> @@ -124,7 +124,7 @@ </func> <func> - <name>run(TestSpecs, InclNodes, ExclNodes) -> ok</name> + <name since="">run(TestSpecs, InclNodes, ExclNodes) -> ok</name> <fsummary>Equivalent to run(TestSpecs, false, InclNodes, ExclNodes). </fsummary> <type> @@ -140,7 +140,7 @@ </func> <func> - <name>run(TestSpecs, AllowUserTerms, InclNodes, ExclNodes) -> ok</name> + <name since="">run(TestSpecs, AllowUserTerms, InclNodes, ExclNodes) -> ok</name> <fsummary>Tests are spawned on the nodes as specified in TestSpecs. </fsummary> <type> @@ -162,7 +162,7 @@ </func> <func> - <name>run_on_node(TestSpecs, Node) -> ok</name> + <name since="">run_on_node(TestSpecs, Node) -> ok</name> <fsummary>Equivalent to run_on_node(TestSpecs, false, Node).</fsummary> <type> <v>TestSpecs = string() | [SeparateOrMerged]</v> @@ -177,7 +177,7 @@ </func> <func> - <name>run_on_node(TestSpecs, AllowUserTerms, Node) -> ok</name> + <name since="">run_on_node(TestSpecs, AllowUserTerms, Node) -> ok</name> <fsummary>Tests are spawned on Node according to TestSpecs.</fsummary> <type> <v>TestSpecs = string() | [SeparateOrMerged]</v> @@ -191,7 +191,7 @@ </func> <func> - <name>run_test(Node, Opts) -> ok</name> + <name since="">run_test(Node, Opts) -> ok</name> <fsummary>Tests are spawned on Node using ct:run_test/1.</fsummary> <type> <v>Node = atom()</v> diff --git a/lib/common_test/doc/src/ct_netconfc.xml b/lib/common_test/doc/src/ct_netconfc.xml index 7ec8f23073..8fbe5f3df6 100644 --- a/lib/common_test/doc/src/ct_netconfc.xml +++ b/lib/common_test/doc/src/ct_netconfc.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_netconfc.xml</file> </header> - <module>ct_netconfc</module> + <module since="OTP R15B02">ct_netconfc</module> <modulesummary>NETCONF client module.</modulesummary> <description> @@ -312,8 +312,8 @@ <funcs> <func> - <name name="action" arity="2"/> - <name name="action" arity="3"/> + <name name="action" arity="2" since="OTP R15B02"/> + <name name="action" arity="3" since="OTP R15B02"/> <fsummary>Executes an action.</fsummary> <desc> <p>Executes an action. If the return type is void, <c>ok</c> is @@ -322,8 +322,8 @@ </func> <func> - <name name="close_session" arity="1"/> - <name name="close_session" arity="2"/> + <name name="close_session" arity="1" since="OTP R15B02"/> + <name name="close_session" arity="2" since="OTP R15B02"/> <fsummary>Requests graceful termination of the session associated with the client.</fsummary> <desc> @@ -339,7 +339,7 @@ </func> <func> - <name name="connect" arity="1"/> + <name name="connect" arity="1" since="OTP 20.0"/> <fsummary>Opens an SSH connection to a NETCONF server.</fsummary> <desc> <p>Opens an SSH connection to a NETCONF server.</p> @@ -361,7 +361,7 @@ </func> <func> - <name name="connect" arity="2"/> + <name name="connect" arity="2" since="OTP 20.0"/> <fsummary>Opens an SSH connection to a named NETCONF server.</fsummary> <desc> <p>Open an SSH connection to a named NETCONF server.</p> @@ -399,8 +399,8 @@ </func> <func> - <name name="copy_config" arity="3"/> - <name name="copy_config" arity="4"/> + <name name="copy_config" arity="3" since="OTP R15B02"/> + <name name="copy_config" arity="4" since="OTP R15B02"/> <fsummary>Copies configuration data.</fsummary> <desc> <p>Copies configuration data.</p> @@ -412,12 +412,12 @@ </func> <func> - <name>create_subscription(Client) -> Result</name> - <name>create_subscription(Client, Stream) -> Result</name> - <name>create_subscription(Client, Stream, Filter) -> Result</name> - <name>create_subscription(Client, Stream, Filter, Timeout) -> Result</name> - <name name="create_subscription" arity="5" clause_i="2"/> - <name name="create_subscription" arity="6"/> + <name since="OTP R15B02">create_subscription(Client) -> Result</name> + <name since="OTP R15B02">create_subscription(Client, Stream) -> Result</name> + <name since="OTP R15B02">create_subscription(Client, Stream, Filter) -> Result</name> + <name since="OTP R15B02">create_subscription(Client, Stream, Filter, Timeout) -> Result</name> + <name name="create_subscription" arity="5" clause_i="2" since="OTP R15B02"/> + <name name="create_subscription" arity="6" since="OTP R15B02"/> <fsummary>Creates a subscription for event notifications.</fsummary> <desc> <p>Creates a subscription for event notifications.</p> @@ -490,8 +490,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="delete_config" arity="2"/> - <name name="delete_config" arity="3"/> + <name name="delete_config" arity="2" since="OTP R15B02"/> + <name name="delete_config" arity="3" since="OTP R15B02"/> <fsummary>Deletes configuration data.</fsummary> <desc> <p>Deletes configuration data.</p> @@ -502,7 +502,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="disconnect" arity="1"/> + <name name="disconnect" arity="1" since="OTP 20.0"/> <fsummary>Closes the given SSH connection.</fsummary> <desc> <p>Closes the given SSH connection.</p> @@ -514,10 +514,10 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="edit_config" arity="3"/> - <name name="edit_config" arity="4" clause_i="1"/> - <name name="edit_config" arity="4" clause_i="2"/> - <name name="edit_config" arity="5"/> + <name name="edit_config" arity="3" since="OTP R15B02"/> + <name name="edit_config" arity="4" clause_i="1" since="OTP 18.0"/> + <name name="edit_config" arity="4" clause_i="2" since="OTP R15B02"/> + <name name="edit_config" arity="5" since="OTP 18.0"/> <fsummary>Edits configuration data.</fsummary> <desc> <p>Edits configuration data.</p> @@ -542,8 +542,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="get" arity="2"/> - <name name="get" arity="3"/> + <name name="get" arity="2" since="OTP R15B02"/> + <name name="get" arity="3" since="OTP R15B02"/> <fsummary>Gets data.</fsummary> <desc> <p>Gets data.</p> @@ -557,8 +557,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="get_capabilities" arity="1"/> - <name name="get_capabilities" arity="2"/> + <name name="get_capabilities" arity="1" since="OTP R15B02"/> + <name name="get_capabilities" arity="2" since="OTP R15B02"/> <fsummary>Returns the server side capabilities.</fsummary> <desc> <p>Returns the server side capabilities.</p> @@ -582,8 +582,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="get_config" arity="3"/> - <name name="get_config" arity="4"/> + <name name="get_config" arity="3" since="OTP R15B02"/> + <name name="get_config" arity="4" since="OTP R15B02"/> <fsummary>Gets configuration data.</fsummary> <desc> <p>Gets configuration data.</p> @@ -597,10 +597,10 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="get_event_streams" arity="1"/> - <name name="get_event_streams" arity="2" clause_i="1"/> - <name name="get_event_streams" arity="2" clause_i="2"/> - <name name="get_event_streams" arity="3"/> + <name name="get_event_streams" arity="1" since="OTP 20.0"/> + <name name="get_event_streams" arity="2" clause_i="1" since="OTP R15B02"/> + <name name="get_event_streams" arity="2" clause_i="2" since="OTP 20.0"/> + <name name="get_event_streams" arity="3" since="OTP R15B02"/> <fsummary>Sends a request to get the specified event streams.</fsummary> <desc> <p>Sends a request to get the specified event streams.</p> @@ -637,8 +637,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="get_session_id" arity="1"/> - <name name="get_session_id" arity="2"/> + <name name="get_session_id" arity="1" since="OTP R15B02"/> + <name name="get_session_id" arity="2" since="OTP R15B02"/> <fsummary>Returns the session Id associated with the specified client.</fsummary> <desc> @@ -647,9 +647,9 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="hello" arity="1"/> - <name name="hello" arity="2"/> - <name name="hello" arity="3"/> + <name name="hello" arity="1" since="OTP R15B02"/> + <name name="hello" arity="2" since="OTP R15B02"/> + <name name="hello" arity="3" since="OTP 17.5.3"/> <fsummary>Exchanges hello messages with the server.</fsummary> <desc> <p>Exchanges <c>hello</c> messages with the server.</p> @@ -660,8 +660,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="kill_session" arity="2"/> - <name name="kill_session" arity="3"/> + <name name="kill_session" arity="2" since="OTP R15B02"/> + <name name="kill_session" arity="3" since="OTP R15B02"/> <fsummary>Forces termination of the session associated with the supplied session Id.</fsummary> <desc> @@ -682,8 +682,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="lock" arity="2"/> - <name name="lock" arity="3"/> + <name name="lock" arity="2" since="OTP R15B02"/> + <name name="lock" arity="3" since="OTP R15B02"/> <fsummary>Locks the configuration target.</fsummary> <desc> <p>Locks the configuration target.</p> @@ -703,7 +703,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="only_open" arity="1"/> + <name name="only_open" arity="1" since="OTP R15B02"/> <fsummary>Opens a NETCONF session, but does not send hello.</fsummary> <desc> <p>Opens a NETCONF session, but does not send <c>hello</c>.</p> @@ -714,7 +714,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="only_open" arity="2"/> + <name name="only_open" arity="2" since="OTP R15B02"/> <fsummary>Opens a named NETCONF session, but does not send hello.</fsummary> <desc> <p>Opens a named NETCONF session, but does not send <c>hello</c>.</p> @@ -725,7 +725,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="open" arity="1"/> + <name name="open" arity="1" since="OTP R15B02"/> <fsummary>Opens a NETCONF session and exchanges hello messages.</fsummary> <desc> <p>Opens a NETCONF session and exchanges <c>hello</c> messages.</p> @@ -749,7 +749,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="open" arity="2"/> + <name name="open" arity="2" since="OTP R15B02"/> <fsummary>Opens a named NETCONF session and exchanges hello messages.</fsummary> <desc> @@ -791,8 +791,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="send" arity="2"/> - <name name="send" arity="3"/> + <name name="send" arity="2" since="OTP R16B02"/> + <name name="send" arity="3" since="OTP R16B02"/> <fsummary>Sends an XML document to the server.</fsummary> <desc> <p>Sends an XML document to the server.</p> @@ -804,8 +804,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="send_rpc" arity="2"/> - <name name="send_rpc" arity="3"/> + <name name="send_rpc" arity="2" since="OTP R16B02"/> + <name name="send_rpc" arity="3" since="OTP R16B02"/> <fsummary>Sends a NETCONF rpc request to the server.</fsummary> <desc> <p>Sends a NETCONF <c>rpc</c> request to the server.</p> @@ -820,10 +820,10 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="session" arity="1"/> - <name name="session" arity="2" clause_i="1"/> - <name name="session" arity="2" clause_i="2"/> - <name name="session" arity="3"/> + <name name="session" arity="1" since="OTP 20.0"/> + <name name="session" arity="2" clause_i="1" since="OTP 20.0"/> + <name name="session" arity="2" clause_i="2" since="OTP 20.0"/> + <name name="session" arity="3" since="OTP 20.0"/> <fsummary>Opens a NETCONF session as a channel on the given SSH connection, and exchanges hello messages with the server.</fsummary> @@ -848,8 +848,8 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre> </func> <func> - <name name="unlock" arity="2"/> - <name name="unlock" arity="3"/> + <name name="unlock" arity="2" since="OTP R15B02"/> + <name name="unlock" arity="3" since="OTP R15B02"/> <fsummary>Unlocks the configuration target.</fsummary> <desc> <p>Unlocks the configuration target.</p> diff --git a/lib/common_test/doc/src/ct_property_test.xml b/lib/common_test/doc/src/ct_property_test.xml index 028e5eb69f..1e01d9a5d7 100644 --- a/lib/common_test/doc/src/ct_property_test.xml +++ b/lib/common_test/doc/src/ct_property_test.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_property_test.xml</file> </header> - <module>ct_property_test</module> + <module since="OTP 17.3">ct_property_test</module> <modulesummary>EXPERIMENTAL support in Common Test for calling property-based tests.</modulesummary> @@ -79,7 +79,7 @@ <funcs> <func> - <name>init_per_suite(Config) -> Config | {skip, Reason}</name> + <name since="OTP 17.3">init_per_suite(Config) -> Config | {skip, Reason}</name> <fsummary>Initializes Config for property testing.</fsummary> <desc><marker id="init_per_suite-1"/> <p>Initializes <c>Config</c> for property testing.</p> @@ -98,7 +98,7 @@ </func> <func> - <name>quickcheck(Property, Config) -> true | {fail, Reason}</name> + <name since="OTP 17.3">quickcheck(Property, Config) -> true | {fail, Reason}</name> <fsummary>Calls quickcheck and returns the result in a form suitable for Common Test.</fsummary> <desc><marker id="quickcheck-2"/> diff --git a/lib/common_test/doc/src/ct_rpc.xml b/lib/common_test/doc/src/ct_rpc.xml index 90e6b833f7..00a4dcec08 100644 --- a/lib/common_test/doc/src/ct_rpc.xml +++ b/lib/common_test/doc/src/ct_rpc.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_rpc.xml</file> </header> - <module>ct_rpc</module> + <module since="">ct_rpc</module> <modulesummary>Common Test specific layer on Erlang/OTP rpc.</modulesummary> <description> @@ -43,7 +43,7 @@ <funcs> <func> - <name>app_node(App, Candidates) -> NodeName</name> + <name since="">app_node(App, Candidates) -> NodeName</name> <fsummary>From a set of candidate nodes determines which of them is running the application App.</fsummary> <type> @@ -61,7 +61,7 @@ </func> <func> - <name>app_node(App, Candidates, FailOnBadRPC) -> NodeName</name> + <name since="">app_node(App, Candidates, FailOnBadRPC) -> NodeName</name> <fsummary>Same as app_node/2, except that argument FailOnBadRPC determines if the search for a candidate node is to stop if badrpc is received at some point.</fsummary> @@ -81,7 +81,7 @@ </func> <func> - <name>app_node(App, Candidates, FailOnBadRPC, Cookie) -> NodeName</name> + <name since="">app_node(App, Candidates, FailOnBadRPC, Cookie) -> NodeName</name> <fsummary>Same as app_node/2, except that argument FailOnBadRPC determines if the search for a candidate node is to stop if badrpc is received at some point.</fsummary> @@ -105,7 +105,7 @@ </func> <func> - <name>call(Node, Module, Function, Args) -> term() | {badrpc, Reason}</name> + <name since="">call(Node, Module, Function, Args) -> term() | {badrpc, Reason}</name> <fsummary>Same as call(Node, Module, Function, Args, infinity).</fsummary> <desc><marker id="call-4"/> <p>Same as <c>call(Node, Module, Function, Args, infinity)</c>.</p> @@ -113,7 +113,7 @@ </func> <func> - <name>call(Node, Module, Function, Args, TimeOut) -> term() | {badrpc, Reason}</name> + <name since="">call(Node, Module, Function, Args, TimeOut) -> term() | {badrpc, Reason}</name> <fsummary>Evaluates apply(Module, Function, Args) on the node Node.</fsummary> <type> @@ -136,7 +136,7 @@ </func> <func> - <name>call(Node, Module, Function, Args, TimeOut, Cookie) -> term() | {badrpc, Reason}</name> + <name since="">call(Node, Module, Function, Args, TimeOut, Cookie) -> term() | {badrpc, Reason}</name> <fsummary>Evaluates apply(Module, Function, Args) on the node Node.</fsummary> <type> @@ -163,7 +163,7 @@ </func> <func> - <name>cast(Node, Module, Function, Args) -> ok</name> + <name since="">cast(Node, Module, Function, Args) -> ok</name> <fsummary>Evaluates apply(Module, Function, Args) on the node Node.</fsummary> <type> @@ -187,7 +187,7 @@ </func> <func> - <name>cast(Node, Module, Function, Args, Cookie) -> ok</name> + <name since="">cast(Node, Module, Function, Args, Cookie) -> ok</name> <fsummary>Evaluates apply(Module, Function, Args) on the node Node.</fsummary> <type> diff --git a/lib/common_test/doc/src/ct_slave.xml b/lib/common_test/doc/src/ct_slave.xml index 9d9aa50051..84e619482d 100644 --- a/lib/common_test/doc/src/ct_slave.xml +++ b/lib/common_test/doc/src/ct_slave.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_slave.xml</file> </header> - <module>ct_slave</module> + <module since="OTP R14B">ct_slave</module> <modulesummary>Common Test framework functions for starting and stopping nodes for Large-Scale Testing.</modulesummary> @@ -50,7 +50,7 @@ <funcs> <func> - <name>start(Node) -> Result</name> + <name since="OTP R14B">start(Node) -> Result</name> <fsummary>Starts an Erlang node with name Node on the local host.</fsummary> <type> @@ -68,7 +68,7 @@ </func> <func> - <name>start(HostOrNode, NodeOrOpts) -> Result</name> + <name since="OTP R14B">start(HostOrNode, NodeOrOpts) -> Result</name> <fsummary>Starts an Erlang node with default options on a specified host, or on the local host with specified options.</fsummary> <type> @@ -90,7 +90,7 @@ </func> <func> - <name>start(Host, Node, Opts) -> Result</name> + <name since="OTP R14B">start(Host, Node, Opts) -> Result</name> <fsummary>Starts an Erlang node with name Node on host Host as specified by the combination of options in Opts.</fsummary> <type> @@ -184,7 +184,7 @@ </func> <func> - <name>stop(Node) -> Result</name> + <name since="OTP R14B">stop(Node) -> Result</name> <fsummary>Stops the running Erlang node with name Node on the local host.</fsummary> <type> @@ -199,7 +199,7 @@ </func> <func> - <name>stop(Host, Node) -> Result</name> + <name since="OTP R14B">stop(Host, Node) -> Result</name> <fsummary>Stops the running Erlang node with name Node on host Host.</fsummary> <type> diff --git a/lib/common_test/doc/src/ct_snmp.xml b/lib/common_test/doc/src/ct_snmp.xml index 0a5e52b16c..343781814a 100644 --- a/lib/common_test/doc/src/ct_snmp.xml +++ b/lib/common_test/doc/src/ct_snmp.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_snmp.xml</file> </header> - <module>ct_snmp</module> + <module since="">ct_snmp</module> <modulesummary>Common Test user interface module for the SNMP application.</modulesummary> <description> @@ -240,7 +240,7 @@ <funcs> <func> - <name>get_next_values(Agent, Oids, MgrAgentConfName) -> SnmpReply</name> + <name since="">get_next_values(Agent, Oids, MgrAgentConfName) -> SnmpReply</name> <fsummary>Issues a synchronous SNMP get next request.</fsummary> <type> <v>Agent = agent_name()</v> @@ -254,7 +254,7 @@ </func> <func> - <name>get_values(Agent, Oids, MgrAgentConfName) -> SnmpReply</name> + <name since="">get_values(Agent, Oids, MgrAgentConfName) -> SnmpReply</name> <fsummary>Issues a synchronous SNMP get request.</fsummary> <type> <v>Agent = agent_name()</v> @@ -268,7 +268,7 @@ </func> <func> - <name>load_mibs(Mibs) -> ok | {error, Reason}</name> + <name since="">load_mibs(Mibs) -> ok | {error, Reason}</name> <fsummary>Loads the MIBs into agent snmp_master_agent.</fsummary> <type> <v>Mibs = [MibName]</v> @@ -281,7 +281,7 @@ </func> <func> - <name>register_agents(MgrAgentConfName, ManagedAgents) -> ok | {error, Reason}</name> + <name since="">register_agents(MgrAgentConfName, ManagedAgents) -> ok | {error, Reason}</name> <fsummary>Explicitly instructs the manager to handle this agent.</fsummary> <type> @@ -300,7 +300,7 @@ </func> <func> - <name>register_users(MgrAgentConfName, Users) -> ok | {error, Reason}</name> + <name since="">register_users(MgrAgentConfName, Users) -> ok | {error, Reason}</name> <fsummary>Registers the manager entity (=user) responsible for specific agent(s).</fsummary> <type> @@ -319,7 +319,7 @@ </func> <func> - <name>register_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}</name> + <name since="">register_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}</name> <fsummary>Explicitly instructs the manager to handle this USM user.</fsummary> <type> <v>MgrAgentConfName = atom()</v> @@ -337,7 +337,7 @@ </func> <func> - <name>set_info(Config) -> [{Agent, OldVarsAndVals, NewVarsAndVals}]</name> + <name since="">set_info(Config) -> [{Agent, OldVarsAndVals, NewVarsAndVals}]</name> <fsummary>Returns a list of all successful set requests performed in the test case in reverse order.</fsummary> <type> @@ -357,7 +357,7 @@ </func> <func> - <name>set_values(Agent, VarsAndVals, MgrAgentConfName, Config) -> SnmpReply</name> + <name since="">set_values(Agent, VarsAndVals, MgrAgentConfName, Config) -> SnmpReply</name> <fsummary>Issues a synchronous SNMP set request.</fsummary> <type> <v>Agent = agent_name()</v> @@ -372,7 +372,7 @@ </func> <func> - <name>start(Config, MgrAgentConfName) -> ok</name> + <name since="">start(Config, MgrAgentConfName) -> ok</name> <fsummary>Equivalent to start(Config, MgrAgentConfName, undefined).</fsummary> <desc><marker id="start-2"/> @@ -383,7 +383,7 @@ </func> <func> - <name>start(Config, MgrAgentConfName, SnmpAppConfName) -> ok</name> + <name since="">start(Config, MgrAgentConfName, SnmpAppConfName) -> ok</name> <fsummary>Starts an SNMP manager and/or agent.</fsummary> <type> <v>Config = [{Key, Value}]</v> @@ -415,7 +415,7 @@ </func> <func> - <name>stop(Config) -> ok</name> + <name since="">stop(Config) -> ok</name> <fsummary>Stops the SNMP manager and/or agent, and removes all files created.</fsummary> <type> @@ -430,7 +430,7 @@ </func> <func> - <name>unload_mibs(Mibs) -> ok | {error, Reason}</name> + <name since="OTP R16B">unload_mibs(Mibs) -> ok | {error, Reason}</name> <fsummary>Unloads the MIBs from agent snmp_master_agent.</fsummary> <type> <v>Mibs = [MibName]</v> @@ -443,7 +443,7 @@ </func> <func> - <name>unregister_agents(MgrAgentConfName) -> ok</name> + <name since="">unregister_agents(MgrAgentConfName) -> ok</name> <fsummary>Unregisters all managed agents.</fsummary> <type> <v>MgrAgentConfName = atom()</v> @@ -455,7 +455,7 @@ </func> <func> - <name>unregister_agents(MgrAgentConfName, ManagedAgents) -> ok</name> + <name since="OTP R16B">unregister_agents(MgrAgentConfName, ManagedAgents) -> ok</name> <fsummary>Unregisters the specified managed agents.</fsummary> <type> <v>MgrAgentConfName = atom()</v> @@ -468,7 +468,7 @@ </func> <func> - <name>unregister_users(MgrAgentConfName) -> ok</name> + <name since="">unregister_users(MgrAgentConfName) -> ok</name> <fsummary>Unregisters all users.</fsummary> <type> <v>MgrAgentConfName = atom()</v> @@ -480,7 +480,7 @@ </func> <func> - <name>unregister_users(MgrAgentConfName, Users) -> ok</name> + <name since="OTP R16B">unregister_users(MgrAgentConfName, Users) -> ok</name> <fsummary>Unregisters the specified users.</fsummary> <type> <v>MgrAgentConfName = atom()</v> @@ -493,7 +493,7 @@ </func> <func> - <name>unregister_usm_users(MgrAgentConfName) -> ok</name> + <name since="OTP R16B">unregister_usm_users(MgrAgentConfName) -> ok</name> <fsummary>Unregisters all USM users.</fsummary> <type> <v>MgrAgentConfName = atom()</v> @@ -505,7 +505,7 @@ </func> <func> - <name>unregister_usm_users(MgrAgentConfName, UsmUsers) -> ok</name> + <name since="OTP R16B">unregister_usm_users(MgrAgentConfName, UsmUsers) -> ok</name> <fsummary>Unregisters the specified USM users.</fsummary> <type> <v>MgrAgentConfName = atom()</v> diff --git a/lib/common_test/doc/src/ct_ssh.xml b/lib/common_test/doc/src/ct_ssh.xml index 0c7efed154..8d9f31aff8 100644 --- a/lib/common_test/doc/src/ct_ssh.xml +++ b/lib/common_test/doc/src/ct_ssh.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_ssh.xml</file> </header> - <module>ct_ssh</module> + <module since="">ct_ssh</module> <modulesummary>SSH/SFTP client module.</modulesummary> <description> @@ -95,7 +95,7 @@ <funcs> <func> - <name>apread(SSH, Handle, Position, Length) -> Result</name> + <name since="">apread(SSH, Handle, Position, Length) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -109,7 +109,7 @@ </func> <func> - <name>apread(SSH, Server, Handle, Position, Length) -> Result</name> + <name since="">apread(SSH, Server, Handle, Position, Length) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -123,7 +123,7 @@ </func> <func> - <name>apwrite(SSH, Handle, Position, Data) -> Result</name> + <name since="">apwrite(SSH, Handle, Position, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -137,7 +137,7 @@ </func> <func> - <name>apwrite(SSH, Server, Handle, Position, Data) -> Result</name> + <name since="">apwrite(SSH, Server, Handle, Position, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -151,7 +151,7 @@ </func> <func> - <name>aread(SSH, Handle, Len) -> Result</name> + <name since="">aread(SSH, Handle, Len) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -165,7 +165,7 @@ </func> <func> - <name>aread(SSH, Server, Handle, Len) -> Result</name> + <name since="">aread(SSH, Server, Handle, Len) -> Result</name> <fsummary>For inforamtion and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -179,7 +179,7 @@ </func> <func> - <name>awrite(SSH, Handle, Data) -> Result</name> + <name since="">awrite(SSH, Handle, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -193,7 +193,7 @@ </func> <func> - <name>awrite(SSH, Server, Handle, Data) -> Result</name> + <name since="">awrite(SSH, Server, Handle, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -207,7 +207,7 @@ </func> <func> - <name>close(SSH, Handle) -> Result</name> + <name since="">close(SSH, Handle) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -221,7 +221,7 @@ </func> <func> - <name>close(SSH, Server, Handle) -> Result</name> + <name since="">close(SSH, Server, Handle) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -235,7 +235,7 @@ </func> <func> - <name>connect(KeyOrName) -> {ok, Handle} | {error, Reason}</name> + <name since="">connect(KeyOrName) -> {ok, Handle} | {error, Reason}</name> <fsummary>Equivalent to connect(KeyOrName, host, []).</fsummary> <desc><marker id="connect-1"/> <p>Equivalent to @@ -245,7 +245,7 @@ </func> <func> - <name>connect(KeyOrName, ConnType) -> {ok, Handle} | {error, Reason}</name> + <name since="">connect(KeyOrName, ConnType) -> {ok, Handle} | {error, Reason}</name> <fsummary>Equivalent to connect(KeyOrName, ConnType, []).</fsummary> <desc><marker id="connect-2"/> <p>Equivalent to @@ -255,7 +255,7 @@ </func> <func> - <name>connect(KeyOrName, ConnType, ExtraOpts) -> {ok, Handle} | {error, Reason}</name> + <name since="">connect(KeyOrName, ConnType, ExtraOpts) -> {ok, Handle} | {error, Reason}</name> <fsummary>Opens an SSH or SFTP connection using the information associated with KeyOrName.</fsummary> <type> @@ -301,7 +301,7 @@ </func> <func> - <name>del_dir(SSH, Name) -> Result</name> + <name since="">del_dir(SSH, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -315,7 +315,7 @@ </func> <func> - <name>del_dir(SSH, Server, Name) -> Result</name> + <name since="">del_dir(SSH, Server, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -329,7 +329,7 @@ </func> <func> - <name>delete(SSH, Name) -> Result</name> + <name since="">delete(SSH, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -343,7 +343,7 @@ </func> <func> - <name>delete(SSH, Server, Name) -> Result</name> + <name since="">delete(SSH, Server, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -357,7 +357,7 @@ </func> <func> - <name>disconnect(SSH) -> ok | {error, Reason}</name> + <name since="">disconnect(SSH) -> ok | {error, Reason}</name> <fsummary>Closes an SSH/SFTP connection.</fsummary> <type> <v>SSH = connection()</v> @@ -369,7 +369,7 @@ </func> <func> - <name>exec(SSH, Command) -> {ok, Data} | {error, Reason}</name> + <name since="">exec(SSH, Command) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to exec(SSH, Command, DefaultTimeout).</fsummary> <desc><marker id="exec-2"/> <p>Equivalent to @@ -379,7 +379,7 @@ </func> <func> - <name>exec(SSH, Command, Timeout) -> {ok, Data} | {error, Reason}</name> + <name since="">exec(SSH, Command, Timeout) -> {ok, Data} | {error, Reason}</name> <fsummary>Requests server to perform Command.</fsummary> <type> <v>SSH = connection()</v> @@ -396,7 +396,7 @@ </func> <func> - <name>exec(SSH, ChannelId, Command, Timeout) -> {ok, Data} | {error, Reason}</name> + <name since="">exec(SSH, ChannelId, Command, Timeout) -> {ok, Data} | {error, Reason}</name> <fsummary>Requests server to perform Command.</fsummary> <type> <v>SSH = connection()</v> @@ -414,7 +414,7 @@ </func> <func> - <name>get_file_info(SSH, Handle) -> Result</name> + <name since="">get_file_info(SSH, Handle) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -428,7 +428,7 @@ </func> <func> - <name>get_file_info(SSH, Server, Handle) -> Result</name> + <name since="">get_file_info(SSH, Server, Handle) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -442,7 +442,7 @@ </func> <func> - <name>list_dir(SSH, Path) -> Result</name> + <name since="">list_dir(SSH, Path) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -456,7 +456,7 @@ </func> <func> - <name>list_dir(SSH, Server, Path) -> Result</name> + <name since="">list_dir(SSH, Server, Path) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -470,7 +470,7 @@ </func> <func> - <name>make_dir(SSH, Name) -> Result</name> + <name since="">make_dir(SSH, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -484,7 +484,7 @@ </func> <func> - <name>make_dir(SSH, Server, Name) -> Result</name> + <name since="">make_dir(SSH, Server, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -498,7 +498,7 @@ </func> <func> - <name>make_symlink(SSH, Name, Target) -> Result</name> + <name since="">make_symlink(SSH, Name, Target) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -512,7 +512,7 @@ </func> <func> - <name>make_symlink(SSH, Server, Name, Target) -> Result</name> + <name since="">make_symlink(SSH, Server, Name, Target) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -526,7 +526,7 @@ </func> <func> - <name>open(SSH, File, Mode) -> Result</name> + <name since="">open(SSH, File, Mode) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -540,7 +540,7 @@ </func> <func> - <name>open(SSH, Server, File, Mode) -> Result</name> + <name since="">open(SSH, Server, File, Mode) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -554,7 +554,7 @@ </func> <func> - <name>opendir(SSH, Path) -> Result</name> + <name since="">opendir(SSH, Path) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -568,7 +568,7 @@ </func> <func> - <name>opendir(SSH, Server, Path) -> Result</name> + <name since="">opendir(SSH, Server, Path) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -582,7 +582,7 @@ </func> <func> - <name>position(SSH, Handle, Location) -> Result</name> + <name since="">position(SSH, Handle, Location) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -596,7 +596,7 @@ </func> <func> - <name>position(SSH, Server, Handle, Location) -> Result</name> + <name since="">position(SSH, Server, Handle, Location) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -610,7 +610,7 @@ </func> <func> - <name>pread(SSH, Handle, Position, Length) -> Result</name> + <name since="">pread(SSH, Handle, Position, Length) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -624,7 +624,7 @@ </func> <func> - <name>pread(SSH, Server, Handle, Position, Length) -> Result</name> + <name since="">pread(SSH, Server, Handle, Position, Length) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -638,7 +638,7 @@ </func> <func> - <name>pwrite(SSH, Handle, Position, Data) -> Result</name> + <name since="">pwrite(SSH, Handle, Position, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -652,7 +652,7 @@ </func> <func> - <name>pwrite(SSH, Server, Handle, Position, Data) -> Result</name> + <name since="">pwrite(SSH, Server, Handle, Position, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -666,7 +666,7 @@ </func> <func> - <name>read(SSH, Handle, Len) -> Result</name> + <name since="">read(SSH, Handle, Len) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -680,7 +680,7 @@ </func> <func> - <name>read(SSH, Server, Handle, Len) -> Result</name> + <name since="">read(SSH, Server, Handle, Len) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -694,7 +694,7 @@ </func> <func> - <name>read_file(SSH, File) -> Result</name> + <name since="">read_file(SSH, File) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -708,7 +708,7 @@ </func> <func> - <name>read_file(SSH, Server, File) -> Result</name> + <name since="">read_file(SSH, Server, File) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -722,7 +722,7 @@ </func> <func> - <name>read_file_info(SSH, Name) -> Result</name> + <name since="">read_file_info(SSH, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -736,7 +736,7 @@ </func> <func> - <name>read_file_info(SSH, Server, Name) -> Result</name> + <name since="">read_file_info(SSH, Server, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -750,7 +750,7 @@ </func> <func> - <name>read_link(SSH, Name) -> Result</name> + <name since="">read_link(SSH, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -764,7 +764,7 @@ </func> <func> - <name>read_link(SSH, Server, Name) -> Result</name> + <name since="">read_link(SSH, Server, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -778,7 +778,7 @@ </func> <func> - <name>read_link_info(SSH, Name) -> Result</name> + <name since="">read_link_info(SSH, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -792,7 +792,7 @@ </func> <func> - <name>read_link_info(SSH, Server, Name) -> Result</name> + <name since="">read_link_info(SSH, Server, Name) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -806,7 +806,7 @@ </func> <func> - <name>receive_response(SSH, ChannelId) -> {ok, Data} | {error, Reason}</name> + <name since="">receive_response(SSH, ChannelId) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to receive_response(SSH, ChannelId, close).</fsummary> <desc><marker id="receive_response-2"/> @@ -817,7 +817,7 @@ ChannelId, close)</c></seealso>.</p> </func> <func> - <name>receive_response(SSH, ChannelId, End) -> {ok, Data} | {error, Reason}</name> + <name since="">receive_response(SSH, ChannelId, End) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to receive_response(SSH, ChannelId, End, DefaultTimeout).</fsummary> <desc><marker id="receive_response-3"/> @@ -828,7 +828,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>receive_response(SSH, ChannelId, End, Timeout) -> {ok, Data} | {timeout, Data} | {error, Reason}</name> + <name since="">receive_response(SSH, ChannelId, End, Timeout) -> {ok, Data} | {timeout, Data} | {error, Reason}</name> <fsummary>Receives expected data from server on the specified session channel.</fsummary> <type> @@ -863,7 +863,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>rename(SSH, OldName, NewName) -> Result</name> + <name since="">rename(SSH, OldName, NewName) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -877,7 +877,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>rename(SSH, Server, OldName, NewName) -> Result</name> + <name since="">rename(SSH, Server, OldName, NewName) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -891,7 +891,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>send(SSH, ChannelId, Data) -> ok | {error, Reason}</name> + <name since="">send(SSH, ChannelId, Data) -> ok | {error, Reason}</name> <fsummary>Equivalent to send(SSH, ChannelId, 0, Data, DefaultTimeout).</fsummary> <desc><marker id="send-3"/> @@ -901,7 +901,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>send(SSH, ChannelId, Data, Timeout) -> ok | {error, Reason}</name> + <name since="">send(SSH, ChannelId, Data, Timeout) -> ok | {error, Reason}</name> <fsummary>Equivalent to send(SSH, ChannelId, 0, Data, Timeout).</fsummary> <desc><marker id="send-4"/> <p>Equivalent to <seealso marker="#send-5"><c>ct_ssh:send(SSH, @@ -910,7 +910,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>send(SSH, ChannelId, Type, Data, Timeout) -> ok | {error, Reason}</name> + <name since="">send(SSH, ChannelId, Type, Data, Timeout) -> ok | {error, Reason}</name> <fsummary>Sends data to server on specified session channel.</fsummary> <type> <v>SSH = connection()</v> @@ -926,7 +926,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>send_and_receive(SSH, ChannelId, Data) -> {ok, Data} | {error, Reason}</name> + <name since="">send_and_receive(SSH, ChannelId, Data) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to send_and_receive(SSH, ChannelId, Data, close).</fsummary> <desc><marker id="send_and_receive-3"/> @@ -937,7 +937,7 @@ ChannelId, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>send_and_receive(SSH, ChannelId, Data, End) -> {ok, Data} | {error, Reason}</name> + <name since="">send_and_receive(SSH, ChannelId, Data, End) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to send_and_receive(SSH, ChannelId, 0, Data, End, DefaultTimeout).</fsummary> <desc><marker id="send_and_receive-4"/> @@ -948,7 +948,7 @@ ChannelId, 0, Data, End, DefaultTimeout)</c></seealso>.</p> </func> <func> - <name>send_and_receive(SSH, ChannelId, Data, End, Timeout) -> {ok, Data} | {error, Reason}</name> + <name since="">send_and_receive(SSH, ChannelId, Data, End, Timeout) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to send_and_receive(SSH, ChannelId, 0, Data, End, Timeout).</fsummary> <desc><marker id="send_and_receive-5"/> @@ -959,7 +959,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>send_and_receive(SSH, ChannelId, Type, Data, End, Timeout) -> {ok, Data} | {error, Reason}</name> + <name since="">send_and_receive(SSH, ChannelId, Type, Data, End, Timeout) -> {ok, Data} | {error, Reason}</name> <fsummary>Sends data to server on specified session channel and waits to receive the server response.</fsummary> <type> @@ -981,7 +981,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>session_close(SSH, ChannelId) -> ok | {error, Reason}</name> + <name since="">session_close(SSH, ChannelId) -> ok | {error, Reason}</name> <fsummary>Closes an SSH session channel.</fsummary> <type> <v>SSH = connection()</v> @@ -994,7 +994,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>session_open(SSH) -> {ok, ChannelId} | {error, Reason}</name> + <name since="">session_open(SSH) -> {ok, ChannelId} | {error, Reason}</name> <fsummary>Equivalent to session_open(SSH, DefaultTimeout).</fsummary> <desc><marker id="session_open-1"/> <p>Equivalent to @@ -1004,7 +1004,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>session_open(SSH, Timeout) -> {ok, ChannelId} | {error, Reason}</name> + <name since="">session_open(SSH, Timeout) -> {ok, ChannelId} | {error, Reason}</name> <fsummary>Opens a channel for an SSH session.</fsummary> <type> <v>SSH = connection()</v> @@ -1018,7 +1018,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>sftp_connect(SSH) -> {ok, Server} | {error, Reason}</name> + <name since="">sftp_connect(SSH) -> {ok, Server} | {error, Reason}</name> <fsummary>Starts an SFTP session on an already existing SSH connection.</fsummary> <type> @@ -1034,7 +1034,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>shell(SSH, ChannelId) -> ok | {error, Reason}</name> + <name since="OTP 20.0">shell(SSH, ChannelId) -> ok | {error, Reason}</name> <fsummary>Equivalent to shell(SSH, ChannelId, DefaultTimeout).</fsummary> <desc><marker id="shell-2"/> <p>Equivalent to @@ -1044,7 +1044,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>shell(SSH, ChannelId, Timeout) -> ok | {error, Reason}</name> + <name since="OTP 20.0">shell(SSH, ChannelId, Timeout) -> ok | {error, Reason}</name> <fsummary>Requests that the user default shell is executed at the server end.</fsummary> <type> @@ -1061,7 +1061,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>subsystem(SSH, ChannelId, Subsystem) -> Status | {error, Reason}</name> + <name since="">subsystem(SSH, ChannelId, Subsystem) -> Status | {error, Reason}</name> <fsummary>Equivalent to subsystem(SSH, ChannelId, Subsystem, DefaultTimeout).</fsummary> <desc><marker id="subsystem-3"/> @@ -1072,7 +1072,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>subsystem(SSH, ChannelId, Subsystem, Timeout) -> Status | {error, Reason}</name> + <name since="">subsystem(SSH, ChannelId, Subsystem, Timeout) -> Status | {error, Reason}</name> <fsummary>Sends a request to execute a predefined subsystem.</fsummary> <type> <v>SSH = connection()</v> @@ -1088,7 +1088,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>write(SSH, Handle, Data) -> Result</name> + <name since="">write(SSH, Handle, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -1102,7 +1102,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>write(SSH, Server, Handle, Data) -> Result</name> + <name since="">write(SSH, Server, Handle, Data) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -1116,7 +1116,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>write_file(SSH, File, Iolist) -> Result</name> + <name since="">write_file(SSH, File, Iolist) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -1130,7 +1130,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>write_file(SSH, Server, File, Iolist) -> Result</name> + <name since="">write_file(SSH, Server, File, Iolist) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -1144,7 +1144,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>write_file_info(SSH, Name, Info) -> Result</name> + <name since="">write_file_info(SSH, Name, Info) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> @@ -1158,7 +1158,7 @@ ChannelId, 0, Data, End, Timeout)</c></seealso>.</p> </func> <func> - <name>write_file_info(SSH, Server, Name, Info) -> Result</name> + <name since="">write_file_info(SSH, Server, Name, Info) -> Result</name> <fsummary>For information and other types, see ssh_sftp(3).</fsummary> <type> <v>SSH = connection()</v> diff --git a/lib/common_test/doc/src/ct_telnet.xml b/lib/common_test/doc/src/ct_telnet.xml index 8e85cccc99..76f5305c46 100644 --- a/lib/common_test/doc/src/ct_telnet.xml +++ b/lib/common_test/doc/src/ct_telnet.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_telnet.xml</file> </header> - <module>ct_telnet</module> + <module since="">ct_telnet</module> <modulesummary>Common Test specific layer on top of Telnet client ct_telnet_client.erl</modulesummary> <description> @@ -205,7 +205,7 @@ <funcs> <func> - <name>close(Connection) -> ok | {error, Reason}</name> + <name since="">close(Connection) -> ok | {error, Reason}</name> <fsummary>Closes the Telnet connection and stops the process managing it.</fsummary> <type> @@ -223,7 +223,7 @@ </func> <func> - <name>cmd(Connection, Cmd) -> {ok, Data} | {error, Reason}</name> + <name since="">cmd(Connection, Cmd) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to cmd(Connection, Cmd, []).</fsummary> <desc><marker id="cmd-2"/> <p>Equivalent to @@ -233,24 +233,27 @@ </func> <func> - <name>cmd(Connection, Cmd, Opts) -> {ok, Data} | {error, Reason}</name> + <name since="">cmd(Connection, Cmd, Opts) -> {ok, Data} | {error, Reason}</name> <fsummary>Sends a command through Telnet and waits for prompt.</fsummary> <type> <v>Connection = connection()</v> <v>Cmd = string()</v> <v>Opts = [Opt]</v> - <v>Opt = {timeout, timeout()} | {newline, boolean()}</v> + <v>Opt = {timeout, timeout()} | {newline, boolean() | string()}</v> <v>Data = [string()]</v> <v>Reason = term()</v> </type> <desc><marker id="cmd-3"/> <p>Sends a command through Telnet and waits for prompt.</p> - <p>By default, this function adds a new line to the end of the + <p>By default, this function adds "\n" to the end of the specified command. If this is not desired, use option <c>{newline,false}</c>. This is necessary, for example, when sending Telnet command sequences prefixed with character - Interprete As Command (IAC).</p> + Interpret As Command (IAC). Option <c>{newline,string()}</c> + can also be used if a different line end than "\n" is + required, for instance <c>{newline,"\r\n"}</c>, to add both + carriage return and newline characters.</p> <p>Option <c>timeout</c> specifies how long the client must wait for prompt. If the time expires, the function returns @@ -262,7 +265,7 @@ </func> <func> - <name>cmdf(Connection, CmdFormat, Args) -> {ok, Data} | {error, Reason}</name> + <name since="">cmdf(Connection, CmdFormat, Args) -> {ok, Data} | {error, Reason}</name> <fsummary>Equivalent to cmdf(Connection, CmdFormat, Args, []).</fsummary> <desc><marker id="cmdf-3"/> <p>Equivalent to @@ -272,7 +275,7 @@ </func> <func> - <name>cmdf(Connection, CmdFormat, Args, Opts) -> {ok, Data} | {error, Reason}</name> + <name since="">cmdf(Connection, CmdFormat, Args, Opts) -> {ok, Data} | {error, Reason}</name> <fsummary>Sends a Telnet command and waits for prompt (uses a format string and a list of arguments to build the command).</fsummary> <type> @@ -280,7 +283,7 @@ <v>CmdFormat = string()</v> <v>Args = list()</v> <v>Opts = [Opt]</v> - <v>Opt = {timeout, timeout()} | {newline, boolean()}</v> + <v>Opt = {timeout, timeout()} | {newline, boolean() | string()}</v> <v>Data = [string()]</v> <v>Reason = term()</v> </type> @@ -294,7 +297,7 @@ </func> <func> - <name>expect(Connection, Patterns) -> term()</name> + <name since="">expect(Connection, Patterns) -> term()</name> <fsummary>Equivalent to expect(Connections, Patterns, []).</fsummary> <desc><marker id="expect-2"/> <p>Equivalent to @@ -304,7 +307,7 @@ </func> <func> - <name>expect(Connection, Patterns, Opts) -> {ok, Match} | {ok, MatchList, HaltReason} | {error, Reason}</name> + <name since="">expect(Connection, Patterns, Opts) -> {ok, Match} | {ok, MatchList, HaltReason} | {error, Reason}</name> <fsummary>Gets data from Telnet and waits for the expected pattern.</fsummary> <type> @@ -339,7 +342,7 @@ subexpression number <c>N</c>. Subexpressions are denoted with <c>'(' ')'</c> in the regular expression.</p> - <p>If a <c>Tag</c> is speciifed, the returned <c>Match</c> also + <p>If a <c>Tag</c> is specified, the returned <c>Match</c> also includes the matched <c>Tag</c>. Otherwise, only <c>RxMatch</c> is returned.</p> @@ -382,7 +385,7 @@ can abort the operation of waiting for prompt.</p></item> <tag><c>repeat | repeat, N</c></tag> <item><p>The pattern(s) must be matched multiple times. If <c>N</c> - is speciified, the pattern(s) are matched <c>N</c> times, and + is specified, the pattern(s) are matched <c>N</c> times, and the function returns <c>HaltReason = done</c>. This option can be interrupted by one or more <c>HaltPatterns</c>. <c>MatchList</c> is always returned, that is, a list of <c>Match</c> instead of @@ -422,7 +425,7 @@ </func> <func> - <name>get_data(Connection) -> {ok, Data} | {error, Reason}</name> + <name since="">get_data(Connection) -> {ok, Data} | {error, Reason}</name> <fsummary>Gets all data received by the Telnet client since the last command was sent.</fsummary> <type> @@ -446,7 +449,7 @@ </func> <func> - <name>open(Name) -> {ok, Handle} | {error, Reason}</name> + <name since="">open(Name) -> {ok, Handle} | {error, Reason}</name> <fsummary>Equivalent to open(Name, telnet).</fsummary> <desc><marker id="open-1"/> <p>Equivalent to @@ -456,7 +459,7 @@ </func> <func> - <name>open(Name, ConnType) -> {ok, Handle} | {error, Reason}</name> + <name since="">open(Name, ConnType) -> {ok, Handle} | {error, Reason}</name> <fsummary>Opens a Telnet connection to the specified target host.</fsummary> <type> @@ -471,7 +474,7 @@ </func> <func> - <name>open(KeyOrName, ConnType, TargetMod) -> {ok, Handle} | {error, Reason}</name> + <name since="">open(KeyOrName, ConnType, TargetMod) -> {ok, Handle} | {error, Reason}</name> <fsummary>Equivalent to open(KeyOrName, ConnType, TargetMod, []).</fsummary> <desc><marker id="open-3"/> <p>Equivalent to @@ -481,7 +484,7 @@ </func> <func> - <name>open(KeyOrName, ConnType, TargetMod, Extra) -> {ok, Handle} | {error, Reason}</name> + <name since="">open(KeyOrName, ConnType, TargetMod, Extra) -> {ok, Handle} | {error, Reason}</name> <fsummary>Opens a Telnet connection to the specified target host.</fsummary> <type> @@ -531,7 +534,7 @@ </func> <func> - <name>send(Connection, Cmd) -> ok | {error, Reason}</name> + <name since="">send(Connection, Cmd) -> ok | {error, Reason}</name> <fsummary>Equivalent to send(Connection, Cmd, []).</fsummary> <desc><marker id="send-2"/> <p>Equivalent to @@ -541,23 +544,26 @@ </func> <func> - <name>send(Connection, Cmd, Opts) -> ok | {error, Reason}</name> + <name since="OTP 17.4">send(Connection, Cmd, Opts) -> ok | {error, Reason}</name> <fsummary>Sends a Telnet command and returns immediately.</fsummary> <type> <v>Connection = connection()</v> <v>Cmd = string()</v> <v>Opts = [Opt]</v> - <v>Opt = {newline, boolean()}</v> + <v>Opt = {newline, boolean() | string()}</v> <v>Reason = term()</v> </type> <desc><marker id="send-3"/> <p>Sends a Telnet command and returns immediately.</p> - <p>By default, this function adds a newline to the end of the + <p>By default, this function adds "\n" to the end of the specified command. If this is not desired, option <c>{newline,false}</c> can be used. This is necessary, for example, when sending Telnet command sequences prefixed with character - Interprete As Command (IAC).</p> + Interpret As Command (IAC). Option <c>{newline,string()}</c> + can also be used if a different line end than "\n" is + required, for instance <c>{newline,"\r\n"}</c>, to add both + carriage return and newline characters.</p> <p>The resulting output from the command can be read with <seealso marker="#get_data-1"><c>ct_telnet:get_data/2</c></seealso> or @@ -566,7 +572,7 @@ </func> <func> - <name>sendf(Connection, CmdFormat, Args) -> ok | {error, Reason}</name> + <name since="">sendf(Connection, CmdFormat, Args) -> ok | {error, Reason}</name> <fsummary>Equivalent to sendf(Connection, CmdFormat, Args, []).</fsummary> <desc><marker id="sendf-3"/> <p>Equivalent to @@ -576,7 +582,7 @@ </func> <func> - <name>sendf(Connection, CmdFormat, Args, Opts) -> ok | {error, Reason}</name> + <name since="OTP 17.4">sendf(Connection, CmdFormat, Args, Opts) -> ok | {error, Reason}</name> <fsummary>Sends a Telnet command and returns immediately (uses a format string and a list of arguments to build the command).</fsummary> <type> @@ -584,12 +590,15 @@ <v>CmdFormat = string()</v> <v>Args = list()</v> <v>Opts = [Opt]</v> - <v>Opt = {newline, boolean()}</v> + <v>Opt = {newline, boolean() | string()}</v> <v>Reason = term()</v> </type> <desc><marker id="sendf-4"/> <p>Sends a Telnet command and returns immediately (uses a format string and a list of arguments to build the command).</p> + + <p>For details, see + <seealso marker="#send-3"><c>ct_telnet:send/3</c></seealso>.</p> </desc> </func> </funcs> diff --git a/lib/common_test/doc/src/ct_testspec.xml b/lib/common_test/doc/src/ct_testspec.xml index 36893f66cf..9cb9a2ae9f 100644 --- a/lib/common_test/doc/src/ct_testspec.xml +++ b/lib/common_test/doc/src/ct_testspec.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ct_testspec.xml</file> </header> - <module>ct_testspec</module> + <module since="OTP 19.3">ct_testspec</module> <modulesummary>Parsing of test specifications for Common Test. </modulesummary> @@ -46,7 +46,7 @@ <funcs> <func> - <name>get_tests(SpecsIn) -> {ok, [{Specs,Tests}]} | {error, Reason}</name> + <name since="OTP 19.3">get_tests(SpecsIn) -> {ok, [{Specs,Tests}]} | {error, Reason}</name> <fsummary>Parse the given test specification files and return the tests to run and skip.</fsummary> <type> <v>SpecsIn = [string()] | [[string()]]</v> diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index 118dcd88bd..38fdc2442e 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -75,6 +75,44 @@ </section> +<section><title>Common_Test 1.15.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The status of a test case which failed with timetrap + timeout in <c>end_per_testcase</c> could not be modified + by returning <c>{fail,Reason}</c> from a + <c>post_end_per_testcase</c> hook function. This is now + corrected.</p> + <p> + Own Id: OTP-15584 Aux Id: ERIERL-282 </p> + </item> + </list> + </section> + +</section> + +<section><title>Common_Test 1.15.4.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The status of a test case which failed with timetrap + timeout in <c>end_per_testcase</c> could not be modified + by returning <c>{fail,Reason}</c> from a + <c>post_end_per_testcase</c> hook function. This is now + corrected.</p> + <p> + Own Id: OTP-15584 Aux Id: ERIERL-282 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.15.4</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -4026,8 +4064,3 @@ <section><title>common_test 1.3.0</title> </section> </chapter> - - - - - diff --git a/lib/common_test/doc/src/unix_telnet.xml b/lib/common_test/doc/src/unix_telnet.xml index b2314a53ec..03d91b7dbe 100644 --- a/lib/common_test/doc/src/unix_telnet.xml +++ b/lib/common_test/doc/src/unix_telnet.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>unix_telnet.xml</file> </header> - <module>unix_telnet</module> + <module since="">unix_telnet</module> <modulesummary>Callback module for ct_telnet, for connecting to a Telnet server on a UNIX host.</modulesummary> @@ -80,7 +80,7 @@ <funcs> <func> - <name>connect(ConnName, Ip, Port, Timeout, KeepAlive, TCPNoDelay, Extra) -> {ok, Handle} | {error, Reason}</name> + <name since="OTP 18.3.3">connect(ConnName, Ip, Port, Timeout, KeepAlive, TCPNoDelay, Extra) -> {ok, Handle} | {error, Reason}</name> <fsummary>Callback for ct_telnet.erl.</fsummary> <type> <v>ConnName = target_name()</v> @@ -107,7 +107,7 @@ </func> <func> - <name>get_prompt_regexp() -> PromptRegexp</name> + <name since="">get_prompt_regexp() -> PromptRegexp</name> <fsummary>Callback for ct_telnet.erl.</fsummary> <type> <v>PromptRegexp = prompt_regexp()</v> diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index a10d939919..a07e61199b 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -592,7 +592,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) -> encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) -> _ = crypto:start(), - {Key,IVec} = make_crypto_key(Key), + {CryptoKey,IVec} = make_crypto_key(Key), case file:read_file(SrcFileName) of {ok,Bin0} -> Bin1 = term_to_binary({SrcFileName,Bin0}), @@ -600,7 +600,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) -> 0 -> Bin1; N -> list_to_binary([Bin1,random_bytes(8-N)]) end, - EncBin = crypto:block_encrypt(des3_cbc, Key, IVec, Bin2), + EncBin = crypto:block_encrypt(des3_cbc, CryptoKey, IVec, Bin2), case file:write_file(EncryptFileName, EncBin) of ok -> io:format("~ts --(encrypt)--> ~ts~n", @@ -631,10 +631,10 @@ decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) -> decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) -> _ = crypto:start(), - {Key,IVec} = make_crypto_key(Key), + {CryptoKey,IVec} = make_crypto_key(Key), case file:read_file(EncryptFileName) of {ok,Bin} -> - DecBin = crypto:block_decrypt(des3_cbc, Key, IVec, Bin), + DecBin = crypto:block_decrypt(des3_cbc, CryptoKey, IVec, Bin), case catch binary_to_term(DecBin) of {'EXIT',_} -> {error,bad_file}; diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index 29188a648e..6a758c4ea3 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -583,7 +583,7 @@ get_config(Client, Source, Filter, Timeout) -> -spec edit_config(Client, Target, Config) -> Result when Client :: client(), Target :: netconf_db(), - Config :: simple_xml(), + Config :: simple_xml() | [simple_xml()], Result :: ok | {error,error_reason()}. edit_config(Client, Target, Config) -> edit_config(Client, Target, Config, ?DEFAULT_TIMEOUT). @@ -591,7 +591,7 @@ edit_config(Client, Target, Config) -> -spec edit_config(Client, Target, Config, OptParams) -> Result when Client :: client(), Target :: netconf_db(), - Config :: simple_xml(), + Config :: simple_xml() | [simple_xml()], OptParams :: [simple_xml()], Result :: ok | {error,error_reason()}; (Client, Target, Config, Timeout) -> Result when @@ -608,10 +608,12 @@ edit_config(Client, Target, Config, OptParams) when is_list(OptParams) -> -spec edit_config(Client, Target, Config, OptParams, Timeout) -> Result when Client :: client(), Target :: netconf_db(), - Config :: simple_xml(), + Config :: simple_xml() | [simple_xml()], OptParams :: [simple_xml()], Timeout :: timeout(), Result :: ok | {error,error_reason()}. +edit_config(Client, Target, Config, OptParams, Timeout) when not is_list(Config)-> + edit_config(Client, Target, [Config], OptParams, Timeout); edit_config(Client, Target, Config, OptParams, Timeout) -> call(Client, {send_rpc_op, edit_config, [Target,Config,OptParams], Timeout}). @@ -1113,7 +1115,7 @@ encode_rpc_operation(get,[Filter]) -> encode_rpc_operation(get_config,[Source,Filter]) -> {'get-config',[{source,[Source]}] ++ filter(Filter)}; encode_rpc_operation(edit_config,[Target,Config,OptParams]) -> - {'edit-config',[{target,[Target]}] ++ OptParams ++ [{config,[Config]}]}; + {'edit-config',[{target,[Target]}] ++ OptParams ++ [{config,Config}]}; encode_rpc_operation(delete_config,[Target]) -> {'delete-config',[{target,[Target]}]}; encode_rpc_operation(copy_config,[Target,Source]) -> diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index f9abecfd38..174008c790 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -194,6 +194,15 @@ send(Connection,Cmd,Opts) -> check_send_opts([{newline,Bool}|Opts]) when is_boolean(Bool) -> check_send_opts(Opts); +check_send_opts([{newline,String}|Opts]) when is_list(String) -> + case lists:all(fun(I) when is_integer(I), I>=0, I=<127 -> true; + (_) -> false + end, String) of + true -> + check_send_opts(Opts); + false -> + {error,{invalid_option,{newline,String}}} + end; check_send_opts([Invalid|_]) -> {error,{invalid_option,Invalid}}; check_send_opts([]) -> @@ -211,10 +220,16 @@ expect(Connection,Patterns) -> expect(Connection,Patterns,Opts) -> case get_handle(Connection) of - {ok,Pid} -> - call(Pid,{expect,Patterns,Opts}); - Error -> - Error + {ok,Pid} -> + case call(Pid,{expect,Patterns,Opts}) of + {error,Reason} when element(1,Reason)==bad_pattern -> + %% Faulty user input - should fail the test case + exit({Reason,{?MODULE,?FUNCTION_NAME,3}}); + Other -> + Other + end; + Error -> + Error end. %%%================================================================= @@ -674,60 +689,68 @@ silent_teln_expect(Name,Pid,Data,Pattern,Prx,Opts) -> %% 3b) Repeat (sequence): 2) is repeated either N times or until a %% halt condition is fulfilled. teln_expect(Name,Pid,Data,Pattern0,Prx,Opts) -> - HaltPatterns = + HaltPatterns0 = case get_ignore_prompt(Opts) of true -> get_haltpatterns(Opts); false -> [prompt | get_haltpatterns(Opts)] end, - - PromptCheck = get_prompt_check(Opts), - - {WaitForPrompt,Pattern1,Opts1} = wait_for_prompt(Pattern0,Opts), - - Seq = get_seq(Opts1), - Pattern2 = convert_pattern(Pattern1,Seq), - {IdleTimeout,TotalTimeout} = get_timeouts(Opts1), - - EO = #eo{teln_pid=Pid, - prx=Prx, - idle_timeout=IdleTimeout, - total_timeout=TotalTimeout, - seq=Seq, - haltpatterns=HaltPatterns, - prompt_check=PromptCheck}, + case convert_pattern(HaltPatterns0,false) of + {ok,HaltPatterns} -> + {WaitForPrompt,Pattern1,Opts1} = wait_for_prompt(Pattern0,Opts), + Seq = get_seq(Opts1), + case convert_pattern(Pattern1,Seq) of + {ok,Pattern2} -> + {IdleTimeout,TotalTimeout} = get_timeouts(Opts1), + PromptCheck = get_prompt_check(Opts1), + + EO = #eo{teln_pid=Pid, + prx=Prx, + idle_timeout=IdleTimeout, + total_timeout=TotalTimeout, + seq=Seq, + haltpatterns=HaltPatterns, + prompt_check=PromptCheck}, - case get_repeat(Opts1) of - false -> - case teln_expect1(Name,Pid,Data,Pattern2,[],EO) of - {ok,Matched,Rest} when WaitForPrompt -> - case lists:reverse(Matched) of - [{prompt,_},Matched1] -> - {ok,Matched1,Rest}; - [{prompt,_}|Matched1] -> - {ok,lists:reverse(Matched1),Rest} - end; - {ok,Matched,Rest} -> - {ok,Matched,Rest}; - {halt,Why,Rest} -> - {error,Why,Rest}; - {error,Reason} -> - {error,Reason} - end; - N -> - EO1 = EO#eo{repeat=N}, - repeat_expect(Name,Pid,Data,Pattern2,[],EO1) + case get_repeat(Opts1) of + false -> + case teln_expect1(Name,Pid,Data,Pattern2,[],EO) of + {ok,Matched,Rest} when WaitForPrompt -> + case lists:reverse(Matched) of + [{prompt,_},Matched1] -> + {ok,Matched1,Rest}; + [{prompt,_}|Matched1] -> + {ok,lists:reverse(Matched1),Rest} + end; + {ok,Matched,Rest} -> + {ok,Matched,Rest}; + {halt,Why,Rest} -> + {error,Why,Rest}; + {error,Reason} -> + {error,Reason} + end; + N -> + EO1 = EO#eo{repeat=N}, + repeat_expect(Name,Pid,Data,Pattern2,[],EO1) + end; + Error -> + Error + end; + Error -> + Error end. -convert_pattern(Pattern,Seq) - when is_list(Pattern) and not is_integer(hd(Pattern)) -> - case Seq of - true -> Pattern; - false -> rm_dupl(Pattern,[]) - end; +convert_pattern(Pattern0,Seq) + when Pattern0==[] orelse (is_list(Pattern0) and not is_integer(hd(Pattern0))) -> + Pattern = + case Seq of + true -> Pattern0; + false -> rm_dupl(Pattern0,[]) + end, + compile_pattern(Pattern,[]); convert_pattern(Pattern,_Seq) -> - [Pattern]. + compile_pattern([Pattern],[]). rm_dupl([P|Ps],Acc) -> case lists:member(P,Acc) of @@ -739,6 +762,25 @@ rm_dupl([P|Ps],Acc) -> rm_dupl([],Acc) -> lists:reverse(Acc). +compile_pattern([prompt|Patterns],Acc) -> + compile_pattern(Patterns,[prompt|Acc]); +compile_pattern([{prompt,_}=P|Patterns],Acc) -> + compile_pattern(Patterns,[P|Acc]); +compile_pattern([{Tag,Pattern}|Patterns],Acc) -> + try re:compile(Pattern,[unicode]) of + {ok,MP} -> compile_pattern(Patterns,[{Tag,MP}|Acc]); + {error,Error} -> {error,{bad_pattern,{Tag,Pattern},Error}} + catch error:badarg -> {error,{bad_pattern,{Tag,Pattern}}} + end; +compile_pattern([Pattern|Patterns],Acc) -> + try re:compile(Pattern,[unicode]) of + {ok,MP} -> compile_pattern(Patterns,[MP|Acc]); + {error,Error} -> {error,{bad_pattern,Pattern,Error}} + catch error:badarg -> {error,{bad_pattern,Pattern}} + end; +compile_pattern([],Acc) -> + {ok,lists:reverse(Acc)}. + get_timeouts(Opts) -> {case lists:keysearch(idle_timeout,1,Opts) of {value,{_,T}} -> @@ -772,7 +814,7 @@ get_seq(Opts) -> get_haltpatterns(Opts) -> case lists:keysearch(halt,1,Opts) of {value,{halt,HaltPatterns}} -> - convert_pattern(HaltPatterns,false); + HaltPatterns; false -> [] end. @@ -1068,7 +1110,7 @@ match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term, when PromptType=/=FoundPrompt -> match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag); match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) -> - case re:run(Line,Pattern,[{capture,all,list},unicode]) of + case re:run(Line,Pattern,[{capture,all,list}]) of nomatch -> match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag); {match,Match} -> @@ -1076,7 +1118,7 @@ match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) -> {RetTag,{Tag,Match}} end; match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) -> - case re:run(Line,Pattern,[{capture,all,list},unicode]) of + case re:run(Line,Pattern,[{capture,all,list}]) of nomatch -> match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag); {match,Match} -> diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl index 76e4b9ea70..007477c855 100644 --- a/lib/common_test/src/ct_telnet_client.erl +++ b/lib/common_test/src/ct_telnet_client.erl @@ -101,9 +101,11 @@ close(Pid) -> end. send_data(Pid, Data) -> - send_data(Pid, Data, true). + send_data(Pid, Data, "\n"). send_data(Pid, Data, true) -> - send_data(Pid, Data++"\n", false); + send_data(Pid, Data, "\n"); +send_data(Pid, Data, Newline) when is_list(Newline) -> + send_data(Pid, Data++Newline, false); send_data(Pid, Data, false) -> Pid ! {send_data, Data}, ok. diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl index a896a0551b..9eda3f2152 100644 --- a/lib/common_test/src/test_server.erl +++ b/lib/common_test/src/test_server.erl @@ -850,17 +850,23 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid, "WARNING: end_per_testcase failed!</font>", {died,W} end, - try do_end_tc_call(Mod,EPTC,{Pid,Report,[EndConf]}, Why) of - _ -> ok - catch - _:FwEndTCErr -> - exit({fw_notify_done,end_tc,FwEndTCErr}) - end, - FailLoc = proplists:get_value(tc_fail_loc, EndConf), + FailLoc0 = proplists:get_value(tc_fail_loc, EndConf), + {RetVal1,FailLoc} = + try do_end_tc_call(Mod,EPTC,{Pid,Report,[EndConf]}, Why) of + Why -> + {RetVal,FailLoc0}; + {failed,_} = R -> + {R,[{Mod,Func}]}; + R -> + {R,FailLoc0} + catch + _:FwEndTCErr -> + exit({fw_notify_done,end_tc,FwEndTCErr}) + end, %% finished, report back (if end_per_testcase fails, a warning %% should be printed as part of the comment) SendTo ! {self(),fw_notify_done, - {Time,RetVal,FailLoc,[],Warn}} + {Time,RetVal1,FailLoc,[],Warn}} end, spawn_link(FwCall); @@ -902,14 +908,25 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) -> FwErrorNotifyErr}) end, Conf = [{tc_status,{failed,Error}}|CurrConf], - try do_end_tc_call(Mod,EndTCFunc,{Pid,Error,[Conf]},Error) of - _ -> ok - catch - _:FwEndTCErr -> - exit({fw_notify_done,end_tc,FwEndTCErr}) - end, + {Time,RetVal,Loc1} = + try do_end_tc_call(Mod,EndTCFunc,{Pid,Error,[Conf]},Error) of + Error -> + {died, Error, Loc}; + {failed,Reason} = NewReturn -> + fw_error_notify(Mod,Func1,Conf,Reason), + {died, NewReturn, [{Mod,Func}]}; + NewReturn -> + T = case Error of + {timetrap_timeout,TT} -> TT; + _ -> 0 + end, + {T, NewReturn, Loc} + catch + _:FwEndTCErr -> + exit({fw_notify_done,end_tc,FwEndTCErr}) + end, %% finished, report back - SendTo ! {self(),fw_notify_done,{died,Error,Loc,[],undefined}} + SendTo ! {self(),fw_notify_done,{Time,RetVal,Loc1,[],undefined}} end, spawn_link(FwCall). diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 0f5636a789..44b86b1dfe 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -84,7 +84,7 @@ all(suite) -> fail_post_suite_cth, skip_pre_suite_cth, skip_pre_end_cth, skip_pre_init_tc_cth, skip_post_suite_cth, recover_post_suite_cth, update_config_cth, - state_update_cth, options_cth, same_id_cth, + state_update_cth, update_result_cth, options_cth, same_id_cth, fail_n_skip_with_minimal_cth, prio_cth, no_config, no_init_suite_config, no_init_config, no_end_config, failed_sequence, repeat_force_stop, config_clash, @@ -209,6 +209,10 @@ state_update_cth(Config) when is_list(Config) -> do_test(state_update_cth, "ct_cth_fail_one_skip_one_SUITE.erl", [state_update_cth,state_update_cth],Config). +update_result_cth(Config) -> + do_test(update_result_cth, "ct_cth_update_result_post_end_tc_SUITE.erl", + [update_result_post_end_tc_cth],Config). + options_cth(Config) when is_list(Config) -> do_test(options_cth, "ct_cth_empty_SUITE.erl", [{empty_cth,[test]}],Config). @@ -1099,6 +1103,106 @@ test_events(state_update_cth) -> {?eh,stop_logging,[]} ]; +test_events(update_result_cth) -> + Suite = ct_cth_update_result_post_end_tc_SUITE, + [ + {?eh,start_logging,'_'}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,tc_start,{Suite,init_per_suite}}, + {?eh,tc_done,{Suite,init_per_suite,ok}}, + + {?eh,tc_start,{Suite,tc_ok_to_fail}}, + {?eh,cth,{'_',post_end_per_testcase,[Suite,tc_ok_to_fail,'_',ok,[]]}}, + {?eh,tc_done,{Suite,tc_ok_to_fail,{failed,{error,"Test failure"}}}}, + {?eh,cth,{'_',on_tc_fail,'_'}}, + {?eh,test_stats,{0,1,{0,0}}}, + + {?eh,tc_start,{Suite,tc_ok_to_skip}}, + {?eh,cth,{'_',post_end_per_testcase,[Suite,tc_ok_to_skip,'_',ok,[]]}}, + {?eh,tc_done,{Suite,tc_ok_to_skip,{skipped,"Test skipped"}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + {?eh,test_stats,{0,1,{1,0}}}, + + {?eh,tc_start,{Suite,tc_fail_to_ok}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,tc_fail_to_ok,'_', + {error,{test_case_failed,"should be changed to ok"}},[]]}}, + {?eh,tc_done,{Suite,tc_fail_to_ok,ok}}, + {?eh,test_stats,{1,1,{1,0}}}, + + {?eh,tc_start,{Suite,tc_fail_to_skip}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,tc_fail_to_skip,'_', + {error,{test_case_failed,"should be changed to skip"}},[]]}}, + {?eh,tc_done,{Suite,tc_fail_to_skip,{skipped,"Test skipped"}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + {?eh,test_stats,{1,1,{2,0}}}, + + {?eh,tc_start,{Suite,tc_timetrap_to_ok}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,tc_timetrap_to_ok,'_',{timetrap_timeout,3000},[]]}}, + {?eh,tc_done,{Suite,tc_timetrap_to_ok,ok}}, + {?eh,test_stats,{2,1,{2,0}}}, + + {?eh,tc_start,{Suite,tc_timetrap_to_skip}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,tc_timetrap_to_skip,'_',{timetrap_timeout,3000},[]]}}, + {?eh,tc_done,{Suite,tc_timetrap_to_skip,{skipped,"Test skipped"}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + {?eh,test_stats,{2,1,{3,0}}}, + + {?eh,tc_start,{Suite,tc_skip_to_fail}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,tc_skip_to_fail,'_', + {skip,"should be changed to fail"},[]]}}, + {?eh,tc_done,{Suite,tc_skip_to_fail,{failed,{error,"Test failure"}}}}, + {?eh,cth,{'_',on_tc_fail,'_'}}, + {?eh,test_stats,{2,2,{3,0}}}, + + {?eh,tc_start,{Suite,end_fail_to_fail}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,end_fail_to_fail,'_', + {failed, + {Suite,end_per_testcase, + {'EXIT',{test_case_failed,"change result when end fails"}}}},[]]}}, + {?eh,tc_done,{Suite,end_fail_to_fail,{failed,{error,"Test failure"}}}}, + {?eh,cth,{'_',on_tc_fail,'_'}}, + {?eh,test_stats,{2,3,{3,0}}}, + + {?eh,tc_start,{Suite,end_fail_to_skip}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,end_fail_to_skip,'_', + {failed, + {Suite,end_per_testcase, + {'EXIT',{test_case_failed,"change result when end fails"}}}},[]]}}, + {?eh,tc_done,{Suite,end_fail_to_skip,{skipped,"Test skipped"}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + {?eh,test_stats,{2,3,{4,0}}}, + + {?eh,tc_start,{Suite,end_timetrap_to_fail}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,end_timetrap_to_fail,'_', + {failed,{Suite,end_per_testcase,{timetrap_timeout,3000}}},[]]}}, + {?eh,tc_done,{Suite,end_timetrap_to_fail,{failed,{error,"Test failure"}}}}, + {?eh,cth,{'_',on_tc_fail,'_'}}, + {?eh,test_stats,{2,4,{4,0}}}, + + {?eh,tc_start,{Suite,end_timetrap_to_skip}}, + {?eh,cth,{'_',post_end_per_testcase, + [Suite,end_timetrap_to_skip,'_', + {failed,{Suite,end_per_testcase,{timetrap_timeout,3000}}},[]]}}, + {?eh,tc_done,{Suite,end_timetrap_to_skip,{skipped,"Test skipped"}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + {?eh,test_stats,{2,4,{5,0}}}, + + {?eh,tc_start,{Suite,end_per_suite}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + test_events(options_cth) -> [ {?eh,start_logging,{'DEF','RUNDIR'}}, diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl new file mode 100644 index 0000000000..a16138ce6f --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_update_result_post_end_tc_SUITE.erl @@ -0,0 +1,101 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(ct_cth_update_result_post_end_tc_SUITE). + +-compile(export_all). + +-include("ct.hrl"). + +suite() -> + [{timetrap,{seconds,3}}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(Config) -> + ok. + +init_per_group(_,Config) -> + Config. + +end_per_group(_,_) -> + ok. + +init_per_testcase(_,Config) -> + Config. + +end_per_testcase(EndTimetrap,_) when EndTimetrap==end_timetrap_to_fail; + EndTimetrap==end_timetrap_to_skip-> + timer:sleep(10000); +end_per_testcase(EndFail,_) when EndFail==end_fail_to_fail; + EndFail==end_fail_to_skip-> + ct:fail("change result when end fails"); +end_per_testcase(_,_) -> + ok. + +all() -> + [tc_ok_to_fail, + tc_ok_to_skip, + tc_fail_to_ok, + tc_fail_to_skip, + tc_timetrap_to_ok, + tc_timetrap_to_skip, + tc_skip_to_fail, + end_fail_to_fail, + end_fail_to_skip, + end_timetrap_to_fail, + end_timetrap_to_skip]. + +%% Test cases starts here. +tc_ok_to_fail(_Config) -> + ok. + +tc_ok_to_skip(_Config) -> + ok. + +tc_fail_to_ok(_Config) -> + ct:fail("should be changed to ok"). + +tc_fail_to_skip(_Config) -> + ct:fail("should be changed to skip"). + +tc_timetrap_to_ok(_Config) -> + timer:sleep(10000), % will time out after 3 sek + ok. + +tc_timetrap_to_skip(_Config) -> + timer:sleep(10000), % will time out after 3 sek + ok. + +tc_skip_to_fail(_Config) -> + {skip,"should be changed to fail"}. + +end_fail_to_fail(_Config) -> + ok. + +end_fail_to_skip(_Config) -> + ok. + +end_timetrap_to_fail(_Config) -> + ok. + +end_timetrap_to_skip(_Config) -> + ok. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl new file mode 100644 index 0000000000..7afb3d8781 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_result_post_end_tc_cth.erl @@ -0,0 +1,98 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2017. 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(update_result_post_end_tc_cth). + + +-include_lib("common_test/src/ct_util.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + + +%% CT Hooks +-compile(export_all). + +init(Id, Opts) -> + empty_cth:init(Id, Opts). + +pre_init_per_suite(Suite, Config, State) -> + empty_cth:pre_init_per_suite(Suite,Config,State). + +post_init_per_suite(Suite,Config,Return,State) -> + empty_cth:post_init_per_suite(Suite,Config,Return,State). + +pre_end_per_suite(Suite,Config,State) -> + empty_cth:pre_end_per_suite(Suite,Config,State). + +post_end_per_suite(Suite,Config,Return,State) -> + empty_cth:post_end_per_suite(Suite,Config,Return,State). + +pre_init_per_group(Suite,Group,Config,State) -> + empty_cth:pre_init_per_group(Suite,Group,Config,State). + +post_init_per_group(Suite,Group,Config,Return,State) -> + empty_cth:post_init_per_group(Suite,Group,Config,Return,State). + +pre_end_per_group(Suite,Group,Config,State) -> + empty_cth:pre_end_per_group(Suite,Group,Config,State). + +post_end_per_group(Suite,Group,Config,Return,State) -> + empty_cth:post_end_per_group(Suite,Group,Config,Return,State). + +pre_init_per_testcase(Suite,TC,Config,State) -> + empty_cth:pre_init_per_testcase(Suite,TC,Config,State). + +post_end_per_testcase(Suite,TC,Config,Return,State) -> + empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State), + change_result(TC,Config,State). + +on_tc_fail(Suite,TC, Reason, State) -> + empty_cth:on_tc_fail(Suite,TC,Reason,State). + +on_tc_skip(Suite,TC, Reason, State) -> + empty_cth:on_tc_skip(Suite,TC,Reason,State). + +terminate(State) -> + empty_cth:terminate(State). + +%%%----------------------------------------------------------------- +%%% +change_result(tc_ok_to_fail,_Config,State) -> + {{fail, "Test failure"}, State}; +change_result(tc_ok_to_skip,_Config,State) -> + {{skip, "Test skipped"}, State}; +change_result(tc_fail_to_ok,Config,State) -> + {lists:keydelete(tc_status,1,Config),State}; +change_result(tc_fail_to_skip,Config,State) -> + {{skip,"Test skipped"},State}; +change_result(tc_timetrap_to_ok,Config,State) -> + {lists:keydelete(tc_status,1,Config),State}; +change_result(tc_timetrap_to_skip,Config,State) -> + {{skip,"Test skipped"},State}; +change_result(tc_skip_to_fail,_Config,State) -> + {{fail, "Test failure"}, State}; +change_result(end_fail_to_fail,_Config,State) -> + {{fail, "Test failure"}, State}; +change_result(end_fail_to_skip,_Config,State) -> + {{skip, "Test skipped"}, State}; +change_result(end_timetrap_to_fail,_Config,State) -> + {{fail, "Test failure"}, State}; +change_result(end_timetrap_to_skip,_Config,State) -> + {{skip, "Test skipped"}, State}. diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl index a2fa099a8c..0d17481e95 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -440,6 +440,12 @@ edit_config(Config) -> ?ok = ct_netconfc:edit_config(Client,running, {server,[{xmlns,"myns"}], [{name,["myserver"]}]}), + ?NS:expect_reply('edit-config',ok), + ?ok = ct_netconfc:edit_config(Client,running, + [{server,[{xmlns,"myns"}], + [{name,["server1"]}]}, + {server,[{xmlns,"myns"}], + [{name,["server2"]}]}]), ?NS:expect_do_reply('close-session',close,ok), ?ok = ct_netconfc:close_session(Client), ok. diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl index a0089c9bc9..f71b7c370f 100644 --- a/lib/common_test/test/ct_telnet_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE.erl @@ -50,10 +50,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. groups() -> - [{legacy, [], [unix_telnet,own_server,timetrap]}, - {raw, [], [unix_telnet,own_server,timetrap]}, - {html, [], [unix_telnet,own_server]}, - {silent, [], [unix_telnet,own_server]}]. + [{legacy, [], [unix_telnet,own_server,faulty_regexp,timetrap]}, + {raw, [], [unix_telnet,own_server,faulty_regexp,timetrap]}, + {html, [], [unix_telnet,own_server,faulty_regexp]}, + {silent, [], [unix_telnet,own_server,faulty_regexp]}]. all() -> [ @@ -119,6 +119,12 @@ own_server(Config) -> all_tests_in_suite(own_server,"ct_telnet_own_server_SUITE", CfgFile,Config). +faulty_regexp(Config) -> + CfgFile = "telnet.faulty_regexp." ++ + atom_to_list(groupname(Config)) ++ ".cfg", + all_tests_in_suite(faulty_regexp,"ct_telnet_faulty_regexp_SUITE", + CfgFile,Config). + timetrap(Config) -> CfgFile = "telnet.timetrap." ++ atom_to_list(groupname(Config)) ++ ".cfg", @@ -225,6 +231,31 @@ events_to_check(unix_telnet,Config) -> all_cases(ct_telnet_basic_SUITE,Config); events_to_check(own_server,Config) -> all_cases(ct_telnet_own_server_SUITE,Config); +events_to_check(faulty_regexp,_Config) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,tc_done, + {ct_telnet_faulty_regexp_SUITE,expect_pattern, + {failed, + {error,{{bad_pattern,"invalid(pattern",{"missing )",15}}, + {ct_telnet,expect,3}}}}}}, + {?eh,tc_done, + {ct_telnet_faulty_regexp_SUITE,expect_pattern_no_string, + {failed, + {error,{{bad_pattern,invalid_pattern}, + {ct_telnet,expect,3}}}}}}, + {?eh,tc_done, + {ct_telnet_faulty_regexp_SUITE,expect_tag_pattern, + {failed, + {error,{{bad_pattern,{tag,"invalid(pattern"},{"missing )",15}}, + {ct_telnet,expect,3}}}}}}, + {?eh,tc_done, + {ct_telnet_faulty_regexp_SUITE,expect_tag_pattern_no_string, + {failed, + {error,{{bad_pattern,{tag,invalid_pattern}}, + {ct_telnet,expect,3}}}}}}, + {?eh,tc_done,{ct_telnet_faulty_regexp_SUITE,expect_pattern_unicode,ok}}, + {?eh,tc_done,{ct_telnet_faulty_regexp_SUITE,expect_tag_pattern_unicode,ok}}, + {?eh,stop_logging,[]}]; events_to_check(timetrap,_Config) -> [{?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,tc_done,{ct_telnet_timetrap_SUITE,expect_timetrap, diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl new file mode 100644 index 0000000000..a5c9451a9c --- /dev/null +++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_faulty_regexp_SUITE.erl @@ -0,0 +1,79 @@ +-module(ct_telnet_faulty_regexp_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(name, telnet_server_conn1). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +suite() -> [{require,?name,{unix,[telnet]}}, + {require,ct_conn_log}, + {ct_hooks, [{cth_conn_log,[]}]}]. + +all() -> + [expect_pattern, + expect_pattern_no_string, + expect_tag_pattern, + expect_tag_pattern_no_string, + expect_pattern_unicode, + expect_tag_pattern_unicode]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(_,Config) -> + ct:log("init_per_testcase: opening telnet connection...",[]), + {ok,_} = ct_telnet:open(?name), + ct:log("...done",[]), + Config. + +end_per_testcase(_,_Config) -> + ct:log("end_per_testcase: closing telnet connection...",[]), + _ = ct_telnet:close(?name), + ct:log("...done",[]), + ok. + +expect_pattern(_) -> + ok = ct_telnet:send(?name, "echo ayt"), + ok = ct_telnet:expect(?name, "invalid(pattern"). + +expect_pattern_no_string(_) -> + ok = ct_telnet:send(?name, "echo ayt"), + ok = ct_telnet:expect(?name, invalid_pattern). + +expect_tag_pattern(_) -> + ok = ct_telnet:send(?name, "echo ayt"), + ok = ct_telnet:expect(?name, {tag,"invalid(pattern"}). + +expect_tag_pattern_no_string(_) -> + ok = ct_telnet:send(?name, "echo ayt"), + ok = ct_telnet:expect(?name, {tag,invalid_pattern}). + +%% Test that a unicode pattern can be given without the testcase +%% failing. Do however notice that there is no real unicode support +%% in ct_telnet yet, that is, the telnet binary mode is not supported. +expect_pattern_unicode(_) -> + ok = ct_telnet:send(?name, "echo ayt"), + {error,{prompt,_}} = ct_telnet:expect(?name, "pattern_with_unicode_αβ"), + ok. + +expect_tag_pattern_unicode(_) -> + ok = ct_telnet:send(?name, "echo ayt"), + {error,{prompt,_}} = ct_telnet:expect(?name, "pattern_with_unicode_αβ"), + ok. diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl index 985fa40ad2..34df57027e 100644 --- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl @@ -58,7 +58,8 @@ all() -> server_speaks, server_disconnects, newline_ayt, - newline_break + newline_break, + newline_string ]. groups() -> @@ -393,3 +394,11 @@ newline_break(_) -> "> " = lists:flatten(R), ok = ct_telnet:close(Handle), ok. + +%% Test option {newline,String} to specify an own newline, e.g. "\r\n" +newline_string(_) -> + {ok, Handle} = ct_telnet:open(telnet_server_conn1), + ok = ct_telnet:send(Handle, "echo hello-", [{newline,"own_nl\n"}]), + {ok,["hello-own_nl"]} = ct_telnet:expect(Handle, ["hello-own_nl"]), + ok = ct_telnet:close(Handle), + ok. diff --git a/lib/common_test/test_server/configure.in b/lib/common_test/test_server/configure.in index 0511d126b4..e07bd4c2aa 100644 --- a/lib/common_test/test_server/configure.in +++ b/lib/common_test/test_server/configure.in @@ -459,11 +459,11 @@ dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a dnl AC_LANG_JAVA instead...) AC_DEFUN(ERL_TRY_LINK_JAVA, [java_link='$JAVAC conftest.java 1>&AC_FD_CC' -changequote(�, �)dnl +changequote(, )dnl cat > conftest.java <<EOF -�$1� +$1 class conftest { public static void main(String[] args) { - �$2� + $2 ; return; }} EOF changequote([, ])dnl diff --git a/lib/common_test/test_server/ts_erl_config.erl b/lib/common_test/test_server/ts_erl_config.erl index 537628e39a..f3972bea4e 100644 --- a/lib/common_test/test_server/ts_erl_config.erl +++ b/lib/common_test/test_server/ts_erl_config.erl @@ -208,7 +208,11 @@ erl_interface(Vars,OsType) -> {filename:join(Dir, "lib"), filename:join([Dir, "src", "eidefs.mk"])}; {srctree, _Root, Target} -> - {filename:join([Dir, "obj", Target]), + Obj = case is_debug_build() of + true -> "obj.debug"; + false -> "obj" + end, + {filename:join([Dir, Obj, Target]), filename:join([Dir, "src", Target, "eidefs.mk"])} end} end, diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index cfbd4c7fda..7f3d6aa60e 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -29,7 +29,7 @@ <rev>A</rev> <file>compile.sgml</file> </header> - <module>compile</module> + <module since="">compile</module> <modulesummary>Erlang Compiler</modulesummary> <description> <p>This module provides an interface to the standard Erlang @@ -40,7 +40,7 @@ <funcs> <func> - <name>env_compiler_options()</name> + <name since="OTP 19.0">env_compiler_options()</name> <fsummary> Compiler options defined via the environment variable <c>ERL_COMPILER_OPTIONS</c> @@ -53,7 +53,7 @@ </desc> </func> <func> - <name>file(File)</name> + <name since="">file(File)</name> <fsummary>Compiles a file.</fsummary> <desc> <p>Is the same as @@ -63,7 +63,7 @@ </func> <func> - <name>file(File, Options) -> CompRet</name> + <name since="">file(File, Options) -> CompRet</name> <fsummary>Compiles a file.</fsummary> <type> <v>CompRet = ModRet | BinRet | ErrRet</v> @@ -718,7 +718,7 @@ module.beam: module.erl \ </func> <func> - <name>forms(Forms)</name> + <name since="">forms(Forms)</name> <fsummary>Compiles a list of forms.</fsummary> <desc> <p>Is the same as @@ -728,7 +728,7 @@ module.beam: module.erl \ </func> <func> - <name>forms(Forms, Options) -> CompRet</name> + <name since="">forms(Forms, Options) -> CompRet</name> <fsummary>Compiles a list of forms.</fsummary> <type> <v>Forms = [Form]</v> @@ -749,7 +749,7 @@ module.beam: module.erl \ </func> <func> - <name>format_error(ErrorDescriptor) -> chars()</name> + <name since="">format_error(ErrorDescriptor) -> chars()</name> <fsummary>Formats an error descriptor.</fsummary> <type> <v>ErrorDescriptor = errordesc()</v> @@ -764,7 +764,7 @@ module.beam: module.erl \ </func> <func> - <name>output_generated(Options) -> true | false</name> + <name since="">output_generated(Options) -> true | false</name> <fsummary>Determines whether the compiler generates an output file.</fsummary> <type> <v>Options = [term()]</v> @@ -779,7 +779,7 @@ module.beam: module.erl \ </func> <func> - <name>noenv_file(File, Options) -> CompRet</name> + <name since="">noenv_file(File, Options) -> CompRet</name> <fsummary>Compiles a file (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <desc> <p>Works like <seealso marker="#file/2">file/2</seealso>, @@ -789,7 +789,7 @@ module.beam: module.erl \ </func> <func> - <name>noenv_forms(Forms, Options) -> CompRet</name> + <name since="">noenv_forms(Forms, Options) -> CompRet</name> <fsummary>Compiles a list of forms (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <desc> <p>Works like <seealso marker="#forms/2">forms/2</seealso>, @@ -799,7 +799,7 @@ module.beam: module.erl \ </func> <func> - <name>noenv_output_generated(Options) -> true | false</name> + <name since="">noenv_output_generated(Options) -> true | false</name> <fsummary>Determines whether the compiler generates an output file (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <type> diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 7addadf82c..02e6203137 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,26 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>An optimization that avoided allocation of a stack + frame for some <c>case</c> expressions was introduced in + OTP 21. (ERL-504/OTP-14808) It turns out that in rare + circumstances, this optimization is not safe. Therefore, + this optimization has been disabled.</p> + <p>A similar optimization will be included in OTP 22 in a + safe way.</p> + <p> + Own Id: OTP-15501 Aux Id: ERL-807, ERL-514, OTP-14808 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index b5c979e529..a1e9eff8f3 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -1114,4 +1114,5 @@ verified_type(nonempty_list=T) -> T; verified_type({tuple,_,Sz,[]}=T) when is_integer(Sz) -> T; verified_type({tuple,_,Sz,[_]}=T) when is_integer(Sz) -> T; verified_type({tuple_element,_,_}=T) -> T; -verified_type(float=T) -> T. +verified_type(float=T) -> T; +verified_type(none=T) -> T. diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index e9152ba88f..d7a7778740 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -79,13 +79,9 @@ function(#k_fdef{anno=#k{a=Anno},func=Name,arity=Arity, try #k_match{} = Kb, %Assertion. - %% Try to suppress the stack frame unless it is - %% really needed. - Body0 = avoid_stack_frame(Kb), - %% Annotate kernel records with variable usage. Vdb0 = init_vars(As), - {Body,_,Vdb} = body(Body0, 1, Vdb0), + {Body,_,Vdb} = body(Kb, 1, Vdb0), %% Generate the BEAM assembly code. {Asm,EntryLabel,St} = cg_fun(Body, As, Vdb, AtomMod, @@ -98,136 +94,6 @@ function(#k_fdef{anno=#k{a=Anno},func=Name,arity=Arity, erlang:raise(Class, Error, Stack) end. - -%% avoid_stack_frame(Kernel) -> Kernel' -%% If possible, avoid setting up a stack frame. Functions -%% that only do matching, calls to guard BIFs, and tail-recursive -%% calls don't need a stack frame. - -avoid_stack_frame(#k_match{body=Body}=M) -> - try - M#k_match{body=avoid_stack_frame_1(Body)} - catch - impossible -> - M - end. - -avoid_stack_frame_1(#k_alt{first=First0,then=Then0}=Alt) -> - First = avoid_stack_frame_1(First0), - Then = avoid_stack_frame_1(Then0), - Alt#k_alt{first=First,then=Then}; -avoid_stack_frame_1(#k_bif{op=Op}=Bif) -> - case Op of - #k_internal{} -> - %% Most internal BIFs clobber the X registers. - throw(impossible); - _ -> - Bif - end; -avoid_stack_frame_1(#k_break{anno=Anno,args=Args}) -> - #k_guard_break{anno=Anno,args=Args}; -avoid_stack_frame_1(#k_guard_break{}=Break) -> - Break; -avoid_stack_frame_1(#k_enter{}=Enter) -> - %% Tail-recursive calls don't need a stack frame. - Enter; -avoid_stack_frame_1(#k_guard{clauses=Cs0}=Guard) -> - Cs = avoid_stack_frame_list(Cs0), - Guard#k_guard{clauses=Cs}; -avoid_stack_frame_1(#k_guard_clause{guard=G0,body=B0}=C) -> - G = avoid_stack_frame_1(G0), - B = avoid_stack_frame_1(B0), - C#k_guard_clause{guard=G,body=B}; -avoid_stack_frame_1(#k_match{anno=A,vars=Vs,body=B0,ret=Ret}) -> - %% Use #k_guard_match{} instead to avoid saving the X registers - %% to the stack before matching. - B = avoid_stack_frame_1(B0), - #k_guard_match{anno=A,vars=Vs,body=B,ret=Ret}; -avoid_stack_frame_1(#k_guard_match{body=B0}=M) -> - B = avoid_stack_frame_1(B0), - M#k_guard_match{body=B}; -avoid_stack_frame_1(#k_protected{arg=Arg0}=Prot) -> - Arg = avoid_stack_frame_1(Arg0), - Prot#k_protected{arg=Arg}; -avoid_stack_frame_1(#k_put{}=Put) -> - Put; -avoid_stack_frame_1(#k_return{}=Ret) -> - Ret; -avoid_stack_frame_1(#k_select{var=#k_var{anno=Vanno},types=Types0}=Select) -> - case member(reuse_for_context, Vanno) of - false -> - Types = avoid_stack_frame_list(Types0), - Select#k_select{types=Types}; - true -> - %% Including binary patterns that overwrite the register containing - %% the binary with the match context may not be safe. For example, - %% bs_match_SUITE:bin_tail_e/1 with inlining will be rejected by - %% beam_validator. - %% - %% Essentially the following code is produced: - %% - %% bs_match {x,0} => {x,0} - %% ... - %% bs_match {x,0} => {x,1} %% ILLEGAL - %% - %% A bs_match instruction will only accept a match context as the - %% source operand if the source and destination registers are the - %% the same (as in the first bs_match instruction above). - %% The second bs_match instruction is therefore illegal. - %% - %% This situation is avoided if there is a stack frame: - %% - %% move {x,0} => {y,0} - %% bs_match {x,0} => {x,0} - %% ... - %% bs_match {y,0} => {x,1} %% LEGAL - %% - throw(impossible) - end; -avoid_stack_frame_1(#k_seq{arg=#k_call{anno=Anno,op=Op}=Call, - body=#k_break{args=BrArgs0}}=Seq) -> - case Op of - #k_remote{mod=#k_atom{val=Mod}, - name=#k_atom{val=Name}, - arity=Arity} -> - case erl_bifs:is_exit_bif(Mod, Name, Arity) of - false -> - %% Will clobber X registers. Must have a stack frame. - throw(impossible); - true -> - %% The call to this BIF will never return. It is safe - %% to suppress the stack frame. - Bif = #k_bif{anno=Anno, - op=#k_internal{name=guard_error,arity=1}, - args=[Call],ret=[]}, - BrArgs = lists:duplicate(length(BrArgs0), #k_nil{}), - GB = #k_guard_break{anno=#k{us=[],ns=[],a=[]},args=BrArgs}, - Seq#k_seq{arg=Bif,body=GB} - end; - _ -> - %% Will clobber X registers. Must have a stack frame. - throw(impossible) - end; -avoid_stack_frame_1(#k_seq{arg=A0,body=B0}=Seq) -> - A = avoid_stack_frame_1(A0), - B = avoid_stack_frame_1(B0), - Seq#k_seq{arg=A,body=B}; -avoid_stack_frame_1(#k_test{}=Test) -> - Test; -avoid_stack_frame_1(#k_type_clause{values=Values0}=TC) -> - Values = avoid_stack_frame_list(Values0), - TC#k_type_clause{values=Values}; -avoid_stack_frame_1(#k_val_clause{body=B0}=VC) -> - B = avoid_stack_frame_1(B0), - VC#k_val_clause{body=B}; -avoid_stack_frame_1(_Body) -> - throw(impossible). - -avoid_stack_frame_list([H|T]) -> - [avoid_stack_frame_1(H)|avoid_stack_frame_list(T)]; -avoid_stack_frame_list([]) -> []. - - %% This pass creates beam format annotated with variable lifetime %% information. Each thing is given an index and for each variable we %% store the first and last index for its occurrence. The variable diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index c9517c3e51..66e578b776 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -766,14 +766,16 @@ expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) -> {Qs,St2} = preprocess_quals(Llc, Qs0, St1), {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2), {Y,Mps++Yps,St}; -expr({op,L,'andalso',E1,E2}, St0) -> +expr({op,_,'andalso',_,_}=E0, St0) -> + {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso', St0), Anno = lineno_anno(L, St0), {#c_var{name=V0},St} = new_var(Anno, St0), V = {var,L,V0}, False = {atom,L,false}, E = make_bool_switch(L, E1, V, E2, False, St0), expr(E, St); -expr({op,L,'orelse',E1,E2}, St0) -> +expr({op,_,'orelse',_,_}=E0, St0) -> + {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse', St0), Anno = lineno_anno(L, St0), {#c_var{name=V0},St} = new_var(Anno, St0), V = {var,L,V0}, @@ -2626,7 +2628,8 @@ cfun(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) -> [],A#a.us,St2}. c_call_erl(Fun, Args) -> - cerl:c_call(cerl:c_atom(erlang), cerl:c_atom(Fun), Args). + As = [compiler_generated], + cerl:ann_c_call(As, cerl:c_atom(erlang), cerl:c_atom(Fun), Args). %% lit_vars(Literal) -> [Var]. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 061076b3ff..9f691716e3 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -157,6 +157,10 @@ coverage(Config) -> [_|_] -> ok end, + + %% Cover beam_type:verified_type(none). + {'EXIT',{badarith,_}} = (catch (id(2) / id(1)) band 16#ff), + ok. booleans(_Config) -> diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index e3f842b668..72e5356a8d 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -378,6 +378,13 @@ untuplify(Config) when is_list(Config) -> %% We do this to cover sys_core_fold:unalias_pat/1. {1,2,3,4,alias,{[1,2],{3,4},alias}} = untuplify_1([1,2], {3,4}, alias), error = untuplify_1([1,2], {3,4}, 42), + + %% Test that a previous bug in v3_codegen is gone. (The sinking of + %% stack frames into only the case arms that needed them was not always + %% safe.) + [33, -1, -33, 1] = untuplify_2(32, 65), + {33, 1, -33, -1} = untuplify_2(65, 32), + ok. untuplify_1(A, B, C) -> @@ -390,6 +397,21 @@ untuplify_1(A, B, C) -> error end. +untuplify_2(V1, V2) -> + {D1,D2,D3,D4} = + if V1 > V2 -> + %% The 1 value was overwritten by the value of V2-V1. + {V1-V2, 1, V2-V1, -1}; + true -> + {V2-V1, -1, V1-V2, 1} + end, + if + D2 > D4 -> + {D1, D2, D3, D4}; + true -> + [D1, D2, D3, D4] + end. + %% Coverage of beam_dead:shortcut_boolean_label/4. shortcut_boolean(Config) when is_list(Config) -> false = shortcut_boolean_1([0]), diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index 33d55996ad..1c23eba06d 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -42,7 +42,7 @@ comprehensions/1,maps/1,maps_bin_opt_info/1, redundant_boolean_clauses/1, latin1_fallback/1,underscore/1,no_warnings/1, - bit_syntax/1,inlining/1]). + bit_syntax/1,inlining/1,tuple_calls/1]). init_per_testcase(_Case, Config) -> Config. @@ -64,7 +64,8 @@ groups() -> bin_opt_info,bin_construction,comprehensions,maps, maps_bin_opt_info, redundant_boolean_clauses,latin1_fallback, - underscore,no_warnings,bit_syntax,inlining]}]. + underscore,no_warnings,bit_syntax,inlining, + tuple_calls]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -952,6 +953,20 @@ inlining(Config) -> run(Config, Ts), ok. +tuple_calls(Config) -> + %% Make sure that no spurious warnings are generated. + Ts = [{inlining_1, + <<"-compile(tuple_calls). + dispatch(X) -> + (list_to_atom(\"prefix_\" ++ + atom_to_list(suffix))):doit(X). + ">>, + [], + []} + ], + run(Config, Ts), + ok. + %%% %%% End of test cases. %%% diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 97179b7fc4..efedb414ad 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.3 +COMPILER_VSN = 7.3.1 diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index df607732bf..194a3d30e9 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -177,7 +177,8 @@ && !defined(HAS_LIBRESSL) \ && defined(HAVE_EC) # define HAVE_ED_CURVE_DH -# if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)) +# if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)) \ + && !defined(FIPS_SUPPORT) # define HAVE_EDDSA # endif #endif @@ -1425,8 +1426,6 @@ static void init_algorithms_types(ErlNifEnv* env) #endif algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb8"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb128"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc256"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ctr"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ecb"); @@ -1441,6 +1440,8 @@ static void init_algorithms_types(ErlNifEnv* env) #ifdef HAVE_AES_IGE algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256"); #endif + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb8"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb128"); #ifndef OPENSSL_NO_DES algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc"); algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb"); @@ -2325,21 +2326,24 @@ static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM return enif_raise_exception(env, atom_notsup); } - if (argv[0] == atom_aes_cfb8 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_8_crypt(env, argc-1, argv+1); + if (argv[0] == atom_aes_cfb8) { + CHECK_NO_FIPS_MODE(); + if ((key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_8_crypt(env, argc-1, argv+1); + } + } + else if (argv[0] == atom_aes_cfb128) { + CHECK_NO_FIPS_MODE(); + if ((key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_128_crypt_nif(env, argc-1, argv+1); + } } - else if (argv[0] == atom_aes_cfb128 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_128_crypt_nif(env, argc-1, argv+1); - } - ivec_size = EVP_CIPHER_iv_length(cipher); #ifdef HAVE_ECB_IVEC_BUG @@ -4357,8 +4361,11 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_ *md = NULL; if (type == atom_none && algorithm == atom_rsa) return PKEY_OK; + if (algorithm == atom_eddsa) #ifdef HAVE_EDDSA - if (algorithm == atom_eddsa) return PKEY_OK; + return PKEY_OK; +#else + return PKEY_NOTSUP; #endif digp = get_digest_type(type); if (!digp) return PKEY_BADARG; diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index b33db0d6e4..5c1909fc7f 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -23,7 +23,7 @@ <title>crypto</title> </header> - <module>crypto</module> + <module since="">crypto</module> <modulesummary>Crypto Functions</modulesummary> <description> <p>This module provides a set of cryptographic functions. @@ -524,7 +524,7 @@ <!--================ FUNCTIONS ================--> <funcs> <func> - <name name="block_encrypt" arity="3"/> + <name name="block_encrypt" arity="3" since="OTP 18.0"/> <fsummary>Encrypt <c>PlainText</c> according to <c>Type</c> block cipher</fsummary> <desc> <p>Encrypt <c>PlainText</c> according to <c>Type</c> block cipher.</p> @@ -537,7 +537,7 @@ </func> <func> - <name name="block_decrypt" arity="3"/> + <name name="block_decrypt" arity="3" since="OTP 18.0"/> <fsummary>Decrypt <c>CipherText</c> according to <c>Type</c> block cipher</fsummary> <desc> <p>Decrypt <c>CipherText</c> according to <c>Type</c> block cipher.</p> @@ -550,9 +550,9 @@ </func> <func> - <name>block_encrypt(Type, Key, Ivec, PlainText) -> CipherText</name> - <name>block_encrypt(AeadType, Key, Ivec, {AAD, PlainText}) -> {CipherText, CipherTag}</name> - <name>block_encrypt(aes_gcm | aes_ccm, Key, Ivec, {AAD, PlainText, TagLength}) -> {CipherText, CipherTag}</name> + <name since="OTP R16B01">block_encrypt(Type, Key, Ivec, PlainText) -> CipherText</name> + <name since="OTP R16B01">block_encrypt(AeadType, Key, Ivec, {AAD, PlainText}) -> {CipherText, CipherTag}</name> + <name since="OTP R16B01">block_encrypt(aes_gcm | aes_ccm, Key, Ivec, {AAD, PlainText, TagLength}) -> {CipherText, CipherTag}</name> <fsummary>Encrypt <c>PlainText</c> according to <c>Type</c> block cipher</fsummary> <type> <v>Type = <seealso marker="#type-block_cipher_with_iv">block_cipher_with_iv()</seealso></v> @@ -577,8 +577,8 @@ </func> <func> - <name>block_decrypt(Type, Key, Ivec, CipherText) -> PlainText</name> - <name>block_decrypt(AeadType, Key, Ivec, {AAD, CipherText, CipherTag}) -> PlainText | error</name> + <name since="OTP R16B01">block_decrypt(Type, Key, Ivec, CipherText) -> PlainText</name> + <name since="OTP R16B01">block_decrypt(AeadType, Key, Ivec, {AAD, CipherText, CipherTag}) -> PlainText | error</name> <fsummary>Decrypt <c>CipherText</c> according to <c>Type</c> block cipher</fsummary> <type> <v>Type = <seealso marker="#type-block_cipher_with_iv">block_cipher_with_iv()</seealso></v> @@ -603,7 +603,7 @@ </func> <func> - <name name="bytes_to_integer" arity="1"/> + <name name="bytes_to_integer" arity="1" since="OTP R16B01"/> <fsummary>Convert binary representation, of an integer, to an Erlang integer.</fsummary> <desc> <p>Convert binary representation, of an integer, to an Erlang integer. @@ -612,7 +612,7 @@ </func> <func> - <name name="compute_key" arity="4"/> + <name name="compute_key" arity="4" since="OTP R16B01"/> <fsummary>Computes the shared secret</fsummary> <desc> <p>Computes the shared secret from the private key and the other party's public key. @@ -622,7 +622,7 @@ </func> <func> - <name name="exor" arity="2"/> + <name name="exor" arity="2" since=""/> <fsummary>XOR data</fsummary> <desc> <p>Performs bit-wise XOR (exclusive or) on the data supplied.</p> @@ -631,8 +631,8 @@ <func> - <name name="generate_key" arity="2"/> - <name name="generate_key" arity="3"/> + <name name="generate_key" arity="2" since="OTP R16B01"/> + <name name="generate_key" arity="3" since="OTP R16B01"/> <fsummary>Generates a public key of type <c>Type</c></fsummary> <desc> <p>Generates a public key of type <c>Type</c>. @@ -653,7 +653,7 @@ </func> <func> - <name name="hash" arity="2"/> + <name name="hash" arity="2" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Computes a message digest of type <c>Type</c> from <c>Data</c>.</p> @@ -663,7 +663,7 @@ </func> <func> - <name name="hash_init" arity="1"/> + <name name="hash_init" arity="1" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Initializes the context for streaming hash operations. <c>Type</c> determines @@ -675,7 +675,7 @@ </func> <func> - <name name="hash_update" arity="2"/> + <name name="hash_update" arity="2" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> @@ -687,7 +687,7 @@ </func> <func> - <name name="hash_final" arity="1"/> + <name name="hash_final" arity="1" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Finalizes the hash operation referenced by <c>Context</c> returned @@ -698,8 +698,8 @@ </func> <func> - <name name="hmac" arity="3"/> - <name name="hmac" arity="4"/> + <name name="hmac" arity="3" since="OTP R16B"/> + <name name="hmac" arity="4" since="OTP R16B"/> <fsummary></fsummary> <desc> <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using @@ -709,7 +709,7 @@ </func> <func> - <name name="hmac_init" arity="2"/> + <name name="hmac_init" arity="2" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines @@ -719,7 +719,7 @@ </func> <func> - <name name="hmac_update" arity="2"/> + <name name="hmac_update" arity="2" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> @@ -738,7 +738,7 @@ </func> <func> - <name name="hmac_final" arity="1"/> + <name name="hmac_final" arity="1" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is @@ -747,7 +747,7 @@ </func> <func> - <name name="hmac_final_n" arity="2"/> + <name name="hmac_final_n" arity="2" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than @@ -756,8 +756,8 @@ </func> <func> - <name name="cmac" arity="3"/> - <name name="cmac" arity="4"/> + <name name="cmac" arity="3" since="OTP 20.0"/> + <name name="cmac" arity="4" since="OTP 20.0"/> <fsummary>Calculates the Cipher-based Message Authentication Code.</fsummary> <desc> <p>Computes a CMAC of type <c>Type</c> from <c>Data</c> using @@ -767,7 +767,7 @@ </func> <func> - <name name="info_fips" arity="0"/> + <name name="info_fips" arity="0" since="OTP 20.0"/> <fsummary>Provides information about the FIPS operating status.</fsummary> <desc> <p>Provides information about the FIPS operating status of @@ -790,7 +790,7 @@ </func> <func> - <name name="enable_fips_mode" arity="1"/> + <name name="enable_fips_mode" arity="1" since="OTP 21.1"/> <fsummary>Change FIPS mode.</fsummary> <desc> <p>Enables (<c>Enable = true</c>) or disables (<c>Enable = false</c>) FIPS mode. Returns <c>true</c> if @@ -805,7 +805,7 @@ </func> <func> - <name name="info_lib" arity="0"/> + <name name="info_lib" arity="0" since=""/> <fsummary>Provides information about the libraries used by crypto.</fsummary> <desc> <p>Provides the name and version of the libraries used by crypto.</p> @@ -826,7 +826,7 @@ </func> <func> - <name name="mod_pow" arity="3"/> + <name name="mod_pow" arity="3" since="OTP R16B01"/> <fsummary>Computes the function: N^P mod M</fsummary> <desc> <p>Computes the function <c>N^P mod M</c>.</p> @@ -834,8 +834,8 @@ </func> <func> - <name name="next_iv" arity="2"/> - <name name="next_iv" arity="3"/> + <name name="next_iv" arity="2" since="OTP R16B01"/> + <name name="next_iv" arity="3" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Returns the initialization vector to be used in the next @@ -847,7 +847,7 @@ </func> <func> - <name name="poly1305" arity="2"/> + <name name="poly1305" arity="2" since="OTP 21.1"/> <fsummary></fsummary> <desc> <p>Computes a POLY1305 message authentication code (<c>Mac</c>) from <c>Data</c> using @@ -856,7 +856,7 @@ </func> <func> - <name name="private_decrypt" arity="4"/> + <name name="private_decrypt" arity="4" since="OTP R16B01"/> <fsummary>Decrypts CipherText using the private Key.</fsummary> <desc> <p>Decrypts the <c>CipherText</c>, encrypted with @@ -870,7 +870,7 @@ </func> <func> - <name name="private_encrypt" arity="4"/> + <name name="private_encrypt" arity="4" since="OTP R16B01"/> <fsummary>Encrypts PlainText using the private Key.</fsummary> <desc> <p>Encrypts the <c>PlainText</c> using the <c>PrivateKey</c> @@ -883,7 +883,7 @@ </func> <func> - <name name="public_decrypt" arity="4"/> + <name name="public_decrypt" arity="4" since="OTP R16B01"/> <fsummary>Decrypts CipherText using the public Key.</fsummary> <desc> <p>Decrypts the <c>CipherText</c>, encrypted with @@ -897,7 +897,7 @@ </func> <func> - <name name="public_encrypt" arity="4"/> + <name name="public_encrypt" arity="4" since="OTP R16B01"/> <fsummary>Encrypts PlainText using the public Key.</fsummary> <desc> <p>Encrypts the <c>PlainText</c> (message digest) using the <c>PublicKey</c> @@ -909,7 +909,7 @@ </func> <func> - <name name="rand_seed" arity="1"/> + <name name="rand_seed" arity="1" since="OTP 17.0"/> <fsummary>Set the seed for random bytes generation</fsummary> <desc> <p>Set the seed for PRNG to the given binary. This calls the @@ -922,7 +922,7 @@ </func> <func> - <name>rand_uniform(Lo, Hi) -> N</name> + <name since="">rand_uniform(Lo, Hi) -> N</name> <fsummary>Generate a random number</fsummary> <type> <v>Lo, Hi, N = integer()</v> @@ -935,7 +935,7 @@ </func> <func> - <name name="start" arity="0"/> + <name name="start" arity="0" since=""/> <fsummary> Equivalent to application:start(crypto). </fsummary> <desc> <p> Equivalent to application:start(crypto).</p> @@ -943,7 +943,7 @@ </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary> Equivalent to application:stop(crypto).</fsummary> <desc> <p> Equivalent to application:stop(crypto).</p> @@ -951,7 +951,7 @@ </func> <func> - <name name="strong_rand_bytes" arity="1"/> + <name name="strong_rand_bytes" arity="1" since="OTP R14B03"/> <fsummary>Generate a binary of random bytes</fsummary> <desc> <p>Generates N bytes randomly uniform 0..255, and returns the @@ -964,7 +964,7 @@ </func> <func> - <name name="rand_seed" arity="0"/> + <name name="rand_seed" arity="0" since="OTP 20.0"/> <fsummary>Strong random number generation plugin state</fsummary> <desc> <p> @@ -992,7 +992,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="rand_seed_s" arity="0"/> + <name name="rand_seed_s" arity="0" since="OTP 20.0"/> <fsummary>Strong random number generation plugin state</fsummary> <desc> <p> @@ -1027,7 +1027,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name>rand_seed_alg(Alg) -> rand:state()</name> + <name since="OTP 21.0">rand_seed_alg(Alg) -> rand:state()</name> <fsummary>Strong random number generation plugin state</fsummary> <type> <v>Alg = crypto | crypto_cache</v> @@ -1063,7 +1063,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name>rand_seed_alg_s(Alg) -> rand:state()</name> + <name since="OTP 21.0">rand_seed_alg_s(Alg) -> rand:state()</name> <fsummary>Strong random number generation plugin state</fsummary> <type> <v>Alg = crypto | crypto_cache</v> @@ -1121,7 +1121,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_init" arity="2"/> + <name name="stream_init" arity="2" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Initializes the state for use in RC4 stream encryption @@ -1134,7 +1134,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_init" arity="3"/> + <name name="stream_init" arity="3" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Initializes the state for use in streaming AES encryption using Counter mode (CTR). @@ -1149,7 +1149,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_encrypt" arity="2"/> + <name name="stream_encrypt" arity="2" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Encrypts <c>PlainText</c> according to the stream cipher <c>Type</c> specified in stream_init/3. @@ -1160,7 +1160,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_decrypt" arity="2"/> + <name name="stream_decrypt" arity="2" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Decrypts <c>CipherText</c> according to the stream cipher <c>Type</c> specified in stream_init/3. @@ -1171,7 +1171,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="supports" arity="0"/> + <name name="supports" arity="0" since="OTP R16B01"/> <fsummary>Provide a list of available crypto algorithms.</fsummary> <desc> <p> Can be used to determine which crypto algorithms that are supported @@ -1183,7 +1183,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ec_curves" arity="0"/> + <name name="ec_curves" arity="0" since="OTP 17.0"/> <fsummary>Provide a list of available named elliptic curves.</fsummary> <desc> <p>Can be used to determine which named elliptic curves are supported.</p> @@ -1191,7 +1191,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ec_curve" arity="1"/> + <name name="ec_curve" arity="1" since="OTP 17.0"/> <fsummary>Get the defining parameters of a elliptic curve.</fsummary> <desc> <p>Return the defining parameters of a elliptic curve.</p> @@ -1199,8 +1199,8 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="sign" arity="4"/> - <name name="sign" arity="5"/> + <name name="sign" arity="4" since="OTP R16B01"/> + <name name="sign" arity="5" since="OTP 20.1"/> <fsummary> Create digital signature.</fsummary> <desc> <p>Creates a digital signature.</p> @@ -1214,8 +1214,8 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="verify" arity="5"/> - <name name="verify" arity="6"/> + <name name="verify" arity="5" since="OTP R16B01"/> + <name name="verify" arity="6" since="OTP 20.1"/> <fsummary>Verifies a digital signature.</fsummary> <desc> <p>Verifies a digital signature</p> @@ -1231,7 +1231,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> <!-- Engine functions --> <func> - <name name="privkey_to_pubkey" arity="2"/> + <name name="privkey_to_pubkey" arity="2" since="OTP 20.2"/> <fsummary>Fetches a public key from an Engine stored private key.</fsummary> <desc> <p>Fetches the corresponding public key from a private key stored in an Engine. @@ -1241,7 +1241,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_get_all_methods" arity="0"/> + <name name="engine_get_all_methods" arity="0" since="OTP 20.2"/> <fsummary>Return list of all possible engine methods</fsummary> <desc> <p> @@ -1259,7 +1259,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_load" arity="3"/> + <name name="engine_load" arity="3" since="OTP 20.2"/> <fsummary>Dynamical load an encryption engine</fsummary> <desc> <p> @@ -1281,7 +1281,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_load" arity="4"/> + <name name="engine_load" arity="4" since="OTP 20.2"/> <fsummary>Dynamical load an encryption engine</fsummary> <desc> <p> @@ -1301,7 +1301,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_unload" arity="1"/> + <name name="engine_unload" arity="1" since="OTP 20.2"/> <fsummary>Dynamical load an encryption engine</fsummary> <desc> <p> @@ -1321,7 +1321,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_by_id" arity="1"/> + <name name="engine_by_id" arity="1" since="OTP 21.0.6"/> <fsummary>Get a reference to an already loaded engine</fsummary> <desc> <p> @@ -1341,7 +1341,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_ctrl_cmd_string" arity="3"/> + <name name="engine_ctrl_cmd_string" arity="3" since="OTP 20.2"/> <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary> <desc> <p> @@ -1358,7 +1358,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_ctrl_cmd_string" arity="4"/> + <name name="engine_ctrl_cmd_string" arity="4" since="OTP 20.2"/> <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary> <desc> <p> @@ -1379,7 +1379,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_add" arity="1"/> + <name name="engine_add" arity="1" since="OTP 21.0.6"/> <fsummary>Add engine to OpenSSL internal list</fsummary> <desc> <p>Add the engine to OpenSSL's internal list.</p> @@ -1392,7 +1392,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_remove" arity="1"/> + <name name="engine_remove" arity="1" since="OTP 21.0.6"/> <fsummary>Remove engine to OpenSSL internal list</fsummary> <desc> <p>Remove the engine from OpenSSL's internal list.</p> @@ -1405,7 +1405,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_get_id" arity="1"/> + <name name="engine_get_id" arity="1" since="OTP 21.0.6"/> <fsummary>Fetch engine ID</fsummary> <desc> <p>Return the ID for the engine, or an empty binary if there is no id set.</p> @@ -1418,7 +1418,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_get_name" arity="1"/> + <name name="engine_get_name" arity="1" since="OTP 21.0.6"/> <fsummary>Fetch engine name</fsummary> <desc> <p>Return the name (eg a description) for the engine, or an empty binary if there is no name set.</p> @@ -1431,7 +1431,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_list" arity="0"/> + <name name="engine_list" arity="0" since="OTP 20.2"/> <fsummary>List the known engine ids</fsummary> <desc> <p>List the id's of all engines in OpenSSL's internal list.</p> @@ -1451,7 +1451,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_loaded" arity="2"/> + <name name="ensure_engine_loaded" arity="2" since="OTP 21.0.6"/> <fsummary>Ensure encryption engine just loaded once</fsummary> <desc> <p> @@ -1473,7 +1473,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_loaded" arity="3"/> + <name name="ensure_engine_loaded" arity="3" since="OTP 21.0.6"/> <fsummary>Ensure encryption engine just loaded once</fsummary> <desc> <p> @@ -1496,7 +1496,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_unloaded" arity="1"/> + <name name="ensure_engine_unloaded" arity="1" since="OTP 21.0.6"/> <fsummary>Unload an engine loaded with the ensure function</fsummary> <desc> <p> @@ -1519,7 +1519,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_unloaded" arity="2"/> + <name name="ensure_engine_unloaded" arity="2" since="OTP 21.0.6"/> <fsummary>Unload an engine loaded with the ensure function</fsummary> <desc> <p> diff --git a/lib/crypto/doc/src/engine_keys.xml b/lib/crypto/doc/src/engine_keys.xml index feeb353d1e..5ac690eb90 100644 --- a/lib/crypto/doc/src/engine_keys.xml +++ b/lib/crypto/doc/src/engine_keys.xml @@ -51,7 +51,7 @@ <p> OTP/Crypto requires that the user provides two or three items of information about the key. The application used by the user is usually on a higher level, for example in - <seealso marker="ssl:ssl#key_option_def">SSL</seealso>. If using + <seealso marker="ssl:ssl#type-key">SSL</seealso>. If using the crypto application directly, it is required that: </p> <list> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 72cb9aabfd..bc8b124b10 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -512,17 +512,17 @@ block_encrypt(Type, Key, Ivec, PlainText) when Type =:= des_cbc; Type =:= aes_cbc256; Type =:= aes_cbc; Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, PlainText, true); + notsup_to_error(block_crypt_nif(Type, Key, Ivec, PlainText, true)); block_encrypt(Type, Key0, Ivec, PlainText) when Type =:= des3_cbc; Type =:= des_ede3 -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, PlainText, true); + notsup_to_error(block_crypt_nif(des_ede3_cbc, Key, Ivec, PlainText, true)); block_encrypt(des3_cbf, Key0, Ivec, PlainText) -> % cfb misspelled Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, PlainText, true); + notsup_to_error(block_crypt_nif(des_ede3_cbf, Key, Ivec, PlainText, true)); block_encrypt(des3_cfb, Key0, Ivec, PlainText) -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, PlainText, true); + notsup_to_error(block_crypt_nif(des_ede3_cfb, Key, Ivec, PlainText, true)); block_encrypt(aes_ige256, Key, Ivec, PlainText) -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, PlainText, true)); block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= aes_gcm; @@ -549,17 +549,17 @@ block_decrypt(Type, Key, Ivec, Data) when Type =:= des_cbc; Type =:= aes_cfb128; Type =:= aes_cbc256; Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, Data, false); + notsup_to_error(block_crypt_nif(Type, Key, Ivec, Data, false)); block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc; Type =:= des_ede3 -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, Data, false); + notsup_to_error(block_crypt_nif(des_ede3_cbc, Key, Ivec, Data, false)); block_decrypt(des3_cbf, Key0, Ivec, Data) -> % cfb misspelled Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false); + notsup_to_error(block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false)); block_decrypt(des3_cfb, Key0, Ivec, Data) -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false); + notsup_to_error(block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false)); block_decrypt(aes_ige256, Key, Ivec, Data) -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false)); block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; @@ -571,13 +571,13 @@ block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; -spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). block_encrypt(Type, Key, PlainText) -> - block_crypt_nif(Type, Key, PlainText, true). + notsup_to_error(block_crypt_nif(Type, Key, PlainText, true)). -spec block_decrypt(Type::block_cipher_without_iv(), Key::key(), Data::iodata()) -> binary(). block_decrypt(Type, Key, Data) -> - block_crypt_nif(Type, Key, Data, false). + notsup_to_error(block_crypt_nif(Type, Key, Data, false)). -spec next_iv(Type:: cbc_cipher(), Data) -> NextIVec when % Type :: cbc_cipher(), %des_cbc | des3_cbc | aes_cbc | aes_ige, diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index e046a25338..988d95a8bc 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -6,7 +6,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- MODULES = \ - blowfish_SUITE \ + crypto_bench_SUITE \ crypto_SUITE \ engine_SUITE @@ -77,7 +77,7 @@ release_spec: release_tests_spec: $(TEST_TARGET) $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) crypto.spec crypto_bench.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/crypto/test/blowfish_SUITE.erl b/lib/crypto/test/blowfish_SUITE.erl deleted file mode 100644 index a931ebb47e..0000000000 --- a/lib/crypto/test/blowfish_SUITE.erl +++ /dev/null @@ -1,300 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009-2018. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% --module(blowfish_SUITE). - -%% Note: This directive should only be used in test suites. --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(TIMEOUT, 120000). % 2 min - --define(KEY, to_bin("0123456789ABCDEFF0E1D2C3B4A59687")). --define(IVEC, to_bin("FEDCBA9876543210")). -%% "7654321 Now is the time for " (includes trailing '\0') --define(DATA, to_bin("37363534333231204E6F77206973207468652074696D6520666F722000")). --define(DATA_PADDED, to_bin("37363534333231204E6F77206973207468652074696D6520666F722000000000")). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - case catch crypto:start() of - ok -> - catch ct:comment("~s",[element(3,hd(crypto:info_lib()))]), - catch ct:log("crypto:info_lib() -> ~p~n" - "crypto:supports() -> ~p~n" - "crypto:version() -> ~p~n" - ,[crypto:info_lib(), crypto:supports(), crypto:version()]), - Config; - _Else -> - {skip,"Could not start crypto!"} - end. - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - crypto:stop(). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% 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. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> -[{group, fips}, - {group, non_fips}]. - -groups() -> - [{fips, [], [no_ecb, no_cbc, no_cfb64, no_ofb64]}, - {non_fips, [], [ecb, cbc, cfb64, ofb64]}]. - -init_per_group(fips, Config) -> - case crypto:info_fips() of - enabled -> - Config; - not_enabled -> - case crypto:enable_fips_mode(true) of - true -> - enabled = crypto:info_fips(), - Config; - false -> - {skip, "Failed to enable FIPS mode"} - end; - not_supported -> - {skip, "FIPS mode not supported"} - end; -init_per_group(non_fips, Config) -> - case crypto:info_fips() of - enabled -> - true = crypto:enable_fips_mode(false), - not_enabled = crypto:info_fips(), - Config; - _NotEnabled -> - Config - end; -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases start here. -%%-------------------------------------------------------------------- - -ecb_test(KeyBytes, ClearBytes, CipherBytes) -> - {Key, Clear, Cipher} = - {to_bin(KeyBytes), to_bin(ClearBytes), to_bin(CipherBytes)}, - ?line m(crypto:block_encrypt(blowfish_ecb, Key, Clear), Cipher), - true. - -ecb(doc) -> - "Test that ECB mode is OK"; -ecb(suite) -> - []; -ecb(Config) when is_list(Config) -> - true = ecb_test("0000000000000000", "0000000000000000", "4EF997456198DD78"), - true = ecb_test("FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"), - true = ecb_test("3000000000000000", "1000000000000001", "7D856F9A613063F2"), - true = ecb_test("1111111111111111", "1111111111111111", "2466DD878B963C9D"), - true = ecb_test("0123456789ABCDEF", "1111111111111111", "61F9C3802281B096"), - true = ecb_test("1111111111111111", "0123456789ABCDEF", "7D0CC630AFDA1EC7"), - true = ecb_test("0000000000000000", "0000000000000000", "4EF997456198DD78"), - true = ecb_test("FEDCBA9876543210", "0123456789ABCDEF", "0ACEAB0FC6A0A28D"), - true = ecb_test("7CA110454A1A6E57", "01A1D6D039776742", "59C68245EB05282B"), - true = ecb_test("0131D9619DC1376E", "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"), - true = ecb_test("07A1133E4A0B2686", "0248D43806F67172", "1730E5778BEA1DA4"), - true = ecb_test("3849674C2602319E", "51454B582DDF440A", "A25E7856CF2651EB"), - true = ecb_test("04B915BA43FEB5B6", "42FD443059577FA2", "353882B109CE8F1A"), - true = ecb_test("0113B970FD34F2CE", "059B5E0851CF143A", "48F4D0884C379918"), - true = ecb_test("0170F175468FB5E6", "0756D8E0774761D2", "432193B78951FC98"), - true = ecb_test("43297FAD38E373FE", "762514B829BF486A", "13F04154D69D1AE5"), - true = ecb_test("07A7137045DA2A16", "3BDD119049372802", "2EEDDA93FFD39C79"), - true = ecb_test("04689104C2FD3B2F", "26955F6835AF609A", "D887E0393C2DA6E3"), - true = ecb_test("37D06BB516CB7546", "164D5E404F275232", "5F99D04F5B163969"), - true = ecb_test("1F08260D1AC2465E", "6B056E18759F5CCA", "4A057A3B24D3977B"), - true = ecb_test("584023641ABA6176", "004BD6EF09176062", "452031C1E4FADA8E"), - true = ecb_test("025816164629B007", "480D39006EE762F2", "7555AE39F59B87BD"), - true = ecb_test("49793EBC79B3258F", "437540C8698F3CFA", "53C55F9CB49FC019"), - true = ecb_test("4FB05E1515AB73A7", "072D43A077075292", "7A8E7BFA937E89A3"), - true = ecb_test("49E95D6D4CA229BF", "02FE55778117F12A", "CF9C5D7A4986ADB5"), - true = ecb_test("018310DC409B26D6", "1D9D5C5018F728C2", "D1ABB290658BC778"), - true = ecb_test("1C587F1C13924FEF", "305532286D6F295A", "55CB3774D13EF201"), - true = ecb_test("0101010101010101", "0123456789ABCDEF", "FA34EC4847B268B2"), - true = ecb_test("1F1F1F1F0E0E0E0E", "0123456789ABCDEF", "A790795108EA3CAE"), - true = ecb_test("E0FEE0FEF1FEF1FE", "0123456789ABCDEF", "C39E072D9FAC631D"), - true = ecb_test("0000000000000000", "FFFFFFFFFFFFFFFF", "014933E0CDAFF6E4"), - true = ecb_test("FFFFFFFFFFFFFFFF", "0000000000000000", "F21E9A77B71C49BC"), - true = ecb_test("0123456789ABCDEF", "0000000000000000", "245946885754369A"), - true = ecb_test("FEDCBA9876543210", "FFFFFFFFFFFFFFFF", "6B5C5A9C5D9E0A5A"), - ok. - -cbc(doc) -> - "Test that CBC mode is OK"; -cbc(suite) -> - []; -cbc(Config) when is_list(Config) -> - true = crypto:block_encrypt(blowfish_cbc, ?KEY, ?IVEC, ?DATA_PADDED) =:= - to_bin("6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"), - ok. - -cfb64(doc) -> - "Test that CFB64 mode is OK"; -cfb64(suite) -> - []; -cfb64(Config) when is_list(Config) -> - true = crypto:block_encrypt(blowfish_cfb64, ?KEY, ?IVEC, ?DATA) =:= - to_bin("E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3"), - ok. - -ofb64(doc) -> - "Test that OFB64 mode is OK"; -ofb64(suite) -> - []; -ofb64(Config) when is_list(Config) -> - true = crypto:block_encrypt(blowfish_ofb64, ?KEY, ?IVEC, ?DATA) =:= - to_bin("E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA"), - ok. - -no_ecb(doc) -> - "Test that ECB mode is disabled"; -no_ecb(suite) -> - []; -no_ecb(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/3, - [blowfish_ecb, - to_bin("0000000000000000"), - to_bin("FFFFFFFFFFFFFFFF")]). - -no_cbc(doc) -> - "Test that CBC mode is disabled"; -no_cbc(suite) -> - []; -no_cbc(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/4, - [blowfish_cbc, ?KEY, ?IVEC, ?DATA_PADDED]). - -no_cfb64(doc) -> - "Test that CFB64 mode is disabled"; -no_cfb64(suite) -> - []; -no_cfb64(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/4, - [blowfish_cfb64, ?KEY, ?IVEC, ?DATA]), - ok. - -no_ofb64(doc) -> - "Test that OFB64 mode is disabled"; -no_ofb64(suite) -> - []; -no_ofb64(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/4, - [blowfish_ofb64, ?KEY, ?IVEC, ?DATA]). - -%% Helper functions - -%% Assert function fails with notsup error -notsup(Fun, Args) -> - ok = try - {error, {return, apply(Fun, Args)}} - catch - error:notsup -> - ok; - Class:Error -> - {error, {Class, Error}} - end. - - -%% Convert a hexadecimal string to a binary. --spec(to_bin(L::string()) -> binary()). -to_bin(L) -> - to_bin(L, []). - -%% @spec dehex(char()) -> integer() -%% @doc Convert a hex digit to its integer value. --spec(dehex(char()) -> integer()). -dehex(C) when C >= $0, C =< $9 -> - C - $0; -dehex(C) when C >= $a, C =< $f -> - C - $a + 10; -dehex(C) when C >= $A, C =< $F -> - C - $A + 10. - --spec(to_bin(L::string(), list()) -> binary()). -to_bin([], Acc) -> - iolist_to_binary(lists:reverse(Acc)); -to_bin([C1, C2 | Rest], Acc) -> - to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]). - -m(X,X) -> ok. diff --git a/lib/crypto/test/crypto.spec b/lib/crypto/test/crypto.spec index cc09970cb3..4a95275687 100644 --- a/lib/crypto/test/crypto.spec +++ b/lib/crypto/test/crypto.spec @@ -1 +1,6 @@ {suites,"../crypto_test",all}. + +{skip_suites, "../crypto_test", [crypto_bench_SUITE + ], + "Benchmarks run separately"}. + diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 6c6188f775..cbfa96cc16 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -99,6 +99,8 @@ groups() -> {group, rsa}, {group, dss}, {group, ecdsa}, + {group, no_ed25519}, + {group, no_ed448}, {group, dh}, {group, ecdh}, {group, no_srp}, @@ -113,8 +115,8 @@ groups() -> {group, no_blowfish_cfb64}, {group, no_blowfish_ofb64}, {group, aes_cbc128}, - {group, aes_cfb8}, - {group, aes_cfb128}, + {group, no_aes_cfb8}, + {group, no_aes_cfb128}, {group, aes_cbc256}, {group, no_aes_ige256}, {group, no_rc2_cbc}, @@ -156,7 +158,7 @@ groups() -> ]}, {dh, [], [generate_compute, compute_bug]}, - {ecdh, [], [generate_all_supported, compute, generate]}, + {ecdh, [], [use_all_elliptic_curves, compute, generate]}, {srp, [], [generate_compute]}, {des_cbc, [], [block]}, {des_cfb, [], [block]}, @@ -183,8 +185,16 @@ groups() -> {chacha20, [], [stream]}, {poly1305, [], [poly1305]}, {aes_cbc, [], [block]}, + {no_aes_cfb8,[], [no_support, no_block]}, + {no_aes_cfb128,[], [no_support, no_block]}, {no_md4, [], [no_support, no_hash]}, {no_md5, [], [no_support, no_hash, no_hmac]}, + {no_ed25519, [], [no_support, no_sign_verify + %% Does not work yet: ,public_encrypt, private_encrypt + ]}, + {no_ed448, [], [no_support, no_sign_verify + %% Does not work yet: ,public_encrypt, private_encrypt + ]}, {no_ripemd160, [], [no_support, no_hash]}, {no_srp, [], [no_support, no_generate_compute]}, {no_des_cbc, [], [no_support, no_block]}, @@ -251,7 +261,7 @@ init_per_group(fips, Config) -> enabled = crypto:info_fips(), FIPSConfig; false -> - {skip, "Failed to enable FIPS mode"} + {fail, "Failed to enable FIPS mode"} end; not_supported -> {skip, "FIPS mode not supported"} @@ -401,17 +411,6 @@ block() -> block(Config) when is_list(Config) -> Fips = proplists:get_bool(fips, Config), Type = ?config(type, Config), - %% See comment about EVP_CIPHER_CTX_set_key_length in - %% block_crypt_nif in crypto.c. - case {Fips, Type} of - {true, aes_cfb8} -> - throw({skip, "Cannot test aes_cfb8 in FIPS mode because of key length issue"}); - {true, aes_cfb128} -> - throw({skip, "Cannot test aes_cfb128 in FIPS mode because of key length issue"}); - _ -> - ok - end, - Blocks = lazy_eval(proplists:get_value(block, Config)), lists:foreach(fun block_cipher/1, Blocks), lists:foreach(fun block_cipher/1, block_iolistify(Blocks)), @@ -500,6 +499,13 @@ sign_verify(Config) when is_list(Config) -> SignVerify = proplists:get_value(sign_verify, Config), lists:foreach(fun do_sign_verify/1, SignVerify). +%%-------------------------------------------------------------------- +no_sign_verify() -> + [{doc, "Test disabled sign/verify digital signatures"}]. +no_sign_verify(Config) when is_list(Config) -> + [SignVerifyHd|_] = proplists:get_value(sign_verify, Config), + notsup(fun do_sign_verify/1, [SignVerifyHd]). + %%-------------------------------------------------------------------- public_encrypt() -> [{doc, "Test public_encrypt/decrypt "}]. @@ -563,32 +569,43 @@ compute(Config) when is_list(Config) -> Gen = proplists:get_value(compute, Config), lists:foreach(fun do_compute/1, Gen). %%-------------------------------------------------------------------- -generate_all_supported() -> - [{doc, " Test that all curves from crypto:ec_curves/0 returns two binaries"}]. -generate_all_supported(_Config) -> +use_all_elliptic_curves() -> + [{doc, " Test that all curves from crypto:ec_curves/0"}]. +use_all_elliptic_curves(_Config) -> + Msg = <<"hello world!">>, + Sups = crypto:supports(), + Curves = proplists:get_value(curves, Sups), + Hashs = proplists:get_value(hashs, Sups), + ct:log("Lib: ~p~nFIPS: ~p~nCurves:~n~p~nHashs: ~p", [crypto:info_lib(), + crypto:info_fips(), + Curves, + Hashs]), Results = - [try - crypto:generate_key(ecdh, C) - of - {B1,B2} when is_binary(B1) and is_binary(B2) -> - %% That is, seems like it works as expected. - {ok,C}; - Err -> - ct:log("ERROR: Curve ~p generated ~p", [C,Err]), - {error,{C,Err}} - catch - Cls:Err:Stack -> - ct:log("ERROR: Curve ~p exception ~p:~p~n~p", [C,Cls,Err,Stack]), - {error,{C,{Cls,Err}}} - end - || C <- crypto:ec_curves(), - not lists:member(C, [ed25519, ed448]) + [{{Curve,Hash}, + try + {Pub,Priv} = crypto:generate_key(ecdh, Curve), + true = is_binary(Pub), + true = is_binary(Priv), + Sig = crypto:sign(ecdsa, Hash, Msg, [Priv, Curve]), + crypto:verify(ecdsa, Hash, Msg, Sig, [Pub, Curve]) + catch + C:E -> + {C,E} + end} + || Curve <- Curves -- [ed25519, ed448, x25519, x448, ipsec3, ipsec4], + Hash <- Hashs -- [md4, md5, ripemd160, sha3_224, sha3_256, sha3_384, sha3_512] ], - OK = [C || {ok,C} <- Results], - ct:log("Ok (len=~p): ~p", [length(OK), OK]), - false = lists:any(fun({error,_}) -> true; - (_) -> false - end, Results). + Fails = + lists:filter(fun({_,true}) -> false; + (_) -> true + end, Results), + case Fails of + [] -> + ok; + _ -> + ct:log("Fails:~n~p",[Fails]), + ct:fail("Bad curve(s)",[]) + end. %%-------------------------------------------------------------------- generate() -> diff --git a/lib/crypto/test/crypto_bench.spec b/lib/crypto/test/crypto_bench.spec new file mode 100644 index 0000000000..b9a26d94db --- /dev/null +++ b/lib/crypto/test/crypto_bench.spec @@ -0,0 +1,3 @@ +{suites, "../crypto_test", [ + crypto_bench_SUITE + ]}. diff --git a/lib/crypto/test/crypto_bench_SUITE.erl b/lib/crypto/test/crypto_bench_SUITE.erl new file mode 100644 index 0000000000..c66a27f0c8 --- /dev/null +++ b/lib/crypto/test/crypto_bench_SUITE.erl @@ -0,0 +1,400 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(crypto_bench_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("common_test/include/ct.hrl"). + +suite() -> [%%{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}, + {timetrap,{minutes,2}} + ]. + +all() -> + [ + {group, textblock_256} + ]. + +groups() -> + [ + {textblock_256, [], [ + {group, ciphers_128}, + {group, ciphers_256} + ]}, + + {ciphers_128, [{repeat, 5}], [ + block, + stream + ]}, + + {ciphers_256, [{repeat, 5}], [ + block, + stream, + chacha + ]} + ]. + +%%%---------------------------------------------------------------- +%%% +init_per_suite(Config0) -> + try crypto:start() of + _ -> + [{_,_,Info}] = crypto:info_lib(), + ct:comment("~s",[Info]), + ct:pal("Crypto version: ~p~n~n~p",[Info,crypto:supports()]), + Config1 = measure_openssl_aes_cbc([128,256], Config0), + calibrate([{sec_goal,10} | Config1]) + + catch _:_ -> + {fail, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + application:stop(crypto). + +%%%---------------------------------------------------------------- +%%% +init_per_group(Group, Config) -> + case atom_to_list(Group) of + "ciphers_"++KeySizeStr -> + KeySize = list_to_integer(KeySizeStr), + [{key_size,KeySize} | Config]; + + "textblock_"++BlockSizeStr -> + BlockSize = list_to_integer(BlockSizeStr), + [{block_size,BlockSize} | Config]; + + _ -> + Config + end. + +end_per_group(_Group, Config) -> + Config. + + +measure_openssl_aes_cbc(KeySizes, Config) -> + BLno_acc = [baseline(aes_cbc, KeySize, false) || KeySize <- KeySizes], + ct:pal("Non-accelerated baseline encryption time [µs/block]:~n~p", [BLno_acc]), + BLacc = [baseline(aes_cbc, KeySize, true) || KeySize <- KeySizes], + ct:pal("Possibly accelerated baseline encryption time [µs/block]:~n~p", [BLacc]), + [{acc,BLacc}, + {no_acc,BLno_acc} | Config]. + +calibrate(Config) -> + Secs = proplists:get_value(sec_goal, Config, 10), + {_,Empty} = data(empty, 0, 0), + {Ne,Te} = run1(Secs*3000, Empty), + report(["Overhead"], Te/Ne), + [{overhead,Te/Ne} | Config]. + +%%%================================================================ +%%% +%%% +block(Config) -> + run_cryptos([aes_cbc, aes_gcm, aes_ccm], + Config). + +stream(Config) -> + run_cryptos([aes_ctr], + Config). + +chacha(Config) -> + run_cryptos([chacha20, chacha20_poly1305], + Config). + + +%%%================================================================ +%%% +%%% + +run_cryptos(Cryptos, Config) -> + KeySize = proplists:get_value(key_size, Config), + BlockSize = proplists:get_value(block_size, Config), + MilliSecGoal = 1000*proplists:get_value(sec_goal,Config), + OverHead = proplists:get_value(overhead, Config, 0), + [try + TimePerOpBrutto = run(Crypto,KeySize,BlockSize,MilliSecGoal), + %% ct:pal("Brutto: ~p Overhead: ~p (~.2f %) Netto: ~p", + %% [TimePerOpBrutto, OverHead, 100*OverHead/TimePerOpBrutto,TimePerOpBrutto - OverHead]), + TimePerOpBrutto - OverHead + of + TimePerOp -> % µs + %% First, Report speed of encrypting blocks of 1000. [blocks/sec] + ReportUnit = 1000, + Label = [fmt(Crypto)," key:",KeySize," block:",BlockSize], + report(Label, + (BlockSize/ReportUnit)*1000000/TimePerOp + ), + + EffCrypto = case Crypto of + X -> X + end, + %% Percent of accelerated speed + case find_value([acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseAcc -> + report(["Percent of acc OpenSSL "|Label], + 100*TimePerOpBaseAcc/TimePerOp % Percent of base *speed* + ) + end, + + %% Percent of non-accelerated speed + case find_value([no_acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseNoAcc -> + report(["Percent of noacc OpenSSL "|Label], + 100*TimePerOpBaseNoAcc/TimePerOp % Percent of base *speed* + ) + end + catch + _:_ -> + ct:pal("~p unsupported",[{Crypto,KeySize,BlockSize}]) + end + || Crypto <- Cryptos, + supported(Crypto) + ]. + + +run(Crypto, KeySize, BlockSize, MilliSecGoal) -> + {_Type, Funs} = data(Crypto, KeySize, BlockSize), + {Nc,Tc} = run1(MilliSecGoal, Funs), + Tc/Nc. + +fmt(X) -> X. + + +find_value(KeyPath, PropList, Default) -> + try find_value(KeyPath, PropList) + of + undefined -> Default + catch + error:function_clause -> Default + end. + +find_value(KeyPath, PropList) -> + lists:foldl(fun(K, L) when is_list(L) -> proplists:get_value(K,L); + (_, _) -> undefined + end, PropList, KeyPath). + +%%%================================================================ +%%% +%%% +funs({block, {Type, Key, IV, Block}}) -> + {fun() -> ok end, + fun(_) -> crypto:block_encrypt(Type, Key, IV, Block) end, + fun(_) -> ok end}; + +funs({stream, {Type, Key, IV, Block}}) -> + {fun() -> {crypto:stream_init(Type, Key, IV),ok} end, + fun({Ctx,_}) -> crypto:stream_encrypt(Ctx, Block) end, + fun(_) -> ok end}. + + +data(aes_cbc, KeySize, BlockSize) -> + Type = case KeySize of + 128 -> aes_cbc128; + 256 -> aes_cbc256 + end, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, Block}})}; + +data(aes_gcm, KeySize, BlockSize) -> + Type = aes_gcm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,16}}})}; + +data(aes_ccm, KeySize, BlockSize) -> + Type = aes_ccm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,12}}})}; + +data(aes_ctr, KeySize, BlockSize) -> + Type = aes_ctr, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(chacha20_poly1305, 256=KeySize, BlockSize) -> + Type = chacha20_poly1305, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + AAD = <<01,02,03,04>>, + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, {AAD,Block}}})}; + +data(chacha20, 256=KeySize, BlockSize) -> + Type = chacha20, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(empty, 0, 0) -> + {undefined, + {fun() -> ok end, + fun(X) -> X end, + fun(_) -> ok end}}. + +%%%================================================================ +%%% +%%% +run1(MilliSecGoal, Funs) -> + Parent = self(), + Pid = spawn(fun() -> + {Fi,Fu,Ff} = Funs, + Ctx0 = Fi(), + erlang:garbage_collect(), + T0 = start_time(), + {N,Ctx} = loop(Fu, Ctx0, 0), + T = elapsed_time(T0), + Ff(Ctx), + Parent ! {result,N,microseconds(T)} + end), + Pid ! go, + receive + after MilliSecGoal -> + Pid ! stop + end, + receive + {result,N,MicroSecs} -> + {N,MicroSecs} + end. + + +loop(F, Ctx, N) -> + receive + stop -> + {N, Ctx} + after 0 -> + loop(F, F(Ctx), N+1) + end. + +%%%---------------------------------------------------------------- +report(LabelList, Value) -> + Label = report_chars(lists:concat(LabelList)), + ct:pal("ct_event:notify ~p: ~p", [Label, Value]), + ct_event:notify( + #event{name = benchmark_data, + data = [{name, Label}, + {value,Value}]}). + +report_chars(Cs) -> + [case C of + $- -> $_; + _ -> C + end || C <- Cs]. + +%%%---------------------------------------------------------------- +supported(Algorithm) -> + lists:member(Algorithm, + [A || {_,As} <- crypto:supports(), A <- As] + ). + +%%%---------------------------------------------------------------- +start_time() -> + erlang:system_time(). + +elapsed_time(StartTime) -> + erlang:system_time() - StartTime. + +microseconds(Time) -> + erlang:convert_time_unit(Time, native, microsecond). + +%%%---------------------------------------------------------------- + +%% Example output: +%% +DT:aes-128-cbc:3:16 +%% +R:135704772:aes-128-cbc:2.980000 +%% +DT:aes-128-cbc:3:64 +%% +R:36835089:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:256 +%% +R:9398616:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:1024 +%% +R:2355683:aes-128-cbc:2.990000 +%% +DT:aes-128-cbc:3:8192 +%% +R:294508:aes-128-cbc:2.990000 +%% +H:16:64:256:1024:8192 +%% +F:22:aes-128-cbc:728616225.50:785815232.00:802015232.00:806762338.46:806892821.40 + +baseline(Crypto, KeySize, EVP) -> + Spec= + case {Crypto,KeySize} of + {aes_cbc, 128} -> "aes-128-cbc"; + {aes_cbc, 256} -> "aes-256-cbc" + end, + {{Crypto,KeySize}, baseline(Spec, EVP)}. + +baseline(Spec, EVP) -> + Cmd = + case EVP of + true -> "openssl speed -mr -evp " ++ Spec; + false-> "openssl speed -mr " ++ Spec + end, + get_base_values(string:tokens(os:cmd(Cmd),"\n"), Spec, []). + + +get_base_values(["+DT:"++Sdt, + "+R:"++Sr + |T], Crypto, Acc) -> + [Crypto0,_GoalSecs0,BlockSize0] = string:tokens(Sdt, ":"), + [Nblocks0,Crypto0,RealSecs0] = string:tokens(Sr, ":"), + Crypto = fix_possible_space_bug(Crypto0), + RealSecs = list_to_float(RealSecs0), + BlockSize = list_to_integer(BlockSize0), + Nblocks = list_to_integer(Nblocks0), + get_base_values(T, Crypto, [{BlockSize, 1000000*RealSecs/Nblocks} | Acc]); + +get_base_values([_|T], Crypto, Acc) -> + get_base_values(T, Crypto, Acc); + +get_base_values([], _, Acc) -> + lists:sort(Acc). + +fix_possible_space_bug(S) -> lists:concat(lists:join("-",string:tokens(S,"- "))). + +%%%---------------------------------------------------------------- +mk_bin(Size) when Size =< 256 -> + list_to_binary(lists:seq(0,Size-1)); + +mk_bin(Size) when 1024 =< Size -> + B = mk_bin(Size div 4), + Brest = mk_bin(Size rem 4), + <<B/binary, B/binary, B/binary, B/binary, Brest/binary>>; + +mk_bin(Size) when 256 < Size -> + B = mk_bin(Size div 2), + Brest = mk_bin(Size rem 2), + <<B/binary, B/binary, Brest/binary>>. + diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl index 8a45fc9076..869db516b4 100644 --- a/lib/crypto/test/engine_SUITE.erl +++ b/lib/crypto/test/engine_SUITE.erl @@ -345,13 +345,13 @@ engine_list(Config) when is_list(Config) -> {skip, "OTP Test engine not found"}; {ok, Engine} -> try - EngineList0 = crypto:engine_list(), case crypto:engine_load(<<"dynamic">>, [{<<"SO_PATH">>, Engine}, <<"LOAD">>], []) of {ok, E} -> EngineList0 = crypto:engine_list(), + false = lists:member(<<"MD5">>, EngineList0), ok = crypto:engine_add(E), [<<"MD5">>] = lists:subtract(crypto:engine_list(), EngineList0), ok = crypto:engine_remove(E), diff --git a/lib/debugger/doc/src/debugger.xml b/lib/debugger/doc/src/debugger.xml index 1ecdbcd064..77285095e7 100644 --- a/lib/debugger/doc/src/debugger.xml +++ b/lib/debugger/doc/src/debugger.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>debugger</module> + <module since="">debugger</module> <modulesummary>Erlang Debugger.</modulesummary> <description> <p>Erlang Debugger for debugging and testing of Erlang programs.</p> @@ -36,10 +36,10 @@ <funcs> <func> - <name>start()</name> - <name>start(File)</name> - <name>start(Mode)</name> - <name>start(Mode, File)</name> + <name since="">start()</name> + <name since="">start(File)</name> + <name since="">start(Mode)</name> + <name since="">start(Mode, File)</name> <fsummary>Start Debugger.</fsummary> <type> <v>Mode = local | global</v> @@ -60,7 +60,7 @@ </func> <func> - <name>quick(Module, Name, Args)</name> + <name since="">quick(Module, Name, Args)</name> <fsummary>Debug a process.</fsummary> <type> <v>Module = Name = atom()</v> diff --git a/lib/debugger/doc/src/i.xml b/lib/debugger/doc/src/i.xml index 628b91e9e4..06b0eb876a 100644 --- a/lib/debugger/doc/src/i.xml +++ b/lib/debugger/doc/src/i.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>i</module> + <module since="">i</module> <modulesummary>Debugger/Interpreter Interface.</modulesummary> <description> <p>The <c>i</c> module provides short forms for some of @@ -51,7 +51,7 @@ <funcs> <func> - <name>im() -> pid()</name> + <name since="">im() -> pid()</name> <fsummary>Start a graphical monitor.</fsummary> <desc> <p>Starts a new graphical monitor. This is the Monitor window, @@ -63,10 +63,10 @@ </func> <func> - <name>ii(AbsModules) -> ok</name> - <name>ii(AbsModule) -> {module, Module} | error</name> - <name>ini(AbsModules) -> ok</name> - <name>ini(AbsModule) -> {module, Module} | error</name> + <name since="">ii(AbsModules) -> ok</name> + <name since="">ii(AbsModule) -> {module, Module} | error</name> + <name since="">ini(AbsModules) -> ok</name> + <name since="">ini(AbsModule) -> {module, Module} | error</name> <fsummary>Interpret a module.</fsummary> <type> <v>AbsModules = [AbsModule]</v> @@ -84,8 +84,8 @@ </func> <func> - <name>iq(AbsModule) -> ok</name> - <name>inq(AbsModule) -> ok</name> + <name since="">iq(AbsModule) -> ok</name> + <name since="">inq(AbsModule) -> ok</name> <fsummary>Stop interpreting a module.</fsummary> <type> <v>AbsModule = Module | File</v> @@ -100,7 +100,7 @@ </func> <func> - <name>il() -> ok</name> + <name since="">il() -> ok</name> <fsummary>Make a printout of all interpreted modules</fsummary> <desc> <p>Makes a printout of all interpreted modules. @@ -110,7 +110,7 @@ </func> <func> - <name>ip() -> ok</name> + <name since="">ip() -> ok</name> <fsummary>Print the current status of all interpreted processes.</fsummary> <desc> @@ -119,7 +119,7 @@ </func> <func> - <name>ic() -> ok</name> + <name since="">ic() -> ok</name> <fsummary>Clear information about processes executing interpreted code.</fsummary> <desc> @@ -129,8 +129,8 @@ </func> <func> - <name>iaa(Flags) -> true</name> - <name>iaa(Flags, Function) -> true</name> + <name since="">iaa(Flags) -> true</name> + <name since="">iaa(Flags, Function) -> true</name> <fsummary>Set when and how to attach to a process.</fsummary> <type> <v>Flags = [init | break | exit]</v> @@ -148,7 +148,7 @@ </func> <func> - <name>ist(Flag) -> true</name> + <name since="">ist(Flag) -> true</name> <fsummary>Set how to save call frames.</fsummary> <type> <v>Flag = all | no_tail | false</v> @@ -160,7 +160,7 @@ </func> <func> - <name>ia(Pid) -> ok | no_proc</name> + <name since="">ia(Pid) -> ok | no_proc</name> <fsummary>Attache to a process.</fsummary> <type> <v>Pid = pid()</v> @@ -172,7 +172,7 @@ </func> <func> - <name>ia(X,Y,Z) -> ok | no_proc</name> + <name since="">ia(X,Y,Z) -> ok | no_proc</name> <fsummary>Attache to a process.</fsummary> <type> <v>X = Y = Z = int()</v> @@ -184,7 +184,7 @@ </func> <func> - <name>ia(Pid, Function) -> ok | no_proc</name> + <name since="">ia(Pid, Function) -> ok | no_proc</name> <fsummary>Attache to a process.</fsummary> <type> <v>Pid = pid()</v> @@ -199,7 +199,7 @@ </func> <func> - <name>ia(X,Y,Z, Function) -> ok | no_proc</name> + <name since="">ia(X,Y,Z, Function) -> ok | no_proc</name> <fsummary>Attache to a process.</fsummary> <type> <v>X = Y = Z = int()</v> @@ -217,7 +217,7 @@ </func> <func> - <name>ib(Module, Line) -> ok | {error, break_exists}</name> + <name since="">ib(Module, Line) -> ok | {error, break_exists}</name> <fsummary>Create a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -229,7 +229,7 @@ </func> <func> - <name>ib(Module, Name, Arity) -> ok | {error, function_not_found} + <name since="">ib(Module, Name, Arity) -> ok | {error, function_not_found} </name> <fsummary>Create breakpoints in the specified function.</fsummary> <type> @@ -243,7 +243,7 @@ </func> <func> - <name>ir() -> ok</name> + <name since="">ir() -> ok</name> <fsummary>Delete all breakpoints.</fsummary> <desc> <p>Deletes all breakpoints.</p> @@ -251,7 +251,7 @@ </func> <func> - <name>ir(Module) -> ok</name> + <name since="">ir(Module) -> ok</name> <fsummary>Delete all breakpoints in a module.</fsummary> <type> <v>Module = atom()</v> @@ -262,7 +262,7 @@ </func> <func> - <name>ir(Module, Line) -> ok</name> + <name since="">ir(Module, Line) -> ok</name> <fsummary>Delete a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -274,7 +274,7 @@ </func> <func> - <name>ir(Module, Name, Arity) -> ok | {error, function_not_found} + <name since="">ir(Module, Name, Arity) -> ok | {error, function_not_found} </name> <fsummary>Delete breakpoints from the specified function.</fsummary> <type> @@ -288,7 +288,7 @@ </func> <func> - <name>ibd(Module, Line) -> ok</name> + <name since="">ibd(Module, Line) -> ok</name> <fsummary>Make a breakpoint inactive.</fsummary> <type> <v>Module = atom()</v> @@ -300,7 +300,7 @@ </func> <func> - <name>ibe(Module, Line) -> ok</name> + <name since="">ibe(Module, Line) -> ok</name> <fsummary>Make a breakpoint active.</fsummary> <type> <v>Module = atom()</v> @@ -312,7 +312,7 @@ </func> <func> - <name>iba(Module, Line, Action) -> ok</name> + <name since="">iba(Module, Line, Action) -> ok</name> <fsummary>Set the trigger action of a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -326,7 +326,7 @@ </func> <func> - <name>ibc(Module, Line, Function) -> ok</name> + <name since="">ibc(Module, Line, Function) -> ok</name> <fsummary>Set the conditional test of a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -348,7 +348,7 @@ </func> <func> - <name>ipb() -> ok</name> + <name since="">ipb() -> ok</name> <fsummary>Print all existing breakpoints.</fsummary> <desc> <p>Prints all existing breakpoints.</p> @@ -356,7 +356,7 @@ </func> <func> - <name>ipb(Module) -> ok</name> + <name since="">ipb(Module) -> ok</name> <fsummary>Print all existing breakpoints in a module.</fsummary> <type> <v>Module = atom()</v> @@ -367,7 +367,7 @@ </func> <func> - <name>iv() -> atom()</name> + <name since="">iv() -> atom()</name> <fsummary>Return the current version number of the interpreter. </fsummary> <desc> @@ -377,7 +377,7 @@ </func> <func> - <name>help() -> ok</name> + <name since="">help() -> ok</name> <fsummary>Print help text.</fsummary> <desc> <p>Prints help text.</p> diff --git a/lib/debugger/doc/src/int.xml b/lib/debugger/doc/src/int.xml index 31e9dfe923..a0078714e6 100644 --- a/lib/debugger/doc/src/int.xml +++ b/lib/debugger/doc/src/int.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>int</module> + <module since="">int</module> <modulesummary>Interpreter Interface.</modulesummary> <description> <p>The Erlang interpreter provides mechanisms for breakpoints and @@ -94,10 +94,10 @@ <funcs> <func> - <name>i(AbsModule) -> {module,Module} | error</name> - <name>i(AbsModules) -> ok</name> - <name>ni(AbsModule) -> {module,Module} | error</name> - <name>ni(AbsModules) -> ok</name> + <name since="">i(AbsModule) -> {module,Module} | error</name> + <name since="">i(AbsModules) -> ok</name> + <name since="">ni(AbsModule) -> {module,Module} | error</name> + <name since="">ni(AbsModules) -> ok</name> <fsummary>Interpret a module.</fsummary> <type> <v>AbsModules = [AbsModule]</v> @@ -144,8 +144,8 @@ </func> <func> - <name>n(AbsModule) -> ok</name> - <name>nn(AbsModule) -> ok</name> + <name since="">n(AbsModule) -> ok</name> + <name since="">nn(AbsModule) -> ok</name> <fsummary>Stop interpreting a module.</fsummary> <type> <v>AbsModule = Module | File | [Module | File]</v> @@ -163,7 +163,7 @@ </func> <func> - <name>interpreted() -> [Module]</name> + <name since="">interpreted() -> [Module]</name> <fsummary>Get all interpreted modules.</fsummary> <type> <v>Module = atom()</v> @@ -174,7 +174,7 @@ </func> <func> - <name>file(Module) -> File | {error,not_loaded}</name> + <name since="">file(Module) -> File | {error,not_loaded}</name> <fsummary>Get the filename for an interpreted module.</fsummary> <type> <v>Module = atom()</v> @@ -187,7 +187,7 @@ </func> <func> - <name>interpretable(AbsModule) -> true | {error,Reason}</name> + <name since="">interpretable(AbsModule) -> true | {error,Reason}</name> <fsummary>Check if a module can be interpreted.</fsummary> <type> <v>AbsModule = Module | File</v> @@ -255,9 +255,9 @@ </func> <func> - <name>auto_attach() -> false | {Flags,Function}</name> - <name>auto_attach(false)</name> - <name>auto_attach(Flags, Function)</name> + <name since="">auto_attach() -> false | {Flags,Function}</name> + <name since="">auto_attach(false)</name> + <name since="">auto_attach(Flags, Function)</name> <fsummary>Get and set when and how to attach to a process.</fsummary> <type> <v>Flags = [init | break | exit]</v> @@ -290,8 +290,8 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>stack_trace() -> Flag</name> - <name>stack_trace(Flag)</name> + <name since="">stack_trace() -> Flag</name> + <name since="">stack_trace(Flag)</name> <fsummary>Get and set if and how to save call frames.</fsummary> <type> <v>Flag = all | no_tail | false</v> @@ -322,7 +322,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>break(Module, Line) -> ok | {error,break_exists}</name> + <name since="">break(Module, Line) -> ok | {error,break_exists}</name> <fsummary>Create a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -334,7 +334,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>delete_break(Module, Line) -> ok</name> + <name since="">delete_break(Module, Line) -> ok</name> <fsummary>Delete a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -346,7 +346,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>break_in(Module, Name, Arity) -> ok + <name since="">break_in(Module, Name, Arity) -> ok | {error,function_not_found}</name> <fsummary>Create breakpoints in the specified function.</fsummary> <type> @@ -360,7 +360,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>del_break_in(Module, Name, Arity) -> ok + <name since="">del_break_in(Module, Name, Arity) -> ok | {error,function_not_found}</name> <fsummary>Delete breakpoints from the specified function.</fsummary> <type> @@ -374,8 +374,8 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>no_break() -> ok</name> - <name>no_break(Module) -> ok</name> + <name since="">no_break() -> ok</name> + <name since="">no_break(Module) -> ok</name> <fsummary>Delete all breakpoints.</fsummary> <desc> <p>Deletes all breakpoints, or all breakpoints in <c>Module</c>.</p> @@ -383,7 +383,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>disable_break(Module, Line) -> ok</name> + <name since="">disable_break(Module, Line) -> ok</name> <fsummary>Make a breakpoint inactive.</fsummary> <type> <v>Module = atom()</v> @@ -395,7 +395,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>enable_break(Module, Line) -> ok</name> + <name since="">enable_break(Module, Line) -> ok</name> <fsummary>Make a breakpoint active.</fsummary> <type> <v>Module = atom()</v> @@ -407,7 +407,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>action_at_break(Module, Line, Action) -> ok</name> + <name since="">action_at_break(Module, Line, Action) -> ok</name> <fsummary>Set the trigger action of a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -421,7 +421,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>test_at_break(Module, Line, Function) -> ok</name> + <name since="">test_at_break(Module, Line, Function) -> ok</name> <fsummary>Set the conditional test of a breakpoint.</fsummary> <type> <v>Module = atom()</v> @@ -438,7 +438,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>get_binding(Var, Bindings) -> {value,Value} | unbound</name> + <name since="">get_binding(Var, Bindings) -> {value,Value} | unbound</name> <fsummary>Retrieve a variable binding.</fsummary> <type> <v>Var = atom()</v> @@ -453,8 +453,8 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>all_breaks() -> [Break]</name> - <name>all_breaks(Module) -> [Break]</name> + <name since="">all_breaks() -> [Break]</name> + <name since="">all_breaks(Module) -> [Break]</name> <fsummary>Get all breakpoints.</fsummary> <type> <v>Break = {Point,Options}</v> @@ -474,7 +474,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>snapshot() -> [Snapshot]</name> + <name since="">snapshot() -> [Snapshot]</name> <fsummary>Get information about all processes executing interpreted code.</fsummary> <type> @@ -519,7 +519,7 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>clear() -> ok</name> + <name since="">clear() -> ok</name> <fsummary>Clear information about processes executing interpreted code.</fsummary> <desc> @@ -529,8 +529,8 @@ spawn(Module, Name, [Pid | Args])</pre> </func> <func> - <name>continue(Pid) -> ok | {error,not_interpreted}</name> - <name>continue(X,Y,Z) -> ok | {error,not_interpreted}</name> + <name since="">continue(Pid) -> ok | {error,not_interpreted}</name> + <name since="">continue(X,Y,Z) -> ok | {error,not_interpreted}</name> <fsummary>Resume process execution.</fsummary> <type> <v>Pid = pid()</v> diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index e34ffd6def..f5e8337eb1 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -29,7 +29,7 @@ <rev></rev> <file>dialyzer.xml</file> </header> - <module>dialyzer</module> + <module since="">dialyzer</module> <modulesummary>Dialyzer, a DIscrepancy AnaLYZer for ERlang programs. </modulesummary> <description> @@ -472,7 +472,7 @@ dialyzer --plts plt_1 ... plt_n -- files_to_analyze</code> <funcs> <func> - <name>format_warning(Msg) -> string()</name> + <name since="">format_warning(Msg) -> string()</name> <fsummary>Get the string version of a warning message.</fsummary> <type> <v>Msg = {Tag, Id, msg()}</v> @@ -485,8 +485,8 @@ dialyzer --plts plt_1 ... plt_n -- files_to_analyze</code> </func> <func> - <name>gui() -> ok | {error, Msg}</name> - <name>gui(OptList) -> ok | {error, Msg}</name> + <name since="">gui() -> ok | {error, Msg}</name> + <name since="">gui(OptList) -> ok | {error, Msg}</name> <fsummary>Dialyzer GUI version.</fsummary> <type> <v>OptList</v> @@ -539,7 +539,7 @@ WarnOpts :: error_handling </func> <func> - <name>plt_info(string()) -> {'ok', [{atom(), any()}]} | {'error', atom()}</name> + <name since="">plt_info(string()) -> {'ok', [{atom(), any()}]} | {'error', atom()}</name> <fsummary>Return information about the specified PLT.</fsummary> <desc> <p>Returns information about the specified PLT.</p> @@ -547,7 +547,7 @@ WarnOpts :: error_handling </func> <func> - <name>run(OptList) -> Warnings</name> + <name since="">run(OptList) -> Warnings</name> <fsummary>Dialyzer command-line version.</fsummary> <type> <v>OptList</v> diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index 5587cf2bdf..c4e3c322e5 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -347,13 +347,11 @@ get_file_contract(Key, ContDict) -> lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) -> ets_dict_find(MFA, ContDict). --spec lookup_meta_info(module() | mfa(), codeserver()) -> meta_info(). +-spec lookup_meta_info(module() | mfa(), codeserver()) -> + {'ok', meta_info()} | 'error'. lookup_meta_info(MorMFA, #codeserver{fun_meta_info = FunMetaInfo}) -> - case ets_dict_find(MorMFA, FunMetaInfo) of - error -> []; - {ok, PropList} -> PropList - end. + ets_dict_find(MorMFA, FunMetaInfo). -spec get_contracts(codeserver()) -> dict:dict(mfa(), dialyzer_contracts:file_contract()). diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index af7f4385ad..9c36d745c3 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -25,7 +25,7 @@ %% get_contract_signature/1, is_overloaded/1, process_contract_remote_types/1, - store_tmp_contract/5]). + store_tmp_contract/6]). -export_type([file_contract/0, plt_contracts/0]). @@ -146,18 +146,18 @@ process_contract_remote_types(CodeServer) -> Mods = dialyzer_codeserver:all_temp_modules(CodeServer), RecordTable = dialyzer_codeserver:get_records_table(CodeServer), ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer), - ContractFun = - fun({{_M, _F, _A}=MFA, {File, TmpContract, Xtra}}, C0) -> - #tmp_contract{contract_funs = CFuns, forms = Forms} = TmpContract, - {NewCs, C2} = lists:mapfoldl(fun(CFun, C1) -> - CFun(ExpTypes, RecordTable, C1) - end, C0, CFuns), - Args = general_domain(NewCs), - Contract = #contract{contracts = NewCs, args = Args, forms = Forms}, - {{MFA, {File, Contract, Xtra}}, C2} - end, ModuleFun = fun(ModuleName) -> + ContractFun = + fun({MFA, {File, TmpContract, Xtra}}, C0) -> + #tmp_contract{contract_funs = CFuns, forms = Forms} = TmpContract, + {NewCs, C2} = lists:mapfoldl(fun(CFun, C1) -> + CFun(ExpTypes, RecordTable, C1) + end, C0, CFuns), + Args = general_domain(NewCs), + Contract = #contract{contracts = NewCs, args = Args, forms = Forms}, + {{MFA, {File, Contract, Xtra}}, C2} + end, Cache = erl_types:cache__new(), {ContractMap, CallbackMap} = dialyzer_codeserver:get_temp_contracts(ModuleName, CodeServer), @@ -474,26 +474,29 @@ insert_constraints([], Map) -> Map. -type spec_data() :: {TypeSpec :: [_], Xtra:: [_]}. --spec store_tmp_contract(mfa(), file_line(), spec_data(), contracts(), types()) -> - contracts(). +-spec store_tmp_contract(module(), mfa(), file_line(), spec_data(), + contracts(), types()) -> contracts(). -store_tmp_contract(MFA, FileLine, {TypeSpec, Xtra}, SpecMap, RecordsDict) -> +store_tmp_contract(Module, MFA, FileLine, {TypeSpec, Xtra}, SpecMap, + RecordsDict) -> %% io:format("contract from form: ~tp\n", [TypeSpec]), - TmpContract = contract_from_form(TypeSpec, MFA, RecordsDict, FileLine), + TmpContract = contract_from_form(TypeSpec, Module, MFA, RecordsDict, FileLine), %% io:format("contract: ~tp\n", [TmpContract]), maps:put(MFA, {FileLine, TmpContract, Xtra}, SpecMap). -contract_from_form(Forms, MFA, RecDict, FileLine) -> - {CFuns, Forms1} = contract_from_form(Forms, MFA, RecDict, FileLine, [], []), +contract_from_form(Forms, Module, MFA, RecDict, FileLine) -> + {CFuns, Forms1} = + contract_from_form(Forms, Module, MFA, RecDict, FileLine, [], []), #tmp_contract{contract_funs = CFuns, forms = Forms1}. -contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], MFA, RecDict, - FileLine, TypeAcc, FormAcc) -> +contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], Module, MFA, + RecDict, FileLine, TypeAcc, FormAcc) -> TypeFun = fun(ExpTypes, RecordTable, Cache) -> {NewType, NewCache} = try - from_form_with_check(Form, ExpTypes, MFA, RecordTable, Cache) + from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, + Cache) catch throw:{error, Msg} -> {File, Line} = FileLine, @@ -506,68 +509,74 @@ contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], MFA, RecDict, end, NewTypeAcc = [TypeFun | TypeAcc], NewFormAcc = [{Form, []} | FormAcc], - contract_from_form(Left, MFA, RecDict, FileLine, NewTypeAcc, NewFormAcc); + contract_from_form(Left, Module, MFA, RecDict, FileLine, NewTypeAcc, + NewFormAcc); contract_from_form([{type, _L1, bounded_fun, [{type, _L2, 'fun', [_, _]} = Form, Constr]}| Left], - MFA, RecDict, FileLine, TypeAcc, FormAcc) -> + Module, MFA, RecDict, FileLine, TypeAcc, FormAcc) -> TypeFun = fun(ExpTypes, RecordTable, Cache) -> {Constr1, VarTable, Cache1} = - process_constraints(Constr, MFA, RecDict, ExpTypes, RecordTable, - Cache), + process_constraints(Constr, Module, MFA, RecDict, ExpTypes, + RecordTable, Cache), {NewType, NewCache} = - from_form_with_check(Form, ExpTypes, MFA, RecordTable, + from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable, Cache1), NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType), {{NewTypeNoVars, Constr1}, NewCache} end, NewTypeAcc = [TypeFun | TypeAcc], NewFormAcc = [{Form, Constr} | FormAcc], - contract_from_form(Left, MFA, RecDict, FileLine, NewTypeAcc, NewFormAcc); -contract_from_form([], _MFA, _RecDict, _FileLine, TypeAcc, FormAcc) -> + contract_from_form(Left, Module, MFA, RecDict, FileLine, NewTypeAcc, + NewFormAcc); +contract_from_form([], _Mod, _MFA, _RecDict, _FileLine, TypeAcc, FormAcc) -> {lists:reverse(TypeAcc), lists:reverse(FormAcc)}. -process_constraints(Constrs, MFA, RecDict, ExpTypes, RecordTable, Cache) -> - {Init0, NewCache} = initialize_constraints(Constrs, MFA, RecDict, ExpTypes, - RecordTable, Cache), +process_constraints(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, + Cache) -> + {Init0, NewCache} = initialize_constraints(Constrs, Module, MFA, RecDict, + ExpTypes, RecordTable, Cache), Init = remove_cycles(Init0), - constraints_fixpoint(Init, MFA, RecDict, ExpTypes, RecordTable, NewCache). + constraints_fixpoint(Init, Module, MFA, RecDict, ExpTypes, RecordTable, + NewCache). -initialize_constraints(Constrs, MFA, RecDict, ExpTypes, RecordTable, Cache) -> - initialize_constraints(Constrs, MFA, RecDict, ExpTypes, RecordTable, +initialize_constraints(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, + Cache) -> + initialize_constraints(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, Cache, []). -initialize_constraints([], _MFA, _RecDict, _ExpTypes, _RecordTable, +initialize_constraints([], _Module, _MFA, _RecDict, _ExpTypes, _RecordTable, Cache, Acc) -> {Acc, Cache}; -initialize_constraints([Constr|Rest], MFA, RecDict, ExpTypes, RecordTable, - Cache, Acc) -> +initialize_constraints([Constr|Rest], Module, MFA, RecDict, ExpTypes, + RecordTable, Cache, Acc) -> case Constr of {type, _, constraint, [{atom, _, is_subtype}, [Type1, Type2]]} -> VarTable = erl_types:var_table__new(), {T1, NewCache} = - final_form(Type1, ExpTypes, MFA, RecordTable, VarTable, Cache), + final_form(Type1, ExpTypes, Module, MFA, RecordTable, VarTable, Cache), Entry = {T1, Type2}, - initialize_constraints(Rest, MFA, RecDict, ExpTypes, RecordTable, - NewCache, [Entry|Acc]); + initialize_constraints(Rest, Module, MFA, RecDict, ExpTypes, + RecordTable, NewCache, [Entry|Acc]); {type, _, constraint, [{atom,_,Name}, List]} -> N = length(List), throw({error, io_lib:format("Unsupported type guard ~tw/~w\n", [Name, N])}) end. -constraints_fixpoint(Constrs, MFA, RecDict, ExpTypes, RecordTable, Cache) -> +constraints_fixpoint(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, + Cache) -> VarTable = erl_types:var_table__new(), {VarTab, NewCache} = - constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable, + constraints_to_dict(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, VarTable, Cache), - constraints_fixpoint(VarTab, MFA, Constrs, RecDict, ExpTypes, + constraints_fixpoint(VarTab, Module, MFA, Constrs, RecDict, ExpTypes, RecordTable, NewCache). -constraints_fixpoint(OldVarTab, MFA, Constrs, RecDict, ExpTypes, +constraints_fixpoint(OldVarTab, Module, MFA, Constrs, RecDict, ExpTypes, RecordTable, Cache) -> {NewVarTab, NewCache} = - constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable, + constraints_to_dict(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, OldVarTab, Cache), case NewVarTab of OldVarTab -> @@ -578,19 +587,23 @@ constraints_fixpoint(OldVarTab, MFA, Constrs, RecDict, ExpTypes, FinalConstrs = maps:fold(Fun, [], NewVarTab), {FinalConstrs, NewVarTab, NewCache}; _Other -> - constraints_fixpoint(NewVarTab, MFA, Constrs, RecDict, ExpTypes, + constraints_fixpoint(NewVarTab, Module, MFA, Constrs, RecDict, ExpTypes, RecordTable, NewCache) end. -final_form(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) -> - from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache). +final_form(Form, ExpTypes, Module, MFA, RecordTable, VarTable, Cache) -> + from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable, + Cache). -from_form_with_check(Form, ExpTypes, MFA, RecordTable, Cache) -> +from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, Cache) -> VarTable = erl_types:var_table__new(), - from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache). + from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable, + Cache). -from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) -> - Site = {spec, MFA}, +from_form_with_check(Form, ExpTypes, Module, MFA, RecordTable, VarTable, + Cache) -> + {_, F, A} = MFA, + Site = {spec, {Module, F, A}}, C1 = erl_types:t_check_record_fields(Form, ExpTypes, Site, RecordTable, VarTable, Cache), %% The check costs some time, and with the assumption that contracts @@ -598,22 +611,22 @@ from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) -> %% erl_types:t_from_form_check_remote(Form, ExpTypes, MFA, RecordTable), erl_types:t_from_form(Form, ExpTypes, Site, RecordTable, VarTable, C1). -constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable, +constraints_to_dict(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, VarTab, Cache) -> {Subtypes, NewCache} = - constraints_to_subs(Constrs, MFA, RecDict, ExpTypes, RecordTable, + constraints_to_subs(Constrs, Module, MFA, RecDict, ExpTypes, RecordTable, VarTab, Cache, []), {insert_constraints(Subtypes), NewCache}. -constraints_to_subs([], _MFA, _RecDict, _ExpTypes, _RecordTable, +constraints_to_subs([], _Module, _MFA, _RecDict, _ExpTypes, _RecordTable, _VarTab, Cache, Acc) -> {Acc, Cache}; -constraints_to_subs([{T1, Form2}|Rest], MFA, RecDict, ExpTypes, RecordTable, - VarTab, Cache, Acc) -> +constraints_to_subs([{T1, Form2}|Rest], Module, MFA, RecDict, ExpTypes, + RecordTable, VarTab, Cache, Acc) -> {T2, NewCache} = - final_form(Form2, ExpTypes, MFA, RecordTable, VarTab, Cache), + final_form(Form2, ExpTypes, Module, MFA, RecordTable, VarTab, Cache), NewAcc = [{subtype, T1, T2}|Acc], - constraints_to_subs(Rest, MFA, RecDict, ExpTypes, RecordTable, + constraints_to_subs(Rest, Module, MFA, RecDict, ExpTypes, RecordTable, VarTab, NewCache, NewAcc). %% Replaces variables with '_' when necessary to break up cycles among @@ -898,6 +911,7 @@ is_remote_types_related(Contract, CSig, Sig, MFA, RecDict) -> t_from_forms_without_remote([{FType, []}], MFA, RecDict) -> Site = {spec, MFA}, + %% FIXME Type1 = erl_types:t_from_form_without_remote(FType, Site, RecDict), {ok, erl_types:subst_all_vars_to_any(Type1)}; t_from_forms_without_remote([{_FType, _Constrs}], _MFA, _RecDict) -> diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index abd89034f3..310301ee0b 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -450,8 +450,9 @@ get_spec_info([{Contract, Ln, [{Id, TypeSpec}]}|Left], error -> SpecData = {TypeSpec, Xtra}, NewActiveMap = - dialyzer_contracts:store_tmp_contract(MFA, {File, Ln}, SpecData, - ActiveMap, RecordsMap), + dialyzer_contracts:store_tmp_contract(ModName, MFA, {File, Ln}, + SpecData, ActiveMap, + RecordsMap), {NewSpecMap, NewCallbackMap} = case Contract of spec -> {NewActiveMap, CallbackMap}; @@ -599,24 +600,32 @@ collect_attribute([], _Tag, _File) -> -spec is_suppressed_fun(mfa(), codeserver()) -> boolean(). is_suppressed_fun(MFA, CodeServer) -> - lookup_fun_property(MFA, nowarn_function, CodeServer). + lookup_fun_property(MFA, nowarn_function, CodeServer, false). -spec is_suppressed_tag(mfa() | module(), dial_warn_tag(), codeserver()) -> boolean(). is_suppressed_tag(MorMFA, Tag, Codeserver) -> - not lookup_fun_property(MorMFA, Tag, Codeserver). - -lookup_fun_property({M, _F, _A}=MFA, Property, CodeServer) -> - MFAPropList = dialyzer_codeserver:lookup_meta_info(MFA, CodeServer), - case proplists:get_value(Property, MFAPropList, no) of - mod -> false; % suppressed in function - func -> true; % requested in function - no -> lookup_fun_property(M, Property, CodeServer) + not lookup_fun_property(MorMFA, Tag, Codeserver, true). + +lookup_fun_property({M, _F, _A}=MFA, Property, CodeServer, NoInfoReturn) -> + case dialyzer_codeserver:lookup_meta_info(MFA, CodeServer) of + error -> + lookup_fun_property(M, Property, CodeServer, NoInfoReturn); + {ok, MFAPropList} -> + case proplists:get_value(Property, MFAPropList, no) of + mod -> false; % suppressed in function + func -> true; % requested in function + no -> lookup_fun_property(M, Property, CodeServer, NoInfoReturn) + end end; -lookup_fun_property(M, Property, CodeServer) when is_atom(M) -> - MPropList = dialyzer_codeserver:lookup_meta_info(M, CodeServer), - proplists:is_defined(Property, MPropList). +lookup_fun_property(M, Property, CodeServer, NoInfoReturn) when is_atom(M) -> + case dialyzer_codeserver:lookup_meta_info(M, CodeServer) of + error -> + NoInfoReturn; + {ok, MPropList} -> + proplists:is_defined(Property, MPropList) + end. %% ============================================================================ %% diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/para b/lib/dialyzer/test/opaque_SUITE_data/results/para index 37b5b7b44e..eca445315c 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/para +++ b/lib/dialyzer/test/opaque_SUITE_data/results/para @@ -29,5 +29,6 @@ para4.erl:74: Attempt to test for equality between a term of type para4_adt:int( para4.erl:79: Attempt to test for equality between a term of type para4_adt:int(2 | 3 | 4) and a term of opaque type para4_adt:int(5 | 6 | 7) para4.erl:84: Attempt to test for equality between a term of type para4_adt:un(3 | 4) and a term of opaque type para4_adt:un(1 | 2) para4.erl:89: Attempt to test for equality between a term of type para4_adt:tup({_,_}) and a term of opaque type para4_adt:tup(tuple()) +para4.erl:94: Attempt to test for equality between a term of type para4_adt:t(#{1=>'a'}) and a term of opaque type para4_adt:t(#{2=>'b'}) para5.erl:13: Attempt to test for inequality between a term of type para5_adt:dd(atom()) and a term of opaque type para5_adt:d() para5.erl:8: The test para5_adt:d() =:= para5_adt:d() can never evaluate to 'true' diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl index b9794672a9..8cd049169d 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl @@ -88,6 +88,11 @@ adt_tt13() -> I2 = adt_tup2(), I1 =:= I2. % opaque attempt +adt_tt14() -> + I1 = adt_map(), + I2 = adt_map2(), + I1 =:= I2. + y3() -> {a, 3}. @@ -132,3 +137,9 @@ adt_tup() -> adt_tup2() -> para4_adt:tup2(). + +adt_map() -> + para4_adt:map(). + +adt_map2() -> + para4_adt:map2(). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl index 407dd198a7..06a6c22677 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl @@ -8,6 +8,8 @@ -export([tup/0, tup2/0]). +-export([map/0, map2/0]). + -export_type([t/1, y/1, int/1, tup/1, un/1]). -type ai() :: atom() | integer(). @@ -106,3 +108,13 @@ tup() -> tup2() -> foo:tup2(). + +-spec map() -> t(#{2 => b}). + +map() -> + foo:map(). + +-spec map2() -> t(#{1 => a}). + +map2() -> + foo:map2(). diff --git a/lib/dialyzer/test/small_SUITE_data/results/spec_other_module b/lib/dialyzer/test/small_SUITE_data/results/spec_other_module new file mode 100644 index 0000000000..ab2e35cf55 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/spec_other_module @@ -0,0 +1,2 @@ + +spec_other_module.erl:7: Contract for function that does not exist: lists:flatten/1 diff --git a/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl b/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl new file mode 100644 index 0000000000..ad5cf3c503 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl @@ -0,0 +1,75 @@ +-module(lists_key_bug). + +%% OTP-15570 + +-export([is_1/1, is_2/1, i/1, t1/0, t2/0, im/0]). + +%% int_set([3]) +is_1(V) -> + K = ikey(V), + case lists:keyfind(K, 1, [{<<"foo">>, bar}]) of + false -> + a; + {_, _} -> + b + end. + +ikey(1) -> + 3; +ikey(2) -> + <<"foo">>. + +%% int_set([3, 5]) +is_2(V) -> + K = iskey(V), + case lists:keyfind(K, 1, [{<<"foo">>, bar}]) of + false -> + a; + {_, _} -> + b + end. + +iskey(1) -> + 12; +iskey(2) -> + 14; +iskey(3) -> + <<"foo">>. + +%% integer() +i(V) -> + K = intkey(V), + case lists:keyfind(K, 1, [{9.0, foo}]) of + false -> + a; + {_, _} -> + b + end. + +intkey(K) when is_integer(K) -> + K + 9999. + +t1() -> + case lists:keyfind({17}, 1, [{{17.0}, true}]) of + false -> + a; + {_, _} -> + b + end. + +t2() -> + case lists:keyfind({17.0}, 1, [{{17}, true}]) of + false -> + a; + {_, _} -> + b + end. + +%% Note: #{1.0 => a} =/= #{1 => a}. +im() -> + case lists:keyfind(#{1.0 => a}, 1, [{#{1 => a}, foo}]) of + false -> + a; + {_, _} -> + b + end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl b/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl new file mode 100644 index 0000000000..b36742b1bd --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl @@ -0,0 +1,7 @@ +-module(spec_other_module). + +%% OTP-15562 and ERL-845. Example provided by Kostis. + +-type deep_list(A) :: [A | deep_list(A)]. + +-spec lists:flatten(deep_list(A)) -> [A]. diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index dfa4c803ed..0a0194af2d 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -55,7 +55,7 @@ limitations under the License. <!-- ===================================================================== --> <!-- ===================================================================== --> -<module>diameter</module> +<module since="OTP R14B03">diameter</module> <modulesummary>Main API of the diameter application.</modulesummary> <description> @@ -1574,7 +1574,7 @@ identifies the configuration.</p> <!-- ===================================================================== --> <func> -<name>add_transport(SvcName, {connect|listen, [Opt]}) +<name since="OTP R14B03">add_transport(SvcName, {connect|listen, [Opt]}) -> {ok, Ref} | {error, Reason}</name> <fsummary>Add transport capability to a service.</fsummary> <type> @@ -1624,7 +1624,7 @@ its transports.</p> <!-- ===================================================================== --> <func> -<name>call(SvcName, App, Request, [Opt]) -> Answer | ok | {error, Reason}</name> +<name since="OTP R14B03">call(SvcName, App, Request, [Opt]) -> Answer | ok | {error, Reason}</name> <fsummary>Send a Diameter request message.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -1730,7 +1730,7 @@ transport connection.</p> <!-- ===================================================================== --> <func> -<name>origin_state_id() -> &dict_Unsigned32;</name> +<name since="OTP R14B03">origin_state_id() -> &dict_Unsigned32;</name> <fsummary>Returns a reasonable Origin-State-Id.</fsummary> <desc> <p> @@ -1748,7 +1748,7 @@ at the time the diameter application was started.</p> <!-- ===================================================================== --> <func> -<name>remove_transport(SvcName, Pred) -> ok | {error, Reason}</name> +<name since="OTP R14B03">remove_transport(SvcName, Pred) -> ok | {error, Reason}</name> <fsummary>Remove previously added transports.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -1795,7 +1795,7 @@ configured on the transport.</p> <!-- ===================================================================== --> <func> -<name>service_info(SvcName, Info) -> term()</name> +<name since="OTP R14B03">service_info(SvcName, Info) -> term()</name> <fsummary>Return information about a started service.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -2114,7 +2114,7 @@ For example:</p> <!-- ===================================================================== --> <func> -<name>services() -> [SvcName]</name> +<name since="OTP R14B03">services() -> [SvcName]</name> <fsummary>Return the list of started services.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -2129,7 +2129,7 @@ Return the list of started services.</p> <!-- ===================================================================== --> <func> -<name>session_id(Ident) -> &dict_OctetString;</name> +<name since="OTP R14B03">session_id(Ident) -> &dict_OctetString;</name> <fsummary>Return a value for a Session-Id AVP.</fsummary> <type> <v>Ident = &dict_DiameterIdentity;</v> @@ -2148,7 +2148,7 @@ the message containing the returned value will be sent.</p> <!-- ===================================================================== --> <func> -<name>start() -> ok | {error, Reason}</name> +<name since="OTP R14B03">start() -> ok | {error, Reason}</name> <fsummary>Start the diameter application.</fsummary> <desc> <p> @@ -2164,7 +2164,7 @@ file, not by calling <c>start/0</c> explicitly.</p> <!-- ===================================================================== --> <func> -<name>start_service(SvcName, Options) -> ok | {error, Reason}</name> +<name since="OTP R14B03">start_service(SvcName, Options) -> ok | {error, Reason}</name> <fsummary>Start a Diameter service.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -2194,7 +2194,7 @@ necessarily the case.</p> <!-- ===================================================================== --> <func> -<name>stop() -> ok | {error, Reason}</name> +<name since="OTP R14B03">stop() -> ok | {error, Reason}</name> <fsummary>Stop the diameter application.</fsummary> <desc> <p> @@ -2208,7 +2208,7 @@ Stop the diameter application.</p> <!-- ===================================================================== --> <func> -<name>stop_service(SvcName) -> ok | {error, Reason}</name> +<name since="OTP R14B03">stop_service(SvcName) -> ok | {error, Reason}</name> <fsummary>Stop a Diameter service.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -2236,7 +2236,7 @@ be called to remove transport configuration.</p> <!-- ===================================================================== --> <func> -<name>subscribe(SvcName) -> true</name> +<name since="OTP R14B03">subscribe(SvcName) -> true</name> <fsummary>Subscribe to event messages.</fsummary> <type> <v>SvcName = &service_name;</v> @@ -2258,7 +2258,7 @@ reception of all transport-related events.</p> <!-- ===================================================================== --> <func> -<name>unsubscribe(SvcName) -> true</name> +<name since="OTP R14B03">unsubscribe(SvcName) -> true</name> <fsummary>Unsubscribe to event messages.</fsummary> <type> <v>SvcName = &service_name;</v> diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml index aa334beb21..82e3d449ef 100644 --- a/lib/diameter/doc/src/diameter_app.xml +++ b/lib/diameter/doc/src/diameter_app.xml @@ -44,7 +44,7 @@ limitations under the License. </header> -<module>diameter_app</module> +<module since="OTP R14B03">diameter_app</module> <modulesummary> Callback module of a Diameter application.</modulesummary> @@ -180,7 +180,7 @@ process.</p> <funcs> <func> -<name>Mod:peer_up(SvcName, Peer, State) -> NewState</name> +<name since="OTP R14B03">Mod:peer_up(SvcName, Peer, State) -> NewState</name> <fsummary>Invoked when a transport connection has been established</fsummary> <type> <v>SvcName = &mod_service_name;</v> @@ -215,7 +215,7 @@ handled independently of &peer_up; and &peer_down;.</p> </func> <func> -<name>Mod:peer_down(SvcName, Peer, State) -> NewState</name> +<name since="OTP R14B03">Mod:peer_down(SvcName, Peer, State) -> NewState</name> <fsummary>Invoked when a transport connection has been lost.</fsummary> <type> <v>SvcName = &mod_service_name;</v> @@ -234,7 +234,7 @@ candidate in &pick_peer; callbacks.</p> </func> <func> -<name>Mod:pick_peer(LocalCandidates, RemoteCandidates, SvcName, State) +<name since="OTP R14B03">Mod:pick_peer(LocalCandidates, RemoteCandidates, SvcName, State) -> Selection | false</name> <fsummary>Select a target peer for an outgoing request.</fsummary> <type> @@ -311,7 +311,7 @@ or &peer_down; callback.</p> </func> <func> -<name>Mod:prepare_request(Packet, SvcName, Peer) -> Action</name> +<name since="OTP R14B03">Mod:prepare_request(Packet, SvcName, Peer) -> Action</name> <fsummary>Return a request for encoding and transport.</fsummary> <type> <v>Packet = &packet;</v> @@ -363,7 +363,7 @@ discarded}</c>.</p> </func> <func> -<name>Mod:prepare_retransmit(Packet, SvcName, Peer) -> Action</name> +<name since="OTP R14B03">Mod:prepare_retransmit(Packet, SvcName, Peer) -> Action</name> <fsummary>Return a request for encoding and retransmission.</fsummary> <type> <v>Packet = &packet;</v> @@ -393,7 +393,7 @@ discarded}</c>.</p> </func> <func> -<name>Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result</name> +<name since="OTP R14B03">Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result</name> <fsummary>Receive an answer message from a peer.</fsummary> <type> <v>Packet = &packet;</v> @@ -437,7 +437,7 @@ The &mod_application_opt; </func> <func> -<name>Mod:handle_error(Reason, Request, SvcName, Peer) -> Result</name> +<name since="OTP R14B03">Mod:handle_error(Reason, Request, SvcName, Peer) -> Result</name> <fsummary>Return an error from a outgoing request.</fsummary> <type> <v>Reason = timeout | failover | term()</v> @@ -465,7 +465,7 @@ not selected.</p> </func> <func> -<name>Mod:handle_request(Packet, SvcName, Peer) -> Action</name> +<name since="OTP R14B03">Mod:handle_request(Packet, SvcName, Peer) -> Action</name> <fsummary>Receive an incoming request.</fsummary> <type> <v>Packet = &packet;</v> diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index 0a34dd7ec7..0384ad2913 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -46,7 +46,7 @@ limitations under the License. <file>diameter_codec.xml</file> </header> -<module>diameter_codec</module> +<module since="OTP R15B03">diameter_codec</module> <modulesummary>Decode and encode of Diameter messages.</modulesummary> <description> @@ -346,7 +346,7 @@ question, as documented in &man_transport;.</p> <funcs> <func> -<name>decode(Mod, Bin) -> Pkt</name> +<name since="OTP R15B03">decode(Mod, Bin) -> Pkt</name> <fsummary>Decode a Diameter message.</fsummary> <type> <v>Mod = &dictionary;</v> @@ -362,7 +362,7 @@ Decode a Diameter message.</p> </func> <func> -<name>encode(Mod, Msg) -> Pkt</name> +<name since="OTP R15B03">encode(Mod, Msg) -> Pkt</name> <fsummary>Encode a Diameter message.</fsummary> <type> <v>Mod = &dictionary;</v> diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml index 112355816f..57e83bbca1 100644 --- a/lib/diameter/doc/src/diameter_make.xml +++ b/lib/diameter/doc/src/diameter_make.xml @@ -45,7 +45,7 @@ limitations under the License. <file>diameter_make.xml</file> </header> -<module>diameter_make</module> +<module since="OTP R14B03">diameter_make</module> <modulesummary>Diameter dictionary compilation.</modulesummary> <description> @@ -67,7 +67,7 @@ interface.</p> <funcs> <func> -<name>codec(File :: iolist() | binary(), [Opt]) -> ok +<name since="OTP R15B">codec(File :: iolist() | binary(), [Opt]) -> ok | {ok, [Out]} | {error, Reason}</name> <fsummary>Compile a dictionary file into Erlang source.</fsummary> @@ -186,7 +186,7 @@ A returned error reason can be converted into a readable string using <!-- ===================================================================== --> <func> -<name>format(Parsed) -> iolist()</name> +<name since="OTP R16B03">format(Parsed) -> iolist()</name> <fsummary>Format a parsed dictionary.</fsummary> <desc> <p> @@ -198,7 +198,7 @@ dictionary format.</p> <!-- ===================================================================== --> <func> -<name>flatten(Parsed) -> term()</name> +<name since="OTP R16B03">flatten(Parsed) -> term()</name> <fsummary>Flatten a parsed dictionary.</fsummary> <desc> @@ -214,7 +214,7 @@ The return value is also a parsed dictionary.</p> <!-- ===================================================================== --> <func> -<name>format_error(Reason) -> string()</name> +<name since="OTP 17.0">format_error(Reason) -> string()</name> <fsummary>Turn an error reason into a readable string.</fsummary> <desc> diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml index 62e958870e..2cf924ee6b 100644 --- a/lib/diameter/doc/src/diameter_sctp.xml +++ b/lib/diameter/doc/src/diameter_sctp.xml @@ -47,7 +47,7 @@ limitations under the License. <file>diameter_sctp.xml</file> </header> -<module>diameter_sctp</module> +<module since="OTP R14B03">diameter_sctp</module> <modulesummary>Diameter transport over SCTP.</modulesummary> <description> @@ -67,7 +67,7 @@ and implements the behaviour documented in <funcs> <func> -<name>start({Type, Ref}, Svc, [Opt]) +<name since="OTP R14B03">start({Type, Ref}, Svc, [Opt]) -> {ok, Pid, [LAddr]} | {error, Reason}</name> <fsummary>Start a transport process.</fsummary> <type> diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index 9f84eeb9fd..fe0cb2d067 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -57,7 +57,7 @@ limitations under the License. <file>diameter_tcp.xml</file> </header> -<module>diameter_tcp</module> +<module since="OTP R14B03">diameter_tcp</module> <modulesummary>Diameter transport over TCP.</modulesummary> <description> @@ -83,7 +83,7 @@ before configuring TLS capability on diameter transports.</p> <funcs> <func> -<name>start({Type, Ref}, Svc, [Opt]) +<name since="OTP R14B03">start({Type, Ref}, Svc, [Opt]) -> {ok, Pid} | {ok, Pid, [LAddr]} | {error, Reason}</name> diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml index 294e8a8864..67fd54bc56 100644 --- a/lib/diameter/doc/src/diameter_transport.xml +++ b/lib/diameter/doc/src/diameter_transport.xml @@ -43,7 +43,7 @@ limitations under the License. <file>diameter_transport.xml</file> </header> -<module>diameter_transport</module> +<module since="OTP R14B03">diameter_transport</module> <modulesummary>Diameter transport interface.</modulesummary> <description> @@ -94,7 +94,7 @@ and has the binary() to send in its <c>bin</c> field.</p> <funcs> <func> -<name>Mod:start({Type, Ref}, Svc, Config) +<name since="OTP R14B03">Mod:start({Type, Ref}, Svc, Config) -> {ok, Pid} | {ok, Pid, LAddrs} | {error, Reason}</name> diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index 4bfc98de40..cc92bd99f0 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -78,6 +78,24 @@ first.</p> </section> +<section><title>diameter 2.1.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix failure of incoming answer message with faulty + Experimental-Result-Code. Failure to decode the AVP + resulted in an uncaught exception, with no no + handle_answer/error callback as a consequence.</p> + <p> + Own Id: OTP-15569 Aux Id: ERIERL-302 </p> + </item> + </list> + </section> + +</section> + <section><title>diameter 2.1.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index d110a3015e..564448de48 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index d2856ae530..2d3e4a2ac9 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -1925,6 +1925,8 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> A = find_avp(Code, Vid, Avps), avp_decode(Dict, Name, ungroup(A)) catch + {diameter_gen, _} -> %% faulty Grouped AVP + undefined; error: _ -> undefined end; diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src index 51830f5276..4e6b983bac 100644 --- a/lib/diameter/src/diameter.appup.src +++ b/lib/diameter/src/diameter.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -59,6 +59,7 @@ {"2.1.2", [{restart_application, diameter}]}, %% 20.1.3 {"2.1.3", [{restart_application, diameter}]}, %% 20.2 {"2.1.4", [{restart_application, diameter}]}, %% 20.3 + {"2.1.4.1", [{restart_application, diameter}]}, %% 20.3.8.19 {"2.1.5", [{update, diameter_peer_fsm}]} %% 21.0 ], [ @@ -100,6 +101,7 @@ {"2.1.2", [{restart_application, diameter}]}, {"2.1.3", [{restart_application, diameter}]}, {"2.1.4", [{restart_application, diameter}]}, + {"2.1.4.1", [{restart_application, diameter}]}, {"2.1.5", [{update, diameter_peer_fsm}]} ] }. diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index da059fa7d6..e5e766d2a0 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -92,9 +92,9 @@ -type connect_option() :: {raddr, inet:ip_address()} | {rport, pos_integer()} - | {ssl_options, true | [ssl:connect_option()]} + | {ssl_options, true | [ssl:tls_client_option()]} | option() - | ssl:connect_option() + | ssl:tls_client_option() | gen_tcp:connect_option(). -type match() :: inet:ip_address() @@ -102,9 +102,9 @@ | [match()]. -type listen_option() :: {accept, match()} - | {ssl_options, true | [ssl:listen_option()]} + | {ssl_options, true | [ssl:tls_server_option()]} | option() - | ssl:listen_option() + | ssl:tls_server_option() | gen_tcp:listen_option(). -type option() :: {port, non_neg_integer()} diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl index b641118c5d..e9d62d3283 100644 --- a/lib/edoc/src/edoc.erl +++ b/lib/edoc/src/edoc.erl @@ -578,7 +578,7 @@ read_source(Name, Opts0) -> Opts = expand_opts(Opts0), case read_source_1(Name, Opts) of {ok, Forms} -> - check_forms(Forms, Name), + check_forms(Forms, Name, Opts), Forms; {error, R} -> edoc_report:error({"error reading file '~ts'.", @@ -692,13 +692,19 @@ fll([T | L], LastLine, Ts) -> fll(L, _LastLine, Ts) -> lists:reverse(L, Ts). -check_forms(Fs, Name) -> +check_forms(Fs, Name, Opts) -> Fun = fun (F) -> case erl_syntax:type(F) of error_marker -> case erl_syntax:error_marker_info(F) of {L, M, D} -> - edoc_report:error(L, Name, {format_error, M, D}); + edoc_report:error(L, Name, {format_error, M, D}), + case proplists:get_bool(preprocess, Opts) of + true -> + ok; + false -> + helpful_message(Name) + end; Other -> edoc_report:report(Name, "unknown error in " "source code: ~w.", [Other]) @@ -710,6 +716,11 @@ check_forms(Fs, Name) -> end, lists:foreach(Fun, Fs). +helpful_message(Name) -> + Ms = ["If the error is caused by too exotic macro", + "definitions or uses of macros, adding option", + "{preprocess, true} can help. See also edoc(3)."], + lists:foreach(fun(M) -> edoc_report:report(Name, M, []) end, Ms). %% @spec get_doc(File::filename()) -> {ModuleName, edoc_module()} %% @equiv get_doc(File, []) diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index f2c7889e58..790a2f4e26 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -28,7 +28,7 @@ <date>2000-06-20</date> <rev>B</rev> </header> - <module>eldap</module> + <module since="OTP R15B01">eldap</module> <modulesummary>LDAP Client</modulesummary> <description> <p>This module provides a client api to the Lightweight Directory Access Protocol (LDAP). @@ -103,7 +103,7 @@ <funcs> <func> - <name>open([Host]) -> {ok, Handle} | {error, Reason}</name> + <name since="OTP R15B01">open([Host]) -> {ok, Handle} | {error, Reason}</name> <fsummary>Open a connection to an LDAP server.</fsummary> <type> <v>Handle = handle()</v> @@ -113,7 +113,7 @@ </desc> </func> <func> - <name>open([Host], [Option]) -> {ok, Handle} | {error, Reason}</name> + <name since="OTP R15B01">open([Host], [Option]) -> {ok, Handle} | {error, Reason}</name> <fsummary>Open a connection to an LDAP server.</fsummary> <type> <v>Handle = handle()</v> @@ -129,7 +129,7 @@ </desc> </func> <func> - <name>close(Handle) -> ok</name> + <name since="OTP R15B01">close(Handle) -> ok</name> <fsummary>Shutdown the connection.</fsummary> <type> <v>Handle = handle()</v> @@ -140,14 +140,14 @@ </desc> </func> <func> - <name>start_tls(Handle, Options) -> return_value()</name> + <name since="OTP R16B03">start_tls(Handle, Options) -> return_value()</name> <fsummary>Upgrade a connection to TLS.</fsummary> <desc> <p>Same as start_tls(Handle, Options, infinity)</p> </desc> </func> <func> - <name>start_tls(Handle, Options, Timeout) -> return_value()</name> + <name since="OTP R16B03">start_tls(Handle, Options, Timeout) -> return_value()</name> <fsummary>Upgrade a connection to TLS.</fsummary> <type> <v>Handle = handle()</v> @@ -176,7 +176,7 @@ </desc> </func> <func> - <name>simple_bind(Handle, Dn, Password) -> return_value()</name> + <name since="OTP R15B01">simple_bind(Handle, Dn, Password) -> return_value()</name> <fsummary>Authenticate the connection.</fsummary> <type> <v>Handle = handle()</v> @@ -188,7 +188,7 @@ </desc> </func> <func> - <name>add(Handle, Dn, [Attribute]) -> return_value()</name> + <name since="OTP R15B01">add(Handle, Dn, [Attribute]) -> return_value()</name> <fsummary>Add an entry.</fsummary> <type> <v>Handle = handle()</v> @@ -209,7 +209,7 @@ </desc> </func> <func> - <name>delete(Handle, Dn) -> return_value()</name> + <name since="OTP R15B01">delete(Handle, Dn) -> return_value()</name> <fsummary>Delete an entry.</fsummary> <type> <v>Dn = string()</v> @@ -223,7 +223,7 @@ </func> <func> - <name>mod_add(Type, [Value]) -> modify_op()</name> + <name since="OTP R15B01">mod_add(Type, [Value]) -> modify_op()</name> <fsummary>Create a modification operation.</fsummary> <type> <v>Type = string()</v> @@ -232,7 +232,7 @@ <desc> <p> Create an add modification operation.</p> </desc> </func> <func> - <name>mod_delete(Type, [Value]) -> modify_op()</name> + <name since="OTP R15B01">mod_delete(Type, [Value]) -> modify_op()</name> <fsummary>Create a modification operation.</fsummary> <type> <v>Type = string()</v> @@ -241,7 +241,7 @@ <desc> <p> Create a delete modification operation.</p> </desc> </func> <func> - <name>mod_replace(Type, [Value]) -> modify_op()</name> + <name since="OTP R15B01">mod_replace(Type, [Value]) -> modify_op()</name> <fsummary>Create a modification operation.</fsummary> <type> <v>Type = string()</v> @@ -251,7 +251,7 @@ </func> <func> - <name>modify(Handle, Dn, [ModifyOp]) -> return_value()</name> + <name since="OTP R15B01">modify(Handle, Dn, [ModifyOp]) -> return_value()</name> <fsummary>Modify an entry.</fsummary> <type> <v>Dn = string()</v> @@ -267,7 +267,7 @@ </desc> </func> <func> - <name>modify_password(Handle, Dn, NewPasswd) -> return_value() | {ok, GenPasswd}</name> + <name since="OTP 18.0">modify_password(Handle, Dn, NewPasswd) -> return_value() | {ok, GenPasswd}</name> <fsummary>Modify the password of a user.</fsummary> <type> <v>Dn = string()</v> @@ -278,7 +278,7 @@ </desc> </func> <func> - <name>modify_password(Handle, Dn, NewPasswd, OldPasswd) -> return_value() | {ok, GenPasswd}</name> + <name since="OTP 18.0">modify_password(Handle, Dn, NewPasswd, OldPasswd) -> return_value() | {ok, GenPasswd}</name> <fsummary>Modify the password of a user.</fsummary> <type> <v>Dn = string()</v> @@ -307,7 +307,7 @@ </desc> </func> <func> - <name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> return_value()</name> + <name since="OTP R15B01">modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> return_value()</name> <fsummary>Modify the DN of an entry.</fsummary> <type> <v>Dn = string()</v> @@ -327,7 +327,7 @@ </desc> </func> <func> - <name>search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {ok, {referral,referrals()}} | {error, Reason}</name> + <name since="OTP R15B01">search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {ok, {referral,referrals()}} | {error, Reason}</name> <fsummary>Search the Directory</fsummary> <type> <v>SearchOptions = #eldap_search{} | [SearchOption]</v> @@ -354,44 +354,44 @@ </func> <func> - <name>baseObject() -> scope()</name> + <name since="OTP R15B01">baseObject() -> scope()</name> <fsummary>Create search scope.</fsummary> <desc> <p> Search baseobject only.</p> </desc> </func> <func> - <name>singleLevel() -> scope()</name> + <name since="OTP R15B01">singleLevel() -> scope()</name> <fsummary>Create search scope.</fsummary> <desc> <p> Search the specified level only, i.e. do not recurse.</p> </desc> </func> <func> - <name>wholeSubtree() -> scope()</name> + <name since="OTP R15B01">wholeSubtree() -> scope()</name> <fsummary>Create search scope.</fsummary> <desc> <p> Search the entire subtree.</p> </desc> </func> <func> - <name>neverDerefAliases() -> dereference()</name> + <name since="OTP R15B01">neverDerefAliases() -> dereference()</name> <fsummary>Create search option.</fsummary> <desc> <p>Never derefrence aliases, treat aliases as entries.</p> </desc> </func> <func> - <name>derefAlways() -> dereference()</name> + <name since="OTP R15B01">derefAlways() -> dereference()</name> <fsummary>Create search option.</fsummary> <desc> <p>Always derefrence aliases.</p> </desc> </func> <func> - <name>derefInSearching() -> dereference()</name> + <name since="OTP R15B01">derefInSearching() -> dereference()</name> <fsummary>Create search option.</fsummary> <desc> <p>Derefrence aliases only when searching.</p> </desc> </func> <func> - <name>derefFindingBaseObj() -> dereference()</name> + <name since="OTP R15B01">derefFindingBaseObj() -> dereference()</name> <fsummary>Create search option.</fsummary> <desc> <p>Derefrence aliases only in finding the base.</p> </desc> </func> <func> - <name>present(Type) -> filter()</name> + <name since="OTP R15B01">present(Type) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Type = string()</v> @@ -399,7 +399,7 @@ <desc> <p>Create a filter which filters on attribute type presence.</p> </desc> </func> <func> - <name>substrings(Type, [SubString]) -> filter()</name> + <name since="OTP R15B01">substrings(Type, [SubString]) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Type = string()</v> @@ -409,7 +409,7 @@ <desc> <p>Create a filter which filters on substrings.</p> </desc> </func> <func> - <name>equalityMatch(Type, Value) -> filter()</name> + <name since="OTP R15B01">equalityMatch(Type, Value) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Type = string()</v> @@ -418,7 +418,7 @@ <desc> <p>Create a equality filter.</p> </desc> </func> <func> - <name>greaterOrEqual(Type, Value) -> filter()</name> + <name since="OTP R15B01">greaterOrEqual(Type, Value) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Type = string()</v> @@ -427,7 +427,7 @@ <desc> <p>Create a greater or equal filter.</p> </desc> </func> <func> - <name>lessOrEqual(Type, Value) -> filter()</name> + <name since="OTP R15B01">lessOrEqual(Type, Value) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Type = string()</v> @@ -436,7 +436,7 @@ <desc> <p>Create a less or equal filter.</p> </desc> </func> <func> - <name>approxMatch(Type, Value) -> filter()</name> + <name since="OTP R15B01">approxMatch(Type, Value) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Type = string()</v> @@ -445,7 +445,7 @@ <desc> <p>Create a approximation match filter.</p> </desc> </func> <func> - <name>extensibleMatch(MatchValue, OptionalAttrs) -> filter()</name> + <name since="OTP 17.4">extensibleMatch(MatchValue, OptionalAttrs) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>MatchValue = string()</v> @@ -459,7 +459,7 @@ <p>creates a filter which performs a <c>caseExactMatch</c> on the attribute <c>sn</c> and matches with the value <c>"Bar"</c>. The default value of <c>dnAttributes</c> is <c>false</c>.</p> </desc> </func> <func> - <name>'and'([Filter]) -> filter()</name> + <name since="OTP R15B01">'and'([Filter]) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Filter = filter()</v> @@ -467,7 +467,7 @@ <desc> <p>Creates a filter where all <c>Filter</c> must be true.</p> </desc> </func> <func> - <name>'or'([Filter]) -> filter()</name> + <name since="OTP R15B01">'or'([Filter]) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Filter = filter()</v> @@ -475,7 +475,7 @@ <desc> <p>Create a filter where at least one of the <c>Filter</c> must be true.</p> </desc> </func> <func> - <name>'not'(Filter) -> filter()</name> + <name since="OTP R15B01">'not'(Filter) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> <v>Filter = filter()</v> diff --git a/lib/erl_docgen/doc/src/docgen_xml_check.xml b/lib/erl_docgen/doc/src/docgen_xml_check.xml index 68253edef7..8d6dceef43 100644 --- a/lib/erl_docgen/doc/src/docgen_xml_check.xml +++ b/lib/erl_docgen/doc/src/docgen_xml_check.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>docgen_xml_check</module> + <module since="OTP R15B">docgen_xml_check</module> <modulesummary>Validate XML documentation source code</modulesummary> <description> <p><c>docgen_xml_check</c> contains functions for validating XML @@ -39,7 +39,7 @@ <funcs> <func> - <name>validate(File) -> ok | error | {error, badfile}</name> + <name since="OTP R15B">validate(File) -> ok | error | {error, badfile}</name> <fsummary>Validate XML source code.</fsummary> <type> <v>File = string()</v> diff --git a/lib/erl_docgen/priv/css/otp_doc.css b/lib/erl_docgen/priv/css/otp_doc.css index 34c6befb0e..89b278215c 100644 --- a/lib/erl_docgen/priv/css/otp_doc.css +++ b/lib/erl_docgen/priv/css/otp_doc.css @@ -71,6 +71,33 @@ a:visited { color: #1b6ec2; text-decoration: none } } .bold_code { font-family: mono, Courier, monospace; font-weight: bold } + +/* Invisible table for function specs, + * just to get since-version out in right margin */ +.func-table, .func-tr, .func-td, .func-since-td { + width: 200%; + border: 0; + padding: 0; + margin: 0; +} + +.func-tr:nth-child(n) { + background: inherit /* turn off zebra striped rows */ +} + +.func-td { + width: 50% +} + +.func-since-td { + width: 50%; + padding-left: 1em +} + +.func-td:hover { + background-color: #f5f5f5; +} + .code { font-family: mono, Courier, monospace; font-weight: normal; @@ -283,3 +310,9 @@ a > .code { .func-types-title{ font-size: 1em; } + +.since{ + color: gray; + font-weight: normal; + font-size: small; +}
\ No newline at end of file diff --git a/lib/erl_docgen/priv/dtd/cref.dtd b/lib/erl_docgen/priv/dtd/cref.dtd index 5ccd98ed89..d392081807 100644 --- a/lib/erl_docgen/priv/dtd/cref.dtd +++ b/lib/erl_docgen/priv/dtd/cref.dtd @@ -30,6 +30,8 @@ <!-- `name' is used in common.refs.dtd and must therefore be defined in each *ref. dtd --> <!ELEMENT name (ret,nametext) > +<!ATTLIST name since CDATA #IMPLIED> + <!ELEMENT ret (#PCDATA) > <!ELEMENT nametext (#PCDATA) > diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd index 78d6771f52..8202ea5a4d 100644 --- a/lib/erl_docgen/priv/dtd/erlref.dtd +++ b/lib/erl_docgen/priv/dtd/erlref.dtd @@ -25,6 +25,7 @@ <!ELEMENT erlref (header,module,modulesummary,description, (section|funcs|datatypes)*,authors?) > <!ELEMENT module (#PCDATA) > +<!ATTLIST module since CDATA #IMPLIED> <!ELEMENT modulesummary (#PCDATA) > <!-- `name' is used in common.refs.dtd and must therefore @@ -34,4 +35,5 @@ arity CDATA #IMPLIED clause_i CDATA #IMPLIED anchor CDATA #IMPLIED + since CDATA #IMPLIED n_vars CDATA #IMPLIED> diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index a0a922216b..c5150d447c 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -191,6 +191,7 @@ <xsl:variable name="name" select="@name"/> <xsl:variable name="arity" select="@arity"/> <xsl:variable name="anchor" select="@anchor"/> + <xsl:variable name="since" select="@since"/> <xsl:variable name="spec0"> <xsl:call-template name="find_spec"/> </xsl:variable> @@ -225,11 +226,12 @@ <xsl:variable name="global_types" select="ancestor::erlref/datatypes"/> <xsl:variable name="local_types" select="../type[string-length(@name) > 0]"/> - <xsl:apply-templates select="$spec/contract/clause/head"> + <xsl:apply-templates select="$spec/contract/clause/head"> <xsl:with-param name="ghlink" select="ancestor-or-self::*[@ghlink]/@ghlink"/> <xsl:with-param name="local_types" select="$local_types"/> <xsl:with-param name="global_types" select="$global_types"/> - </xsl:apply-templates> + <xsl:with-param name="since" select="$since"/> + </xsl:apply-templates> </xsl:when> </xsl:choose> </xsl:template> @@ -238,19 +240,32 @@ <xsl:param name="ghlink"/> <xsl:param name="local_types"/> <xsl:param name="global_types"/> + <xsl:param name="since"/> <xsl:variable name="id" select="concat(concat(concat(concat(../../../name,'-'),../../../arity),'-'),generate-id(.))"/> - <div class="bold_code func-head" - onMouseOver="document.getElementById('ghlink-{$id}').style.visibility = 'visible';" - onMouseOut="document.getElementById('ghlink-{$id}').style.visibility = 'hidden';"> - <xsl:call-template name="ghlink"> - <xsl:with-param name="ghlink" select="$ghlink"/> - <xsl:with-param name="id" select="$id"/> - </xsl:call-template> - <xsl:apply-templates mode="local_type"> - <xsl:with-param name="local_types" select="$local_types"/> - <xsl:with-param name="global_types" select="$global_types"/> - </xsl:apply-templates> - </div> + <table class="func-table"> + <tr class="func-tr"> + <td class="func-td"> + <div class="bold_code func-head" + onMouseOver="document.getElementById('ghlink-{$id}').style.visibility = 'visible';" + onMouseOut="document.getElementById('ghlink-{$id}').style.visibility = 'hidden';"> + <xsl:call-template name="ghlink"> + <xsl:with-param name="ghlink" select="$ghlink"/> + <xsl:with-param name="id" select="$id"/> + </xsl:call-template> + <xsl:apply-templates mode="local_type"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> + </div> + </td> + <td class="func-since-td"> + <xsl:if test="string-length($since) > 0"> + <span class="since"><xsl:value-of select="$since"/> + </span> + </xsl:if> + </td> + </tr> + </table> </xsl:template> <!-- The *last* <name name="..." arity=".."/> --> @@ -1884,6 +1899,16 @@ <xsl:with-param name="partnum" select="$partnum"/> </xsl:apply-templates> </div> + <!-- Since --> + <xsl:if test="string-length(../module/@since) > 0"> + <xsl:call-template name="h3_title_link"> + <xsl:with-param name="title">Since</xsl:with-param> + </xsl:call-template> + <div class="REFBODY module-since"> + Module <xsl:value-of select="../module"/> was introduced in + <xsl:value-of select="../module/@since"/>. + </div> + </xsl:if> </xsl:template> <!-- Lib --> @@ -2030,11 +2055,10 @@ <xsl:template match="func"> <xsl:param name="partnum"/> - <p><xsl:apply-templates select="name"/> - <xsl:apply-templates - select="name[string-length(@arity) > 0 and position()=last()]" - mode="types"/> - </p> + <xsl:apply-templates select="name"/> + <xsl:apply-templates + select="name[string-length(@arity) > 0 and position()=last()]" + mode="types"/> <xsl:apply-templates select="fsummary|type|desc"> <xsl:with-param name="partnum" select="$partnum"/> @@ -2093,19 +2117,29 @@ <xsl:choose> <xsl:when test="ancestor::cref"> - <span class="bold_code bc-7"> - <xsl:call-template name="title_link"> - <xsl:with-param name="link" select="substring-before(nametext, '(')"/> - <xsl:with-param name="title"> - <xsl:value-of select="ret"/> - <xsl:call-template name="maybe-space-after-ret"> - <xsl:with-param name="s" select="ret"/> - </xsl:call-template> - <xsl:value-of select="nametext"/> - </xsl:with-param> - </xsl:call-template> - </span> - <br/> + <table class="func-table"> + <tr class="func-tr"> + <td class="func-td"> + <span class="bold_code bc-7"> + <xsl:call-template name="title_link"> + <xsl:with-param name="link" select="substring-before(nametext, '(')"/> + <xsl:with-param name="title"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </xsl:with-param> + </xsl:call-template> + </span> + </td> + <td class="func-since-td"> + <xsl:if test="string-length(@since) > 0"> + <span class="since"><xsl:value-of select="@since"/></span> + </xsl:if> + </td> + </tr> + </table> </xsl:when> <xsl:when test="ancestor::erlref"> <xsl:variable name="fname"> @@ -2136,14 +2170,25 @@ </div> </xsl:when> <xsl:otherwise> - <div class="bold_code fun-type"> - <xsl:call-template name="title_link"> - <xsl:with-param name="link" select="concat(concat($fname,'-'),$arity)"/> - <xsl:with-param name="title"> - <xsl:apply-templates/> - </xsl:with-param> - </xsl:call-template> - </div> + <table class="func-table"> + <tr class="func-tr"> + <td class="func-td"> + <div class="bold_code fun-type"> + <xsl:call-template name="title_link"> + <xsl:with-param name="link" select="concat(concat($fname,'-'),$arity)"/> + <xsl:with-param name="title"> + <xsl:apply-templates/> + </xsl:with-param> + </xsl:call-template> + </div> + </td> + <td class="func-since-td"> + <xsl:if test="string-length(@since) > 0"> + <span class="since"><xsl:value-of select="@since"/></span> + </xsl:if> + </td> + </tr> + </table> </xsl:otherwise> </xsl:choose> </xsl:when> diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in index a155ceef7e..747750c1fb 100644 --- a/lib/erl_interface/configure.in +++ b/lib/erl_interface/configure.in @@ -82,6 +82,15 @@ AC_ARG_ENABLE(threads, esac ], [ threads_disabled=maybe ]) +AC_ARG_ENABLE(mask-real-errno, +[ --disable-mask-real-errno do not mask real 'errno'], +[ case "$enableval" in + no) mask_real_errno=no ;; + *) mask_real_errno=yes ;; + esac ], +[ mask_real_errno=yes ]) + + dnl ---------------------------------------------------------------------- dnl Checks for programs dnl ---------------------------------------------------------------------- @@ -100,6 +109,10 @@ AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(void *) AC_CHECK_SIZEOF(long long) +if test $mask_real_errno = yes; then + AC_DEFINE(EI_HIDE_REAL_ERRNO, 1, [Define if 'errno' should not be exposed as is in 'erl_errno']) +fi + dnl We set EI_64BIT mode when long is 8 bytes, this makes things dnl work on windows and unix correctly if test $ac_cv_sizeof_long = 8; then @@ -158,7 +171,7 @@ AC_CHECK_LIB([socket], [getpeername]) # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h malloc.h netdb.h netinet/in.h stddef.h stdlib.h string.h sys/param.h sys/socket.h sys/select.h sys/time.h unistd.h sys/types.h]) +AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h malloc.h netdb.h netinet/in.h stddef.h stdlib.h string.h sys/param.h sys/socket.h sys/select.h sys/time.h unistd.h sys/types.h sys/uio.h]) # Checks for typedefs, structures, and compiler characteristics. # fixme AC_C_CONST & AC_C_VOLATILE needed for Windows? @@ -193,7 +206,7 @@ AC_CHECK_FUNCS([dup2 gethostbyaddr gethostbyname \ gethostbyaddr_r \ gethostbyname_r gethostname writev \ gethrtime gettimeofday inet_ntoa memchr memmove memset select \ - socket strchr strerror strrchr strstr uname]) + socket strchr strerror strrchr strstr uname sysconf]) AC_CHECK_FUNC(res_gethostbyname, [], AC_CHECK_LIB(resolv, res_gethostbyname) ) @@ -255,6 +268,7 @@ AC_SUBST(EI_THREADS) case "$threads_disabled" in no|maybe) LM_CHECK_THR_LIB + ETHR_CHK_GCC_ATOMIC_OPS([]) case "$THR_LIB_NAME" in "") @@ -268,7 +282,7 @@ case "$threads_disabled" in EI_THREADS="true" THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600" ;; - pthread) + pthread) EI_THREADS="true" ;; *) diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml index 9502fb1ee7..ae322255ad 100644 --- a/lib/erl_interface/doc/src/ei.xml +++ b/lib/erl_interface/doc/src/ei.xml @@ -124,7 +124,7 @@ typedef enum { <funcs> <func> - <name><ret>int</ret><nametext>ei_decode_atom(const char *buf, int *index, char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_atom(const char *buf, int *index, char *p)</nametext></name> <fsummary>Decode an atom.</fsummary> <desc> <p>Decodes an atom from the binary format. The <c>NULL</c>-terminated @@ -134,7 +134,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_atom_as(const char *buf, int *index, char *p, int plen, erlang_char_encoding want, erlang_char_encoding* was, erlang_char_encoding* result)</nametext></name> + <name since="OTP R16B"><ret>int</ret><nametext>ei_decode_atom_as(const char *buf, int *index, char *p, int plen, erlang_char_encoding want, erlang_char_encoding* was, erlang_char_encoding* result)</nametext></name> <fsummary>Decode an atom.</fsummary> <desc> <p>Decodes an atom from the binary format. The <c>NULL</c>-terminated @@ -158,7 +158,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_bignum(const char *buf, int *index, mpz_t obj)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_bignum(const char *buf, int *index, mpz_t obj)</nametext></name> <fsummary>Decode a GMP arbitrary precision integer.</fsummary> <desc> <p>Decodes an integer in the binary format to a GMP @@ -168,7 +168,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_binary(const char *buf, int *index, void *p, long *len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_binary(const char *buf, int *index, void *p, long *len)</nametext></name> <fsummary>Decode a binary.</fsummary> <desc> <p>Decodes a binary from the binary format. Parameter @@ -180,7 +180,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_boolean(const char *buf, int *index, int *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_boolean(const char *buf, int *index, int *p)</nametext></name> <fsummary>Decode a boolean.</fsummary> <desc> <p>Decodes a boolean value from the binary format. @@ -190,7 +190,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_char(const char *buf, int *index, char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_char(const char *buf, int *index, char *p)</nametext></name> <fsummary>Decode an 8-bit integer between 0-255.</fsummary> <desc> <p>Decodes a char (8-bit) integer between 0-255 from the binary format. @@ -203,7 +203,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_double(const char *buf, int *index, double *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_double(const char *buf, int *index, double *p)</nametext></name> <fsummary>Decode a double.</fsummary> <desc> <p>Decodes a double-precision (64-bit) floating @@ -212,7 +212,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_ei_term(const char* buf, int* index, ei_term* term)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_ei_term(const char* buf, int* index, ei_term* term)</nametext></name> <fsummary>Decode a term, without previous knowledge of type.</fsummary> <desc> <p>Decodes any term, or at least tries to. If the term @@ -233,8 +233,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_fun(const char *buf, int *index, erlang_fun *p)</nametext></name> - <name><ret>void</ret><nametext>free_fun(erlang_fun* f)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_fun(const char *buf, int *index, erlang_fun *p)</nametext></name> + <name since=""><ret>void</ret><nametext>free_fun(erlang_fun* f)</nametext></name> <fsummary>Decode a fun.</fsummary> <desc> <p>Decodes a fun from the binary format. Parameter @@ -248,7 +248,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_list_header(const char *buf, int *index, int *arity)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_list_header(const char *buf, int *index, int *arity)</nametext></name> <fsummary>Decode a list.</fsummary> <desc> <p>Decodes a list header from the binary @@ -265,7 +265,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_long(const char *buf, int *index, long *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_long(const char *buf, int *index, long *p)</nametext></name> <fsummary>Decode integer.</fsummary> <desc> <p>Decodes a long integer from the binary format. @@ -275,7 +275,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_longlong(const char *buf, int *index, long long *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_longlong(const char *buf, int *index, long long *p)</nametext></name> <fsummary>Decode integer.</fsummary> <desc> <p>Decodes a GCC <c>long long</c> or Visual C++ @@ -286,7 +286,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_map_header(const char *buf, int *index, int *arity)</nametext></name> + <name since="OTP 17.0"><ret>int</ret><nametext>ei_decode_map_header(const char *buf, int *index, int *arity)</nametext></name> <fsummary>Decode a map.</fsummary> <desc> <p>Decodes a map header from the binary @@ -299,7 +299,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_pid(const char *buf, int *index, erlang_pid *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_pid(const char *buf, int *index, erlang_pid *p)</nametext></name> <fsummary>Decode a <c>pid</c>.</fsummary> <desc> <p>Decodes a process identifier (pid) from the binary format.</p> @@ -307,7 +307,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_port(const char *buf, int *index, erlang_port *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_port(const char *buf, int *index, erlang_port *p)</nametext></name> <fsummary>Decode a port.</fsummary> <desc> <p>Decodes a port identifier from the binary format.</p> @@ -315,7 +315,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_ref(const char *buf, int *index, erlang_ref *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_ref(const char *buf, int *index, erlang_ref *p)</nametext></name> <fsummary>Decode a reference.</fsummary> <desc> <p>Decodes a reference from the binary format.</p> @@ -323,7 +323,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_string(const char *buf, int *index, char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_string(const char *buf, int *index, char *p)</nametext></name> <fsummary>Decode a string.</fsummary> <desc> <p>Decodes a string from the binary format. A @@ -338,7 +338,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_term(const char *buf, int *index, void *t)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_term(const char *buf, int *index, void *t)</nametext></name> <fsummary>Decode a <c>ETERM</c>.</fsummary> <desc> <p>Decodes a term from the binary format. The term @@ -352,7 +352,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_trace(const char *buf, int *index, erlang_trace *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_trace(const char *buf, int *index, erlang_trace *p)</nametext></name> <fsummary>Decode a trace token.</fsummary> <desc> <p>Decodes an Erlang trace token from the binary format.</p> @@ -360,7 +360,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_tuple_header(const char *buf, int *index, int *arity)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_tuple_header(const char *buf, int *index, int *arity)</nametext></name> <fsummary>Decode a tuple.</fsummary> <desc> <p>Decodes a tuple header, the number of elements @@ -370,7 +370,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_ulong(const char *buf, int *index, unsigned long *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_ulong(const char *buf, int *index, unsigned long *p)</nametext></name> <fsummary>Decode unsigned integer.</fsummary> <desc> <p>Decodes an unsigned long integer from the binary format. @@ -380,7 +380,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_ulonglong(const char *buf, int *index, unsigned long long *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_ulonglong(const char *buf, int *index, unsigned long long *p)</nametext></name> <fsummary>Decode unsigned integer.</fsummary> <desc> <p>Decodes a GCC <c>unsigned long long</c> or Visual C++ @@ -390,7 +390,7 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_decode_version(const char *buf, int *index, int *version)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_decode_version(const char *buf, int *index, int *version)</nametext></name> <fsummary>Decode an empty list (<c>nil</c>).</fsummary> <desc> <p>Decodes the version magic number for the @@ -400,10 +400,10 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_atom(char *buf, int *index, const char *p)</nametext></name> - <name><ret>int</ret><nametext>ei_encode_atom_len(char *buf, int *index, const char *p, int len)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_atom(ei_x_buff* x, const char *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_atom_len(ei_x_buff* x, const char *p, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_atom(char *buf, int *index, const char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_atom_len(char *buf, int *index, const char *p, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_atom(ei_x_buff* x, const char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_atom_len(ei_x_buff* x, const char *p, int len)</nametext></name> <fsummary>Encode an atom.</fsummary> <desc> <p>Encodes an atom in the binary format. Parameter <c>p</c> @@ -415,10 +415,10 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_atom_as(char *buf, int *index, const char *p, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> - <name><ret>int</ret><nametext>ei_encode_atom_len_as(char *buf, int *index, const char *p, int len, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_atom_as(ei_x_buff* x, const char *p, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_atom_len_as(ei_x_buff* x, const char *p, int len, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> + <name since="OTP R16B"><ret>int</ret><nametext>ei_encode_atom_as(char *buf, int *index, const char *p, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> + <name since="OTP R16B"><ret>int</ret><nametext>ei_encode_atom_len_as(char *buf, int *index, const char *p, int len, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> + <name since="OTP R16B"><ret>int</ret><nametext>ei_x_encode_atom_as(ei_x_buff* x, const char *p, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> + <name since="OTP R16B"><ret>int</ret><nametext>ei_x_encode_atom_len_as(ei_x_buff* x, const char *p, int len, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name> <fsummary>Encode an atom.</fsummary> <desc> <p>Encodes an atom in the binary format. Parameter <c>p</c> is the name of the atom with @@ -435,8 +435,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_bignum(char *buf, int *index, mpz_t obj)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_bignum(ei_x_buff *x, mpz_t obj)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_bignum(char *buf, int *index, mpz_t obj)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_bignum(ei_x_buff *x, mpz_t obj)</nametext></name> <fsummary>Encode an arbitrary precision integer.</fsummary> <desc> <p>Encodes a GMP <c>mpz_t</c> integer to binary format. @@ -446,8 +446,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_binary(char *buf, int *index, const void *p, long len)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_binary(ei_x_buff* x, const void *p, long len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_binary(char *buf, int *index, const void *p, long len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_binary(ei_x_buff* x, const void *p, long len)</nametext></name> <fsummary>Encode a binary.</fsummary> <desc> <p>Encodes a binary in the binary format. The data is at @@ -456,8 +456,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_boolean(char *buf, int *index, int p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_boolean(ei_x_buff* x, int p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_boolean(char *buf, int *index, int p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_boolean(ei_x_buff* x, int p)</nametext></name> <fsummary>Encode a boolean.</fsummary> <desc> <p>Encodes a boolean value as the atom <c>true</c> if @@ -467,8 +467,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_char(char *buf, int *index, char p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_char(ei_x_buff* x, char p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_char(char *buf, int *index, char p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_char(ei_x_buff* x, char p)</nametext></name> <fsummary>Encode an 8-bit integer between 0-255.</fsummary> <desc> <p>Encodes a char (8-bit) as an integer between 0-255 in the binary @@ -481,8 +481,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_double(char *buf, int *index, double p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_double(ei_x_buff* x, double p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_double(char *buf, int *index, double p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_double(ei_x_buff* x, double p)</nametext></name> <fsummary>Encode a double float.</fsummary> <desc> <p>Encodes a double-precision (64-bit) floating point number in @@ -493,8 +493,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_empty_list(char* buf, int* index)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_empty_list(ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_empty_list(char* buf, int* index)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_empty_list(ei_x_buff* x)</nametext></name> <fsummary>Encode an empty list (<c>nil</c>).</fsummary> <desc> <p>Encodes an empty list. It is often used at the tail of a list.</p> @@ -502,8 +502,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_fun(char *buf, int *index, const erlang_fun *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_fun(char *buf, int *index, const erlang_fun *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun)</nametext></name> <fsummary>Encode a fun.</fsummary> <desc> <p>Encodes a fun in the binary format. Parameter <c>p</c> @@ -515,8 +515,8 @@ typedef enum { </func> <func> - <name><ret>int</ret><nametext>ei_encode_list_header(char *buf, int *index, int arity)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_list_header(ei_x_buff* x, int arity)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_list_header(char *buf, int *index, int arity)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_list_header(ei_x_buff* x, int arity)</nametext></name> <fsummary>Encode a list.</fsummary> <desc> <p>Encodes a list header, with a specified @@ -552,8 +552,8 @@ ei_x_encode_empty_list(&x);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_long(char *buf, int *index, long p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_long(ei_x_buff* x, long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_long(char *buf, int *index, long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_long(ei_x_buff* x, long p)</nametext></name> <fsummary>Encode integer.</fsummary> <desc> <p>Encodes a long integer in the binary format. @@ -563,8 +563,8 @@ ei_x_encode_empty_list(&x);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_longlong(char *buf, int *index, long long p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_longlong(ei_x_buff* x, long long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_longlong(char *buf, int *index, long long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_longlong(ei_x_buff* x, long long p)</nametext></name> <fsummary>Encode integer.</fsummary> <desc> <p>Encodes a GCC <c>long long</c> or Visual C++ @@ -574,8 +574,8 @@ ei_x_encode_empty_list(&x);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_map_header(char *buf, int *index, int arity)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_map_header(ei_x_buff* x, int arity)</nametext></name> + <name since="OTP 17.0"><ret>int</ret><nametext>ei_encode_map_header(char *buf, int *index, int arity)</nametext></name> + <name since="OTP 17.0"><ret>int</ret><nametext>ei_x_encode_map_header(ei_x_buff* x, int arity)</nametext></name> <fsummary>Encode a map.</fsummary> <desc> <p>Encodes a map header, with a specified arity. The next @@ -595,8 +595,8 @@ ei_x_encode_string(&x, "Banana");</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_pid(char *buf, int *index, const erlang_pid *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_pid(ei_x_buff* x, const erlang_pid *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_pid(char *buf, int *index, const erlang_pid *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_pid(ei_x_buff* x, const erlang_pid *p)</nametext></name> <fsummary>Encode a pid.</fsummary> <desc> <p>Encodes an Erlang process identifier (pid) in the binary @@ -607,8 +607,8 @@ ei_x_encode_string(&x, "Banana");</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_port(char *buf, int *index, const erlang_port *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_port(ei_x_buff* x, const erlang_port *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_port(char *buf, int *index, const erlang_port *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_port(ei_x_buff* x, const erlang_port *p)</nametext></name> <fsummary>Encode a port.</fsummary> <desc> <p>Encodes an Erlang port in the binary format. Parameter @@ -619,8 +619,8 @@ ei_x_encode_string(&x, "Banana");</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_ref(char *buf, int *index, const erlang_ref *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_ref(char *buf, int *index, const erlang_ref *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p)</nametext></name> <fsummary>Encode a ref.</fsummary> <desc> <p>Encodes an Erlang reference in the binary format. Parameter @@ -631,10 +631,10 @@ ei_x_encode_string(&x, "Banana");</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_string(char *buf, int *index, const char *p)</nametext></name> - <name><ret>int</ret><nametext>ei_encode_string_len(char *buf, int *index, const char *p, int len)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_string(ei_x_buff* x, const char *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_string_len(ei_x_buff* x, const char* s, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_string(char *buf, int *index, const char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_string_len(char *buf, int *index, const char *p, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_string(ei_x_buff* x, const char *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_string_len(ei_x_buff* x, const char* s, int len)</nametext></name> <fsummary>Encode a string.</fsummary> <desc> <p>Encodes a string in the binary format. (A string in Erlang @@ -645,8 +645,8 @@ ei_x_encode_string(&x, "Banana");</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_term(char *buf, int *index, void *t)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_term(ei_x_buff* x, void *t)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_term(char *buf, int *index, void *t)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_term(ei_x_buff* x, void *t)</nametext></name> <fsummary>Encode an <c>erl_interface</c> term.</fsummary> <desc> <p>Encodes an <c>ETERM</c>, as obtained from @@ -656,8 +656,8 @@ ei_x_encode_string(&x, "Banana");</pre> </desc> </func> <func> - <name><ret>int</ret><nametext>ei_encode_trace(char *buf, int *index, const erlang_trace *p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_trace(char *buf, int *index, const erlang_trace *p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p)</nametext></name> <fsummary>Encode a trace token.</fsummary> <desc> <p>Encodes an Erlang trace token in the binary format. @@ -668,8 +668,8 @@ ei_x_encode_string(&x, "Banana");</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_tuple_header(char *buf, int *index, int arity)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_tuple_header(ei_x_buff* x, int arity)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_tuple_header(char *buf, int *index, int arity)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_tuple_header(ei_x_buff* x, int arity)</nametext></name> <fsummary>Encode a tuple.</fsummary> <desc> <p>Encodes a tuple header, with a specified @@ -687,8 +687,8 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_ulong(char *buf, int *index, unsigned long p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_ulong(ei_x_buff* x, unsigned long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_ulong(char *buf, int *index, unsigned long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_ulong(ei_x_buff* x, unsigned long p)</nametext></name> <fsummary>Encode unsigned integer.</fsummary> <desc> <p>Encodes an unsigned long integer in the binary format. @@ -698,8 +698,8 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_ulonglong(char *buf, int *index, unsigned long long p)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_ulonglong(ei_x_buff* x, unsigned long long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_ulonglong(char *buf, int *index, unsigned long long p)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_ulonglong(ei_x_buff* x, unsigned long long p)</nametext></name> <fsummary>Encode unsigned integer.</fsummary> <desc> <p>Encodes a GCC <c>unsigned long long</c> or Visual C++ @@ -709,8 +709,8 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_encode_version(char *buf, int *index)</nametext></name> - <name><ret>int</ret><nametext>ei_x_encode_version(ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_encode_version(char *buf, int *index)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_encode_version(ei_x_buff* x)</nametext></name> <fsummary>Encode version.</fsummary> <desc> <p>Encodes a version magic number for the binary format. Must @@ -719,7 +719,7 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name> <fsummary>Fetch the type and size of an encoded term.</fsummary> <desc> <p>Returns the type in <c>type</c> and size in @@ -733,8 +733,23 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_print_term(FILE* fp, const char* buf, int* index)</nametext></name> - <name><ret>int</ret><nametext>ei_s_print_term(char** s, const char* buf, int* index)</nametext></name> + <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_init(void)</nametext></name> + <fsummary>Initialize the ei library.</fsummary> + <desc> + <p>Initialize the <c>ei</c> library. This function should be called once + (and only once) before calling any other functionality in the <c>ei</c> + library. However, note the exception below.</p> + <p>If the <c>ei</c> library is used together with the <c>erl_interface</c> + library, this function should <em>not</em> be called directly. It will be + called by the <c>erl_init()</c> function which should be used to initialize + the combination of the two libraries instead.</p> + <p>On success zero is returned. On failure a posix error code is returned.</p> + </desc> + </func> + + <func> + <name since=""><ret>int</ret><nametext>ei_print_term(FILE* fp, const char* buf, int* index)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_s_print_term(char** s, const char* buf, int* index)</nametext></name> <fsummary>Print a term in clear text.</fsummary> <desc> <p>Prints a term, in clear text, to the file @@ -759,7 +774,7 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>void</ret><nametext>ei_set_compat_rel(release_number)</nametext></name> + <name since=""><ret>void</ret><nametext>ei_set_compat_rel(release_number)</nametext></name> <fsummary>Set the ei library in compatibility mode.</fsummary> <type> <v>unsigned release_number;</v> @@ -794,7 +809,7 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_skip_term(const char* buf, int* index)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_skip_term(const char* buf, int* index)</nametext></name> <fsummary>Skip a term.</fsummary> <desc> <p>Skips a term in the specified buffer; @@ -815,8 +830,8 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_x_append(ei_x_buff* x, const ei_x_buff* x2)</nametext></name> - <name><ret>int</ret><nametext>ei_x_append_buf(ei_x_buff* x, const char* buf, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_append(ei_x_buff* x, const ei_x_buff* x2)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_append_buf(ei_x_buff* x, const char* buf, int len)</nametext></name> <fsummary>Append a buffer at the end.</fsummary> <desc> <p>Appends data at the end of buffer <c>x</c>.</p> @@ -824,8 +839,8 @@ ei_encode_tuple_header(buf, &i, 0);</pre> </func> <func> - <name><ret>int</ret><nametext>ei_x_format(ei_x_buff* x, const char* fmt, ...)</nametext></name> - <name><ret>int</ret><nametext>ei_x_format_wo_ver(ei_x_buff* x, const char *fmt, ... )</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_format(ei_x_buff* x, const char* fmt, ...)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_format_wo_ver(ei_x_buff* x, const char *fmt, ... )</nametext></name> <fsummary>Format a term from a format string and parameters.</fsummary> <desc> <p>Formats a term, given as a string, to a buffer. @@ -853,7 +868,7 @@ encodes the tuple {numbers,12,3.14159}</pre> </func> <func> - <name><ret>int</ret><nametext>ei_x_free(ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_free(ei_x_buff* x)</nametext></name> <fsummary>Free a buffer.</fsummary> <desc> <p>Frees an <c>ei_x_buff</c> buffer. @@ -862,8 +877,8 @@ encodes the tuple {numbers,12,3.14159}</pre> </func> <func> - <name><ret>int</ret><nametext>ei_x_new(ei_x_buff* x)</nametext></name> - <name><ret>int</ret><nametext>ei_x_new_with_version(ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_new(ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_x_new_with_version(ei_x_buff* x)</nametext></name> <fsummary>Allocate a new buffer.</fsummary> <desc> <p>Allocates a new <c>ei_x_buff</c> buffer. The diff --git a/lib/erl_interface/doc/src/ei_connect.xml b/lib/erl_interface/doc/src/ei_connect.xml index 607a7cbff4..e318dd6664 100644 --- a/lib/erl_interface/doc/src/ei_connect.xml +++ b/lib/erl_interface/doc/src/ei_connect.xml @@ -85,20 +85,288 @@ the <c>_tmo</c> suffix.</p> </section> + <section> + <marker id="ussi"/> + <title>User Supplied Socket Implementation</title> + <p>By default <c>ei</c> supplies a TCP/IPv4 socket interface + that is used when communicating. The user can however plug in + his/her own IPv4 socket implementation. This, for example, in order + to communicate over TLS. A user supplied socket implementation + is plugged in by passing a + <seealso marker="#ei_socket_callbacks">callback structure</seealso> + to either + <seealso marker="#ei_connect_init"><c>ei_connect_init_ussi()</c></seealso> + or + <seealso marker="#ei_connect_init"><c>ei_connect_xinit_ussi()</c></seealso>.</p> + + <p>All callbacks in the <c>ei_socket_callbacks</c> structure + <em>should</em> return zero on success; and a posix error + code on failure.</p> + + <p>The <c>addr</c> argument of the <c>listen</c>, <c>accept</c>, + and <c>connect</c> callbacks refer to appropriate address + structure for currently used protocol. Currently <c>ei</c> + only supports IPv4. That is, at this time <c>addr</c> always + points to a <c>struct sockaddr_in</c> structure.</p> + + <p>The <c>ei_socket_callbacks</c> structure may be enlarged in + the future. All fields not set, <em>needs</em> to be zeroed out.</p> + + <marker id="ei_socket_callbacks"/> + <code type="none"><![CDATA[ +typedef struct { + int flags; + int (*socket)(void **ctx, void *setup_ctx); + int (*close)(void *ctx); + int (*listen)(void *ctx, void *addr, int *len, int backlog); + int (*accept)(void **ctx, void *addr, int *len, unsigned tmo); + int (*connect)(void *ctx, void *addr, int len, unsigned tmo); + int (*writev)(void *ctx, const void *iov, int iovcnt, ssize_t *len, unsigned tmo); + int (*write)(void *ctx, const char *buf, ssize_t *len, unsigned tmo); + int (*read)(void *ctx, char *buf, ssize_t *len, unsigned tmo); + int (*handshake_packet_header_size)(void *ctx, int *sz); + int (*connect_handshake_complete)(void *ctx); + int (*accept_handshake_complete)(void *ctx); + int (*get_fd)(void *ctx, int *fd); +} ei_socket_callbacks; + ]]></code> + + <taglist> + + <tag><c>flags</c></tag> + <item> + <p>Flags informing <c>ei</c> about the behaviour of the + callbacks. Flags should be bitwise or:ed together. If no flag, + is set, the <c>flags</c> field should contain <c>0</c>. Currently, + supported flags:</p> + <taglist> + <tag><c>EI_SCLBK_FLG_FULL_IMPL</c></tag> + <item> + <p> + If set, the <c>accept()</c>, <c>connect()</c>, + <c>writev()</c>, <c>write()</c>, and <c>read()</c> callbacks + implements timeouts. The timeout is passed in the <c>tmo</c> + argument and is given in milli seconds. Note that the + <c>tmo</c> argument to these callbacks differ from the + timeout arguments in the <c>ei</c> API. Zero means a zero + timeout. That is, poll and timeout immediately unless the + operation is successful. <c>EI_SCLBK_INF_TMO</c> + (max <c>unsigned</c>) means infinite timeout. The file + descriptor is in blocking mode when a callback is called, + and it must be in blocking mode when the callback returns. + </p> + <p> + If not set, <c>ei</c> will implement the timeout using + <c>select()</c> in order to determine when to call the + callbacks and when to time out. The <c>tmo</c> arguments + of the <c>accept()</c>, <c>connect()</c>, <c>writev()</c>, + <c>write()</c>, and <c>read()</c> callbacks should be + ignored. The callbacks may be called in non-blocking mode. + The callbacks are not allowed to change between blocking + and non-blocking mode. In order for this to work, + <c>select()</c> needs to interact with the socket primitives + used the same way as it interacts with the ordinary socket + primitives. If this is not the case, the callbacks + <em>need</em> to implement timeouts and this flag should + be set. + </p> + </item> + </taglist> + <p>More flags may be introduced in the future.</p> + </item> + + <tag><c>int (*socket)(void **ctx, void *setup_ctx)</c></tag> + <item> + <p>Create a socket and a context for the socket.</p> + + <p>On success it should set <c>*ctx</c> to point to a context for + the created socket. This context will be passed to all other + socket callbacks. This function will be passed the same + <c>setup_context</c> as passed to the preceeding + <seealso marker="#ei_connect_init"><c>ei_connect_init_ussi()</c></seealso> + or + <seealso marker="#ei_connect_init"><c>ei_connect_xinit_ussi()</c></seealso> + call.</p> + + <note><p>During the lifetime of a socket, the pointer <c>*ctx</c> + <em>has</em> to remain the same. That is, it cannot later be + relocated.</p></note> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*close)(void *ctx)</c></tag> + <item> + <p>Close the socket identified by <c>ctx</c> and destroy the context.</p> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*listen)(void *ctx, void *addr, int *len, int backlog)</c></tag> + <item> + <p>Bind the socket identified by <c>ctx</c> to a local interface + and then listen on it.</p> + + <p>The <c>addr</c> and <c>len</c> arguments are both input and output + arguments. When called <c>addr</c> points to an address structure of + lenght <c>*len</c> containing information on how to bind the socket. + Uppon return this callback should have updated the structure referred + by <c>addr</c> with information on how the socket actually was bound. + <c>*len</c> should be updated to reflect the size of <c>*addr</c> + updated. <c>backlog</c> identifies the size of the backlog for the + listen socket.</p> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*accept)(void **ctx, void *addr, int *len, unsigned tmo)</c></tag> + <item> + <p>Accept connections on the listen socket identified by + <c>*ctx</c>.</p> + + <p>When a connection is accepted, a new context for the accepted + connection should be created and <c>*ctx</c> should be updated + to point to the new context for the accepted connection. When + called <c>addr</c> points to an uninitialized address structure + of lenght <c>*len</c>. Uppon return this callback should have + updated this structure with information about the client address. + <c>*len</c> should be updated to reflect the size of <c>*addr</c> + updated. + </p> + + <p>If the <c>EI_SCLBK_FLG_FULL_IMPL</c> flag has been set, + <c>tmo</c> contains timeout time in milliseconds.</p> + + <note><p>During the lifetime of a socket, the pointer <c>*ctx</c> + <em>has</em> to remain the same. That is, it cannot later be + relocated.</p></note> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*connect)(void *ctx, void *addr, int len, unsigned tmo)</c></tag> + <item> + <p>Connect the socket identified by <c>ctx</c> to the address + identified by <c>addr</c>.</p> + + <p>When called <c>addr</c> points to an address structure of + lenght <c>len</c> containing information on where to connect.</p> + + <p>If the <c>EI_SCLBK_FLG_FULL_IMPL</c> flag has been set, + <c>tmo</c> contains timeout time in milliseconds.</p> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*writev)(void *ctx, const void *iov, long iovcnt, ssize_t *len, unsigned tmo)</c></tag> + <item> + <p>Write data on the connected socket identified by <c>ctx</c>.</p> + + <p><c>iov</c> points to an array of <c>struct iovec</c> structures of + length <c>iovcnt</c> containing data to write to the socket. On success, + this callback should set <c>*len</c> to the amount of bytes successfully + written on the socket.</p> + + <p>If the <c>EI_SCLBK_FLG_FULL_IMPL</c> flag has been set, + <c>tmo</c> contains timeout time in milliseconds.</p> + + <p>This callback is optional. Set the <c>writev</c> field + in the the <c>ei_socket_callbacks</c> structure to <c>NULL</c> if not + implemented.</p> + </item> + + <tag><c>int (*write)(void *ctx, const char *buf, ssize_t *len, unsigned tmo)</c></tag> + <item> + <p>Write data on the connected socket identified by <c>ctx</c>.</p> + + <p>When called <c>buf</c> points to a buffer of length <c>*len</c> + containing the data to write on the socket. On success, this callback + should set <c>*len</c> to the amount of bytes successfully written on + the socket.</p> + + <p>If the <c>EI_SCLBK_FLG_FULL_IMPL</c> flag has been set, + <c>tmo</c> contains timeout time in milliseconds.</p> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*read)(void *ctx, char *buf, ssize_t *len, unsigned tmo)</c></tag> + <item> + <p>Read data on the connected socket identified by <c>ctx</c>.</p> + + <p><c>buf</c> points to a buffer of length <c>*len</c> where the + read data should be placed. On success, this callback should update + <c>*len</c> to the amount of bytes successfully read on the socket.</p> + + <p>If the <c>EI_SCLBK_FLG_FULL_IMPL</c> flag has been set, + <c>tmo</c> contains timeout time in milliseconds.</p> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*handshake_packet_header_size)(void *ctx, int *sz)</c></tag> + <item> + <p>Inform about handshake packet header size to use during the Erlang + distribution handshake.</p> + + <p>On success, <c>*sz</c> should be set to the handshake packet header + size to use. Valid values are <c>2</c> and <c>4</c>. Erlang TCP + distribution use a handshake packet size of <c>2</c> and Erlang TLS + distribution use a handshake packet size of <c>4</c>.</p> + + <p>This callback is mandatory.</p> + </item> + + <tag><c>int (*connect_handshake_complete)(void *ctx)</c></tag> + <item> + <p>Called when a locally started handshake has completed successfully.</p> + + <p>This callback is optional. Set the <c>connect_handshake_complete</c> field + in the <c>ei_socket_callbacks</c> structure to <c>NULL</c> if not implemented.</p> + </item> + + <tag><c>int (*accept_handshake_complete)(void *ctx)</c></tag> + <item> + <p>Called when a remotely started handshake has completed successfully.</p> + + <p>This callback is optional. Set the <c>accept_handshake_complete</c> field in + the <c>ei_socket_callbacks</c> structure to <c>NULL</c> if not implemented.</p> + </item> + + <tag><c>int (*get_fd)(void *ctx, int *fd)</c></tag> + <item> + <p>Inform about file descriptor used by the socket which is identified + by <c>ctx</c>.</p> + + <note><p>During the lifetime of a socket, the file descriptor + <em>has</em> to remain the same. That is, repeated calls to this + callback with the same context <c>should</c> always report the same + file descriptor.</p> + <p>The file descriptor <em>has</em> to be a real file descriptor. + That is, no other operation should be able to get the same file + descriptor until it has been released by the <c>close()</c> + callback.</p> + </note> + + <p>This callback is mandatory.</p> + </item> + </taglist> + </section> <funcs> <func> - <name><ret>struct hostent</ret><nametext>*ei_gethostbyaddr(const char *addr, int len, int type)</nametext></name> - <name><ret>struct hostent</ret><nametext>*ei_gethostbyaddr_r(const char *addr, int length, int type, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name> - <name><ret>struct hostent</ret><nametext>*ei_gethostbyname(const char *name)</nametext></name> - <name><ret>struct hostent</ret><nametext>*ei_gethostbyname_r(const char *name, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>ei_gethostbyaddr(const char *addr, int len, int type)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>ei_gethostbyaddr_r(const char *addr, int length, int type, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>ei_gethostbyname(const char *name)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>ei_gethostbyname_r(const char *name, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)</nametext></name> <fsummary>Name lookup functions.</fsummary> <desc> <p>Convenience functions for some common name lookup functions.</p> </desc> </func> + <func> - <name><ret>int</ret><nametext>ei_accept(ei_cnode *ec, int listensock, ErlConnect *conp)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_accept(ei_cnode *ec, int listensock, ErlConnect *conp)</nametext></name> <fsummary>Accept a connection from another node.</fsummary> <desc> <p>Used by a server process to accept a @@ -130,7 +398,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>ei_accept_tmo(ei_cnode *ec, int listensock, ErlConnect *conp, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_accept_tmo(ei_cnode *ec, int listensock, ErlConnect *conp, unsigned timeout_ms)</nametext></name> <fsummary>Accept a connection from another node with optional time-out.</fsummary> <desc> @@ -141,8 +409,16 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>ei_connect(ei_cnode* ec, char *nodename)</nametext></name> - <name><ret>int</ret><nametext>ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename)</nametext></name> + <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_close_connection(int fd)</nametext></name> + <fsummary>Close a connection.</fsummary> + <desc> + <p>Closes a previously opened connection or listen socket.</p> + </desc> + </func> + + <func> + <name since=""><ret>int</ret><nametext>ei_connect(ei_cnode* ec, char *nodename)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename)</nametext></name> <fsummary>Establish a connection to an Erlang node.</fsummary> <desc> <p>Sets up a connection to an Erlang node.</p> @@ -192,8 +468,10 @@ fd = ei_xconnect(&ec, &addr, ALIVE); </func> <func> - <name><ret>int</ret><nametext>ei_connect_init(ei_cnode* ec, const char* this_node_name, const char *cookie, short creation)</nametext></name> - <name><ret>int</ret><nametext>ei_connect_xinit(ei_cnode* ec, const char *thishostname, const char *thisalivename, const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, short creation)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_connect_init(ei_cnode* ec, const char* this_node_name, const char *cookie, short creation)</nametext></name> + <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_connect_init_ussi(ei_cnode* ec, const char* this_node_name, const char *cookie, short creation, ei_socket_callbacks *cbs, int cbs_sz, void *setup_context)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_connect_xinit(ei_cnode* ec, const char *thishostname, const char *thisalivename, const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, short creation)</nametext></name> + <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname, const char *thisalivename, const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, short creation, ei_socket_callbacks *cbs, int cbs_sz, void *setup_context)</nametext></name> <fsummary>Initialize for a connection.</fsummary> <desc> <p>Initializes the <c>ec</c> structure, to @@ -236,6 +514,21 @@ fd = ei_xconnect(&ec, &addr, ALIVE); <item> <p><c>thispaddr</c> if the IP address of the host.</p> </item> + <item> + <p><c>cbs</c> is a pointer to a + <seealso marker="#ei_socket_callbacks">callback structure</seealso> + implementing and alternative socket interface.</p> + </item> + <item> + <p><c>cbs_sz</c> is the size of the structure + pointed to by <c>cbs</c>.</p> + </item> + <item> + <p><c>setup_context</c> is a pointer to a structure that + will be passed as second argument to the <c>socket</c> callback + in the <c>cbs</c> structure.</p> + </item> + </list> <p>A C-node acting as a server is assigned a creation number when it calls <c>ei_publish()</c>.</p> @@ -273,8 +566,8 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned timeout_ms)</nametext></name> - <name><ret>int</ret><nametext>ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned timeout_ms)</nametext></name> <fsummary>Establish a connection to an Erlang node with optional time-out.</fsummary> <desc> @@ -286,8 +579,8 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_get_tracelevel(void)</nametext></name> - <name><ret>void</ret><nametext>ei_set_tracelevel(int level)</nametext></name> + <name since="OTP R13B04"><ret>int</ret><nametext>ei_get_tracelevel(void)</nametext></name> + <name since="OTP R13B04"><ret>void</ret><nametext>ei_set_tracelevel(int level)</nametext></name> <fsummary>Get and set functions for tracing.</fsummary> <desc> <p>Used to set tracing on the distribution. The levels are different @@ -299,7 +592,46 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_publish(ei_cnode *ec, int port)</nametext></name> + <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_listen(ei_cnode *ec, int *port, int backlog)</nametext></name> + <name since="OTP @OTP-15442@"><ret>int</ret><nametext>ei_xlisten(ei_cnode *ec, Erl_IpAddr adr, int *port, int backlog)</nametext></name> + <fsummary>Create a listen socket.</fsummary> + <desc> + <p>Used by a server process to setup a listen socket which + later can be used for accepting connections from client processes. + </p> + <list type="bulleted"> + <item> + <p><c>ec</c> is the C-node structure.</p> + </item> + <item> + <p><c>adr</c> is local interface to bind to.</p> + </item> + <item> + <p><c>port</c> is a pointer to an integer containing the + port number to bind to. If <c>*port</c> equals <c>0</c> + when calling <c>ei_listen()</c>, the socket will be bound to + an ephemeral port. On success, <c>ei_listen()</c> will update + the value of <c>*port</c> to the port actually bound to. + </p> + </item> + <item> + <p><c>backlog</c> is maximum backlog of pending connections.</p> + </item> + </list> + <p><c>ei_listen</c> will create a socket, bind to a port on the + local interface identified by <c>adr</c> (or all local interfaces if + <c>ei_listen()</c> is called), and mark the socket as a passive socket + (that is, a socket that will be used for accepting incoming connections). + </p> + <p> + On success, a file descriptor is returned which can be used in a call to + <c>ei_accept()</c>. On failure, <c>ERL_ERROR</c> is returned and + <c>erl_errno</c> is set to <c>EIO</c>.</p> + </desc> + </func> + + <func> + <name since=""><ret>int</ret><nametext>ei_publish(ei_cnode *ec, int port)</nametext></name> <fsummary>Publish a node name.</fsummary> <desc> <p>Used by a server process to register @@ -336,7 +668,7 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_publish_tmo(ei_cnode *ec, int port, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_publish_tmo(ei_cnode *ec, int port, unsigned timeout_ms)</nametext></name> <fsummary>Publish a node name with optional time-out.</fsummary> <desc> <p>Equivalent to @@ -346,7 +678,7 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_receive(int fd, unsigned char* bufp, int bufsize)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_receive(int fd, unsigned char* bufp, int bufsize)</nametext></name> <fsummary>Receive a message.</fsummary> <desc> <p>Receives a message consisting of a sequence @@ -387,7 +719,7 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_receive_encoded(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_receive_encoded(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen)</nametext></name> <fsummary>Obsolete function for receiving a message.</fsummary> <desc> <p>This function is retained for compatibility with code @@ -417,7 +749,7 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned timeout_ms)</nametext></name> <fsummary>Obsolete function for receiving a message with time-out. </fsummary> <desc> @@ -428,8 +760,8 @@ if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) { </func> <func> - <name><ret>int</ret><nametext>ei_receive_msg(int fd, erlang_msg* msg, ei_x_buff* x)</nametext></name> - <name><ret>int</ret><nametext>ei_xreceive_msg(int fd, erlang_msg* msg, ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_receive_msg(int fd, erlang_msg* msg, ei_x_buff* x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_xreceive_msg(int fd, erlang_msg* msg, ei_x_buff* x)</nametext></name> <fsummary>Receive a message.</fsummary> <desc> <p>Receives a message to the buffer in <c>x</c>. @@ -493,8 +825,8 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>ei_receive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned imeout_ms)</nametext></name> - <name><ret>int</ret><nametext>ei_xreceive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_receive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned imeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_xreceive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned timeout_ms)</nametext></name> <fsummary>Receive a message with optional time-out.</fsummary> <desc> <p>Equivalent to <c>ei_receive_msg</c> and <c>ei_xreceive_msg</c> @@ -504,7 +836,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>ei_receive_tmo(int fd, unsigned char* bufp, int bufsize, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_receive_tmo(int fd, unsigned char* bufp, int bufsize, unsigned timeout_ms)</nametext></name> <fsummary>Receive a message with optional time-out.</fsummary> <desc> <p>Equivalent to @@ -514,7 +846,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>ei_reg_send(ei_cnode* ec, int fd, char* server_name, char* buf, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_send(ei_cnode* ec, int fd, char* server_name, char* buf, int len)</nametext></name> <fsummary>Send a message to a registered name.</fsummary> <desc> <p>Sends an Erlang term to a registered process.</p> @@ -546,7 +878,7 @@ if (ei_reg_send(&ec, fd, x.buff, x.index) < 0) </func> <func> - <name><ret>int</ret><nametext>ei_reg_send_tmo(ei_cnode* ec, int fd, char* server_name, char* buf, int len, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_send_tmo(ei_cnode* ec, int fd, char* server_name, char* buf, int len, unsigned timeout_ms)</nametext></name> <fsummary>Send a message to a registered name with optional time-out </fsummary> <desc> @@ -557,9 +889,9 @@ if (ei_reg_send(&ec, fd, x.buff, x.index) < 0) </func> <func> - <name><ret>int</ret><nametext>ei_rpc(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen, ei_x_buff *x)</nametext></name> - <name><ret>int</ret><nametext>ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen)</nametext></name> - <name><ret>int</ret><nametext>ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg, ei_x_buff *x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_rpc(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen, ei_x_buff *x)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg, ei_x_buff *x)</nametext></name> <fsummary>Remote Procedure Call from C to Erlang.</fsummary> <desc> <p>Supports calling Erlang functions on remote nodes. @@ -658,7 +990,7 @@ if (ei_decode_version(result.buff, &index) < 0 </func> <func> - <name><ret>erlang_pid *</ret><nametext>ei_self(ei_cnode *ec)</nametext></name> + <name since=""><ret>erlang_pid *</ret><nametext>ei_self(ei_cnode *ec)</nametext></name> <fsummary>Retrieve the pid of the C-node.</fsummary> <desc> <p>Retrieves the pid of the C-node. Every C-node @@ -671,7 +1003,7 @@ if (ei_decode_version(result.buff, &index) < 0 </func> <func> - <name><ret>int</ret><nametext>ei_send(int fd, erlang_pid* to, char* buf, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_send(int fd, erlang_pid* to, char* buf, int len)</nametext></name> <fsummary>Send a message.</fsummary> <desc> <p>Sends an Erlang term to a process.</p> @@ -692,7 +1024,7 @@ if (ei_decode_version(result.buff, &index) < 0 </func> <func> - <name><ret>int</ret><nametext>ei_send_encoded(int fd, erlang_pid* to, char* buf, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_send_encoded(int fd, erlang_pid* to, char* buf, int len)</nametext></name> <fsummary>Obsolete function to send a message.</fsummary> <desc> <p>Works exactly as <c>ei_send</c>, the alternative name is retained for @@ -702,7 +1034,7 @@ if (ei_decode_version(result.buff, &index) < 0 </func> <func> - <name><ret>int</ret><nametext>ei_send_encoded_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_send_encoded_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name> <fsummary>Obsolete function to send a message with optional time-out. </fsummary> <desc> @@ -713,7 +1045,7 @@ if (ei_decode_version(result.buff, &index) < 0 </func> <func> - <name><ret>int</ret><nametext>ei_send_reg_encoded(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_send_reg_encoded(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name> <fsummary>Obsolete function to send a message to a registered name. </fsummary> <desc> @@ -741,7 +1073,7 @@ self->num = fd; </func> <func> - <name><ret>int</ret><nametext>ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, const char *buf, int len)</nametext></name> <fsummary>Obsolete function to send a message to a registered name with time-out.</fsummary> <desc> @@ -752,7 +1084,7 @@ self->num = fd; </func> <func> - <name><ret>int</ret><nametext>ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)</nametext></name> <fsummary>Send a message with optional time-out.</fsummary> <desc> <p>Equivalent to @@ -762,9 +1094,9 @@ self->num = fd; </func> <func> - <name><ret>const char *</ret><nametext>ei_thisnodename(ei_cnode *ec)</nametext></name> - <name><ret>const char *</ret><nametext>ei_thishostname(ei_cnode *ec)</nametext></name> - <name><ret>const char *</ret><nametext>ei_thisalivename(ei_cnode *ec)</nametext></name> + <name since=""><ret>const char *</ret><nametext>ei_thisnodename(ei_cnode *ec)</nametext></name> + <name since=""><ret>const char *</ret><nametext>ei_thishostname(ei_cnode *ec)</nametext></name> + <name since=""><ret>const char *</ret><nametext>ei_thisalivename(ei_cnode *ec)</nametext></name> <fsummary>Retrieve some values.</fsummary> <desc> <p>Can be used to retrieve information about @@ -779,7 +1111,7 @@ self->num = fd; </func> <func> - <name><ret>int</ret><nametext>ei_unpublish(ei_cnode *ec)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_unpublish(ei_cnode *ec)</nametext></name> <fsummary>Forcefully unpublish a node name.</fsummary> <desc> <p>Can be called by a process to unregister a @@ -802,7 +1134,7 @@ self->num = fd; </func> <func> - <name><ret>int</ret><nametext>ei_unpublish_tmo(ei_cnode *ec, unsigned timeout_ms)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_unpublish_tmo(ei_cnode *ec, unsigned timeout_ms)</nametext></name> <fsummary>Unpublish a node name with optional time-out.</fsummary> <desc> <p>Equivalent to diff --git a/lib/erl_interface/doc/src/ei_users_guide.xml b/lib/erl_interface/doc/src/ei_users_guide.xml index 0eed50b50b..2dfd99e35a 100644 --- a/lib/erl_interface/doc/src/ei_users_guide.xml +++ b/lib/erl_interface/doc/src/ei_users_guide.xml @@ -162,12 +162,20 @@ $ ld -L/usr/local/otp/lib/erl_interface-3.2.3/ </section> <section> - <title>Initializing the Erl_Interface Library</title> - <p>Before calling any of the other <c>Erl_Interface</c> functions, call - <c>erl_init()</c> exactly once to initialize the library. + <title>Initializing the Libraries</title> + <p> + Before calling any of the other functions in the <c>erl_interface</c> + and <c>ei</c> libraries, call <c>erl_init()</c> exactly once to initialize + both libraries. <c>erl_init()</c> takes two arguments. However, the arguments - are no longer used by <c>Erl_Interface</c> and are therefore to be - specified as <c>erl_init(NULL,0)</c>.</p> + are no longer used by <c>erl_interface</c> and are therefore to be + specified as <c>erl_init(NULL,0)</c>. + </p> + <p> + If you only use the <c>ei</c> library, instead initialize it by calling + <c>ei_init()</c> exactly once before calling any other functions in + the <c>ei</c> library. + </p> </section> <section> diff --git a/lib/erl_interface/doc/src/erl_connect.xml b/lib/erl_interface/doc/src/erl_connect.xml index 76ef6588c2..139ac9e2f0 100644 --- a/lib/erl_interface/doc/src/erl_connect.xml +++ b/lib/erl_interface/doc/src/erl_connect.xml @@ -49,7 +49,7 @@ <funcs> <func> - <name><ret>int</ret><nametext>erl_accept(listensock, conp)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_accept(listensock, conp)</nametext></name> <fsummary>Accept a connection.</fsummary> <type> <v>int listensock;</v> @@ -78,7 +78,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>erl_close_connection(fd)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_close_connection(fd)</nametext></name> <fsummary>Close a connection to an Erlang node.</fsummary> <type> <v>int fd;</v> @@ -95,8 +95,8 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>erl_connect(node)</nametext></name> - <name><ret>int</ret><nametext>erl_xconnect(addr, alive)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_connect(node)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_xconnect(addr, alive)</nametext></name> <fsummary>Establish a connection to an Erlang node.</fsummary> <type> <v>char *node, *alive;</v> @@ -149,8 +149,8 @@ erl_xconnect( &addr , ALIVE ); </func> <func> - <name><ret>int</ret><nametext>erl_connect_init(number, cookie, creation)</nametext></name> - <name><ret>int</ret><nametext>erl_connect_xinit(host, alive, node, addr, cookie, creation)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_connect_init(number, cookie, creation)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_connect_xinit(host, alive, node, addr, cookie, creation)</nametext></name> <fsummary>Initialize communication.</fsummary> <type> <v>int number;</v> @@ -246,7 +246,7 @@ if (!erl_connect_init(17, "samplecookiestring...", 0)) </func> <func> - <name><ret>int</ret><nametext>erl_publish(port)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_publish(port)</nametext></name> <fsummary>Publish a node name.</fsummary> <type> <v>int port;</v> @@ -277,7 +277,7 @@ if (!erl_connect_init(17, "samplecookiestring...", 0)) </func> <func> - <name><ret>int</ret><nametext>erl_receive(fd, bufp, bufsize)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_receive(fd, bufp, bufsize)</nametext></name> <fsummary>Receive a message.</fsummary> <type> <v>int fd;</v> @@ -316,7 +316,7 @@ if (!erl_connect_init(17, "samplecookiestring...", 0)) </func> <func> - <name><ret>int</ret><nametext>erl_receive_msg(fd, bufp, bufsize, emsg)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_receive_msg(fd, bufp, bufsize, emsg)</nametext></name> <fsummary>Receive and decode a message.</fsummary> <type> <v>int fd;</v> @@ -411,7 +411,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>erl_reg_send(fd, to, msg)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_reg_send(fd, to, msg)</nametext></name> <fsummary>Send a message to a registered name.</fsummary> <type> <v>int fd;</v> @@ -439,9 +439,9 @@ typedef struct { </func> <func> - <name><ret>ETERM *</ret><nametext>erl_rpc(fd, mod, fun, args)</nametext></name> - <name><ret>int</ret><nametext>erl_rpc_from(fd, timeout, emsg)</nametext></name> - <name><ret>int</ret><nametext>erl_rpc_to(fd, mod, fun, args)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_rpc(fd, mod, fun, args)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_rpc_from(fd, timeout, emsg)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_rpc_to(fd, mod, fun, args)</nametext></name> <fsummary>Remote Procedure Call.</fsummary> <type> <v>int fd, timeout;</v> @@ -511,7 +511,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>erl_send(fd, to, msg)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_send(fd, to, msg)</nametext></name> <fsummary>Send a message.</fsummary> <type> <v>int fd;</v> @@ -541,11 +541,11 @@ typedef struct { </func> <func> - <name><ret>const char *</ret><nametext>erl_thisalivename()</nametext></name> - <name><ret>const char *</ret><nametext>erl_thiscookie()</nametext></name> - <name><ret>short</ret><nametext>erl_thiscreation()</nametext></name> - <name><ret>const char *</ret><nametext>erl_thishostname()</nametext></name> - <name><ret>const char *</ret><nametext>erl_thisnodename()</nametext></name> + <name since=""><ret>const char *</ret><nametext>erl_thisalivename()</nametext></name> + <name since=""><ret>const char *</ret><nametext>erl_thiscookie()</nametext></name> + <name since=""><ret>short</ret><nametext>erl_thiscreation()</nametext></name> + <name since=""><ret>const char *</ret><nametext>erl_thishostname()</nametext></name> + <name since=""><ret>const char *</ret><nametext>erl_thisnodename()</nametext></name> <fsummary>Retrieve some values.</fsummary> <desc> <p>Retrieves information about @@ -556,7 +556,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>erl_unpublish(alive)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_unpublish(alive)</nametext></name> <fsummary>Forcefully unpublish a node name.</fsummary> <type> <v>char *alive;</v> @@ -583,7 +583,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>erl_xreceive_msg(fd, bufpp, bufsizep, emsg)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_xreceive_msg(fd, bufpp, bufsizep, emsg)</nametext></name> <fsummary>Receive and decode a message.</fsummary> <type> <v>int fd;</v> @@ -616,10 +616,10 @@ typedef struct { </func> <func> - <name><ret>struct hostent</ret><nametext>*erl_gethostbyaddr(addr, length, type)</nametext></name> - <name><ret>struct hostent</ret><nametext>*erl_gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, h_errnop)</nametext></name> - <name><ret>struct hostent</ret><nametext>*erl_gethostbyname(name)</nametext></name> - <name><ret>struct hostent</ret><nametext>*erl_gethostbyname_r(name, hostp, buffer, buflen, h_errnop)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyaddr(addr, length, type)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, h_errnop)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyname(name)</nametext></name> + <name since=""><ret>struct hostent *</ret><nametext>erl_gethostbyname_r(name, hostp, buffer, buflen, h_errnop)</nametext></name> <fsummary>Name lookup functions.</fsummary> <type> diff --git a/lib/erl_interface/doc/src/erl_error.xml b/lib/erl_interface/doc/src/erl_error.xml index 8139c9b343..6fac94e442 100644 --- a/lib/erl_interface/doc/src/erl_error.xml +++ b/lib/erl_interface/doc/src/erl_error.xml @@ -47,7 +47,7 @@ <funcs> <func> - <name><ret>void</ret><nametext>erl_err_msg(FormatStr, ... )</nametext></name> + <name since=""><ret>void</ret><nametext>erl_err_msg(FormatStr, ... )</nametext></name> <fsummary>Non-fatal error, and not system call error.</fsummary> <type> <v>const char *FormatStr;</v> @@ -59,7 +59,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_err_quit(FormatStr, ... )</nametext></name> + <name since=""><ret>void</ret><nametext>erl_err_quit(FormatStr, ... )</nametext></name> <fsummary>Fatal error, but not system call error.</fsummary> <type> <v>const char *FormatStr;</v> @@ -73,7 +73,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_err_ret(FormatStr, ... )</nametext></name> + <name since=""><ret>void</ret><nametext>erl_err_ret(FormatStr, ... )</nametext></name> <fsummary>Non-fatal system call error.</fsummary> <type> <v>const char *FormatStr;</v> @@ -86,7 +86,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_err_sys(FormatStr, ... )</nametext></name> + <name since=""><ret>void</ret><nametext>erl_err_sys(FormatStr, ... )</nametext></name> <fsummary>Fatal system call error.</fsummary> <type> <v>const char *FormatStr;</v> @@ -113,7 +113,7 @@ <funcs> <func> - <name><ret>volatile int</ret><nametext>erl_errno</nametext></name> + <name since=""><ret>volatile int</ret><nametext>erl_errno</nametext></name> <fsummary>Variable <c>erl_errno</c> contains the Erl_Interface error number. You can change the value if you wish. </fsummary> diff --git a/lib/erl_interface/doc/src/erl_eterm.xml b/lib/erl_interface/doc/src/erl_eterm.xml index 9a05196a70..070ed30dfe 100644 --- a/lib/erl_interface/doc/src/erl_eterm.xml +++ b/lib/erl_interface/doc/src/erl_eterm.xml @@ -142,7 +142,7 @@ <funcs> <func> - <name><ret>ETERM *</ret><nametext>erl_cons(head, tail)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_cons(head, tail)</nametext></name> <fsummary>Prepend a term to the head of a list.</fsummary> <type> <v>ETERM *head;</v> @@ -181,7 +181,7 @@ erl_free_compound(list); </func> <func> - <name><ret>ETERM *</ret><nametext>erl_copy_term(term)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_copy_term(term)</nametext></name> <fsummary>Create a copy of an Erlang term.</fsummary> <type> <v>ETERM *term;</v> @@ -193,7 +193,7 @@ erl_free_compound(list); </func> <func> - <name><ret>ETERM *</ret><nametext>erl_element(position, tuple)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_element(position, tuple)</nametext></name> <fsummary>Extract an element from an Erlang tuple.</fsummary> <type> <v>int position;</v> @@ -215,7 +215,7 @@ erl_free_compound(list); </func> <func> - <name><ret>ETERM *</ret><nametext>erl_hd(list)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_hd(list)</nametext></name> <fsummary>Extract the first element from a list.</fsummary> <type> <v>ETERM *list;</v> @@ -230,7 +230,7 @@ erl_free_compound(list); </func> <func> - <name><ret>void</ret><nametext>erl_init(NULL, 0)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_init(NULL, 0)</nametext></name> <fsummary>Initialization routine.</fsummary> <type> <v>void *NULL;</v> @@ -245,7 +245,7 @@ erl_free_compound(list); </func> <func> - <name><ret>int</ret><nametext>erl_iolist_length(list)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_iolist_length(list)</nametext></name> <fsummary>Return the length of an I/O list.</fsummary> <type> <v>ETERM *list;</v> @@ -262,7 +262,7 @@ erl_free_compound(list); </func> <func> - <name><ret>ETERM *</ret><nametext>erl_iolist_to_binary(term)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_iolist_to_binary(term)</nametext></name> <fsummary>Convert an I/O list to a binary.</fsummary> <type> <v>ETERM *list;</v> @@ -289,7 +289,7 @@ iohead ::= Binary </func> <func> - <name><ret>char *</ret><nametext>erl_iolist_to_string(list)</nametext></name> + <name since=""><ret>char *</ret><nametext>erl_iolist_to_string(list)</nametext></name> <fsummary>Convert an I/O list to a <c>NULL</c>-terminated string.</fsummary> <type> <v>ETERM *list;</v> @@ -312,7 +312,7 @@ iohead ::= Binary </func> <func> - <name><ret>int</ret><nametext>erl_length(list)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_length(list)</nametext></name> <fsummary>Determine the length of a list.</fsummary> <type> <v>ETERM *list;</v> @@ -328,7 +328,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_atom(string)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_atom(string)</nametext></name> <fsummary>Create an atom.</fsummary> <type> <v>const char *string;</v> @@ -355,7 +355,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_binary(bptr, size)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_binary(bptr, size)</nametext></name> <fsummary>Create a binary object.</fsummary> <type> <v>char *bptr;</v> @@ -378,7 +378,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_empty_list()</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_empty_list()</nametext></name> <fsummary>Create an empty Erlang list.</fsummary> <desc> <p>Creates and returns an empty Erlang list. @@ -388,7 +388,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_estring(string, len)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_estring(string, len)</nametext></name> <fsummary>Create an Erlang string.</fsummary> <type> <v>char *string;</v> @@ -408,7 +408,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_float(f)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_float(f)</nametext></name> <fsummary>Create an Erlang float.</fsummary> <type> <v>double f;</v> @@ -426,7 +426,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_int(n)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_int(n)</nametext></name> <fsummary>Create an Erlang integer.</fsummary> <type> <v>int n;</v> @@ -443,7 +443,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_list(array, arrsize)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_list(array, arrsize)</nametext></name> <fsummary>Create a list from an array.</fsummary> <type> <v>ETERM **array;</v> @@ -465,7 +465,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_long_ref(node, n1, n2, n3, creation)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_long_ref(node, n1, n2, n3, creation)</nametext></name> <fsummary>Create an Erlang reference.</fsummary> <type> <v>const char *node;</v> @@ -495,7 +495,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_pid(node, number, serial, creation)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_pid(node, number, serial, creation)</nametext></name> <fsummary>Create a process identifier.</fsummary> <type> <v>const char *node;</v> @@ -525,7 +525,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_port(node, number, creation)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_port(node, number, creation)</nametext></name> <fsummary>Create a port identifier.</fsummary> <type> <v>const char *node;</v> @@ -550,7 +550,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_ref(node, number, creation)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_ref(node, number, creation)</nametext></name> <fsummary>Create an old Erlang reference.</fsummary> <type> <v>const char *node;</v> @@ -578,7 +578,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_string(string)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_string(string)</nametext></name> <fsummary>Create a string.</fsummary> <type> <v>char *string;</v> @@ -593,7 +593,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_tuple(array, arrsize)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_tuple(array, arrsize)</nametext></name> <fsummary>Create an Erlang tuple from an array.</fsummary> <type> <v>ETERM **array;</v> @@ -621,7 +621,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_uint(n)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_uint(n)</nametext></name> <fsummary>Create an unsigned integer.</fsummary> <type> <v>unsigned int n;</v> @@ -638,7 +638,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_mk_var(name)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_mk_var(name)</nametext></name> <fsummary>Create an Erlang variable.</fsummary> <type> <v>char *name;</v> @@ -653,7 +653,7 @@ iohead ::= Binary </func> <func> - <name><ret>int</ret><nametext>erl_print_term(stream, term)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_print_term(stream, term)</nametext></name> <fsummary>Print an Erlang term.</fsummary> <type> <v>FILE *stream;</v> @@ -672,7 +672,7 @@ iohead ::= Binary </func> <func> - <name><ret>void</ret><nametext>erl_set_compat_rel(release_number)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_set_compat_rel(release_number)</nametext></name> <fsummary>Set the Erl_Interface library in compatibility mode.</fsummary> <type> <v>unsigned release_number;</v> @@ -706,7 +706,7 @@ iohead ::= Binary </func> <func> - <name><ret>int</ret><nametext>erl_size(term)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_size(term)</nametext></name> <fsummary>Return the arity of a tuple or binary.</fsummary> <type> <v>ETERM *term;</v> @@ -723,7 +723,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_tl(list)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_tl(list)</nametext></name> <fsummary>Extract the tail from a list.</fsummary> <type> <v>ETERM *list;</v> @@ -738,7 +738,7 @@ iohead ::= Binary </func> <func> - <name><ret>ETERM *</ret><nametext>erl_var_content(term, name)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_var_content(term, name)</nametext></name> <fsummary>Extract the content of a variable.</fsummary> <type> <v>ETERM *term;</v> diff --git a/lib/erl_interface/doc/src/erl_format.xml b/lib/erl_interface/doc/src/erl_format.xml index 5b8b7b5e78..b5e895c720 100644 --- a/lib/erl_interface/doc/src/erl_format.xml +++ b/lib/erl_interface/doc/src/erl_format.xml @@ -41,7 +41,7 @@ <funcs> <func> - <name><ret>ETERM *</ret><nametext>erl_format(FormatStr, ...)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_format(FormatStr, ...)</nametext></name> <fsummary>Create an Erlang term.</fsummary> <type> <v>char *FormatStr;</v> @@ -81,7 +81,7 @@ erl_format("[{name,~a},{age,~i},{data,~w}]", </func> <func> - <name><ret>int</ret><nametext>erl_match(Pattern, Term)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_match(Pattern, Term)</nametext></name> <fsummary>Perform pattern matching.</fsummary> <type> <v>ETERM *Pattern,*Term;</v> diff --git a/lib/erl_interface/doc/src/erl_global.xml b/lib/erl_interface/doc/src/erl_global.xml index 2fa0045adf..72d43e81d5 100644 --- a/lib/erl_interface/doc/src/erl_global.xml +++ b/lib/erl_interface/doc/src/erl_global.xml @@ -48,7 +48,7 @@ <funcs> <func> - <name><ret>char **</ret><nametext>erl_global_names(fd,count)</nametext></name> + <name since=""><ret>char **</ret><nametext>erl_global_names(fd,count)</nametext></name> <fsummary>Obtain list of global names.</fsummary> <type> <v>int fd;</v> @@ -79,7 +79,7 @@ </func> <func> - <name><ret>int</ret><nametext>erl_global_register(fd,name,pid)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_global_register(fd,name,pid)</nametext></name> <fsummary>Register a name in global.</fsummary> <type> <v>int fd;</v> @@ -103,7 +103,7 @@ </func> <func> - <name><ret>int</ret><nametext>erl_global_unregister(fd,name)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_global_unregister(fd,name)</nametext></name> <fsummary>Unregister a name from global.</fsummary> <type> <v>int fd;</v> @@ -122,7 +122,7 @@ </func> <func> - <name><ret>ETERM *</ret><nametext>erl_global_whereis(fd,name,node)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_global_whereis(fd,name,node)</nametext></name> <fsummary>Look up a name in global.</fsummary> <type> <v>int fd;</v> diff --git a/lib/erl_interface/doc/src/erl_malloc.xml b/lib/erl_interface/doc/src/erl_malloc.xml index c0eebc29e9..aae3b7e078 100644 --- a/lib/erl_interface/doc/src/erl_malloc.xml +++ b/lib/erl_interface/doc/src/erl_malloc.xml @@ -41,7 +41,7 @@ <funcs> <func> - <name><ret>ETERM *</ret><nametext>erl_alloc_eterm(etype)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_alloc_eterm(etype)</nametext></name> <fsummary>Allocate an ETERM structure.</fsummary> <type> <v>unsigned char etype;</v> @@ -89,7 +89,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_eterm_release(void)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_eterm_release(void)</nametext></name> <fsummary>Clear the ETERM freelist.</fsummary> <desc> <p>Clears the freelist, where blocks are placed when they are @@ -99,7 +99,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_eterm_statistics(allocated, freed)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_eterm_statistics(allocated, freed)</nametext></name> <fsummary>Report term allocation statistics.</fsummary> <type> <v>long *allocated;</v> @@ -127,7 +127,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_free(ptr)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_free(ptr)</nametext></name> <fsummary>Free some memory.</fsummary> <type> <v>void *ptr;</v> @@ -139,7 +139,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_free_array(array, size)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_free_array(array, size)</nametext></name> <fsummary>Free an array of ETERM structures.</fsummary> <type> <v>ETERM **array;</v> @@ -156,7 +156,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_free_compound(t)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_free_compound(t)</nametext></name> <fsummary>Free an array of ETERM structures.</fsummary> <type> <v>ETERM *t;</v> @@ -179,7 +179,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_free_term(t)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_free_term(t)</nametext></name> <fsummary>Free an ETERM structure.</fsummary> <type> <v>ETERM *t;</v> @@ -190,7 +190,7 @@ </func> <func> - <name><ret>void</ret><nametext>erl_malloc(size)</nametext></name> + <name since=""><ret>void</ret><nametext>erl_malloc(size)</nametext></name> <fsummary>Allocate some memory.</fsummary> <type> <v>long size;</v> diff --git a/lib/erl_interface/doc/src/erl_marshal.xml b/lib/erl_interface/doc/src/erl_marshal.xml index 2ad658f78b..1a6d3bb43c 100644 --- a/lib/erl_interface/doc/src/erl_marshal.xml +++ b/lib/erl_interface/doc/src/erl_marshal.xml @@ -42,7 +42,7 @@ <funcs> <func> - <name><ret>int</ret><nametext>erl_compare_ext(bufp1, bufp2)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_compare_ext(bufp1, bufp2)</nametext></name> <fsummary>Compare encoded byte sequences.</fsummary> <type> <v>unsigned char *bufp1,*bufp2;</v> @@ -62,8 +62,8 @@ </func> <func> - <name><ret>ETERM *</ret><nametext>erl_decode(bufp)</nametext></name> - <name><ret>ETERM *</ret><nametext>erl_decode_buf(bufpp)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_decode(bufp)</nametext></name> + <name since=""><ret>ETERM *</ret><nametext>erl_decode_buf(bufpp)</nametext></name> <fsummary>Convert a term from Erlang external format.</fsummary> <type> <v>unsigned char *bufp;</v> @@ -102,8 +102,8 @@ </func> <func> - <name><ret>int</ret><nametext>erl_encode(term, bufp)</nametext></name> - <name><ret>int</ret><nametext>erl_encode_buf(term, bufpp)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_encode(term, bufp)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_encode_buf(term, bufpp)</nametext></name> <fsummary>Convert a term into Erlang external format.</fsummary> <type> <v>ETERM *term;</v> @@ -179,7 +179,7 @@ </func> <func> - <name><ret>int</ret><nametext>erl_ext_size(bufp)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_ext_size(bufp)</nametext></name> <fsummary>Count elements in encoded term.</fsummary> <type> <v>unsigned char *bufp;</v> @@ -190,7 +190,7 @@ </func> <func> - <name><ret>unsigned char</ret><nametext>erl_ext_type(bufp)</nametext></name> + <name since=""><ret>unsigned char</ret><nametext>erl_ext_type(bufp)</nametext></name> <fsummary>Determine type of an encoded byte sequence.</fsummary> <type> <v>unsigned char *bufp;</v> @@ -228,7 +228,7 @@ </func> <func> - <name><ret>unsigned char *</ret><nametext>erl_peek_ext(bufp, pos)</nametext></name> + <name since=""><ret>unsigned char *</ret><nametext>erl_peek_ext(bufp, pos)</nametext></name> <fsummary>Step over encoded term.</fsummary> <type> <v>unsigned char *bufp;</v> @@ -252,7 +252,7 @@ </func> <func> - <name><ret>int</ret><nametext>erl_term_len(t)</nametext></name> + <name since=""><ret>int</ret><nametext>erl_term_len(t)</nametext></name> <fsummary>Determine encoded size of term.</fsummary> <type> <v>ETERM *t;</v> diff --git a/lib/erl_interface/doc/src/registry.xml b/lib/erl_interface/doc/src/registry.xml index 6d70fb3475..1c90c5c9dd 100644 --- a/lib/erl_interface/doc/src/registry.xml +++ b/lib/erl_interface/doc/src/registry.xml @@ -44,7 +44,7 @@ <funcs> <func> - <name><ret>int</ret><nametext>ei_reg_close(reg)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_close(reg)</nametext></name> <fsummary>Close a registry.</fsummary> <type> <v>ei_reg *reg;</v> @@ -59,7 +59,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_delete(reg,key)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_delete(reg,key)</nametext></name> <fsummary>Delete an object from the registry.</fsummary> <type> <v>ei_reg *reg;</v> @@ -85,7 +85,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_dump(fd,reg,mntab,flags)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_dump(fd,reg,mntab,flags)</nametext></name> <fsummary>Back up a registry to Mnesia.</fsummary> <type> <v>int fd;</v> @@ -125,7 +125,7 @@ </func> <func> - <name><ret>double</ret><nametext>ei_reg_getfval(reg,key)</nametext></name> + <name since=""><ret>double</ret><nametext>ei_reg_getfval(reg,key)</nametext></name> <fsummary>Get a floating point object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -151,7 +151,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_getival(reg,key)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_getival(reg,key)</nametext></name> <fsummary>Get an integer object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -177,7 +177,7 @@ </func> <func> - <name><ret>const void *</ret><nametext>ei_reg_getpval(reg,key,size)</nametext></name> + <name since=""><ret>const void *</ret><nametext>ei_reg_getpval(reg,key,size)</nametext></name> <fsummary>Get a binary object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -207,7 +207,7 @@ </func> <func> - <name><ret>const char *</ret><nametext>ei_reg_getsval(reg,key)</nametext></name> + <name since=""><ret>const char *</ret><nametext>ei_reg_getsval(reg,key)</nametext></name> <fsummary>Get a string object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -232,7 +232,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_getval(reg,key,flags,v,...)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_getval(reg,key,flags,v,...)</nametext></name> <fsummary>Get any object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -278,7 +278,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_markdirty(reg,key)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_markdirty(reg,key)</nametext></name> <fsummary>Mark an object as dirty.</fsummary> <type> <v>ei_reg *reg;</v> @@ -305,7 +305,7 @@ </func> <func> - <name><ret>ei_reg *</ret><nametext>ei_reg_open(size)</nametext></name> + <name since=""><ret>ei_reg *</ret><nametext>ei_reg_open(size)</nametext></name> <fsummary>Create and open a registry.</fsummary> <type> <v>int size;</v> @@ -326,7 +326,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_purge(reg)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_purge(reg)</nametext></name> <fsummary>Remove deleted objects.</fsummary> <type> <v>ei_reg *reg;</v> @@ -346,7 +346,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_resize(reg,newsize)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_resize(reg,newsize)</nametext></name> <fsummary>Resize a registry.</fsummary> <type> <v>ei_reg *reg;</v> @@ -363,7 +363,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_restore(fd,reg,mntab)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_restore(fd,reg,mntab)</nametext></name> <fsummary>Restore a registry from Mnesia.</fsummary> <type> <v>int fd;</v> @@ -399,7 +399,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_setfval(reg,key,f)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_setfval(reg,key,f)</nametext></name> <fsummary>Assign a floating point object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -424,7 +424,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_setival(reg,key,i)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_setival(reg,key,i)</nametext></name> <fsummary>Assign an integer object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -448,7 +448,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_setpval(reg,key,p,size)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_setpval(reg,key,p,size)</nametext></name> <fsummary>Assign a binary object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -479,7 +479,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_setsval(reg,key,s)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_setsval(reg,key,s)</nametext></name> <fsummary>Assign a string object.</fsummary> <type> <v>ei_reg *reg;</v> @@ -507,7 +507,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_setval(reg,key,flags,v,...)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_setval(reg,key,flags,v,...)</nametext></name> <fsummary>Assign a value to any object type.</fsummary> <type> <v>ei_reg *reg;</v> @@ -552,7 +552,7 @@ </func> <func> - <name><ret>int</ret><nametext>ei_reg_stat(reg,key,obuf)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_stat(reg,key,obuf)</nametext></name> <fsummary>Get object information.</fsummary> <type> <v>ei_reg *reg;</v> @@ -590,7 +590,7 @@ struct ei_reg_stat { </func> <func> - <name><ret>int</ret><nametext>ei_reg_tabstat(reg,obuf)</nametext></name> + <name since=""><ret>int</ret><nametext>ei_reg_tabstat(reg,obuf)</nametext></name> <fsummary>Get registry information.</fsummary> <type> <v>ei_reg *reg;</v> diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h index 948f89be85..ca4960b252 100644 --- a/lib/erl_interface/include/ei.h +++ b/lib/erl_interface/include/ei.h @@ -35,6 +35,9 @@ #include <winsock2.h> #include <windows.h> #include <winbase.h> +typedef LONG_PTR ssize_t; /* Sigh... */ +#else +#include <sys/types.h> /* ssize_t */ #endif #include <stdio.h> /* Need type FILE */ @@ -286,6 +289,31 @@ typedef struct { char nodename[MAXNODELEN+1]; } ErlConnect; +#define EI_SCLBK_INF_TMO (~((unsigned) 0)) + +#define EI_SCLBK_FLG_FULL_IMPL (1 << 0) + +typedef struct { + int flags; + + int (*socket)(void **ctx, void *setup_ctx); + int (*close)(void *ctx); + int (*listen)(void *ctx, void *addr, int *len, int backlog); + int (*accept)(void **ctx, void *addr, int *len, unsigned tmo); + int (*connect)(void *ctx, void *addr, int len, unsigned tmo); + int (*writev)(void *ctx, const void *iov, int iovcnt, ssize_t *len, unsigned tmo); + int (*write)(void *ctx, const char *buf, ssize_t *len, unsigned tmo); + int (*read)(void *ctx, char *buf, ssize_t *len, unsigned tmo); + + int (*handshake_packet_header_size)(void *ctx, int *sz); + int (*connect_handshake_complete)(void *ctx); + int (*accept_handshake_complete)(void *ctx); + int (*get_fd)(void *ctx, int *fd); + + /* end of version 1 */ + +} ei_socket_callbacks; + typedef struct ei_cnode_s { char thishostname[EI_MAXHOSTNAMELEN+1]; char thisnodename[MAXNODELEN+1]; @@ -295,6 +323,8 @@ typedef struct ei_cnode_s { char ei_connect_cookie[EI_MAX_COOKIE_SIZE+1]; short creation; erlang_pid self; + ei_socket_callbacks *cbs; + void *setup_context; } ei_cnode; typedef struct in_addr *Erl_IpAddr; @@ -308,7 +338,6 @@ typedef struct ei_x_buff_TAG { int index; } ei_x_buff; - /* -------------------------------------------------------------------- */ /* Function definitions (listed in same order as documentation) */ /* -------------------------------------------------------------------- */ @@ -322,6 +351,16 @@ int ei_connect_xinit (ei_cnode* ec, const char *thishostname, Erl_IpAddr thisipaddr, const char *cookie, const short creation); +int ei_connect_init_ussi(ei_cnode* ec, const char* this_node_name, + const char *cookie, short creation, + ei_socket_callbacks *cbs, int cbs_sz, + void *setup_context); +int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname, + const char *thisalivename, const char *thisnodename, + Erl_IpAddr thisipaddr, const char *cookie, + const short creation, ei_socket_callbacks *cbs, + int cbs_sz, void *setup_context); + int ei_connect(ei_cnode* ec, char *nodename); int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms); int ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename); @@ -348,11 +387,15 @@ int ei_rpc_from(ei_cnode* ec, int fd, int timeout, erlang_msg* msg, int ei_publish(ei_cnode* ec, int port); int ei_publish_tmo(ei_cnode* ec, int port, unsigned ms); +int ei_listen(ei_cnode *ec, int *port, int backlog); +int ei_xlisten(ei_cnode *ec, Erl_IpAddr adr, int *port, int backlog); int ei_accept(ei_cnode* ec, int lfd, ErlConnect *conp); int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms); int ei_unpublish(ei_cnode* ec); int ei_unpublish_tmo(const char *alive, unsigned ms); +int ei_close_connection(int fd); + const char *ei_thisnodename(const ei_cnode* ec); const char *ei_thishostname(const ei_cnode* ec); const char *ei_thisalivename(const ei_cnode* ec); @@ -626,6 +669,8 @@ struct ei_reg_tabstat { }; +int ei_init(void); + /* -------------------------------------------------------------------- */ /* XXXXXXXXXXX */ /* -------------------------------------------------------------------- */ diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in index 614e7325a9..b0bb9bfadf 100644 --- a/lib/erl_interface/src/Makefile.in +++ b/lib/erl_interface/src/Makefile.in @@ -31,12 +31,11 @@ .PHONY : debug opt release clean distclean depend -TARGET = @TARGET@ - # ---------------------------------------------------- # Application version and release dir specification # ---------------------------------------------------- include ../vsn.mk +include $(ERL_TOP)/make/target.mk include $(TARGET)/eidefs.mk include $(ERL_TOP)/make/output.mk @@ -417,7 +416,8 @@ MISCSRC = \ misc/eimd5.c \ misc/get_type.c \ misc/show_msg.c \ - misc/ei_compat.c + misc/ei_compat.c \ + misc/ei_init.c REGISTRYSRC = \ registry/hash_dohash.c \ diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 9df4fa3b6c..7a304e6d4f 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -42,10 +42,8 @@ #include <inetLib.h> #include <unistd.h> -#include <sys/types.h> #include <sys/times.h> #include <unistd.h> -#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> @@ -55,7 +53,6 @@ #else /* some other unix */ #include <unistd.h> -#include <sys/types.h> #include <sys/times.h> #if TIME_WITH_SYS_TIME @@ -84,6 +81,7 @@ #include <string.h> #include <errno.h> #include <ctype.h> +#include <stddef.h> #include "eiext.h" #include "ei_portio.h" @@ -98,11 +96,16 @@ #include "ei_epmd.h" #include "ei_internal.h" +static int ei_connect_initialized = 0; int ei_tracelevel = 0; #define COOKIE_FILE "/.erlang.cookie" #define EI_MAX_HOME_PATH 1024 +#define EI_SOCKET_CALLBACKS_SZ_V1 \ + (offsetof(ei_socket_callbacks, get_fd) \ + + sizeof(int (*)(void *))) + /* FIXME why not macro? */ static char *null_cookie = ""; @@ -113,35 +116,51 @@ static int get_home(char *buf, int size); static unsigned gen_challenge(void); static void gen_digest(unsigned challenge, char cookie[], unsigned char digest[16]); -static int send_status(int fd, char *status, unsigned ms); -static int recv_status(int fd, unsigned ms); -static int send_challenge(int fd, char *nodename, - unsigned challenge, unsigned version, unsigned ms); -static int recv_challenge(int fd, unsigned *challenge, - unsigned *version, - unsigned *flags, ErlConnect *namebuf, unsigned ms); -static int send_challenge_reply(int fd, unsigned char digest[16], +static int send_status(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, char *status, unsigned ms); +static int recv_status(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned ms); +static int send_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + char *nodename, unsigned challenge, + unsigned version, unsigned ms); +static int recv_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + unsigned *challenge, unsigned *version, + unsigned *flags, char *namebuf, unsigned ms); +static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned char digest[16], unsigned challenge, unsigned ms); -static int recv_challenge_reply(int fd, - unsigned our_challenge, +static int recv_challenge_reply(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned our_challenge, char cookie[], unsigned *her_challenge, unsigned ms); -static int send_challenge_ack(int fd, unsigned char digest[16], unsigned ms); -static int recv_challenge_ack(int fd, - unsigned our_challenge, +static int send_challenge_ack(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned char digest[16], + unsigned ms); +static int recv_challenge_ack(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned our_challenge, char cookie[], unsigned ms); -static int send_name(int fd, char *nodename, - unsigned version, unsigned ms); +static int send_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + char *nodename, unsigned version, unsigned ms); -/* Common for both handshake types */ -static int recv_name(int fd, - unsigned *version, - unsigned *flags, ErlConnect *namebuf, unsigned ms); +static int recv_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + unsigned *version, unsigned *flags, char *namebuf, + unsigned ms); static struct hostent* dyn_gethostbyname_r(const char *name, struct hostent *hostp, char **buffer_p, int buflen, int *h_errnop); +static void abort_connection(ei_socket_callbacks *cbs, void *ctx); +static int close_connection(ei_socket_callbacks *cbs, void *ctx, int fd); + +static char * +estr(int e) +{ + char *str = strerror(e); + if (!str) + return "unknown error"; + return str; +} /*************************************************************************** @@ -154,25 +173,208 @@ dyn_gethostbyname_r(const char *name, struct hostent *hostp, char **buffer_p, typedef struct ei_socket_info_s { int socket; + ei_socket_callbacks *cbs; + void *ctx; int dist_version; ei_cnode cnode; /* A copy, not a pointer. We don't know when freed */ char cookie[EI_MAX_COOKIE_SIZE+1]; } ei_socket_info; +/*************************************************************************** + * + * XXX + * + ***************************************************************************/ + +#ifndef ETHR_HAVE___atomic_compare_exchange_n +# define ETHR_HAVE___atomic_compare_exchange_n 0 +#endif +#ifndef ETHR_HAVE___atomic_load_n +# define ETHR_HAVE___atomic_load_n 0 +#endif +#ifndef ETHR_HAVE___atomic_store_n +# define ETHR_HAVE___atomic_store_n 0 +#endif + +#if defined(_REENTRANT) \ + && (!(ETHR_HAVE___atomic_compare_exchange_n & SIZEOF_VOID_P) \ + || !(ETHR_HAVE___atomic_load_n & SIZEOF_VOID_P) \ + || !(ETHR_HAVE___atomic_store_n & SIZEOF_VOID_P)) +# undef EI_DISABLE_SEQ_SOCKET_INFO +# define EI_DISABLE_SEQ_SOCKET_INFO +#endif + +#ifdef __WIN32__ +# undef EI_DISABLE_SEQ_SOCKET_INFO +# define EI_DISABLE_SEQ_SOCKET_INFO +#endif + +#ifndef EI_DISABLE_SEQ_SOCKET_INFO + +#ifdef _REENTRANT + +#define EI_ATOMIC_CMPXCHG_ACQ_REL(VARP, XCHGP, NEW) \ + __atomic_compare_exchange_n((VARP), (XCHGP), (NEW), 0, \ + __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) +#define EI_ATOMIC_LOAD_ACQ(VARP) \ + __atomic_load_n((VARP), __ATOMIC_ACQUIRE) +#define EI_ATOMIC_STORE_REL(VARP, NEW) \ + __atomic_store_n((VARP), (NEW), __ATOMIC_RELEASE) + +#else /* ! _REENTRANT */ + +#define EI_ATOMIC_CMPXCHG_ACQ_REL(VARP, XCHGP, NEW) \ + (*(VARP) == *(XCHGP) \ + ? ((*(VARP) = (NEW)), !0) \ + : ((*(XCHGP) = *(VARP)), 0)) +#define EI_ATOMIC_LOAD_ACQ(VARP) (*(VARP)) +#define EI_ATOMIC_STORE_REL(VARP, NEW) (*(VARP) = (NEW)) + +#endif /* ! _REENTRANT */ + +#define EI_SOCKET_INFO_SEG_BITS 5 +#define EI_SOCKET_INFO_SEG_SIZE (1 << EI_SOCKET_INFO_SEG_BITS) +#define EI_SOCKET_INFO_SEG_MASK (EI_SOCKET_INFO_SEG_SIZE - 1) + +typedef struct { + int max_fds; + ei_socket_info *segments[1]; /* Larger in reality... */ +} ei_socket_info_data__; + +static ei_socket_info_data__ *socket_info_data = NULL; + +static int init_socket_info(int late) +{ + int max_fds; + int i; + size_t segments_len; + ei_socket_info_data__ *info_data, *xchg; + + if (EI_ATOMIC_LOAD_ACQ(&socket_info_data) != NULL) + return 0; /* Already initialized... */ + +#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) + max_fds = sysconf(_SC_OPEN_MAX); +#else + max_fds = 1024; +#endif + + if (max_fds < 0) + return EIO; + + segments_len = ((max_fds-1)/EI_SOCKET_INFO_SEG_SIZE + 1); + + info_data = malloc(sizeof(ei_socket_info_data__) + + (sizeof(ei_socket_info *)*(segments_len-1))); + if (!info_data) + return ENOMEM; + + info_data->max_fds = max_fds; + for (i = 0; i < segments_len; i++) + info_data->segments[i] = NULL; + + xchg = NULL; + if (!EI_ATOMIC_CMPXCHG_ACQ_REL(&socket_info_data, &xchg, info_data)) + free(info_data); /* Already initialized... */ + + return 0; +} + +static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec, + ei_socket_callbacks *cbs, void *ctx) +{ + int six; + ei_socket_info *seg, *si; + int socket; + + if (fd < 0 || socket_info_data->max_fds <= fd) + return -1; + + socket = fd; + six = fd >> EI_SOCKET_INFO_SEG_BITS; + seg = EI_ATOMIC_LOAD_ACQ(&socket_info_data->segments[six]); + + if (!seg) { + ei_socket_info *xchg; + int i; + seg = malloc(sizeof(ei_socket_info)*EI_SOCKET_INFO_SEG_SIZE); + if (!seg) + return -1; + for (i = 0; i < EI_SOCKET_INFO_SEG_SIZE; i++) { + seg[i].socket = -1; + } + + xchg = NULL; + if (!EI_ATOMIC_CMPXCHG_ACQ_REL(&socket_info_data->segments[six], &xchg, seg)) { + free(seg); + seg = xchg; + } + } + + si = &seg[fd & EI_SOCKET_INFO_SEG_MASK]; + + if (dist_version < 0) { + socket = -1; + si->cbs = NULL; + si->ctx = NULL; + } + else { + si->dist_version = dist_version; + si->cnode = *ec; + si->cbs = cbs; + si->ctx = ctx; + strcpy(si->cookie, cookie); + } + + EI_ATOMIC_STORE_REL(&si->socket, socket); + + return 0; +} + +static ei_socket_info* get_ei_socket_info(int fd) +{ + int six, socket; + ei_socket_info *seg, *si; + + if (fd < 0 || socket_info_data->max_fds <= fd) + return NULL; + + six = fd >> EI_SOCKET_INFO_SEG_BITS; + seg = EI_ATOMIC_LOAD_ACQ(&socket_info_data->segments[six]); + + if (!seg) + return NULL; + + si = &seg[fd & EI_SOCKET_INFO_SEG_MASK]; + socket = EI_ATOMIC_LOAD_ACQ(&si->socket); + if (socket != fd) + return NULL; + return si; +} + +#else /* EI_DISABLE_SEQ_SOCKET_INFO */ + int ei_n_sockets = 0, ei_sz_sockets = 0; ei_socket_info *ei_sockets = NULL; + #ifdef _REENTRANT ei_mutex_t* ei_sockets_lock = NULL; #endif /* _REENTRANT */ +static int init_socket_info(int late) +{ +#ifdef _REENTRANT + if (late) + return ENOTSUP; /* Refuse doing unsafe initialization... */ + ei_sockets_lock = ei_mutex_create(); + if (!ei_sockets_lock) + return ENOMEM; +#endif /* _REENTRANT */ + return 0; +} -/*************************************************************************** - * - * XXX - * - ***************************************************************************/ - -static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec) +static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec, + ei_socket_callbacks *cbs, void *ctx) { int i; @@ -182,11 +384,13 @@ static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode * for (i = 0; i < ei_n_sockets; ++i) { if (ei_sockets[i].socket == fd) { if (dist_version == -1) { - memmove(&ei_sockets[i], &ei_sockets[i+1], + memmove(&ei_sockets[i], &ei_sockets[i+1], sizeof(ei_sockets[0])*(ei_n_sockets-i-1)); } else { ei_sockets[i].dist_version = dist_version; /* Copy the content, see ei_socket_info */ + ei_sockets[i].cbs = cbs; + ei_sockets[i].ctx = ctx; ei_sockets[i].cnode = *ec; strcpy(ei_sockets[i].cookie, cookie); } @@ -209,7 +413,9 @@ static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode * } ei_sockets[ei_n_sockets].socket = fd; ei_sockets[ei_n_sockets].dist_version = dist_version; - ei_sockets[i].cnode = *ec; + ei_sockets[ei_n_sockets].cnode = *ec; + ei_sockets[ei_n_sockets].cbs = cbs; + ei_sockets[ei_n_sockets].ctx = ctx; strcpy(ei_sockets[ei_n_sockets].cookie, cookie); ++ei_n_sockets; } @@ -219,14 +425,6 @@ static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode * return 0; } -#if 0 -/* FIXME not used ?! */ -static int remove_ei_socket_info(int fd, int dist_version, char* cookie) -{ - return put_ei_socket_info(fd, -1, NULL); -} -#endif - static ei_socket_info* get_ei_socket_info(int fd) { int i; @@ -248,6 +446,13 @@ static ei_socket_info* get_ei_socket_info(int fd) return NULL; } +#endif /* EI_DISABLE_SEQ_SOCKET_INFO */ + +static int remove_ei_socket_info(int fd) +{ + return put_ei_socket_info(fd, -1, NULL, NULL, NULL, NULL); +} + ei_cnode *ei_fd_to_cnode(int fd) { ei_socket_info *sockinfo = get_ei_socket_info(fd); @@ -255,6 +460,19 @@ ei_cnode *ei_fd_to_cnode(int fd) return &sockinfo->cnode; } +int ei_get_cbs_ctx__(ei_socket_callbacks **cbs, void **ctx, int fd) +{ + ei_socket_info *sockinfo = get_ei_socket_info(fd); + if (sockinfo) { + *cbs = sockinfo->cbs; + *ctx = sockinfo->ctx; + return 0; + } + + *cbs = NULL; + *ctx = NULL; + return EBADF; +} /*************************************************************************** * Get/Set tracelevel @@ -333,21 +551,6 @@ const char *ei_getfdcookie(int fd) return r; } -/* call with cookie to set value to use on descriptor fd, -* or specify NULL to use default -*/ -/* FIXME why defined but not used? */ -#if 0 -static int ei_setfdcookie(ei_cnode* ec, int fd, char *cookie) -{ - int dist_version = ei_distversion(fd); - - if (cookie == NULL) - cookie = ec->ei_connect_cookie; - return put_ei_socket_info(fd, dist_version, cookie); -} -#endif - static int get_int32(unsigned char *s) { return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] )); @@ -400,34 +603,62 @@ static int initWinSock(void) } #endif +static int init_connect(int late) +{ + int error; + + /* + * 'late' is non-zero when not called via ei_init(). Such a + * call is not supported, but we for now save the day if + * it easy to do so; otherwise, return ENOTSUP. + */ + +#ifdef __WIN32__ + if (!initWinSock()) { + EI_TRACE_ERR0("ei_init_connect","can't initiate winsock"); + return EIO; + } +#endif /* win32 */ + + error = init_socket_info(late); + if (error) { + EI_TRACE_ERR0("ei_init_connect","can't initiate socket info"); + return error; + } + + ei_connect_initialized = !0; + return 0; +} + +int ei_init_connect(void) +{ + return init_connect(0); +} + /* * Perhaps run this routine instead of ei_connect_init/2 ? * Initailize by setting: * thishostname, thisalivename, thisnodename and thisipaddr */ -int ei_connect_xinit(ei_cnode* ec, const char *thishostname, - const char *thisalivename, const char *thisnodename, - Erl_IpAddr thisipaddr, const char *cookie, - const short creation) +int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname, + const char *thisalivename, const char *thisnodename, + Erl_IpAddr thisipaddr, const char *cookie, + const short creation, ei_socket_callbacks *cbs, + int cbs_sz, void *setup_context) { char *dbglevel; - -/* FIXME this code was enabled for 'erl'_connect_xinit(), why not here? */ -#if 0 -#ifdef __WIN32__ - if (!initWinSock()) { - EI_TRACE_ERR0("ei_connect_xinit","can't initiate winsock"); - return ERL_ERROR; - } -#endif -#endif -#ifdef _REENTRANT - if (ei_sockets_lock == NULL) { - ei_sockets_lock = ei_mutex_create(); - } -#endif /* _REENTRANT */ + if (!ei_connect_initialized) + init_connect(!0); + if (cbs != &ei_default_socket_callbacks) + EI_SET_HAVE_PLUGIN_SOCKET_IMPL__; + + if (cbs_sz < EI_SOCKET_CALLBACKS_SZ_V1) { + EI_TRACE_ERR0("ei_connect_xinit","invalid size of ei_socket_callbacks struct"); + return ERL_ERROR; + } + ec->creation = creation & 0x3; /* 2 bits */ if (cookie) { @@ -469,6 +700,9 @@ int ei_connect_xinit(ei_cnode* ec, const char *thishostname, ec->self.serial = 0; ec->self.creation = creation & 0x3; /* 2 bits */ + ec->cbs = cbs; + ec->setup_context = setup_context; + if ((dbglevel = getenv("EI_TRACELEVEL")) != NULL || (dbglevel = getenv("ERL_DEBUG_DIST")) != NULL) ei_tracelevel = atoi(dbglevel); @@ -476,14 +710,27 @@ int ei_connect_xinit(ei_cnode* ec, const char *thishostname, return 0; } +int ei_connect_xinit(ei_cnode* ec, const char *thishostname, + const char *thisalivename, const char *thisnodename, + Erl_IpAddr thisipaddr, const char *cookie, + const short creation) +{ + return ei_connect_xinit_ussi(ec, thishostname, thisalivename, thisnodename, + thisipaddr, cookie, creation, + &ei_default_socket_callbacks, + sizeof(ei_default_socket_callbacks), + NULL); +} /* * Initialize by set: thishostname, thisalivename, * thisnodename and thisipaddr. At success return 0, * otherwise return -1. */ -int ei_connect_init(ei_cnode* ec, const char* this_node_name, - const char *cookie, short creation) +int ei_connect_init_ussi(ei_cnode* ec, const char* this_node_name, + const char *cookie, short creation, + ei_socket_callbacks *cbs, int cbs_sz, + void *setup_context) { char thishostname[EI_MAXHOSTNAMELEN+1]; char thisnodename[MAXNODELEN+1]; @@ -494,17 +741,8 @@ int ei_connect_init(ei_cnode* ec, const char* this_node_name, int ei_h_errno; int res; -#ifdef __WIN32__ - if (!initWinSock()) { - EI_TRACE_ERR0("ei_connect_xinit","can't initiate winsock"); - return ERL_ERROR; - } -#endif /* win32 */ -#ifdef _REENTRANT - if (ei_sockets_lock == NULL) { - ei_sockets_lock = ei_mutex_create(); - } -#endif /* _REENTRANT */ + if (!ei_connect_initialized) + init_connect(!0); /* gethostname requires len to be max(hostname) + 1 */ if (gethostname(thishostname, EI_MAXHOSTNAMELEN+1) == -1) { @@ -561,43 +799,22 @@ int ei_connect_init(ei_cnode* ec, const char* this_node_name, sprintf(thisnodename, "%s@%s", this_node_name, hp->h_name); } } - res = ei_connect_xinit(ec, thishostname, thisalivename, thisnodename, - (struct in_addr *)*hp->h_addr_list, cookie, creation); + res = ei_connect_xinit_ussi(ec, thishostname, thisalivename, thisnodename, + (struct in_addr *)*hp->h_addr_list, cookie, creation, + cbs, cbs_sz, setup_context); if (buf != buffer) free(buf); return res; } - -/* connects to port at ip-address ip_addr -* and returns fd to socket -* port has to be in host byte order -*/ -static int cnct(uint16 port, struct in_addr *ip_addr, int addr_len, unsigned ms) +int ei_connect_init(ei_cnode* ec, const char* this_node_name, + const char *cookie, short creation) { - int s, res; - struct sockaddr_in iserv_addr; - - if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - erl_errno = errno; - return ERL_ERROR; - } - - memset((char*)&iserv_addr, 0, sizeof(struct sockaddr_in)); - memcpy((char*)&iserv_addr.sin_addr, (char*)ip_addr, addr_len); - iserv_addr.sin_family = AF_INET; - iserv_addr.sin_port = htons(port); - - if ((res = ei_connect_t(s, (struct sockaddr*)&iserv_addr, - sizeof(iserv_addr),ms)) < 0) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - closesocket(s); - return ERL_ERROR; - } - - return s; -} /* cnct */ - + return ei_connect_init_ussi(ec, this_node_name, cookie, creation, + &ei_default_socket_callbacks, + sizeof(ei_default_socket_callbacks), + NULL); +} /* * Same as ei_gethostbyname_r, but also handles ERANGE error @@ -758,91 +975,218 @@ int ei_connect(ei_cnode* ec, char *nodename) * the node through epmd at that host * */ -int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned ms) +int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned ms) { - struct in_addr *ip_addr=(struct in_addr *) adr; + ei_socket_callbacks *cbs = ec->cbs; + void *ctx; int rport = 0; /*uint16 rport = 0;*/ int sockd; - int one = 1; int dist = 0; - ErlConnect her_name; unsigned her_flags, her_version; - + unsigned our_challenge, her_challenge; + unsigned char our_digest[16]; + int err; + int pkt_sz; + struct sockaddr_in addr; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + erl_errno = EIO; /* Default error code */ EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to %s", alivename); - if ((rport = ei_epmd_port_tmo(ip_addr,alivename,&dist, ms)) < 0) { + if ((rport = ei_epmd_port_tmo(ip_addr,alivename,&dist, tmo)) < 0) { EI_TRACE_ERR0("ei_xconnect","-> CONNECT can't get remote port"); /* ei_epmd_port_tmo() has set erl_errno */ return ERL_NO_PORT; } - - /* we now have port number to enode, try to connect */ - if((sockd = cnct((uint16)rport, ip_addr, sizeof(struct in_addr),ms)) < 0) { - EI_TRACE_ERR0("ei_xconnect","-> CONNECT socket connect failed"); - /* cnct() has set erl_errno */ - return ERL_CONNECT_FAIL; - } - - EI_TRACE_CONN0("ei_xconnect","-> CONNECT connected to remote"); - /* FIXME why connect before checking 'dist' output from ei_epmd_port() ?! */ if (dist <= 4) { EI_TRACE_ERR0("ei_xconnect","-> CONNECT remote version not compatible"); - goto error; + return ERL_ERROR; } - else { - unsigned our_challenge, her_challenge; - unsigned char our_digest[16]; - - if (send_name(sockd, ec->thisnodename, (unsigned) dist, ms)) - goto error; - if (recv_status(sockd, ms)) - goto error; - if (recv_challenge(sockd, &her_challenge, &her_version, - &her_flags, &her_name, ms)) - goto error; - our_challenge = gen_challenge(); - gen_digest(her_challenge, ec->ei_connect_cookie, our_digest); - if (send_challenge_reply(sockd, our_digest, our_challenge, ms)) - goto error; - if (recv_challenge_ack(sockd, our_challenge, - ec->ei_connect_cookie, ms)) - goto error; - put_ei_socket_info(sockd, dist, null_cookie, ec); /* FIXME check == 0 */ + + err = ei_socket_ctx__(cbs, &ctx, ec->setup_context); + if (err) { + EI_TRACE_ERR2("ei_xconnect","-> SOCKET failed: %s (%d)", + estr(err), err); + erl_errno = err; + return ERL_CONNECT_FAIL; + } + + memset((void *) &addr, 0, sizeof(struct sockaddr_in)); + memcpy((void *) &addr.sin_addr, (void *) ip_addr, sizeof(addr.sin_addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(rport); + + err = ei_connect_ctx_t__(cbs, ctx, (void *) &addr, sizeof(addr), tmo); + if (err) { + EI_TRACE_ERR2("ei_xconnect","-> CONNECT socket connect failed: %s (%d)", + estr(err), err); + abort_connection(cbs, ctx); + erl_errno = err; + return ERL_CONNECT_FAIL; } - setsockopt(sockd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); - setsockopt(sockd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)); + EI_TRACE_CONN0("ei_xconnect","-> CONNECT connected to remote"); - EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote = %s",alivename); + err = EI_GET_FD__(cbs, ctx, &sockd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + goto error; + } + + err = cbs->handshake_packet_header_size(ctx, &pkt_sz); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + goto error; + } + + if (send_name(cbs, ctx, pkt_sz, ec->thisnodename, (unsigned) dist, tmo)) + goto error; + if (recv_status(cbs, ctx, pkt_sz, tmo)) + goto error; + if (recv_challenge(cbs, ctx, pkt_sz, &her_challenge, + &her_version, &her_flags, NULL, tmo)) + goto error; + our_challenge = gen_challenge(); + gen_digest(her_challenge, ec->ei_connect_cookie, our_digest); + if (send_challenge_reply(cbs, ctx, pkt_sz, our_digest, our_challenge, tmo)) + goto error; + if (recv_challenge_ack(cbs, ctx, pkt_sz, our_challenge, + ec->ei_connect_cookie, tmo)) + goto error; + if (put_ei_socket_info(sockd, dist, null_cookie, ec, cbs, ctx) != 0) + goto error; + + if (cbs->connect_handshake_complete) { + err = cbs->connect_handshake_complete(ctx); + if (err) { + EI_TRACE_ERR2("ei_xconnect","-> CONNECT failed: %s (%d)", + estr(err), err); + close_connection(cbs, ctx, sockd); + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } + } + EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote = %s",alivename); + erl_errno = 0; return sockd; error: EI_TRACE_ERR0("ei_xconnect","-> CONNECT failed"); - closesocket(sockd); + abort_connection(cbs, ctx); return ERL_ERROR; } /* ei_xconnect */ -int ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename) +int ei_xconnect(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename) { - return ei_xconnect_tmo(ec, adr, alivename, 0); + return ei_xconnect_tmo(ec, ip_addr, alivename, 0); } +int ei_listen(ei_cnode *ec, int *port, int backlog) +{ + struct in_addr ip_addr; + ip_addr.s_addr = htonl(INADDR_ANY); + return ei_xlisten(ec, &ip_addr, port, backlog); +} + +int ei_xlisten(ei_cnode *ec, struct in_addr *ip_addr, int *port, int backlog) +{ + ei_socket_callbacks *cbs = ec->cbs; + struct sockaddr_in sock_addr; + void *ctx; + int fd, err, len; + + err = ei_socket_ctx__(cbs, &ctx, ec->setup_context); + if (err) { + EI_TRACE_ERR2("ei_xlisten","-> SOCKET failed: %s (%d)", + estr(err), err); + erl_errno = err; + return ERL_ERROR; + } + + memset((void *) &sock_addr, 0, sizeof(struct sockaddr_in)); + memcpy((void *) &sock_addr.sin_addr, (void *) ip_addr, sizeof(*ip_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons((short) *port); + + len = sizeof(sock_addr); + err = ei_listen_ctx__(cbs, ctx, (void *) &sock_addr, &len, backlog); + if (err) { + EI_TRACE_ERR2("ei_xlisten","-> listen failed: %s (%d)", + estr(err), err); + erl_errno = err; + goto error; + } + + if (len != sizeof(sock_addr)) { + if (len < offsetof(struct sockaddr_in, sin_addr) + sizeof(sock_addr.sin_addr) + || len < offsetof(struct sockaddr_in, sin_port) + sizeof(sock_addr.sin_port)) { + erl_errno = EIO; + EI_TRACE_ERR0("ei_xlisten","-> get info failed"); + goto error; + } + } + + memcpy((void *) ip_addr, (void *) &sock_addr.sin_addr, sizeof(*ip_addr)); + *port = (int) ntohs(sock_addr.sin_port); + + err = EI_GET_FD__(cbs, ctx, &fd); + if (err) { + erl_errno = err; + goto error; + } + + if (put_ei_socket_info(fd, 0, null_cookie, ec, cbs, ctx) != 0) { + EI_TRACE_ERR0("ei_xlisten","-> save socket info failed"); + erl_errno = EIO; + goto error; + } + + erl_errno = 0; + + return fd; + +error: + abort_connection(cbs, ctx); + return ERL_ERROR; +} + +static int close_connection(ei_socket_callbacks *cbs, void *ctx, int fd) +{ + int err; + remove_ei_socket_info(fd); + err = ei_close_ctx__(cbs, ctx); + if (err) { + erl_errno = err; + return ERL_ERROR; + } + return 0; +} - /* - * For symmetry reasons -*/ -#if 0 int ei_close_connection(int fd) { - return closesocket(fd); + ei_socket_callbacks *cbs; + void *ctx; + int err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); + if (err) + erl_errno = err; + else { + if (close_connection(cbs, ctx, fd) == 0) + return 0; + } + EI_TRACE_ERR2("ei_close_connection","<- CLOSE socket close failed: %s (%d)", + estr(erl_errno), erl_errno); + return ERL_ERROR; } /* ei_close_connection */ -#endif + +static void abort_connection(ei_socket_callbacks *cbs, void *ctx) +{ + (void) ei_close_ctx__(cbs, ctx); +} /* * Accept and initiate a connection from another @@ -857,25 +1201,71 @@ int ei_accept(ei_cnode* ec, int lfd, ErlConnect *conp) int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms) { int fd; - struct sockaddr_in cli_addr; - int cli_addr_len=sizeof(struct sockaddr_in); unsigned her_version, her_flags; - ErlConnect her_name; + char tmp_nodename[MAXNODELEN+1]; + char *her_name; + int pkt_sz, err; + struct sockaddr_in addr; + int addr_len = sizeof(struct sockaddr_in); + ei_socket_callbacks *cbs; + void *ctx; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; erl_errno = EIO; /* Default error code */ + + err = EI_GET_CBS_CTX__(&cbs, &ctx, lfd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } + EI_TRACE_CONN0("ei_accept","<- ACCEPT waiting for connection"); + + if (conp) { + her_name = &conp->nodename[0]; + } + else { + her_name = &tmp_nodename[0]; + } - if ((fd = ei_accept_t(lfd, (struct sockaddr*) &cli_addr, - &cli_addr_len, ms )) < 0) { - EI_TRACE_ERR0("ei_accept","<- ACCEPT socket accept failed"); - erl_errno = (fd == -2) ? ETIMEDOUT : EIO; - goto error; + /* + * ei_accept_ctx_t__() replaces the pointer to the listen context + * with a pointer to the accepted connection context on success. + */ + err = ei_accept_ctx_t__(cbs, &ctx, (void *) &addr, &addr_len, tmo); + if (err) { + EI_TRACE_ERR2("ei_accept","<- ACCEPT socket accept failed: %s (%d)", + estr(err), err); + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } + + err = EI_GET_FD__(cbs, ctx, &fd); + if (err) { + EI_TRACE_ERR2("ei_accept","<- ACCEPT get fd failed: %s (%d)", + estr(err), err); + EI_CONN_SAVE_ERRNO__(err); + } + + if (addr_len != sizeof(struct sockaddr_in)) { + if (addr_len < (offsetof(struct sockaddr_in, sin_addr) + + sizeof(addr.sin_addr))) { + EI_TRACE_ERR0("ei_accept","<- ACCEPT get addr failed"); + goto error; + } + } + + err = cbs->handshake_packet_header_size(ctx, &pkt_sz); + if (err) { + EI_TRACE_ERR2("ei_accept","<- ACCEPT get packet size failed: %s (%d)", + estr(err), err); + EI_CONN_SAVE_ERRNO__(err); } EI_TRACE_CONN0("ei_accept","<- ACCEPT connected to remote"); - if (recv_name(fd, &her_version, &her_flags, &her_name, ms)) { + if (recv_name(cbs, ctx, pkt_sz, &her_version, &her_flags, her_name, tmo)) { EI_TRACE_ERR0("ei_accept","<- ACCEPT initial ident failed"); goto error; } @@ -888,34 +1278,45 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms) unsigned our_challenge; unsigned her_challenge; unsigned char our_digest[16]; - - if (send_status(fd,"ok", ms)) + + if (send_status(cbs, ctx, pkt_sz, "ok", tmo)) goto error; our_challenge = gen_challenge(); - if (send_challenge(fd, ec->thisnodename, - our_challenge, her_version, ms)) + if (send_challenge(cbs, ctx, pkt_sz, ec->thisnodename, + our_challenge, her_version, tmo)) goto error; - if (recv_challenge_reply(fd, our_challenge, - ec->ei_connect_cookie, - &her_challenge, ms)) + if (recv_challenge_reply(cbs, ctx, pkt_sz, our_challenge, + ec->ei_connect_cookie, &her_challenge, tmo)) goto error; gen_digest(her_challenge, ec->ei_connect_cookie, our_digest); - if (send_challenge_ack(fd, our_digest, ms)) + if (send_challenge_ack(cbs, ctx, pkt_sz, our_digest, tmo)) goto error; - put_ei_socket_info(fd, her_version, null_cookie, ec); + if (put_ei_socket_info(fd, her_version, null_cookie, ec, cbs, ctx) != 0) + goto error; + } + if (conp) { + memcpy((void *) conp->ipadr, (void *) &addr.sin_addr, sizeof(conp->ipadr)); + } + + if (cbs->accept_handshake_complete) { + err = cbs->accept_handshake_complete(ctx); + if (err) { + EI_TRACE_ERR2("ei_xconnect","-> ACCEPT handshake failed: %s (%d)", + estr(err), err); + close_connection(cbs, ctx, fd); + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } } - if (conp) - *conp = her_name; - EI_TRACE_CONN1("ei_accept","<- ACCEPT (ok) remote = %s",her_name.nodename); + EI_TRACE_CONN1("ei_accept","<- ACCEPT (ok) remote = %s",her_name); erl_errno = 0; /* No error */ return fd; error: EI_TRACE_ERR0("ei_accept","<- ACCEPT failed"); - if (fd>=0) - closesocket(fd); + abort_connection(cbs, ctx); return ERL_ERROR; } /* ei_accept */ @@ -927,36 +1328,57 @@ error: */ int ei_receive_tmo(int fd, unsigned char *bufp, int bufsize, unsigned ms) { - int len; + ssize_t len; unsigned char fourbyte[4]={0,0,0,0}; - int res; - - if ((res = ei_read_fill_t(fd, (char *) bufp, 4, ms)) != 4) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + int err; + ei_socket_callbacks *cbs; + void *ctx; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + + err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } + + len = (ssize_t) 4; + err = ei_read_fill_ctx_t__(cbs, ctx, (char *) bufp, &len, tmo); + if (!err && len != (ssize_t) 4) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); return ERL_ERROR; } /* Tick handling */ - if ((len = get_int32(bufp)) == ERL_TICK) - { - ei_write_fill_t(fd, (char *) fourbyte, 4, ms); + len = get_int32(bufp); + if (len == ERL_TICK) { + len = 4; + ei_write_fill_ctx_t__(cbs, ctx, (char *) fourbyte, &len, tmo); /* FIXME ok to ignore error or timeout? */ erl_errno = EAGAIN; return ERL_TICK; } - else if (len > bufsize) - { + + if (len > bufsize) { /* FIXME: We should drain the message. */ erl_errno = EMSGSIZE; return ERL_ERROR; } - else if ((res = ei_read_fill_t(fd, (char *) bufp, len, ms)) != len) - { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return ERL_ERROR; + else { + ssize_t need = len; + err = ei_read_fill_ctx_t__(cbs, ctx, (char *) bufp, &len, tmo); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } + if (len != need) { + erl_errno = EIO; + return ERL_ERROR; + } } - return len; + return (int) len; } @@ -1112,36 +1534,11 @@ int ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun, int ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg, ei_x_buff *x) { - fd_set readmask; - struct timeval tv; - struct timeval *t = NULL; - - if (timeout >= 0) { - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - t = &tv; - } - - FD_ZERO(&readmask); - FD_SET(fd,&readmask); - - switch (select(fd+1, &readmask, NULL, NULL, t)) { - case -1: - erl_errno = EIO; - return ERL_ERROR; - - case 0: - erl_errno = ETIMEDOUT; - return ERL_TIMEOUT; - - default: - if (FD_ISSET(fd, &readmask)) { - return ei_xreceive_msg(fd, msg, x); - } else { - erl_errno = EIO; - return ERL_ERROR; - } - } + unsigned tmo = timeout < 0 ? EI_SCLBK_INF_TMO : (unsigned) timeout; + int res = ei_xreceive_msg_tmo(fd, msg, x, tmo); + if (res < 0 && erl_errno == ETIMEDOUT) + return ERL_TIMEOUT; + return res; } /* rpc_from */ /* @@ -1295,19 +1692,34 @@ static char *hex(char digest[16], char buff[33]) return buff; } -static int read_2byte_package(int fd, char **buf, int *buflen, - int *is_static, unsigned ms) +static int read_hs_package(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, char **buf, int *buflen, + int *is_static, unsigned ms) { - unsigned char nbuf[2]; + unsigned char nbuf[4]; unsigned char *x = nbuf; - unsigned len; - int res; - - if((res = ei_read_fill_t(fd, (char *)nbuf, 2, ms)) != 2) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + ssize_t len, need; + int err; + + len = (ssize_t) pkt_sz; + err = ei_read_fill_ctx_t__(cbs, ctx, (char *)nbuf, &len, ms); + if (!err && len != (ssize_t) pkt_sz) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); return -1; } - len = get16be(x); + + switch (pkt_sz) { + case 2: + len = get16be(x); + break; + case 4: + len = get32be(x); + break; + default: + return -1; + } if (len > *buflen) { if (*is_static) { @@ -1329,20 +1741,26 @@ static int read_2byte_package(int fd, char **buf, int *buflen, *buflen = len; } } - if ((res = ei_read_fill_t(fd, *buf, len, ms)) != len) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + need = len; + err = ei_read_fill_ctx_t__(cbs, ctx, *buf, &len, ms); + if (!err && len != need) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); return -1; } return len; } -static int send_status(int fd, char *status, unsigned ms) +static int send_status(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, char *status, unsigned ms) { char *buf, *s; char dbuf[DEFBUF_SIZ]; - int siz = strlen(status) + 1 + 2; - int res; + int siz = strlen(status) + 1 + pkt_sz; + int err; + ssize_t len; buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf; if (!buf) { @@ -1350,14 +1768,28 @@ static int send_status(int fd, char *status, unsigned ms) return -1; } s = buf; - put16be(s,siz - 2); + switch (pkt_sz) { + case 2: + put16be(s,siz - 2); + break; + case 4: + put32be(s,siz - 4); + break; + default: + return -1; + } put8(s, 's'); memcpy(s, status, strlen(status)); - if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { - EI_TRACE_ERR0("send_status","-> SEND_STATUS socket write failed"); + len = (ssize_t) siz; + err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms); + if (!err && len != (ssize_t) siz) + err = EIO; + if (err) { + EI_TRACE_ERR2("send_status","-> SEND_STATUS socket write failed: %s (%d)", + estr(err), err); if (buf != dbuf) - free(buf); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + free(buf); + EI_CONN_SAVE_ERRNO__(err); return -1; } EI_TRACE_CONN1("send_status","-> SEND_STATUS (%s)",status); @@ -1367,7 +1799,8 @@ static int send_status(int fd, char *status, unsigned ms) return 0; } -static int recv_status(int fd, unsigned ms) +static int recv_status(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned ms) { char dbuf[DEFBUF_SIZ]; char *buf = dbuf; @@ -1375,7 +1808,8 @@ static int recv_status(int fd, unsigned ms) int buflen = DEFBUF_SIZ; int rlen; - if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) <= 0) { + if ((rlen = read_hs_package(cbs, ctx, pkt_sz, + &buf, &buflen, &is_static, ms)) <= 0) { EI_TRACE_ERR1("recv_status", "<- RECV_STATUS socket read failed (%d)", rlen); goto error; @@ -1396,7 +1830,10 @@ error: return -1; } -static int send_name_or_challenge(int fd, char *nodename, +static int send_name_or_challenge(ei_socket_callbacks *cbs, + void *ctx, + int pkt_sz, + char *nodename, int f_chall, unsigned challenge, unsigned version, @@ -1405,9 +1842,10 @@ static int send_name_or_challenge(int fd, char *nodename, char *buf; unsigned char *s; char dbuf[DEFBUF_SIZ]; - int siz = 2 + 1 + 2 + 4 + strlen(nodename); + int siz = pkt_sz + 1 + 2 + 4 + strlen(nodename); const char* function[] = {"SEND_NAME", "SEND_CHALLENGE"}; - int res; + int err; + ssize_t len; if (f_chall) siz += 4; @@ -1417,7 +1855,16 @@ static int send_name_or_challenge(int fd, char *nodename, return -1; } s = (unsigned char *)buf; - put16be(s,siz - 2); + switch (pkt_sz) { + case 2: + put16be(s,siz - 2); + break; + case 4: + put32be(s,siz - 4); + break; + default: + return -1; + } put8(s, 'n'); put16be(s, version); put32be(s, (DFLAG_EXTENDED_REFERENCES @@ -1433,13 +1880,16 @@ static int send_name_or_challenge(int fd, char *nodename, if (f_chall) put32be(s, challenge); memcpy(s, nodename, strlen(nodename)); - - if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { + len = (ssize_t) siz; + err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms); + if (!err && len != (ssize_t) siz) + err = EIO; + if (err) { EI_TRACE_ERR1("send_name_or_challenge", "-> %s socket write failed", function[f_chall]); if (buf != dbuf) free(buf); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + EI_CONN_SAVE_ERRNO__(err); return -1; } @@ -1448,9 +1898,9 @@ static int send_name_or_challenge(int fd, char *nodename, return 0; } -static int recv_challenge(int fd, unsigned *challenge, - unsigned *version, - unsigned *flags, ErlConnect *namebuf, unsigned ms) +static int recv_challenge(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned *challenge, unsigned *version, + unsigned *flags, char *namebuf, unsigned ms) { char dbuf[DEFBUF_SIZ]; char *buf = dbuf; @@ -1458,13 +1908,13 @@ static int recv_challenge(int fd, unsigned *challenge, int buflen = DEFBUF_SIZ; int rlen; char *s; - struct sockaddr_in sin; - socklen_t sin_len = sizeof(sin); char tag; - + char tmp_nodename[MAXNODELEN+1]; + erl_errno = EIO; /* Default */ - if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) <= 0) { + if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, + &is_static, ms)) <= 0) { EI_TRACE_ERR1("recv_challenge", "<- RECV_CHALLENGE socket read failed (%d)",rlen); goto error; @@ -1505,22 +1955,19 @@ static int recv_challenge(int fd, unsigned *challenge, goto error; } - if (getpeername(fd, (struct sockaddr *) &sin, &sin_len) < 0) { - EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE can't get peername"); - erl_errno = errno; - goto error; - } - memcpy(namebuf->ipadr, &(sin.sin_addr.s_addr), - sizeof(sin.sin_addr.s_addr)); - memcpy(namebuf->nodename, s, rlen - 11); - namebuf->nodename[rlen - 11] = '\0'; + if (!namebuf) + namebuf = &tmp_nodename[0]; + + memcpy(namebuf, s, rlen - 11); + namebuf[rlen - 11] = '\0'; + if (!is_static) free(buf); EI_TRACE_CONN4("recv_challenge","<- RECV_CHALLENGE (ok) node = %s, " "version = %u, " "flags = %u, " "challenge = %d", - namebuf->nodename, + namebuf, *version, *flags, *challenge @@ -1533,24 +1980,40 @@ error: return -1; } -static int send_challenge_reply(int fd, unsigned char digest[16], +static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned char digest[16], unsigned challenge, unsigned ms) { char *s; char buf[DEFBUF_SIZ]; - int siz = 2 + 1 + 4 + 16; - int res; + int siz = pkt_sz + 1 + 4 + 16; + int err; + ssize_t len; s = buf; - put16be(s,siz - 2); + switch (pkt_sz) { + case 2: + put16be(s,siz - 2); + break; + case 4: + put32be(s,siz - 4); + break; + default: + return -1; + } put8(s, 'r'); put32be(s, challenge); memcpy(s, digest, 16); - - if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { - EI_TRACE_ERR0("send_challenge_reply", - "-> SEND_CHALLENGE_REPLY socket write failed"); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + + len = (ssize_t) siz; + err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms); + if (!err && len != (ssize_t) siz) + err = EIO; + if (err) { + EI_TRACE_ERR2("send_challenge_reply", + "-> SEND_CHALLENGE_REPLY socket write failed: %s (%d)", + estr(err), err); + EI_CONN_SAVE_ERRNO__(err); return -1; } @@ -1563,11 +2026,13 @@ static int send_challenge_reply(int fd, unsigned char digest[16], return 0; } -static int recv_challenge_reply (int fd, - unsigned our_challenge, - char cookie[], - unsigned *her_challenge, - unsigned ms) +static int recv_challenge_reply(ei_socket_callbacks *cbs, + void *ctx, + int pkt_sz, + unsigned our_challenge, + char cookie[], + unsigned *her_challenge, + unsigned ms) { char dbuf[DEFBUF_SIZ]; char *buf = dbuf; @@ -1580,7 +2045,7 @@ static int recv_challenge_reply (int fd, erl_errno = EIO; /* Default */ - if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) != 21) { + if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 21) { EI_TRACE_ERR1("recv_challenge_reply", "<- RECV_CHALLENGE_REPLY socket read failed (%d)",rlen); goto error; @@ -1620,23 +2085,38 @@ error: return -1; } -static int send_challenge_ack(int fd, unsigned char digest[16], unsigned ms) +static int send_challenge_ack(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + unsigned char digest[16], unsigned ms) { char *s; char buf[DEFBUF_SIZ]; - int siz = 2 + 1 + 16; - int res; + int siz = pkt_sz + 1 + 16; + int err; + ssize_t len; s = buf; - - put16be(s,siz - 2); + switch (pkt_sz) { + case 2: + put16be(s,siz - 2); + break; + case 4: + put32be(s,siz - 4); + break; + default: + return -1; + } put8(s, 'a'); memcpy(s, digest, 16); - if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { - EI_TRACE_ERR0("recv_challenge_reply", - "-> SEND_CHALLENGE_ACK socket write failed"); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + len = (ssize_t) siz; + err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms); + if (!err && len != (ssize_t) siz) + err = EIO; + if (err) { + EI_TRACE_ERR2("recv_challenge_reply", + "-> SEND_CHALLENGE_ACK socket write failed: %s (%d)", + estr(err), err); + EI_CONN_SAVE_ERRNO__(err); return -1; } @@ -1649,8 +2129,8 @@ static int send_challenge_ack(int fd, unsigned char digest[16], unsigned ms) return 0; } -static int recv_challenge_ack(int fd, - unsigned our_challenge, +static int recv_challenge_ack(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned our_challenge, char cookie[], unsigned ms) { char dbuf[DEFBUF_SIZ]; @@ -1664,7 +2144,7 @@ static int recv_challenge_ack(int fd, erl_errno = EIO; /* Default */ - if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) != 17) { + if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 17) { EI_TRACE_ERR1("recv_challenge_ack", "<- RECV_CHALLENGE_ACK socket read failed (%d)",rlen); goto error; @@ -1701,20 +2181,24 @@ error: return -1; } -static int send_name(int fd, char *nodename, unsigned version, unsigned ms) +static int send_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + char *nodename, unsigned version, unsigned ms) { - return send_name_or_challenge(fd, nodename, 0, 0, version, ms); + return send_name_or_challenge(cbs, ctx, pkt_sz, nodename, 0, + 0, version, ms); } -static int send_challenge(int fd, char *nodename, - unsigned challenge, unsigned version, unsigned ms) +static int send_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz, + char *nodename, unsigned challenge, unsigned version, + unsigned ms) { - return send_name_or_challenge(fd, nodename, 1, challenge, version, ms); + return send_name_or_challenge(cbs, ctx, pkt_sz, nodename, 1, + challenge, version, ms); } -static int recv_name(int fd, - unsigned *version, - unsigned *flags, ErlConnect *namebuf, unsigned ms) +static int recv_name(ei_socket_callbacks *cbs, void *ctx, + int pkt_sz, unsigned *version, + unsigned *flags, char *namebuf, unsigned ms) { char dbuf[DEFBUF_SIZ]; char *buf = dbuf; @@ -1722,13 +2206,13 @@ static int recv_name(int fd, int buflen = DEFBUF_SIZ; int rlen; char *s; - struct sockaddr_in sin; - socklen_t sin_len = sizeof(sin); + char tmp_nodename[MAXNODELEN+1]; char tag; erl_errno = EIO; /* Default */ - if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) <= 0) { + if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, + &is_static, ms)) <= 0) { EI_TRACE_ERR1("recv_name","<- RECV_NAME socket read failed (%d)",rlen); goto error; } @@ -1759,21 +2243,18 @@ static int recv_name(int fd, erl_errno = EIO; goto error; } - - if (getpeername(fd, (struct sockaddr *) &sin, &sin_len) < 0) { - EI_TRACE_ERR0("recv_name","<- RECV_NAME can't get peername"); - erl_errno = errno; - goto error; - } - memcpy(namebuf->ipadr, &(sin.sin_addr.s_addr), - sizeof(sin.sin_addr.s_addr)); - memcpy(namebuf->nodename, s, rlen - 7); - namebuf->nodename[rlen - 7] = '\0'; + + if (!namebuf) + namebuf = &tmp_nodename[0]; + + memcpy(namebuf, s, rlen - 7); + namebuf[rlen - 7] = '\0'; + if (!is_static) free(buf); EI_TRACE_CONN3("recv_name", "<- RECV_NAME (ok) node = %s, version = %u, flags = %u", - namebuf->nodename,*version,*flags); + namebuf,*version,*flags); erl_errno = 0; return 0; @@ -1867,3 +2348,4 @@ static int get_cookie(char *buf, int bufsize) return 1; /* Success! */ } + diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c index 022a43d255..225fddc784 100644 --- a/lib/erl_interface/src/connect/ei_resolve.c +++ b/lib/erl_interface/src/connect/ei_resolve.c @@ -57,9 +57,9 @@ #ifdef HAVE_GETHOSTBYNAME_R -void ei_init_resolve(void) +int ei_init_resolve(void) { - return; /* Do nothing */ + return 0; /* Do nothing */ } #else /* !HAVE_GETHOSTBYNAME_R */ @@ -103,7 +103,7 @@ static int verify_dns_configuration(void); * our own, which are just wrappers around hostGetByName() and * hostGetByAddr(). Here we look up the functions. */ -void ei_init_resolve(void) +int ei_init_resolve(void) { #ifdef VXWORKS @@ -134,9 +134,12 @@ void ei_init_resolve(void) #ifdef _REENTRANT ei_gethost_sem = ei_mutex_create(); + if (!ei_gethost_sem) + return ENOMEM; #endif /* _REENTRANT */ ei_resolve_initialized = 1; + return 0; } #ifdef VXWORKS @@ -312,9 +315,11 @@ static struct hostent *my_gethostbyname_r(const char *name, struct hostent *src; struct hostent *rval = NULL; - /* FIXME this should have been done in 'erl'_init()? */ - if (!ei_resolve_initialized) ei_init_resolve(); - + if (!ei_resolve_initialized) { + *h_errnop = NO_RECOVERY; + return NULL; + } + #ifdef _REENTRANT /* === BEGIN critical section === */ if (ei_mutex_lock(ei_gethost_sem,0) != 0) { @@ -377,7 +382,10 @@ static struct hostent *my_gethostbyaddr_r(const char *addr, struct hostent *rval = NULL; /* FIXME this should have been done in 'erl'_init()? */ - if (!ei_resolve_initialized) ei_init_resolve(); + if (!ei_resolve_initialized) { + *h_errnop = NO_RECOVERY; + return NULL; + } #ifdef _REENTRANT /* === BEGIN critical section === */ diff --git a/lib/erl_interface/src/connect/ei_resolve.h b/lib/erl_interface/src/connect/ei_resolve.h index 10a49ffbc6..5711d7da76 100644 --- a/lib/erl_interface/src/connect/ei_resolve.h +++ b/lib/erl_interface/src/connect/ei_resolve.h @@ -20,6 +20,6 @@ #ifndef _EI_RESOLVE_H #define _EI_RESOLVE_H -void ei_init_resolve(void); +int ei_init_resolve(void); #endif /* _EI_RESOLVE_H */ diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c index 7b9dbfc387..47eea06ced 100644 --- a/lib/erl_interface/src/connect/eirecv.c +++ b/lib/erl_interface/src/connect/eirecv.c @@ -60,22 +60,36 @@ ei_recv_internal (int fd, int arity; int version; int index = 0; - int i = 0; - int res; + int err; int show_this_msg = 0; + ei_socket_callbacks *cbs; + void *ctx; + ssize_t rlen; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + + err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; + } /* get length field */ - if ((res = ei_read_fill_t(fd, header, 4, ms)) != 4) - { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; + rlen = 4; + err = ei_read_fill_ctx_t__(cbs, ctx, header, &rlen, tmo); + if (!err && rlen != 4) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); return -1; } + len = get32be(s); /* got tick - respond and return */ if (!len) { char tock[] = {0,0,0,0}; - ei_write_fill_t(fd, tock, sizeof(tock), ms); /* Failure no problem */ + ssize_t wlen = sizeof(tock); + ei_write_fill_ctx_t__(cbs, ctx, tock, &wlen, tmo); /* Failure no problem */ *msglenp = 0; return 0; /* maybe flag ERL_EAGAIN [sverkerw] */ } @@ -86,9 +100,12 @@ ei_recv_internal (int fd, ei_trace(-1,NULL); /* read enough to get at least entire header */ - bytesread = (len > EIRECVBUF ? EIRECVBUF : len); - if ((i = ei_read_fill_t(fd,header,bytesread,ms)) != bytesread) { - erl_errno = (i == -2) ? ETIMEDOUT : EIO; + rlen = bytesread = (len > EIRECVBUF ? EIRECVBUF : len); + err = ei_read_fill_ctx_t__(cbs, ctx, header, &rlen, tmo); + if (!err && rlen != bytesread) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); return -1; } @@ -212,12 +229,17 @@ ei_recv_internal (int fd, */ if (msglen > *bufsz) { if (staticbufp) { - int sz = EIRECVBUF; /* flush in rest of packet */ while (remain > 0) { - if (remain < sz) sz = remain; - if ((i=ei_read_fill_t(fd,header,sz,ms)) <= 0) break; - remain -= i; + rlen = remain > EIRECVBUF ? EIRECVBUF : remain; + err = ei_read_fill_ctx_t__(cbs, ctx, header, &rlen, tmo); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; + } + if (rlen == 0) + break; + remain -= rlen; } erl_errno = EMSGSIZE; return -1; @@ -247,11 +269,15 @@ ei_recv_internal (int fd, /* read the rest of the message into callers buffer */ if (remain > 0) { - if ((i = ei_read_fill_t(fd,mbuf+bytesread-index,remain,ms)) != remain) { - *msglenp = bytesread-index+1; /* actual bytes in users buffer */ - erl_errno = (i == -2) ? ETIMEDOUT : EIO; - return -1; - } + rlen = remain; + err = ei_read_fill_ctx_t__(cbs, ctx, mbuf+bytesread-index, &rlen, tmo); + if (!err && rlen != remain) + err = EIO; + if (err) { + *msglenp = bytesread-index+1; /* actual bytes in users buffer */ + EI_CONN_SAVE_ERRNO__(err); + return -1; + } } if (show_this_msg) diff --git a/lib/erl_interface/src/connect/send.c b/lib/erl_interface/src/connect/send.c index 37d7db6d68..d97532d123 100644 --- a/lib/erl_interface/src/connect/send.c +++ b/lib/erl_interface/src/connect/send.c @@ -58,10 +58,17 @@ int ei_send_encoded_tmo(int fd, const erlang_pid *to, char *s, header[1200]; /* see size calculation below */ erlang_trace *token = NULL; int index = 5; /* reserve 5 bytes for control message */ - int res; -#ifdef HAVE_WRITEV - struct iovec v[2]; -#endif + int err; + ei_socket_callbacks *cbs; + void *ctx; + ssize_t len, tot_len; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + + err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } /* are we tracing? */ /* check that he can receive trace tokens first */ @@ -91,30 +98,47 @@ int ei_send_encoded_tmo(int fd, const erlang_pid *to, if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); -#ifdef HAVE_WRITEV - - v[0].iov_base = (char *)header; - v[0].iov_len = index; - v[1].iov_base = (char *)msg; - v[1].iov_len = msglen; - - if ((res = ei_writev_fill_t(fd,v,2,ms)) != index+msglen) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; - } - -#else /* !HAVE_WRITEV */ - - if ((res = ei_write_fill_t(fd,header,index,ms)) != index) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + +#ifdef EI_HAVE_STRUCT_IOVEC__ + if (ei_socket_callbacks_have_writev__(cbs)) { + struct iovec v[2]; + + v[0].iov_base = (char *)header; + v[0].iov_len = index; + v[1].iov_base = (char *)msg; + v[1].iov_len = msglen; + + len = tot_len = (ssize_t) index+msglen; + err = ei_writev_fill_ctx_t__(cbs, ctx, v, 2, &len, tmo); + if (!err && len != tot_len) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; + } + + return 0; } - if ((res = ei_write_fill_t(fd,msg,msglen,ms)) != msglen) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; +#endif /* EI_HAVE_STRUCT_IOVEC__ */ + + /* no writev() */ + len = tot_len = (ssize_t) index; + err = ei_write_fill_ctx_t__(cbs, ctx, header, &len, tmo); + if (!err && len != tot_len) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; } -#endif /* !HAVE_WRITEV */ + len = tot_len = (ssize_t) msglen; + err = ei_write_fill_ctx_t__(cbs, ctx, msg, &len, tmo); + if (!err && len != tot_len) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; + } return 0; } diff --git a/lib/erl_interface/src/connect/send_exit.c b/lib/erl_interface/src/connect/send_exit.c index 2e298e3221..b4f7e14c7f 100644 --- a/lib/erl_interface/src/connect/send_exit.c +++ b/lib/erl_interface/src/connect/send_exit.c @@ -55,6 +55,17 @@ int ei_send_exit_tmo(int fd, const erlang_pid *from, const erlang_pid *to, char *s; int index = 0; int len = strlen(reason) + 1080; /* see below */ + ei_socket_callbacks *cbs; + void *ctx; + int err; + ssize_t wlen; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + + err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } if (len > EISMALLBUF) if (!(dbuf = malloc(len))) @@ -92,10 +103,16 @@ int ei_send_exit_tmo(int fd, const erlang_pid *from, const erlang_pid *to, if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,msgbuf,NULL); - ei_write_fill_t(fd,msgbuf,index,ms); - /* FIXME ignore timeout etc? erl_errno?! */ - - if (dbuf) free(dbuf); + wlen = (ssize_t) index; + err = ei_write_fill_ctx_t__(cbs, ctx, msgbuf, &wlen, tmo); + if (!err && wlen != (ssize_t) index) + err = EIO; + if (dbuf) + free(dbuf); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } return 0; } diff --git a/lib/erl_interface/src/connect/send_reg.c b/lib/erl_interface/src/connect/send_reg.c index 62478f042d..80d61e57b5 100644 --- a/lib/erl_interface/src/connect/send_reg.c +++ b/lib/erl_interface/src/connect/send_reg.c @@ -51,11 +51,17 @@ int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, char *s, header[1400]; /* see size calculation below */ erlang_trace *token = NULL; int index = 5; /* reserve 5 bytes for control message */ - int res; + int err; + ei_socket_callbacks *cbs; + void *ctx; + ssize_t len, tot_len; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; -#ifdef HAVE_WRITEV - struct iovec v[2]; -#endif + err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return ERL_ERROR; + } /* are we tracing? */ /* check that he can receive trace tokens first */ @@ -86,29 +92,45 @@ int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); -#ifdef HAVE_WRITEV +#ifdef EI_HAVE_STRUCT_IOVEC__ + if (ei_socket_callbacks_have_writev__(cbs)) { + struct iovec v[2]; - v[0].iov_base = (char *)header; - v[0].iov_len = index; - v[1].iov_base = (char *)msg; - v[1].iov_len = msglen; + v[0].iov_base = (char *)header; + v[0].iov_len = index; + v[1].iov_base = (char *)msg; + v[1].iov_len = msglen; - if ((res = ei_writev_fill_t(fd,v,2,ms)) != index+msglen) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + len = tot_len = (ssize_t) index+msglen; + err = ei_writev_fill_ctx_t__(cbs, ctx, v, 2, &len, tmo); + if (!err && len != tot_len) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; + } + return 0; } -#else - +#endif /* EI_HAVE_STRUCT_IOVEC__ */ + /* no writev() */ - if ((res = ei_write_fill_t(fd,header,index,ms)) != index) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + len = tot_len = (ssize_t) index; + err = ei_write_fill_ctx_t__(cbs, ctx, header, &len, tmo); + if (!err && len != tot_len) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; } - if ((res = ei_write_fill_t(fd,msg,msglen,ms)) != msglen) { - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + + len = tot_len = (ssize_t) msglen; + err = ei_write_fill_ctx_t__(cbs, ctx, msg, &len, tmo); + if (!err && len != tot_len) + err = EIO; + if (err) { + EI_CONN_SAVE_ERRNO__(err); + return -1; } -#endif return 0; } diff --git a/lib/erl_interface/src/epmd/epmd_port.c b/lib/erl_interface/src/epmd/epmd_port.c index 2ec418b24a..492c3fb3aa 100644 --- a/lib/erl_interface/src/epmd/epmd_port.c +++ b/lib/erl_interface/src/epmd/epmd_port.c @@ -62,31 +62,38 @@ int ei_epmd_connect_tmo(struct in_addr *inaddr, unsigned ms) { static unsigned int epmd_port = 0; - struct sockaddr_in saddr; - int sd; - int res; + int port, sd, err; + struct in_addr ip_addr; + struct sockaddr_in addr; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + + err = ei_socket__(&sd); + if (err) { + erl_errno = err; + return -1; + } if (epmd_port == 0) { char* port_str = getenv("ERL_EPMD_PORT"); epmd_port = (port_str != NULL) ? atoi(port_str) : EPMD_PORT; } - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_port = htons(epmd_port); - saddr.sin_family = AF_INET; - if (!inaddr) saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - else memmove(&saddr.sin_addr,inaddr,sizeof(saddr.sin_addr)); + port = (int) epmd_port; - if (((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)) - { - erl_errno = errno; - return -1; + if (!inaddr) { + ip_addr.s_addr = htonl(INADDR_LOOPBACK); + inaddr = &ip_addr; } + + memset((void *) &addr, 0, sizeof(struct sockaddr_in)); + memcpy((void *) &addr.sin_addr, (void *) inaddr, sizeof(addr.sin_addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); - if ((res = ei_connect_t(sd,(struct sockaddr *)&saddr,sizeof(saddr),ms)) < 0) - { - erl_errno = (res == -2) ? ETIMEDOUT : errno; - closesocket(sd); + err = ei_connect_t__(sd, (void *) &addr, sizeof(addr), tmo); + if (err) { + erl_errno = err; + ei_close__(sd); return -1; } @@ -104,6 +111,9 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, int port; int dist_high, dist_low, proto; int res; + int err; + ssize_t dlen; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; #if defined(VXWORKS) char ntoabuf[32]; #endif @@ -124,10 +134,14 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, return -1; } - if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + dlen = len + 2; + err = ei_write_fill_t__(fd, buf, &dlen, tmo); + if (!err && dlen != (ssize_t) len + 2) + erl_errno = EIO; + if (err) { + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); + return -1; } #ifdef VXWORKS @@ -142,12 +156,15 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, "-> PORT2_REQ alive=%s ip=%s",alive,inet_ntoa(*addr)); #endif - /* read first two bytes (response type, response) */ - if ((res = ei_read_fill_t(fd, buf, 2, ms)) != 2) { - EI_TRACE_ERR0("ei_epmd_r4_port","<- CLOSE"); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - closesocket(fd); - return -2; /* version mismatch */ + dlen = (ssize_t) 2; + err = ei_read_fill_t__(fd, buf, &dlen, tmo); + if (!err && dlen != (ssize_t) 2) + erl_errno = EIO; + if (err) { + EI_TRACE_ERR0("ei_epmd_r4_port","<- CLOSE"); + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); + return -2; } s = buf; @@ -156,7 +173,7 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, if (res != EI_EPMD_PORT2_RESP) { /* response type */ EI_TRACE_ERR1("ei_epmd_r4_port","<- unknown (%d)",res); EI_TRACE_ERR0("ei_epmd_r4_port","-> CLOSE"); - closesocket(fd); + ei_close__(fd); erl_errno = EIO; return -1; } @@ -167,7 +184,7 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, if ((res = get8(s))) { /* got negative response */ EI_TRACE_ERR1("ei_epmd_r4_port","<- PORT2_RESP result=%d (failure)",res); - closesocket(fd); + ei_close__(fd); erl_errno = EIO; return -1; } @@ -175,14 +192,18 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, EI_TRACE_CONN1("ei_epmd_r4_port","<- PORT2_RESP result=%d (ok)",res); /* expecting remaining 8 bytes */ - if ((res = ei_read_fill_t(fd,buf,8,ms)) != 8) { + dlen = (ssize_t) 8; + err = ei_read_fill_t__(fd, buf, &dlen, tmo); + if (!err && dlen != (ssize_t) 8) + err = EIO; + if (err) { EI_TRACE_ERR0("ei_epmd_r4_port","<- CLOSE"); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - closesocket(fd); + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); return -1; } - closesocket(fd); + ei_close__(fd); s = buf; port = get16be(s); diff --git a/lib/erl_interface/src/epmd/epmd_publish.c b/lib/erl_interface/src/epmd/epmd_publish.c index 47d68a6db0..20b8e867e8 100644 --- a/lib/erl_interface/src/epmd/epmd_publish.c +++ b/lib/erl_interface/src/epmd/epmd_publish.c @@ -68,8 +68,10 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) int nlen = strlen(alive); int len = elen + nlen + 13; /* hard coded: be careful! */ int n; - int res, creation; - + int err, res, creation; + ssize_t dlen; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; + if (len > sizeof(buf)-2) { erl_errno = ERANGE; @@ -93,29 +95,39 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) if ((fd = ei_epmd_connect_tmo(NULL,ms)) < 0) return fd; - if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + dlen = (ssize_t) len+2; + err = ei_write_fill_t__(fd, buf, &dlen, tmo); + if (!err && dlen != (ssize_t) len + 2) + erl_errno = EIO; + if (err) { + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); + return -1; } EI_TRACE_CONN6("ei_epmd_r4_publish", "-> ALIVE2_REQ alive=%s port=%d ntype=%d " "proto=%d dist-high=%d dist-low=%d", alive,port,'H',EI_MYPROTO,EI_DIST_HIGH,EI_DIST_LOW); - - if ((n = ei_read_fill_t(fd, buf, 4, ms)) != 4) { + + dlen = (ssize_t) 4; + err = ei_read_fill_t__(fd, buf, &dlen, tmo); + n = (int) dlen; + if (!err && n != 4) + err = EIO; + if (err) { EI_TRACE_ERR0("ei_epmd_r4_publish","<- CLOSE"); - closesocket(fd); - erl_errno = (n == -2) ? ETIMEDOUT : EIO; + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); return -2; /* version mismatch */ } + /* Don't close fd here! It keeps us registered with epmd */ s = buf; if (((res=get8(s)) != EI_EPMD_ALIVE2_RESP)) { /* response */ EI_TRACE_ERR1("ei_epmd_r4_publish","<- unknown (%d)",res); EI_TRACE_ERR0("ei_epmd_r4_publish","-> CLOSE"); - closesocket(fd); + ei_close__(fd); erl_errno = EIO; return -1; } @@ -124,7 +136,7 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) if (((res=get8(s)) != 0)) { /* 0 == success */ EI_TRACE_ERR1("ei_epmd_r4_publish"," result=%d (fail)",res); - closesocket(fd); + ei_close__(fd); erl_errno = EIO; return -1; } diff --git a/lib/erl_interface/src/epmd/epmd_unpublish.c b/lib/erl_interface/src/epmd/epmd_unpublish.c index 255d0ffb59..c112f74147 100644 --- a/lib/erl_interface/src/epmd/epmd_unpublish.c +++ b/lib/erl_interface/src/epmd/epmd_unpublish.c @@ -58,7 +58,9 @@ int ei_unpublish_tmo(const char *alive, unsigned ms) char buf[EPMDBUF]; char *s = (char*)buf; int len = 1 + strlen(alive); - int fd, res; + int fd, err; + ssize_t dlen; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; if (len > sizeof(buf)-3) { erl_errno = ERANGE; @@ -72,20 +74,29 @@ int ei_unpublish_tmo(const char *alive, unsigned ms) /* FIXME can't connect, return success?! At least commen whats up */ if ((fd = ei_epmd_connect_tmo(NULL,ms)) < 0) return fd; - if ((res = ei_write_fill_t(fd, buf, len+2,ms)) != len+2) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + dlen = (ssize_t) len+2; + err = ei_write_fill_t__(fd, buf, &dlen, tmo); + if (!err && dlen != (ssize_t) len + 2) + erl_errno = EIO; + if (err) { + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); + return -1; } EI_TRACE_CONN1("ei_unpublish_tmo","-> STOP %s",alive); - - if ((res = ei_read_fill_t(fd, buf, 7, ms)) != 7) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; + + dlen = (ssize_t) 7; + err = ei_read_fill_t__(fd, buf, &dlen, tmo); + if (!err && dlen != (ssize_t) 7) + erl_errno = EIO; + if (err) { + ei_close__(fd); + EI_CONN_SAVE_ERRNO__(err); + return -1; } - closesocket(fd); + + ei_close__(fd); buf[7]=(char)0; /* terminate the string */ if (!strcmp("STOPPED",(char *)buf)) { diff --git a/lib/erl_interface/src/legacy/erl_connect.c b/lib/erl_interface/src/legacy/erl_connect.c index 7ffd545d3e..e2fd4611c0 100644 --- a/lib/erl_interface/src/legacy/erl_connect.c +++ b/lib/erl_interface/src/legacy/erl_connect.c @@ -179,15 +179,13 @@ int erl_xconnect(Erl_IpAddr addr, char *alivename) * * API: erl_close_connection() * - * Close a connection. FIXME call ei_close_connection() later. - * * Returns 0 on success and -1 on failure. * ***************************************************************************/ int erl_close_connection(int fd) { - return closesocket(fd); + return ei_close_connection(fd); } /* @@ -220,7 +218,10 @@ int erl_reg_send(int fd, char *server_name, ETERM *msg) ei_x_buff x; int r; - ei_x_new_with_version(&x); + if (ei_x_new_with_version(&x) < 0) { + erl_errno = ENOMEM; + return 0; + } if (ei_x_encode_term(&x, msg) < 0) { erl_errno = EINVAL; r = 0; diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c index 9ad92121f4..7ed2bdbc93 100644 --- a/lib/erl_interface/src/legacy/erl_eterm.c +++ b/lib/erl_interface/src/legacy/erl_eterm.c @@ -65,7 +65,7 @@ void erl_init(void *hp,long heap_size) { erl_init_malloc(hp, heap_size); erl_init_marshal(); - ei_init_resolve(); + (void) ei_init(); } void erl_set_compat_rel(unsigned rel) diff --git a/lib/erl_interface/src/misc/ei_init.c b/lib/erl_interface/src/misc/ei_init.c new file mode 100644 index 0000000000..5357968657 --- /dev/null +++ b/lib/erl_interface/src/misc/ei_init.c @@ -0,0 +1,32 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2019. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "ei.h" +#include "ei_resolve.h" +#include "ei_internal.h" + +int +ei_init(void) +{ + int error = ei_init_connect(); + if (error) + return error; + return ei_init_resolve(); +} diff --git a/lib/erl_interface/src/misc/ei_internal.h b/lib/erl_interface/src/misc/ei_internal.h index aa6aacd703..f28dd6d668 100644 --- a/lib/erl_interface/src/misc/ei_internal.h +++ b/lib/erl_interface/src/misc/ei_internal.h @@ -22,19 +22,20 @@ #ifndef _EI_INTERNAL_H #define _EI_INTERNAL_H +#ifdef EI_HIDE_REAL_ERRNO +# define EI_CONN_SAVE_ERRNO__(E) \ + ((E) == ETIMEDOUT ? (erl_errno = ETIMEDOUT) : (erl_errno = EIO)) +#else +# define EI_CONN_SAVE_ERRNO__(E) \ + (erl_errno = (E)) +#endif + /* * Some useful stuff not to be exported to users. */ #ifdef __WIN32__ #define MAXPATHLEN 256 -#define writesocket(sock,buf,nbyte) send(sock,buf,nbyte,0) -#define readsocket(sock,buf,nbyte) recv(sock,buf,nbyte,0) -#else /* not __WIN32__ */ -#define writesocket write -#define readsocket read -#define closesocket close -#define ioctlsocket ioctl #endif /* @@ -152,7 +153,12 @@ extern int ei_tracelevel; +int ei_init_connect(void); + void ei_trace_printf(const char *name, int level, const char *format, ...); int ei_internal_use_r9_pids_ports(void); + +int ei_get_cbs_ctx__(ei_socket_callbacks **cbs, void **ctx, int fd); + #endif /* _EI_INTERNAL_H */ diff --git a/lib/erl_interface/src/misc/ei_portio.c b/lib/erl_interface/src/misc/ei_portio.c index 8cd35bf2e5..bccc86c1b1 100644 --- a/lib/erl_interface/src/misc/ei_portio.c +++ b/lib/erl_interface/src/misc/ei_portio.c @@ -19,9 +19,13 @@ * */ + +#include "eidef.h" + #ifdef __WIN32__ #include <winsock2.h> #include <windows.h> +#include <winbase.h> #include <process.h> #include <stdio.h> #include <stdlib.h> @@ -35,10 +39,6 @@ static unsigned long param_one = 1; #define SET_BLOCKING(Sock) ioctlsocket((Sock),FIONBIO,¶m_zero) #define SET_NONBLOCKING(Sock) ioctlsocket((Sock),FIONBIO,¶m_one) -#define ERROR_WOULDBLOCK WSAEWOULDBLOCK -#define ERROR_TIMEDOUT WSAETIMEDOUT -#define ERROR_INPROGRESS WSAEINPROGRESS -#define GET_SOCKET_ERROR() WSAGetLastError() #define MEANS_SOCKET_ERROR(Ret) ((Ret == SOCKET_ERROR)) #define IS_INVALID_SOCKET(Sock) ((Sock) == INVALID_SOCKET) @@ -50,125 +50,414 @@ static unsigned long param_one = 1; #include <taskLib.h> #include <inetLib.h> #include <selectLib.h> -#include <sys/types.h> #include <ioLib.h> #include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <timers.h> static unsigned long param_zero = 0; static unsigned long param_one = 1; #define SET_BLOCKING(Sock) ioctl((Sock),FIONBIO,(int)¶m_zero) #define SET_NONBLOCKING(Sock) ioctl((Sock),FIONBIO,(int)¶m_one) -#define ERROR_WOULDBLOCK EWOULDBLOCK -#define ERROR_TIMEDOUT ETIMEDOUT -#define ERROR_INPROGRESS EINPROGRESS -#define GET_SOCKET_ERROR() (errno) #define MEANS_SOCKET_ERROR(Ret) ((Ret) == ERROR) #define IS_INVALID_SOCKET(Sock) ((Sock) < 0) #else /* other unix */ #include <stdlib.h> -#include <sys/types.h> #include <sys/socket.h> -#include <sys/uio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> -#ifndef EWOULDBLOCK -#define ERROR_WOULDBLOCK EAGAIN -#else -#define ERROR_WOULDBLOCK EWOULDBLOCK -#endif #define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \ fcntl((fd), F_GETFL, 0) & ~O_NONBLOCK) #define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \ fcntl((fd), F_GETFL, 0) | O_NONBLOCK) -#define ERROR_TIMEDOUT ETIMEDOUT -#define ERROR_INPROGRESS EINPROGRESS -#define GET_SOCKET_ERROR() (errno) #define MEANS_SOCKET_ERROR(Ret) ((Ret) < 0) #define IS_INVALID_SOCKET(Sock) ((Sock) < 0) #endif /* common includes */ -#include "eidef.h" +#include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "ei_portio.h" -#include "ei_internal.h" - #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #else #include <time.h> #endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include "ei_portio.h" +#include "ei_internal.h" + +#ifdef __WIN32__ -#ifdef HAVE_WRITEV -static int ei_writev_t(int fd, struct iovec *iov, int iovcnt, unsigned ms) +#define writesocket(sock,buf,nbyte) send(sock,buf,nbyte,0) +#define readsocket(sock,buf,nbyte) recv(sock,buf,nbyte,0) + +static int get_error(void) { - int res; - if (ms != 0) { - fd_set writemask; - struct timeval tv; - tv.tv_sec = (time_t) (ms / 1000U); - ms %= 1000U; - tv.tv_usec = (time_t) (ms * 1000U); - FD_ZERO(&writemask); - FD_SET(fd,&writemask); - switch (select(fd+1, NULL, &writemask, NULL, &tv)) { - case -1 : - return -1; /* i/o error */ - case 0: - return -2; /* timeout */ - default: - if (!FD_ISSET(fd, &writemask)) { - return -1; /* Other error */ - } - } + switch (WSAGetLastError()) { + case WSAEWOULDBLOCK: return EWOULDBLOCK; + case WSAETIMEDOUT: return ETIMEDOUT; + case WSAEINPROGRESS: return EINPROGRESS; + case WSA_NOT_ENOUGH_MEMORY: return ENOMEM; + case WSA_INVALID_PARAMETER: return EINVAL; + case WSAEBADF: return EBADF; + case WSAEINVAL: return EINVAL; + case WSAEADDRINUSE: return EADDRINUSE; + case WSAENETUNREACH: return ENETUNREACH; + case WSAECONNABORTED: return ECONNABORTED; + case WSAECONNRESET: return ECONNRESET; + case WSAECONNREFUSED: return ECONNREFUSED; + case WSAEHOSTUNREACH: return EHOSTUNREACH; + case WSAEMFILE: return EMFILE; + case WSAEALREADY: return EALREADY; + default: return EIO; } +} + +#else /* not __WIN32__ */ + +#define writesocket write +#define readsocket read +#define closesocket close +#define ioctlsocket ioctl + +static int get_error(void) +{ + int err = errno; + if (err == 0) + return EIO; /* Make sure never to return 0 as error code... */ + return err; +} + +#endif + +int ei_plugin_socket_impl__ = 0; + +/* + * Callbacks for communication over TCP/IPv4 + */ + +static int tcp_get_fd(void *ctx, int *fd) +{ + return EI_DFLT_CTX_TO_FD__(ctx, fd); +} + +static int tcp_hs_packet_header_size(void *ctx, int *sz) +{ + int fd; + *sz = 2; + return EI_DFLT_CTX_TO_FD__(ctx, &fd); +} + +static int tcp_handshake_complete(void *ctx) +{ + int res, fd, one = 1; + + res = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (res) + return res; + + res = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + return 0; +} + +static int tcp_socket(void **ctx, void *setup_ctx) +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (MEANS_SOCKET_ERROR(fd)) + return get_error(); + + *ctx = EI_FD_AS_CTX__(fd); + return 0; +} + +static int tcp_close(void *ctx) +{ + int fd, res; + + res = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (res) + return res; + + res = closesocket(fd); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + return 0; +} + +static int tcp_listen(void *ctx, void *addr, int *len, int backlog) +{ + int res, fd; + socklen_t sz = (socklen_t) *len; + int on = 1; + + res = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (res) + return res; + + res = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + res = bind(fd, (struct sockaddr *) addr, sz); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + res = getsockname(fd, (struct sockaddr *) addr, (socklen_t *) &sz); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + *len = (int) sz; + + res = listen(fd, backlog); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + return 0; +} + +static int tcp_accept(void **ctx, void *addr, int *len, unsigned unused) +{ + int fd, res; + socklen_t addr_len = (socklen_t) *len; + + if (!ctx) + return EINVAL; + + res = EI_DFLT_CTX_TO_FD__(*ctx, &fd); + if (res) + return res; + + res = accept(fd, (struct sockaddr*) addr, &addr_len); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + *len = (int) addr_len; + + *ctx = EI_FD_AS_CTX__(res); + return 0; +} + +static int tcp_connect(void *ctx, void *addr, int len, unsigned unused) +{ + int res, fd; + + res = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (res) + return res; + + res = connect(fd, (struct sockaddr *) addr, len); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + + return 0; +} + +#if defined(EI_HAVE_STRUCT_IOVEC__) && defined(HAVE_WRITEV) + +static int tcp_writev(void *ctx, const void *viov, int iovcnt, ssize_t *len, unsigned unused) +{ + const struct iovec *iov = (const struct iovec *) viov; + int fd, error; + ssize_t res; + + error = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (error) + return error; + res = writev(fd, iov, iovcnt); - return (res < 0) ? -1 : res; + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + *len = res; + return 0; +} + +#endif + +static int tcp_write(void *ctx, const char* buf, ssize_t *len, unsigned unused) +{ + int error, fd; + ssize_t res; + + error = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (error) + return error; + + res = writesocket(fd, buf, *len); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + *len = res; + return 0; +} + +static int tcp_read(void *ctx, char* buf, ssize_t *len, unsigned unused) +{ + int error, fd; + ssize_t res; + + error = EI_DFLT_CTX_TO_FD__(ctx, &fd); + if (error) + return error; + + res = readsocket(fd, buf, *len); + if (MEANS_SOCKET_ERROR(res)) + return get_error(); + *len = res; + return 0; +} + +ei_socket_callbacks ei_default_socket_callbacks = { + 0, /* flags */ + tcp_socket, + tcp_close, + tcp_listen, + tcp_accept, + tcp_connect, +#if defined(EI_HAVE_STRUCT_IOVEC__) && defined(HAVE_WRITEV) + tcp_writev, +#else + NULL, +#endif + tcp_write, + tcp_read, + + tcp_hs_packet_header_size, + tcp_handshake_complete, + tcp_handshake_complete, + tcp_get_fd + +}; + + +/* + * + */ + +#if defined(EI_HAVE_STRUCT_IOVEC__) + +int ei_socket_callbacks_have_writev__(ei_socket_callbacks *cbs) +{ + return !!cbs->writev; } -int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, unsigned ms) +static int writev_ctx_t__(ei_socket_callbacks *cbs, void *ctx, + const struct iovec *iov, int iovcnt, + ssize_t *len, + unsigned ms) { - int i; - int done; + int error; + + if (!(cbs->flags & EI_SCLBK_FLG_FULL_IMPL) && ms != EI_SCLBK_INF_TMO) { + int fd; + + error = EI_GET_FD__(cbs, ctx, &fd); + if (error) + return error; + + do { + fd_set writemask; + struct timeval tv; + + tv.tv_sec = (time_t) (ms / 1000U); + ms %= 1000U; + tv.tv_usec = (time_t) (ms * 1000U); + FD_ZERO(&writemask); + FD_SET(fd,&writemask); + switch (select(fd+1, NULL, &writemask, NULL, &tv)) { + case -1 : + error = get_error(); + if (error != EINTR) + return error; + break; + case 0: + return ETIMEDOUT; /* timeout */ + default: + if (!FD_ISSET(fd, &writemask)) { + return EIO; /* Other error */ + } + error = 0; + break; + } + } while (error == EINTR); + } + do { + error = cbs->writev(ctx, (const void *) iov, iovcnt, len, ms); + } while (error == EINTR); + return error; +} + +int ei_writev_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, + const struct iovec *iov, int iovcnt, + ssize_t *len, + unsigned ms) +{ + ssize_t i, done, sum; struct iovec *iov_base = NULL; struct iovec *current_iov; int current_iovcnt; - int sum; + int fd, error; + int basic; + + if (!cbs->writev) + return ENOTSUP; + + error = EI_GET_FD__(cbs, ctx, &fd); + if (error) + return error; + basic = !(cbs->flags & EI_SCLBK_FLG_FULL_IMPL); + for (sum = 0, i = 0; i < iovcnt; ++i) { sum += iov[i].iov_len; } - if (ms != 0U) { + if (basic && ms != 0U) { SET_NONBLOCKING(fd); } current_iovcnt = iovcnt; current_iov = (struct iovec *) iov; done = 0; for (;;) { - i = ei_writev_t(fd, current_iov, current_iovcnt, ms); - if (i <= 0) { /* ei_writev_t should always return at least 1 */ + + error = writev_ctx_t__(cbs, ctx, current_iov, current_iovcnt, &i, ms); + if (error) { + *len = done; if (ms != 0U) { SET_BLOCKING(fd); } if (iov_base != NULL) { free(iov_base); } - return (i); - } + return error; + } done += i; if (done < sum) { if (iov_base == NULL) { iov_base = malloc(sizeof(struct iovec) * iovcnt); if (iov_base == NULL) { - return -1; + *len = done; + return ENOMEM; } memcpy(iov_base, iov, sizeof(struct iovec) * iovcnt); current_iov = iov_base; @@ -189,195 +478,383 @@ int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, unsigned break; } } - if (ms != 0U) { + if (basic && ms != 0U) { SET_BLOCKING(fd); } if (iov_base != NULL) { free(iov_base); } - return (sum); + *len = done; + return 0; } +#endif /* defined(EI_HAVE_STRUCT_IOVEC__) */ -#endif - -int ei_connect_t(int fd, void *sinp, int sin_siz, unsigned ms) +int ei_socket_ctx__(ei_socket_callbacks *cbs, void **ctx, void *setup_ctx) { int res; - int error; - int s_res; - struct timeval tv; - fd_set writefds; - fd_set exceptfds; - - if (ms == 0) { - res = connect(fd, sinp, sin_siz); - return (res < 0) ? -1 : res; - } else { - SET_NONBLOCKING(fd); - res = connect(fd, sinp, sin_siz); - error = GET_SOCKET_ERROR(); - SET_BLOCKING(fd); - if (!MEANS_SOCKET_ERROR(res)) { - return (res < 0) ? -1 : res; - } else { - if (error != ERROR_WOULDBLOCK && - error != ERROR_INPROGRESS) { - return -1; - } else { - tv.tv_sec = (long) (ms/1000U); - ms %= 1000U; - tv.tv_usec = (long) (ms * 1000U); - FD_ZERO(&writefds); - FD_SET(fd,&writefds); - FD_ZERO(&exceptfds); - FD_SET(fd,&exceptfds); - s_res = select(fd + 1, NULL, &writefds, &exceptfds, &tv); - switch (s_res) { - case 0: - return -2; - case 1: - if (FD_ISSET(fd, &exceptfds)) { - return -1; - } else { - return 0; /* Connect completed */ - } - default: - return -1; - } - } - } - } + + do { + res = cbs->socket(ctx, setup_ctx); + } while (res == EINTR); + + return res; } -int ei_accept_t(int fd, void *addr, void *addrlen, unsigned ms) +int ei_close_ctx__(ei_socket_callbacks *cbs, void *ctx) { - int res; - if (ms != 0) { - fd_set readmask; - struct timeval tv; - tv.tv_sec = (time_t) (ms / 1000U); - ms %= 1000U; - tv.tv_usec = (time_t) (ms * 1000U); - FD_ZERO(&readmask); - FD_SET(fd,&readmask); - switch (select(fd+1, &readmask, NULL, NULL, &tv)) { - case -1 : - return -1; /* i/o error */ - case 0: - return -2; /* timeout */ - default: - if (!FD_ISSET(fd, &readmask)) { - return -1; /* Other error */ - } - } - } - res = (int) accept(fd,addr,addrlen); - return (res < 0) ? -1 : res; + return cbs->close(ctx); } + +int ei_connect_ctx_t__(ei_socket_callbacks *cbs, void *ctx, + void *addr, int len, unsigned ms) +{ + int res, fd; + + if ((cbs->flags & EI_SCLBK_FLG_FULL_IMPL) || ms == EI_SCLBK_INF_TMO) { + do { + res = cbs->connect(ctx, addr, len, ms); + } while (res == EINTR); + return res; + } + + res = EI_GET_FD__(cbs, ctx, &fd); + if (res) + return res; + SET_NONBLOCKING(fd); + do { + res = cbs->connect(ctx, addr, len, 0); + } while (res == EINTR); + SET_BLOCKING(fd); + switch (res) { + case EINPROGRESS: + case EAGAIN: +#ifdef EWOULDBLOCK +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif +#endif + break; + default: + return res; + } -static int ei_read_t(int fd, char* buf, int len, unsigned ms) + while (1) { + struct timeval tv; + fd_set writefds; + fd_set exceptfds; + + tv.tv_sec = (long) (ms/1000U); + ms %= 1000U; + tv.tv_usec = (long) (ms * 1000U); + FD_ZERO(&writefds); + FD_SET(fd,&writefds); + FD_ZERO(&exceptfds); + FD_SET(fd,&exceptfds); + res = select(fd + 1, NULL, &writefds, &exceptfds, &tv); + switch (res) { + case -1: + res = get_error(); + if (res != EINTR) + return res; + break; + case 0: + return ETIMEDOUT; + case 1: + if (!FD_ISSET(fd, &exceptfds)) + return 0; /* Connect completed */ + /* fall through... */ + default: + return EIO; + } + } +} + +int ei_listen_ctx__(ei_socket_callbacks *cbs, void *ctx, + void *adr, int *len, int backlog) { int res; - if (ms != 0) { - fd_set readmask; - struct timeval tv; - tv.tv_sec = (time_t) (ms / 1000U); - ms %= 1000U; - tv.tv_usec = (time_t) (ms * 1000U); - FD_ZERO(&readmask); - FD_SET(fd,&readmask); - switch (select(fd+1, &readmask, NULL, NULL, &tv)) { - case -1 : - return -1; /* i/o error */ - case 0: - return -2; /* timeout */ - default: - if (!FD_ISSET(fd, &readmask)) { - return -1; /* Other error */ - } - } + + do { + res = cbs->listen(ctx, adr, len, backlog); + } while (res == EINTR); + return res; +} + +int ei_accept_ctx_t__(ei_socket_callbacks *cbs, void **ctx, + void *addr, int *len, unsigned ms) +{ + int error; + + if (!(cbs->flags & EI_SCLBK_FLG_FULL_IMPL) && ms != EI_SCLBK_INF_TMO) { + int fd; + + error = EI_GET_FD__(cbs, *ctx, &fd); + if (error) + return error; + + do { + fd_set readmask; + struct timeval tv; + + tv.tv_sec = (time_t) (ms / 1000U); + ms %= 1000U; + tv.tv_usec = (time_t) (ms * 1000U); + FD_ZERO(&readmask); + FD_SET(fd,&readmask); + switch (select(fd+1, &readmask, NULL, NULL, &tv)) { + case -1 : + error = get_error(); + if (error != EINTR) + return error; + break; + case 0: + return ETIMEDOUT; /* timeout */ + default: + if (!FD_ISSET(fd, &readmask)) { + return EIO; /* Other error */ + } + error = 0; + break; + } + } while (error == EINTR); } - res = readsocket(fd, buf, len); - return (res < 0) ? -1 : res; + do { + error = cbs->accept(ctx, addr, len, ms); + } while (error == EINTR); + return error; } -static int ei_write_t(int fd, const char* buf, int len, unsigned ms) +static int read_ctx_t__(ei_socket_callbacks *cbs, void *ctx, + char* buf, ssize_t *len, unsigned ms) { - int res; - if (ms != 0) { - fd_set writemask; - struct timeval tv; - tv.tv_sec = (time_t) (ms / 1000U); - ms %= 1000U; - tv.tv_usec = (time_t) (ms * 1000U); - FD_ZERO(&writemask); - FD_SET(fd,&writemask); - switch (select(fd+1, NULL, &writemask, NULL, &tv)) { - case -1 : - return -1; /* i/o error */ - case 0: - return -2; /* timeout */ - default: - if (!FD_ISSET(fd, &writemask)) { - return -1; /* Other error */ - } - } + int error; + + if (!(cbs->flags & EI_SCLBK_FLG_FULL_IMPL) && ms != EI_SCLBK_INF_TMO) { + int fd; + + error = EI_GET_FD__(cbs, ctx, &fd); + if (error) + return error; + + do { + fd_set readmask; + struct timeval tv; + + tv.tv_sec = (time_t) (ms / 1000U); + ms %= 1000U; + tv.tv_usec = (time_t) (ms * 1000U); + FD_ZERO(&readmask); + FD_SET(fd,&readmask); + switch (select(fd+1, &readmask, NULL, NULL, &tv)) { + case -1 : + error = get_error(); + if (error != EINTR) + return error; + break; + case 0: + return ETIMEDOUT; /* timeout */ + default: + if (!FD_ISSET(fd, &readmask)) { + return EIO; /* Other error */ + } + error = 0; + break; + } + } while (error == EINTR); + } + do { + error = cbs->read(ctx, buf, len, ms); + } while (error == EINTR); + return error; +} + +static int write_ctx_t__(ei_socket_callbacks *cbs, void *ctx, const char* buf, ssize_t *len, unsigned ms) +{ + int error; + + if (!(cbs->flags & EI_SCLBK_FLG_FULL_IMPL) && ms != EI_SCLBK_INF_TMO) { + int fd; + + error = EI_GET_FD__(cbs, ctx, &fd); + if (error) + return error; + + do { + fd_set writemask; + struct timeval tv; + + tv.tv_sec = (time_t) (ms / 1000U); + ms %= 1000U; + tv.tv_usec = (time_t) (ms * 1000U); + FD_ZERO(&writemask); + FD_SET(fd,&writemask); + switch (select(fd+1, NULL, &writemask, NULL, &tv)) { + case -1 : + error = get_error(); + if (error != EINTR) + return error; + break; + case 0: + return ETIMEDOUT; /* timeout */ + default: + if (!FD_ISSET(fd, &writemask)) { + return EIO; /* Other error */ + } + error = 0; + break; + } + } while (error == EINTR); } - res = writesocket(fd, buf, len); - return (res < 0) ? -1 : res; + do { + error = cbs->write(ctx, buf, len, ms); + } while (error == EINTR); + return error; } /* * Fill buffer, return buffer length, 0 for EOF, < 0 (and sets errno) * for error. */ -int ei_read_fill_t(int fd, char* buf, int len, unsigned ms) +int ei_read_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, char* buf, ssize_t *len, unsigned ms) { - int i,got=0; + ssize_t got = 0; + ssize_t want = *len; do { - i = ei_read_t(fd, buf+got, len-got, ms); - if (i <= 0) - return (i); - got += i; - } while (got < len); - return (len); - + ssize_t read_len = want-got; + int error; + + do { + error = read_ctx_t__(cbs, ctx, buf+got, &read_len, ms); + } while (error == EINTR); + if (error) + return error; + if (read_len == 0) { + *len = got; + return 0; + } + got += read_len; + } while (got < want); + + *len = got; + return 0; } /* read_fill */ -int ei_read_fill(int fd, char* buf, int len) +int ei_read_fill_ctx__(ei_socket_callbacks *cbs, void *ctx, char* buf, ssize_t *len) { - return ei_read_fill_t(fd, buf, len, 0); + return ei_read_fill_ctx_t__(cbs, ctx, buf, len, 0); } /* write entire buffer on fd or fail (setting errno) */ -int ei_write_fill_t(int fd, const char *buf, int len, unsigned ms) +int ei_write_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, const char *buf, ssize_t *len, unsigned ms) { - int i,done=0; - if (ms != 0U) { + ssize_t tot = *len, done = 0; + int error, fd = -1, basic = !(cbs->flags & EI_SCLBK_FLG_FULL_IMPL); + + if (basic && ms != 0U) { + error = EI_GET_FD__(cbs, ctx, &fd); + if (error) + return error; SET_NONBLOCKING(fd); } do { - i = ei_write_t(fd, buf+done, len-done, ms); - if (i <= 0) { - if (ms != 0U) { + ssize_t write_len = tot-done; + error = write_ctx_t__(cbs, ctx, buf+done, &write_len, ms); + if (error) { + *len = done; + if (basic && ms != 0U) { SET_BLOCKING(fd); } - return (i); + return error; } - done += i; - } while (done < len); - if (ms != 0U) { + done += write_len; + } while (done < tot); + if (basic && ms != 0U) { SET_BLOCKING(fd); } - return (len); + *len = done; + return 0; +} + +int ei_write_fill_ctx__(ei_socket_callbacks *cbs, void *ctx, const char *buf, ssize_t *len) +{ + return ei_write_fill_ctx_t__(cbs, ctx, buf, len, 0); +} + +/* + * Internal API for TCP/IPv4 + */ + +int ei_connect_t__(int fd, void *addr, int len, unsigned ms) +{ + return ei_connect_ctx_t__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + addr, len, ms); } -int ei_write_fill(int fd, const char *buf, int len) +int ei_socket__(int *fd) { - return ei_write_fill_t(fd, buf, len, 0); + void *ctx; + int error = ei_socket_ctx__(&ei_default_socket_callbacks, &ctx, NULL); + if (error) + return error; + return EI_GET_FD__(&ei_default_socket_callbacks, ctx, fd); } +int ei_close__(int fd) +{ + return ei_close_ctx__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd)); +} + +int ei_listen__(int fd, void *adr, int *len, int backlog) +{ + return ei_listen_ctx__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + adr, len, backlog); +} + +int ei_accept_t__(int *fd, void *addr, int *len, unsigned ms) +{ + void *ctx = EI_FD_AS_CTX__(*fd); + int error = ei_accept_ctx_t__(&ei_default_socket_callbacks, &ctx, + addr, len, ms); + if (error) + return error; + return EI_GET_FD__(&ei_default_socket_callbacks, ctx, fd); +} + +int ei_read_fill_t__(int fd, char* buf, ssize_t *len, unsigned ms) +{ + return ei_read_fill_ctx_t__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + buf, len, ms); +} + +int ei_read_fill__(int fd, char* buf, ssize_t *len) +{ + return ei_read_fill_ctx_t__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + buf, len, 0); +} + +int ei_write_fill_t__(int fd, const char *buf, ssize_t *len, unsigned ms) +{ + return ei_write_fill_ctx_t__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + buf, len, ms); +} + +int ei_write_fill__(int fd, const char *buf, ssize_t *len) +{ + return ei_write_fill_ctx_t__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + buf, len, 0); +} + +#if defined(EI_HAVE_STRUCT_IOVEC__) && defined(HAVE_WRITEV) + +int ei_writev_fill_t__(int fd, const struct iovec *iov, int iovcnt, ssize_t *len, unsigned ms) +{ + return ei_writev_fill_ctx_t__(&ei_default_socket_callbacks, EI_FD_AS_CTX__(fd), + iov, iovcnt, len, ms); +} + +#endif + diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h index bded811a35..a84b5ca09c 100644 --- a/lib/erl_interface/src/misc/ei_portio.h +++ b/lib/erl_interface/src/misc/ei_portio.h @@ -21,21 +21,94 @@ */ #ifndef _EI_PORTIO_H #define _EI_PORTIO_H -#if !defined(__WIN32__) && !defined(VXWORKS) -#ifdef HAVE_WRITEV + +#undef EI_HAVE_STRUCT_IOVEC__ +#if !defined(__WIN32__) && !defined(VXWORKS) && defined(HAVE_SYS_UIO_H) /* Declaration of struct iovec *iov should be visible in this scope. */ -#include <sys/uio.h> +# include <sys/uio.h> +# define EI_HAVE_STRUCT_IOVEC__ #endif + +/* + * Internal API. Should not be used outside of the erl_interface application... + */ + +int ei_socket_ctx__(ei_socket_callbacks *cbs, void **ctx, void *setup); +int ei_close_ctx__(ei_socket_callbacks *cbs, void *ctx); +int ei_listen_ctx__(ei_socket_callbacks *cbs, void *ctx, void *adr, int *len, int backlog); +int ei_accept_ctx_t__(ei_socket_callbacks *cbs, void **ctx, void *addr, int *len, unsigned ms); +int ei_connect_ctx_t__(ei_socket_callbacks *cbs, void *ctx, void *addr, int len, unsigned ms); +int ei_read_fill_ctx__(ei_socket_callbacks *cbs, void *ctx, char* buf, ssize_t *len); +int ei_write_fill_ctx__(ei_socket_callbacks *cbs, void *ctx, const char *buf, ssize_t *len); +int ei_read_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, char* buf, ssize_t *len, unsigned ms); +int ei_write_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, const char *buf, ssize_t *len, unsigned ms); +#if defined(EI_HAVE_STRUCT_IOVEC__) +int ei_writev_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, const struct iovec *iov, int iovcnt, ssize_t *len, unsigned ms); +int ei_socket_callbacks_have_writev__(ei_socket_callbacks *cbs); #endif -int ei_accept_t(int fd, void *addr, void *addrlen, unsigned ms); -int ei_connect_t(int fd, void *sinp, int sin_siz, unsigned ms); -int ei_read_fill(int fd, char* buf, int len); -int ei_write_fill(int fd, const char *buf, int len); -int ei_read_fill_t(int fd, char* buf, int len, unsigned ms); -int ei_write_fill_t(int fd, const char *buf, int len, unsigned ms); -#ifdef HAVE_WRITEV -int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, unsigned ms); +ei_socket_callbacks ei_default_socket_callbacks; + +#define EI_FD_AS_CTX__(FD) \ + ((void *) (long) (FD)) + +#define EI_DFLT_CTX_TO_FD__(CTX, FD) \ + ((int) (long) (CTX) < 0 \ + ? EBADF \ + : (*(FD) = (int) (long) (CTX), 0)) + +#define EI_GET_FD__(CBS, CTX, FD) \ + ((CBS) == &ei_default_socket_callbacks \ + ? EI_DFLT_CTX_TO_FD__((CTX), FD) \ + : (CBS)->get_fd((CTX), (FD))) + +extern int ei_plugin_socket_impl__; + +#if !defined(_REENTRANT) + +#define EI_HAVE_PLUGIN_SOCKET_IMPL__ \ + ei_plugin_socket_impl__ +#define EI_SET_HAVE_PLUGIN_SOCKET_IMPL__ \ + ei_plugin_socket_impl__ = 1 + +#elif ((ETHR_HAVE___atomic_load_n & SIZEOF_INT) \ + && (ETHR_HAVE___atomic_store_n & SIZEOF_INT)) + +#define EI_HAVE_PLUGIN_SOCKET_IMPL__ \ + __atomic_load_n(&ei_plugin_socket_impl__, __ATOMIC_ACQUIRE) +#define EI_SET_HAVE_PLUGIN_SOCKET_IMPL__ \ + __atomic_store_n(&ei_plugin_socket_impl__, 1, __ATOMIC_RELEASE) + +#else + +/* No gcc atomics; always lookup using ei_get_cbs_ctx()... */ +#define EI_HAVE_PLUGIN_SOCKET_IMPL__ 0 +#define EI_SET_HAVE_PLUGIN_SOCKET_IMPL__ (void) 0 + +#endif + +#define EI_GET_CBS_CTX__(CBS, CTX, FD) \ + (EI_HAVE_PLUGIN_SOCKET_IMPL__ \ + ? ei_get_cbs_ctx__((CBS), (CTX), (FD)) \ + : ((FD) < 0 \ + ? EBADF \ + : (*(CBS) = &ei_default_socket_callbacks, \ + *(CTX) = EI_FD_AS_CTX__((FD)), \ + 0))) +/* + * The following uses our own TCP/IPv4 socket implementation... + */ +int ei_socket__(int *fd); +int ei_close__(int fd); +int ei_listen__(int fd, void *adr, int *len, int backlog); +int ei_accept_t__(int *fd, void *addr, int *len, unsigned ms); +int ei_connect_t__(int fd, void *addr, int len, unsigned ms); +int ei_read_fill__(int fd, char* buf, ssize_t *len); +int ei_write_fill__(int fd, const char *buf, ssize_t *len); +int ei_read_fill_t__(int fd, char* buf, ssize_t *len, unsigned ms); +int ei_write_fill_t__(int fd, const char *buf, ssize_t *len, unsigned ms); +#if defined(EI_HAVE_STRUCT_IOVEC__) && defined(HAVE_WRITEV) +int ei_writev_fill_t__(int fd, const struct iovec *iov, int iovcnt, ssize_t *len, unsigned ms); #endif #endif /* _EI_PORTIO_H */ diff --git a/lib/erl_interface/src/not_used/send_link.c b/lib/erl_interface/src/not_used/send_link.c index 7be476fd93..38fae27df4 100644 --- a/lib/erl_interface/src/not_used/send_link.c +++ b/lib/erl_interface/src/not_used/send_link.c @@ -50,6 +50,7 @@ static int link_unlink(int fd, const erlang_pid *from, const erlang_pid *to, char *s; int index = 0; int n; + unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; index = 5; /* max sizes: */ ei_encode_version(msgbuf,&index); /* 1 */ @@ -69,7 +70,7 @@ static int link_unlink(int fd, const erlang_pid *from, const erlang_pid *to, if (ei_trace_distribution > 1) ei_show_sendmsg(stderr,msgbuf,NULL); #endif - n = ei_write_fill_t(fd,msgbuf,index,ms); + n = ei_write_fill_t__(fd,msgbuf,index,tmo); return (n==index ? 0 : -1); } diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl index 78a433d21b..9c9c3f86b6 100644 --- a/lib/erl_interface/test/ei_accept_SUITE.erl +++ b/lib/erl_interface/test/ei_accept_SUITE.erl @@ -81,12 +81,10 @@ ei_accept(Config) when is_list(Config) -> ei_threaded_accept(Config) when is_list(Config) -> Einode = filename:join(proplists:get_value(data_dir, Config), "eiaccnode"), - N = 1, % 3, + N = 3, Host = atom_to_list(node()), - Port = 6767, - start_einode(Einode, N, Host, Port), + start_einode(Einode, N, Host), io:format("started eiaccnode"), - %%spawn_link(fun() -> start_einode(Einode, N, Host, Port) end), TestServerPid = self(), [spawn_link(fun() -> send_rec_einode(I, TestServerPid) end) || I <- lists:seq(0, N-1)], [receive I -> ok end || I <- lists:seq(0, N-1) ], @@ -159,10 +157,9 @@ send_rec_einode(N, TestServerPid) -> ct:fail(EINode) end. -start_einode(Einode, N, Host, Port) -> +start_einode(Einode, N, Host) -> Einodecmd = Einode ++ " " ++ atom_to_list(erlang:get_cookie()) - ++ " " ++ integer_to_list(N) ++ " " ++ Host ++ " " - ++ integer_to_list(Port) ++ " nothreads", + ++ " " ++ integer_to_list(N) ++ " " ++ Host, io:format("Einodecmd ~p ~n", [Einodecmd]), open_port({spawn, Einodecmd}, []), ok. diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c index 50df848b69..c209f506b1 100644 --- a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c +++ b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c @@ -74,6 +74,8 @@ TESTCASE(interpret) int i; ei_term term; + ei_init(); + ei_x_new(&x); while (get_bin_term(&x, &term) == 0) { char* buf = x.buff, func[MAXATOMLEN]; @@ -125,45 +127,26 @@ static void cmd_ei_connect_init(char* buf, int len) ei_x_free(&res); } -static int my_listen(int port) -{ - int listen_fd; - struct sockaddr_in addr; - const char *on = "1"; - - if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) - return -1; - - setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on)); - - memset((void*) &addr, 0, (size_t) sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - if (bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) - return -1; - - listen(listen_fd, 5); - return listen_fd; -} - static void cmd_ei_publish(char* buf, int len) { int index = 0; - int listen, r; - long port; + int iport, lfd, r; + long lport; ei_x_buff x; int i; /* get port */ - if (ei_decode_long(buf, &index, &port) < 0) + if (ei_decode_long(buf, &index, &lport) < 0) fail("expected int (port)"); /* Make a listen socket */ - if ((listen = my_listen(port)) <= 0) + + iport = (int) lport; + lfd = ei_listen(&ec, &iport, 5); + if (lfd < 0) fail("listen"); + lport = (long) iport; - if ((i = ei_publish(&ec, port)) == -1) + if ((i = ei_publish(&ec, lport)) == -1) fail("ei_publish"); #ifdef VXWORKS save_fd(i); @@ -171,7 +154,7 @@ static void cmd_ei_publish(char* buf, int len) /* send listen-fd, result and errno */ ei_x_new_with_version(&x); ei_x_encode_tuple_header(&x, 3); - ei_x_encode_long(&x, listen); + ei_x_encode_long(&x, (long) lfd); ei_x_encode_long(&x, i); ei_x_encode_long(&x, erl_errno); send_bin_term(&x); diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c b/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c index 308f843530..90c7a2259f 100644 --- a/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c +++ b/lib/erl_interface/test/ei_accept_SUITE_data/eiaccnode.c @@ -47,8 +47,6 @@ #define MAIN main #endif -static int my_listen(int port); - /* A small einode. To be called from the test case ei_accept_SUITE:multi_thread @@ -64,7 +62,6 @@ static int my_listen(int port); */ static const char* cookie, * desthost; -static int port; /* actually base port */ #ifndef SD_SEND #ifdef SHUTWR @@ -74,10 +71,6 @@ static int port; /* actually base port */ #endif #endif -#ifndef __WIN32__ -#define closesocket(fd) close(fd) -#endif - #ifdef __WIN32__ static DWORD WINAPI #else @@ -86,26 +79,32 @@ static void* einode_thread(void* num) { int n = (int)num; + int port; ei_cnode ec; - char myname[100], destname[100]; + char myname[100], destname[100], filename[100]; int r, fd, listen; ErlConnect conn; erlang_msg msg; -/* FILE* f;*/ + FILE* file; - sprintf(myname, "eiacc%d", n); - printf("thread %d (%s) listening\n", n, myname, destname); + sprintf(filename, "eiacc%d_trace.txt", n); + file = fopen(filename, "w"); + + sprintf(myname, "eiacc%d", n); fflush(file); r = ei_connect_init(&ec, myname, cookie, 0); - if ((listen = my_listen(port+n)) <= 0) { - printf("listen err\n"); + port = 0; + listen = ei_listen(&ec, &port, 5); + if (listen <= 0) { + fprintf(file, "listen err\n"); fflush(file); exit(7); } - if (ei_publish(&ec, port + n) == -1) { - printf("ei_publish port %d\n", port+n); + fprintf(file, "thread %d (%s:%s) listening on port %d\n", n, myname, destname, port); + if (ei_publish(&ec, port) == -1) { + fprintf(file, "ei_publish port %d\n", port+n); fflush(file); exit(8); } fd = ei_accept(&ec, listen, &conn); - printf("ei_accept %d\n", fd); + fprintf(file, "ei_accept %d\n", fd); fflush(file); if (fd >= 0) { ei_x_buff x, xs; int index, version; @@ -117,37 +116,38 @@ static void* if (got == ERL_TICK) continue; if (got == ERL_ERROR) { - printf("receive error %d\n", n); + fprintf(file, "receive error %d\n", n); fflush(file); return 0; } - printf("received %d\n", got); + fprintf(file, "received %d\n", got); fflush(file); break; } index = 0; if (ei_decode_version(x.buff, &index, &version) != 0) { - printf("ei_decode_version %d\n", n); + fprintf(file, "ei_decode_version %d\n", n); fflush(file); return 0; } if (ei_decode_pid(x.buff, &index, &pid) != 0) { - printf("ei_decode_pid %d\n", n); + fprintf(file, "ei_decode_pid %d\n", n); fflush(file); return 0; } -/* fprintf(f, "got pid from %s \n", pid.node);*/ + fprintf(file, "got pid from %s \n", pid.node); fflush(file); ei_x_new_with_version(&xs); ei_x_encode_tuple_header(&xs, 2); ei_x_encode_long(&xs, n); ei_x_encode_pid(&xs, &pid); r = ei_send(fd, &pid, xs.buff, xs.index); -/* fprintf(f, "sent %d bytes %d\n", xs.index, r);*/ + fprintf(file, "sent %d bytes %d\n", xs.index, r); fflush(file); shutdown(fd, SD_SEND); - closesocket(fd); + ei_close_connection(fd); ei_x_free(&x); ei_x_free(&xs); } else { - printf("coudn't connect fd %d r %d\n", fd, r); + fprintf(file, "coudn't connect fd %d r %d\n", fd, r); fflush(file); } - printf("done thread %d\n", n); -/* fclose(f);*/ + ei_close_connection(listen); + fprintf(file, "done thread %d\n", n); + fclose(file); return 0; } @@ -170,12 +170,16 @@ MAIN(int argc, char *argv[]) if (n > 100) exit(2); desthost = argv[3]; - port = atoi(argv[4]); -#ifndef VXWORKS - no_threads = argv[5] != NULL && strcmp(argv[5], "nothreads") == 0; -#else + if (argc == 3) + no_threads = 0; + else + no_threads = argv[4] != NULL && strcmp(argv[4], "nothreads") == 0; +#ifdef VXWORKS no_threads = 1; #endif + + ei_init(); + for (i = 0; i < n; ++i) { if (!no_threads) { #ifndef VXWORKS @@ -209,27 +213,3 @@ MAIN(int argc, char *argv[]) printf("ok\n"); return 0; } - -static int my_listen(int port) -{ - int listen_fd; - struct sockaddr_in addr; - const char *on = "1"; - - if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) - return -1; - - setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on)); - - memset((void*) &addr, 0, (size_t) sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - if (bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) - return -1; - - listen(listen_fd, 5); - return listen_fd; -} - diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c index 29c03d7604..58c0c7f8d8 100644 --- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c +++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c @@ -73,6 +73,8 @@ TESTCASE(interpret) int i; ei_term term; + ei_init(); + ei_x_new(&x); while (get_bin_term(&x, &term) == 0) { char* buf = x.buff, func[MAXATOMLEN]; diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c index f945a7d378..e516f310b6 100644 --- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c +++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c @@ -321,6 +321,8 @@ int ei_decode_my_string(const char *buf, int *index, char *to, TESTCASE(test_ei_decode_long) { + ei_init(); + EI_DECODE_2 (decode_long, 2, long, 0); EI_DECODE_2 (decode_long, 2, long, 255); EI_DECODE_2 (decode_long, 5, long, 256); @@ -363,6 +365,8 @@ TESTCASE(test_ei_decode_long) TESTCASE(test_ei_decode_ulong) { + ei_init(); + EI_DECODE_2 (decode_ulong, 2, unsigned long, 0); EI_DECODE_2 (decode_ulong, 2, unsigned long, 255); EI_DECODE_2 (decode_ulong, 5, unsigned long, 256); @@ -409,6 +413,8 @@ TESTCASE(test_ei_decode_ulong) TESTCASE(test_ei_decode_longlong) { + ei_init(); + #ifndef VXWORKS EI_DECODE_2 (decode_longlong, 2, EI_LONGLONG, 0); EI_DECODE_2 (decode_longlong, 2, EI_LONGLONG, 255); @@ -443,6 +449,8 @@ TESTCASE(test_ei_decode_longlong) TESTCASE(test_ei_decode_ulonglong) { + ei_init(); + #ifndef VXWORKS EI_DECODE_2 (decode_ulonglong, 2, EI_ULONGLONG, 0); EI_DECODE_2 (decode_ulonglong, 2, EI_ULONGLONG, 255); @@ -478,6 +486,8 @@ TESTCASE(test_ei_decode_ulonglong) TESTCASE(test_ei_decode_char) { + ei_init(); + EI_DECODE_2(decode_char, 2, char, 0); EI_DECODE_2(decode_char, 2, char, 0x7f); EI_DECODE_2(decode_char, 2, char, 0xff); @@ -491,6 +501,8 @@ TESTCASE(test_ei_decode_char) TESTCASE(test_ei_decode_nonoptimal) { + ei_init(); + EI_DECODE_2(decode_char, 2, char, 42); EI_DECODE_2(decode_char, 5, char, 42); EI_DECODE_2(decode_char, 4, char, 42); @@ -612,6 +624,8 @@ TESTCASE(test_ei_decode_nonoptimal) TESTCASE(test_ei_decode_misc) { + ei_init(); + /* EI_DECODE_0(decode_version); */ @@ -647,6 +661,7 @@ TESTCASE(test_ei_decode_misc) TESTCASE(test_ei_decode_utf8_atom) { + ei_init(); EI_DECODE_STRING_4(decode_my_atom_as, 4, P99({229,0}), /* LATIN1 "�" */ P99({ERLANG_ANY,ERLANG_LATIN1,ERLANG_LATIN1})); diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c index 9977683d59..55d9ed1b1a 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c +++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c @@ -477,6 +477,8 @@ TESTCASE(test_ei_decode_encode) { int i; + ei_init(); + decode_encode_one(&fun_type); decode_encode_one(&pid_type); decode_encode_one(&port_type); diff --git a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c index 32811fdf22..6f63cc5d7e 100644 --- a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c +++ b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c @@ -403,6 +403,8 @@ TESTCASE(test_ei_encode_long) { + ei_init(); + EI_ENCODE_1(encode_long, 0); EI_ENCODE_1(encode_long, 255); @@ -430,6 +432,8 @@ TESTCASE(test_ei_encode_long) TESTCASE(test_ei_encode_ulong) { + ei_init(); + EI_ENCODE_1(encode_ulong, 0); EI_ENCODE_1(encode_ulong, 255); @@ -454,6 +458,7 @@ TESTCASE(test_ei_encode_ulong) TESTCASE(test_ei_encode_longlong) { + ei_init(); #ifndef VXWORKS @@ -494,6 +499,7 @@ TESTCASE(test_ei_encode_longlong) TESTCASE(test_ei_encode_ulonglong) { + ei_init(); #ifndef VXWORKS @@ -527,6 +533,8 @@ TESTCASE(test_ei_encode_ulonglong) TESTCASE(test_ei_encode_char) { + ei_init(); + EI_ENCODE_1(encode_char, 0); EI_ENCODE_1(encode_char, 0x7f); @@ -540,6 +548,8 @@ TESTCASE(test_ei_encode_char) TESTCASE(test_ei_encode_misc) { + ei_init(); + EI_ENCODE_0(encode_version); EI_ENCODE_1(encode_double, 0.0); @@ -594,6 +604,8 @@ TESTCASE(test_ei_encode_fails) char buf[1024]; int index; + ei_init(); + /* FIXME the ei_x versions are not tested */ index = 0; @@ -660,6 +672,7 @@ TESTCASE(test_ei_encode_fails) TESTCASE(test_ei_encode_utf8_atom) { + ei_init(); EI_ENCODE_3(encode_atom_as, "�", ERLANG_LATIN1, ERLANG_UTF8); EI_ENCODE_3(encode_atom_as, "�", ERLANG_LATIN1, ERLANG_LATIN1); @@ -686,6 +699,7 @@ TESTCASE(test_ei_encode_utf8_atom) TESTCASE(test_ei_encode_utf8_atom_len) { + ei_init(); EI_ENCODE_4(encode_atom_len_as, "���", 1, ERLANG_LATIN1, ERLANG_UTF8); EI_ENCODE_4(encode_atom_len_as, "���", 2, ERLANG_LATIN1, ERLANG_LATIN1); diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c index 8450332b28..1c0443c0f4 100644 --- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c +++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c @@ -48,6 +48,8 @@ send_format(char* format) TESTCASE(atoms) { + ei_init(); + send_format("''"); send_format("'a'"); send_format("'A'"); @@ -82,6 +84,8 @@ TESTCASE(atoms) TESTCASE(tuples) { + ei_init(); + send_format("{}"); send_format("{a}"); send_format("{a, b}"); @@ -108,6 +112,8 @@ TESTCASE(lists) ei_x_buff x; static char str[65537]; + ei_init(); + send_format("[]"); send_format("[a]"); send_format("[a, b]"); @@ -177,6 +183,8 @@ TESTCASE(format_wo_ver) { */ ei_x_buff x; + ei_init(); + ei_x_new (&x); ei_x_format(&x, "[-1, +2, ~c, {~a,~s},{~a,~i}]", 'c', "a", "b", "c", 10); send_bin_term(&x); diff --git a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c index 15cfbcae34..80be3016e6 100644 --- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c +++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c @@ -84,6 +84,8 @@ static void send_printed3f(char* format, float f1, float f2) TESTCASE(atoms) { + ei_init(); + send_printed("''"); send_printed("'a'"); send_printed("'A'"); @@ -118,6 +120,8 @@ TESTCASE(atoms) TESTCASE(tuples) { + ei_init(); + send_printed("{}"); send_printed("{a}"); send_printed("{a, b}"); @@ -138,6 +142,8 @@ TESTCASE(lists) { ei_x_buff x; + ei_init(); + send_printed("[]"); send_printed("[a]"); send_printed("[a, b]"); @@ -164,6 +170,8 @@ TESTCASE(strings) { ei_x_buff x; + ei_init(); + send_printed("\"\n\""); send_printed("\"\r\n\""); send_printed("\"a\""); diff --git a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c index 39846e4a58..693e405f75 100644 --- a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c +++ b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c @@ -96,6 +96,8 @@ TESTCASE(framework_check) int i; #endif + ei_init(); + OPEN_DEBUGFILE(1); DEBUGF(("B�rjar... \n")); @@ -340,6 +342,7 @@ TESTCASE(recv_tmo) int com_sock = -1; ei_cnode nodeinfo; + ei_init(); OPEN_DEBUGFILE(5); @@ -450,6 +453,7 @@ TESTCASE(send_tmo) int com_sock = -1; ei_cnode nodeinfo; + ei_init(); OPEN_DEBUGFILE(4); @@ -591,7 +595,7 @@ TESTCASE(connect_tmo) int com_sock = -1; ei_cnode nodeinfo; - + ei_init(); OPEN_DEBUGFILE(3); @@ -680,7 +684,7 @@ TESTCASE(accept_tmo) ErlConnect peer; ei_cnode nodeinfo; - + ei_init(); OPEN_DEBUGFILE(2); diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c b/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c index bead0f8413..b87feb9dfc 100644 --- a/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c +++ b/lib/erl_interface/test/erl_eterm_SUITE_data/cnode.c @@ -20,7 +20,7 @@ #include <stdlib.h> #include <stdio.h> - +#include <string.h> #include "ei.h" #include "erl_interface.h" @@ -68,6 +68,7 @@ MAIN(int argc, char **argv) char host[80]; int number; ETERM *ref, *ref1, *ref2; + FILE *dfile = fopen("cnode_debug_printout", "w"); erl_init(NULL, 0); @@ -80,28 +81,30 @@ MAIN(int argc, char **argv) gethostname(host, sizeof(host)); sprintf(node, "c%d@%s", number, host); - printf("s = %d\n", s); + fprintf(dfile, "s = %d\n", s); fflush(dfile); sprintf(server, "test_server@%s", host); fd = erl_connect(server); - printf("fd = %d\n", fd); + fprintf(dfile, "fd = %d\n", fd); -/* printf("dist = %d\n", erl_distversion(fd)); */ +/* fprintf(dfile, "dist = %d\n", erl_distversion(fd)); */ #if 1 ref = erl_mk_long_ref(node, 4711, 113, 98, 0); #else ref = erl_mk_ref(node, 4711, 0); #endif - printf("ref = %d\n", ref); + fprintf(dfile, "ref = %p\n", ref); fflush(dfile); s = erl_reg_send(fd, "mip", ref); - printf("s = %d\n", s); + fprintf(dfile, "s = %d\n", s); fflush(dfile); { ETERM* emsg; emsg = SELF(fd); - erl_reg_send(fd,"mip",emsg); + fprintf(dfile, "pid = %p\n", emsg); fflush(dfile); + s = erl_reg_send(fd,"mip",emsg); + fprintf(dfile, "s2 = %d\n", s); fflush(dfile); erl_free_term(emsg); } @@ -116,28 +119,29 @@ MAIN(int argc, char **argv) #endif switch (s) { case ERL_TICK: - printf("tick\n"); + fprintf(dfile, "tick\n"); break; case ERL_ERROR: - printf("error\n"); + fprintf(dfile, "error: %s (%d)\n", strerror(erl_errno), erl_errno); break; case ERL_MSG: - printf("msg %d\n", msgsize); + fprintf(dfile, "msg %d\n", msgsize); break; default: - printf("unknown result %d\n", s); + fprintf(dfile, "unknown result %d\n", s); break; } + fflush(dfile); } while (s == ERL_TICK); s = erl_reg_send(fd, "mip", msg.msg); - printf("s = %d\n", s); + fprintf(dfile, "s = %d\n", s); fflush(dfile); s = erl_reg_send(fd, "mip", msg.to); - printf("s = %d\n", s); + fprintf(dfile, "s = %d\n", s); fflush(dfile); #if 0 /* from = NULL! */ s = erl_reg_send(fd, "mip", msg.from); - printf("s = %d\n", s); + fprintf(dfile, "s = %d\n", s); fflush(dfile); #endif #if 0 @@ -150,17 +154,19 @@ MAIN(int argc, char **argv) ref1 = erl_mk_long_ref(node, 4711, 113, 98, 0); ref2 = erl_mk_ref(node, 4711, 0); s = erl_encode(ref1, buf1); - printf("enc1 s = %d\n", s); + fprintf(dfile, "enc1 s = %d\n", s); fflush(dfile); s = erl_encode(ref2, buf2); - printf("enc2 s = %d\n", s); + fprintf(dfile, "enc2 s = %d\n", s); fflush(dfile); s = erl_compare_ext(buf1, buf2); - printf("comp s = %d\n", s); + fprintf(dfile, "comp s = %d\n", s); fflush(dfile); /* Compare, in another way */ s = erl_match(ref1, ref2); - printf("match s = %d\n", s); + fprintf(dfile, "match s = %d\n", s); fflush(dfile); #endif + fclose(dfile); + erl_close_connection(fd); return 0; diff --git a/lib/et/doc/src/et.xml b/lib/et/doc/src/et.xml index 3009b559e1..a362d00b3e 100644 --- a/lib/et/doc/src/et.xml +++ b/lib/et/doc/src/et.xml @@ -32,14 +32,14 @@ <rev>%VSN%</rev> <file>et</file> </header> - <module>et</module> + <module since="">et</module> <modulesummary>Main API of the Event Trace (ET) application</modulesummary> <description> <p>Interface module for the Event Trace (ET) application</p> </description> <funcs> <func> - <name>trace_me(DetailLevel, From, To, Label, Contents) -> hopefully_traced</name> + <name since="OTP R13B04">trace_me(DetailLevel, From, To, Label, Contents) -> hopefully_traced</name> <fsummary>A function that is intended to be traced.</fsummary> <type> <v>DetailLevel = integer(X) when X =< 0, X >= 100</v> @@ -70,7 +70,7 @@ </func> <func> - <name>trace_me(DetailLevel, FromTo, Label, Contents) -> hopefully_traced</name> + <name since="OTP R13B04">trace_me(DetailLevel, FromTo, Label, Contents) -> hopefully_traced</name> <fsummary>A function that is intended to be traced.</fsummary> <desc> <p>Invokes <c>et:trace_me/5</c> with both <c>From</c> and <c>To</c> @@ -79,8 +79,8 @@ </func> <func> - <name>phone_home(DetailLevel, FromTo, Label, Contents) -> hopefully_traced</name> - <name>phone_home(DetailLevel, From, To, Label, Contents) -> hopefully_traced</name> + <name since="">phone_home(DetailLevel, FromTo, Label, Contents) -> hopefully_traced</name> + <name since="">phone_home(DetailLevel, From, To, Label, Contents) -> hopefully_traced</name> <fsummary>Send a signal to the outer space</fsummary> <desc> <p>These functions sends a signal to the outer space and the @@ -90,8 +90,8 @@ </desc> </func> <func> - <name>report_event(DetailLevel, FromTo, Label, Contents) -> hopefully_traced</name> - <name>report_event(DetailLevel, From, To, Label, Contents) -> hopefully_traced</name> + <name since="">report_event(DetailLevel, FromTo, Label, Contents) -> hopefully_traced</name> + <name since="">report_event(DetailLevel, From, To, Label, Contents) -> hopefully_traced</name> <fsummary>Deprecated functions</fsummary> <desc> <p>Deprecated functions which for the time being are kept for diff --git a/lib/et/doc/src/et_collector.xml b/lib/et/doc/src/et_collector.xml index fd90ecfc41..f908612797 100644 --- a/lib/et/doc/src/et_collector.xml +++ b/lib/et/doc/src/et_collector.xml @@ -32,14 +32,14 @@ <rev>%VSN%</rev> <file>et_collector.xml</file> </header> - <module>et_collector</module> + <module since="">et_collector</module> <modulesummary>Collect trace events and provide a backing storage appropriate for iteration </modulesummary> <description> <p>Interface module for the Event Trace (ET) application</p> </description> <funcs> <func> - <name>start_link(Options) -> {ok, CollectorPid} | {error, Reason}</name> + <name since="">start_link(Options) -> {ok, CollectorPid} | {error, Reason}</name> <fsummary>Start a collector process</fsummary> <type> <v>Options = [option()]</v> @@ -105,7 +105,7 @@ </desc> </func> <func> - <name>stop(CollectorPid) -> ok</name> + <name since="">stop(CollectorPid) -> ok</name> <fsummary>Stop a collector process</fsummary> <type> <v>CollectorPid = pid()</v> @@ -115,7 +115,7 @@ </desc> </func> <func> - <name>save_event_file(CollectorPid, FileName, Options) -> ok | {error, Reason}</name> + <name since="">save_event_file(CollectorPid, FileName, Options) -> ok | {error, Reason}</name> <fsummary>Save the events to a file</fsummary> <type> <v>CollectorPid = pid()</v> @@ -139,7 +139,7 @@ </desc> </func> <func> - <name>load_event_file(CollectorPid, FileName) -> {ok, BadBytes} | exit(Reason)</name> + <name since="">load_event_file(CollectorPid, FileName) -> {ok, BadBytes} | exit(Reason)</name> <fsummary>Load the event table from a file</fsummary> <type> <v>CollectorPid = pid()</v> @@ -152,9 +152,9 @@ </desc> </func> <func> - <name>report(Handle, TraceOrEvent) -> {ok, Continuation} | exit(Reason)</name> - <name>report_event(Handle, DetailLevel, FromTo, Label, Contents) -> {ok, Continuation} | exit(Reason)</name> - <name>report_event(Handle, DetailLevel, From, To, Label, Contents) -> {ok, Continuation} | exit(Reason)</name> + <name since="">report(Handle, TraceOrEvent) -> {ok, Continuation} | exit(Reason)</name> + <name since="">report_event(Handle, DetailLevel, FromTo, Label, Contents) -> {ok, Continuation} | exit(Reason)</name> + <name since="">report_event(Handle, DetailLevel, From, To, Label, Contents) -> {ok, Continuation} | exit(Reason)</name> <fsummary>Report an event to the collector</fsummary> <type> <v>Handle = Initial | Continuation</v> @@ -181,7 +181,7 @@ </desc> </func> <func> - <name>make_key(Type, Stuff) -> Key</name> + <name since="">make_key(Type, Stuff) -> Key</name> <fsummary>Make a key out of an event record or an old key</fsummary> <type> <v>Type = record(table_handle) | trace_ts | event_ts</v> @@ -193,7 +193,7 @@ </desc> </func> <func> - <name>get_table_handle(CollectorPid) -> Handle</name> + <name since="">get_table_handle(CollectorPid) -> Handle</name> <fsummary>Return a table handle</fsummary> <type> <v>CollectorPid = pid()</v> @@ -204,7 +204,7 @@ </desc> </func> <func> - <name>get_global_pid() -> CollectorPid | exit(Reason)</name> + <name since="">get_global_pid() -> CollectorPid | exit(Reason)</name> <fsummary>Return a the identity of the globally registered collector if there is any</fsummary> <type> <v>CollectorPid = pid()</v> @@ -216,7 +216,7 @@ </desc> </func> <func> - <name>change_pattern(CollectorPid, RawPattern) -> {old_pattern, TracePattern}</name> + <name since="">change_pattern(CollectorPid, RawPattern) -> {old_pattern, TracePattern}</name> <fsummary>Change active trace pattern globally on all trace nodes</fsummary> <type> <v>CollectorPid = pid()</v> @@ -232,9 +232,9 @@ </desc> </func> <func> - <name>dict_insert(CollectorPid, {filter, collector}, FilterFun) -> ok</name> - <name>dict_insert(CollectorPid, {subscriber, SubscriberPid}, Void) -> ok</name> - <name>dict_insert(CollectorPid, Key, Val) -> ok</name> + <name since="">dict_insert(CollectorPid, {filter, collector}, FilterFun) -> ok</name> + <name since="">dict_insert(CollectorPid, {subscriber, SubscriberPid}, Void) -> ok</name> + <name since="">dict_insert(CollectorPid, Key, Val) -> ok</name> <fsummary>Insert a dictionary entry and send a {et, {dict_insert, Key, Val}} tuple to all registered subscribers.</fsummary> <type> <v>CollectorPid = pid()</v> @@ -259,7 +259,7 @@ </desc> </func> <func> - <name>dict_lookup(CollectorPid, Key) -> [Val]</name> + <name since="">dict_lookup(CollectorPid, Key) -> [Val]</name> <fsummary>Lookup a dictionary entry and return zero or one value</fsummary> <type> <v>CollectorPid = pid()</v> @@ -273,7 +273,7 @@ </desc> </func> <func> - <name>dict_delete(CollectorPid, Key) -> ok</name> + <name since="">dict_delete(CollectorPid, Key) -> ok</name> <fsummary>Delete a dictionary entry and send a {et, {dict_delete, Key}} tuple to all registered subscribers.</fsummary> <type> <v>CollectorPid = pid()</v> @@ -290,7 +290,7 @@ </desc> </func> <func> - <name>dict_match(CollectorPid, Pattern) -> [Match]</name> + <name since="">dict_match(CollectorPid, Pattern) -> [Match]</name> <fsummary>Match some dictionary entries</fsummary> <type> <v>CollectorPid = pid()</v> @@ -306,7 +306,7 @@ </desc> </func> <func> - <name>multicast(_CollectorPid, Msg) -> ok</name> + <name since="">multicast(_CollectorPid, Msg) -> ok</name> <fsummary>Sends a message to all registered subscribers</fsummary> <type> <v>CollectorPid = pid()</v> @@ -318,7 +318,7 @@ </desc> </func> <func> - <name>start_trace_client(CollectorPid, Type, Parameters) -> file_loaded | {trace_client_pid, pid()} | exit(Reason)</name> + <name since="">start_trace_client(CollectorPid, Type, Parameters) -> file_loaded | {trace_client_pid, pid()} | exit(Reason)</name> <fsummary>Load raw Erlang trace from a file, port or process.</fsummary> <type> <v>Type = dbg_trace_client_type()</v> @@ -330,14 +330,14 @@ </desc> </func> <func> - <name>iterate(Handle, Prev, Limit) -> NewAcc</name> + <name since="">iterate(Handle, Prev, Limit) -> NewAcc</name> <fsummary>Iterates over the currently stored events</fsummary> <desc> <p>Short for iterate(Handle, Prev, Limit, undefined, Prev) -> NewAcc</p> </desc> </func> <func> - <name>iterate(Handle, Prev, Limit, Fun, Acc) -> NewAcc</name> + <name since="">iterate(Handle, Prev, Limit, Fun, Acc) -> NewAcc</name> <fsummary>Iterate over the currently stored events</fsummary> <type> <v>Handle = collector_pid() | table_handle()</v> @@ -361,7 +361,7 @@ </desc> </func> <func> - <name>clear_table(Handle) -> ok</name> + <name since="">clear_table(Handle) -> ok</name> <fsummary>Clear the event table</fsummary> <type> <v>Handle = collector_pid() | table_handle()</v> diff --git a/lib/et/doc/src/et_selector.xml b/lib/et/doc/src/et_selector.xml index 30ca74c872..3c766cafb7 100644 --- a/lib/et/doc/src/et_selector.xml +++ b/lib/et/doc/src/et_selector.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>et_selector.xml</file> </header> - <module>et_selector</module> + <module since="">et_selector</module> <modulesummary>Define event transforms and trace patterns</modulesummary> <description> <p></p> @@ -40,7 +40,7 @@ <funcs> <func> - <name>make_pattern(RawPattern) -> TracePattern</name> + <name since="">make_pattern(RawPattern) -> TracePattern</name> <fsummary>Makes a trace pattern suitable to feed change_pattern/1</fsummary> <type> @@ -61,7 +61,7 @@ </func> <func> - <name>change_pattern(Pattern) -> ok</name> + <name since="">change_pattern(Pattern) -> ok</name> <fsummary>Activates/deactivates tracing by changing the current trace pattern</fsummary> @@ -85,7 +85,7 @@ </desc> </func> <func> - <name>parse_event(Mod, ValidTraceData) -> false | true | {true, Event}</name> + <name since="">parse_event(Mod, ValidTraceData) -> false | true | {true, Event}</name> <fsummary>Transforms trace data and makes an event record out of it</fsummary> diff --git a/lib/et/doc/src/et_viewer.xml b/lib/et/doc/src/et_viewer.xml index e0b39636e9..9d59eef668 100644 --- a/lib/et/doc/src/et_viewer.xml +++ b/lib/et/doc/src/et_viewer.xml @@ -32,14 +32,14 @@ <rev>%VSN%</rev> <file>et_viewer.xml</file> </header> - <module>et_viewer</module> + <module since="">et_viewer</module> <modulesummary>Displays a sequence chart for trace events (messages/actions)</modulesummary> <description> <p></p> </description> <funcs> <func> - <name>file(FileName) -> {ok, ViewerPid} | {error, Reason}</name> + <name since="">file(FileName) -> {ok, ViewerPid} | {error, Reason}</name> <fsummary>Start a new event viewer and a corresponding collector and load them with trace events from a trace file.</fsummary> <type> <v>FileName() = string()</v> @@ -52,7 +52,7 @@ </desc> </func> <func> - <name>start() -> ok</name> + <name since="">start() -> ok</name> <fsummary>Simplified start of a sequence chart viewer with global tracing activated.</fsummary> <desc> <p>Simplified start of a sequence chart viewer with @@ -62,7 +62,7 @@ </desc> </func> <func> - <name>start(Options) -> ok</name> + <name since="">start(Options) -> ok</name> <fsummary>Start of a sequence chart viewer without linking to the parent process.</fsummary> <desc> <p>Start of a sequence chart viewer without linking @@ -70,7 +70,7 @@ </desc> </func> <func> - <name>start_link(Options) -> {ok, ViewerPid} | {error, Reason}</name> + <name since="">start_link(Options) -> {ok, ViewerPid} | {error, Reason}</name> <fsummary>Start a sequence chart viewer for trace events (messages/actions)</fsummary> <type> <v>Options = [option() | collector_option()]</v> @@ -125,7 +125,7 @@ </desc> </func> <func> - <name>get_collector_pid(ViewerPid) -> CollectorPid</name> + <name since="">get_collector_pid(ViewerPid) -> CollectorPid</name> <fsummary>Returns the identifier of the collector process</fsummary> <type> <v>ViewerPid = pid()</v> @@ -136,7 +136,7 @@ </desc> </func> <func> - <name>stop(ViewerPid) -> ok</name> + <name since="">stop(ViewerPid) -> ok</name> <fsummary>Stops a viewer</fsummary> <type> <v>ViewerPid = pid()</v> diff --git a/lib/ftp/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml index 34e3ff84b0..9645b03364 100644 --- a/lib/ftp/doc/src/ftp.xml +++ b/lib/ftp/doc/src/ftp.xml @@ -29,7 +29,7 @@ <rev>B</rev> <file>ftp.xml</file> </header> - <module>ftp</module> + <module since="">ftp</module> <modulesummary>A File Transfer Protocol client.</modulesummary> <description> @@ -272,7 +272,7 @@ <funcs> <func> - <name>account(Pid, Account) -> ok | {error, Reason}</name> + <name since="">account(Pid, Account) -> ok | {error, Reason}</name> <fsummary>Specifies which account to use.</fsummary> <type> <v>Pid = pid()</v> @@ -289,8 +289,8 @@ </func> <func> - <name>append(Pid, LocalFile) -> </name> - <name>append(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name> + <name since="">append(Pid, LocalFile) -> </name> + <name since="">append(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Transfers a file to remote server, and appends it to <c>Remotefile</c>.</fsummary> <type> @@ -310,7 +310,7 @@ </func> <func> - <name>append_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name> + <name since="">append_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Transfers a binary into a remote file.</fsummary> <type> <v>Pid = pid()</v> @@ -328,7 +328,7 @@ </func> <func> - <name>append_chunk(Pid, Bin) -> ok | {error, Reason}</name> + <name since="">append_chunk(Pid, Bin) -> ok | {error, Reason}</name> <fsummary>Appends a chunk to the remote file.</fsummary> <type> <v>Pid = pid()</v> @@ -348,7 +348,7 @@ </func> <func> - <name>append_chunk_start(Pid, File) -> ok | {error, Reason}</name> + <name since="">append_chunk_start(Pid, File) -> ok | {error, Reason}</name> <fsummary>Starts transfer of file chunks for appending to <c>File</c>.</fsummary> <type> <v>Pid = pid()</v> @@ -365,7 +365,7 @@ </func> <func> - <name>append_chunk_end(Pid) -> ok | {error, Reason}</name> + <name since="">append_chunk_end(Pid) -> ok | {error, Reason}</name> <fsummary>Stops transfer of chunks for appending.</fsummary> <type> <v>Pid = pid()</v> @@ -381,7 +381,7 @@ </func> <func> - <name>cd(Pid, Dir) -> ok | {error, Reason}</name> + <name since="">cd(Pid, Dir) -> ok | {error, Reason}</name> <fsummary>Changes remote working directory.</fsummary> <type> <v>Pid = pid()</v> @@ -397,7 +397,7 @@ </func> <func> - <name>close(Pid) -> ok</name> + <name since="">close(Pid) -> ok</name> <fsummary>Ends the FTP session.</fsummary> <type> <v>Pid = pid()</v> @@ -411,7 +411,7 @@ </func> <func> - <name>delete(Pid, File) -> ok | {error, Reason}</name> + <name since="">delete(Pid, File) -> ok | {error, Reason}</name> <fsummary>Deletes a file at the remote server.</fsummary> <type> <v>Pid = pid()</v> @@ -426,7 +426,7 @@ </func> <func> - <name>formaterror(Tag) -> string()</name> + <name since="">formaterror(Tag) -> string()</name> <fsummary>Returns error diagnostics.</fsummary> <type> <v>Tag = {error, atom()} | atom()</v> @@ -440,7 +440,7 @@ </func> <func> - <name>lcd(Pid, Dir) -> ok | {error, Reason}</name> + <name since="">lcd(Pid, Dir) -> ok | {error, Reason}</name> <fsummary>Changes local working directory.</fsummary> <type> <v>Pid = pid()</v> @@ -455,7 +455,7 @@ </func> <func> - <name>lpwd(Pid) -> {ok, Dir}</name> + <name since="">lpwd(Pid) -> {ok, Dir}</name> <fsummary>Gets local current working directory.</fsummary> <type> <v>Pid = pid()</v> @@ -470,8 +470,8 @@ </func> <func> - <name>ls(Pid) -> </name> - <name>ls(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name> + <name since="">ls(Pid) -> </name> + <name since="">ls(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name> <fsummary>List of files.</fsummary> <type> <v>Pid = pid()</v> @@ -493,7 +493,7 @@ </func> <func> - <name>mkdir(Pid, Dir) -> ok | {error, Reason}</name> + <name since="">mkdir(Pid, Dir) -> ok | {error, Reason}</name> <fsummary>Creates a remote directory.</fsummary> <type> <v>Pid = pid()</v> @@ -510,8 +510,8 @@ </func> <func> - <name>nlist(Pid) -> </name> - <name>nlist(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name> + <name since="">nlist(Pid) -> </name> + <name since="">nlist(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name> <fsummary>List of files.</fsummary> <type> <v>Pid = pid()</v> @@ -535,8 +535,8 @@ </func> <func> - <name>open(Host) -> {ok, Pid} | {error, Reason}</name> - <name>open(Host, Opts) -> {ok, Pid} | {error, Reason}</name> + <name since="">open(Host) -> {ok, Pid} | {error, Reason}</name> + <name since="">open(Host, Opts) -> {ok, Pid} | {error, Reason}</name> <fsummary>Starts a standalone FTP client.</fsummary> <type> <v>Host = string() | ip_address()</v> @@ -550,7 +550,7 @@ <v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v> <v>port() = integer() > 0 (default is 21)</v> <v>mode() = active | passive (default is passive)</v> - <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v> + <v>tls_options() = [<seealso marker="ssl:ssl#type-tls_option">ssl:tls_option()</seealso>]</v> <v>sock_opts() = [<seealso marker="kernel:gen_tcp#type-option">gen_tcp:option()</seealso> except for ipv6_v6only, active, packet, mode, packet_size and header</v> <v>timeout() = integer() > 0 (default is 60000 milliseconds)</v> <v>dtimeout() = integer() > 0 | infinity (default is infinity)</v> @@ -587,7 +587,7 @@ </func> <func> - <name>pwd(Pid) -> {ok, Dir} | {error, Reason}</name> + <name since="">pwd(Pid) -> {ok, Dir} | {error, Reason}</name> <fsummary>Gets the remote current working directory.</fsummary> <type> <v>Pid = pid()</v> @@ -603,8 +603,8 @@ </func> <func> - <name>recv(Pid, RemoteFile) -> </name> - <name>recv(Pid, RemoteFile, LocalFile) -> ok | {error, Reason}</name> + <name since="">recv(Pid, RemoteFile) -> </name> + <name since="">recv(Pid, RemoteFile, LocalFile) -> ok | {error, Reason}</name> <fsummary>Transfers a file from remote server.</fsummary> <type> <v>Pid = pid()</v> @@ -627,7 +627,7 @@ </func> <func> - <name>recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, Reason}</name> + <name since="">recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, Reason}</name> <fsummary>Transfers a file from remote server as a binary.</fsummary> <type> <v>Pid = pid()</v> @@ -644,7 +644,7 @@ </func> <func> - <name>recv_chunk_start(Pid, RemoteFile) -> ok | {error, Reason}</name> + <name since="">recv_chunk_start(Pid, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Starts chunk-reading of the remote file.</fsummary> <type> <v>Pid = pid()</v> @@ -660,7 +660,7 @@ </func> <func> - <name>recv_chunk(Pid) -> ok | {ok, Bin} | {error, Reason}</name> + <name since="">recv_chunk(Pid) -> ok | {ok, Bin} | {error, Reason}</name> <fsummary>Receives a chunk of the remote file.</fsummary> <type> <v>Pid = pid()</v> @@ -682,7 +682,7 @@ </func> <func> - <name>rename(Pid, Old, New) -> ok | {error, Reason}</name> + <name since="">rename(Pid, Old, New) -> ok | {error, Reason}</name> <fsummary>Renames a file at the remote server.</fsummary> <type> <v>Pid = pid()</v> @@ -697,7 +697,7 @@ </func> <func> - <name>rmdir(Pid, Dir) -> ok | {error, Reason}</name> + <name since="">rmdir(Pid, Dir) -> ok | {error, Reason}</name> <fsummary>Removes a remote directory.</fsummary> <type> <v>Pid = pid()</v> @@ -714,8 +714,8 @@ </func> <func> - <name>send(Pid, LocalFile) -></name> - <name>send(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name> + <name since="">send(Pid, LocalFile) -></name> + <name since="">send(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Transfers a file to the remote server.</fsummary> <type> <v>Pid = pid()</v> @@ -732,7 +732,7 @@ </func> <func> - <name>send_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name> + <name since="">send_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name> <fsummary>Transfers a binary into a remote file.</fsummary> <type> <v>Pid = pid()</v> @@ -749,7 +749,7 @@ </func> <func> - <name>send_chunk(Pid, Bin) -> ok | {error, Reason}</name> + <name since="">send_chunk(Pid, Bin) -> ok | {error, Reason}</name> <fsummary>Writes a chunk to the remote file.</fsummary> <type> <v>Pid = pid()</v> @@ -769,7 +769,7 @@ </func> <func> - <name>send_chunk_start(Pid, File) -> ok | {error, Reason}</name> + <name since="">send_chunk_start(Pid, File) -> ok | {error, Reason}</name> <fsummary>Starts transfer of file chunks.</fsummary> <type> <v>Pid = pid()</v> @@ -785,7 +785,7 @@ </func> <func> - <name>send_chunk_end(Pid) -> ok | {error, Reason}</name> + <name since="">send_chunk_end(Pid) -> ok | {error, Reason}</name> <fsummary>Stops transfer of chunks.</fsummary> <type> <v>Pid = pid()</v> @@ -801,7 +801,7 @@ </func> <func> - <name>start_service(ServiceConfig) -> {ok, Pid} | {error, Reason}</name> + <name since="OTP 21.0">start_service(ServiceConfig) -> {ok, Pid} | {error, Reason}</name> <fsummary>Dynamically starts an <c>FTP</c> session after the <c>ftp</c> application has been started.</fsummary> <type> @@ -820,7 +820,7 @@ </func> <func> - <name>stop_service(Reference) -> ok | {error, Reason} </name> + <name since="OTP 21.0">stop_service(Reference) -> ok | {error, Reason} </name> <fsummary>Stops an FTP session.</fsummary> <type> <v>Reference = pid() | term() - service-specified reference</v> @@ -832,7 +832,7 @@ </func> <func> - <name>type(Pid, Type) -> ok | {error, Reason}</name> + <name since="">type(Pid, Type) -> ok | {error, Reason}</name> <fsummary>Sets transfer type to <c>ascii</c>or <c>binary</c>.</fsummary> <type> <v>Pid = pid()</v> @@ -849,7 +849,7 @@ </func> <func> - <name>user(Pid, User, Password) -> ok | {error, Reason}</name> + <name since="">user(Pid, User, Password) -> ok | {error, Reason}</name> <fsummary>User login.</fsummary> <type> <v>Pid = pid()</v> @@ -864,7 +864,7 @@ </func> <func> - <name>user(Pid, User, Password, Account) -> ok | {error, Reason}</name> + <name since="">user(Pid, User, Password, Account) -> ok | {error, Reason}</name> <fsummary>User login.</fsummary> <type> <v>Pid = pid()</v> @@ -880,7 +880,7 @@ </func> <func> - <name>quote(Pid, Command) -> [FTPLine]</name> + <name since="">quote(Pid, Command) -> [FTPLine]</name> <fsummary>Sends an arbitrary FTP command.</fsummary> <type> <v>Pid = pid()</v> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 48ce641ab9..8ae1cd4ab7 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -2225,10 +2225,7 @@ type_order() -> t_map(), t_list(), t_bitstr()]. key_comparisons_fail(X0, KeyPos, TupleList, Opaques) -> - X = case t_is_number(t_inf(X0, t_number(), Opaques), Opaques) of - false -> X0; - true -> t_number() - end, + X = erl_types:t_widen_to_number(X0), lists:all(fun(Tuple) -> Key = type(erlang, element, 2, [KeyPos, Tuple]), t_is_none(t_inf(Key, X, Opaques)) diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 9abb4d31d9..d61cd8664c 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -66,7 +66,6 @@ t_find_opaque_mismatch/3, t_find_unknown_opaque/3, t_fixnum/0, - t_map/2, t_non_neg_fixnum/0, t_pos_fixnum/0, t_float/0, @@ -205,6 +204,7 @@ t_unopaque/1, t_unopaque/2, t_var/1, t_var_name/1, + t_widen_to_number/1, %% t_assign_variables_to_subtype/2, type_is_defined/4, record_field_diffs_to_string/2, @@ -1594,6 +1594,50 @@ lift_list_to_pos_empty(?nil) -> ?nil; lift_list_to_pos_empty(?list(Content, Termination, _)) -> ?list(Content, Termination, ?unknown_qual). +-spec t_widen_to_number(erl_type()) -> erl_type(). + +%% Widens integers and floats to t_number(). +%% Used by erl_bif_types:key_comparison_fail(). + +t_widen_to_number(?any) -> ?any; +t_widen_to_number(?none) -> ?none; +t_widen_to_number(?unit) -> ?unit; +t_widen_to_number(?atom(_Set) = T) -> T; +t_widen_to_number(?bitstr(_Unit, _Base) = T) -> T; +t_widen_to_number(?float) -> t_number(); +t_widen_to_number(?function(Domain, Range)) -> + ?function(t_widen_to_number(Domain), t_widen_to_number(Range)); +t_widen_to_number(?identifier(_Types) = T) -> T; +t_widen_to_number(?int_range(_From, _To)) -> t_number(); +t_widen_to_number(?int_set(_Set)) -> t_number(); +t_widen_to_number(?integer(_Types)) -> t_number(); +t_widen_to_number(?list(Type, Tail, Size)) -> + ?list(t_widen_to_number(Type), t_widen_to_number(Tail), Size); +t_widen_to_number(?map(Pairs, DefK, DefV)) -> + L = [{t_widen_to_number(K), MNess, t_widen_to_number(V)} || + {K, MNess, V} <- Pairs], + t_map(L, t_widen_to_number(DefK), t_widen_to_number(DefV)); +t_widen_to_number(?matchstate(_P, _Slots) = T) -> T; +t_widen_to_number(?nil) -> ?nil; +t_widen_to_number(?number(_Set, _Tag)) -> t_number(); +t_widen_to_number(?opaque(Set)) -> + L = [Opaque#opaque{struct = t_widen_to_number(S)} || + #opaque{struct = S} = Opaque <- set_to_list(Set)], + ?opaque(ordsets:from_list(L)); +t_widen_to_number(?product(Types)) -> + ?product(list_widen_to_number(Types)); +t_widen_to_number(?tuple(?any, _, _) = T) -> T; +t_widen_to_number(?tuple(Types, Arity, Tag)) -> + ?tuple(list_widen_to_number(Types), Arity, Tag); +t_widen_to_number(?tuple_set(_) = Tuples) -> + t_sup([t_widen_to_number(T) || T <- t_tuple_subtypes(Tuples)]); +t_widen_to_number(?union(List)) -> + ?union(list_widen_to_number(List)); +t_widen_to_number(?var(_Id)= T) -> T. + +list_widen_to_number(List) -> + [t_widen_to_number(E) || E <- List]. + %%----------------------------------------------------------------------------- %% Maps %% @@ -3104,9 +3148,18 @@ is_compat_arg(?list(Contents1, Termination1, Size1), is_compat_arg(?product(Types1), ?product(Types2)) -> is_compat_list(Types1, Types2); is_compat_arg(?map(Pairs1, DefK1, DefV1), ?map(Pairs2, DefK2, DefV2)) -> - (is_compat_list(Pairs1, Pairs2) andalso - is_compat_arg(DefK1, DefK2) andalso - is_compat_arg(DefV1, DefV2)); + {Ks1, _, Vs1} = lists:unzip3(Pairs1), + {Ks2, _, Vs2} = lists:unzip3(Pairs2), + Key1 = t_sup([DefK1 | Ks1]), + Key2 = t_sup([DefK2 | Ks2]), + case is_compat_arg(Key1, Key2) of + true -> + Value1 = t_sup([DefV1 | Vs1]), + Value2 = t_sup([DefV2 | Vs2]), + is_compat_arg(Value1, Value2); + false -> + false + end; is_compat_arg(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false; is_compat_arg(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false; is_compat_arg(?tuple(Elements1, Arity, _), @@ -4156,39 +4209,6 @@ t_abstract_records(?opaque(_)=Type, RecDict) -> t_abstract_records(T, _RecDict) -> T. -%% Map over types. Depth first. Used by the contract checker. ?list is -%% not fully implemented so take care when changing the type in Termination. - --spec t_map(fun((erl_type()) -> erl_type()), erl_type()) -> erl_type(). - -t_map(Fun, ?list(Contents, Termination, Size)) -> - Fun(?list(t_map(Fun, Contents), t_map(Fun, Termination), Size)); -t_map(Fun, ?function(Domain, Range)) -> - Fun(?function(t_map(Fun, Domain), t_map(Fun, Range))); -t_map(Fun, ?product(Types)) -> - Fun(?product([t_map(Fun, T) || T <- Types])); -t_map(Fun, ?union(Types)) -> - Fun(t_sup([t_map(Fun, T) || T <- Types])); -t_map(Fun, ?tuple(?any, ?any, ?any) = T) -> - Fun(T); -t_map(Fun, ?tuple(Elements, _Arity, _Tag)) -> - Fun(t_tuple([t_map(Fun, E) || E <- Elements])); -t_map(Fun, ?tuple_set(_) = Tuples) -> - Fun(t_sup([t_map(Fun, T) || T <- t_tuple_subtypes(Tuples)])); -t_map(Fun, ?opaque(Set)) -> - L = [Opaque#opaque{struct = NewS} || - #opaque{struct = S} = Opaque <- set_to_list(Set), - not t_is_none(NewS = t_map(Fun, S))], - Fun(case L of - [] -> ?none; - _ -> ?opaque(ordsets:from_list(L)) - end); -t_map(Fun, ?map(Pairs,DefK,DefV)) -> - %% TODO: - Fun(t_map(Pairs, Fun(DefK), Fun(DefV))); -t_map(Fun, T) -> - Fun(T). - %%============================================================================= %% %% Prettyprinter diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml index 2dec5acbf9..6d3547f4fe 100644 --- a/lib/inets/doc/src/http_uri.xml +++ b/lib/inets/doc/src/http_uri.xml @@ -30,7 +30,7 @@ <rev></rev> </header> - <module>http_uri</module> + <module since="OTP R15B01">http_uri</module> <modulesummary>URI utility module</modulesummary> <description> @@ -79,7 +79,7 @@ <funcs> <func> - <name>decode(HexEncodedURI) -> URI</name> + <name since="OTP R15B01">decode(HexEncodedURI) -> URI</name> <fsummary>Decodes a hexadecimal encoded URI.</fsummary> <type> @@ -93,7 +93,7 @@ </desc> </func> <func> - <name>encode(URI) -> HexEncodedURI</name> + <name since="OTP R15B01">encode(URI) -> HexEncodedURI</name> <fsummary>Encodes a hexadecimal encoded URI.</fsummary> <type> @@ -109,8 +109,8 @@ </func> <func> - <name>parse(URI) -> {ok, Result} | {error, Reason}</name> - <name>parse(URI, Options) -> {ok, Result} | {error, Reason}</name> + <name since="OTP R15B01">parse(URI) -> {ok, Result} | {error, Reason}</name> + <name since="OTP R15B01">parse(URI, Options) -> {ok, Result} | {error, Reason}</name> <fsummary>Parses a URI.</fsummary> <type> <v>URI = uri()</v> @@ -165,7 +165,7 @@ fun(SchemeStr :: string() | binary()) -> </func> <func> - <name>scheme_defaults() -> SchemeDefaults</name> + <name since="OTP R15B01">scheme_defaults() -> SchemeDefaults</name> <fsummary>A list of the scheme and their default ports.</fsummary> <type> <v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v> diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index a2871f3b95..7451b314ec 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -30,7 +30,7 @@ <rev></rev> </header> - <module>httpc</module> + <module since="OTP R13B04">httpc</module> <modulesummary>An HTTP/1.1 client</modulesummary> <description> @@ -151,8 +151,8 @@ <funcs> <func> - <name>cancel_request(RequestId) -></name> - <name>cancel_request(RequestId, Profile) -> ok</name> + <name since="OTP R13B04">cancel_request(RequestId) -></name> + <name since="OTP R13B04">cancel_request(RequestId, Profile) -> ok</name> <fsummary>Cancels an asynchronous HTTP request.</fsummary> <type> <v>RequestId = request_id() - A unique identifier as returned @@ -169,9 +169,9 @@ </func> <func> - <name>cookie_header(Url) -> </name> - <name>cookie_header(Url, Profile | Opts) -> header() | {error, Reason}</name> - <name>cookie_header(Url, Opts, Profile) -> header() | {error, Reason}</name> + <name since="OTP R13B04">cookie_header(Url) -> </name> + <name since="OTP R13B04">cookie_header(Url, Profile | Opts) -> header() | {error, Reason}</name> + <name since="OTP R15B">cookie_header(Url, Opts, Profile) -> header() | {error, Reason}</name> <fsummary>Returns the cookie header that would have been sent when making a request to URL using the profile <c>Profile</c>.</fsummary> <type> @@ -193,8 +193,8 @@ </func> <func> - <name>get_options(OptionItems) -> {ok, Values} | {error, Reason}</name> - <name>get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}</name> + <name since="OTP R15B01">get_options(OptionItems) -> {ok, Values} | {error, Reason}</name> + <name since="OTP R15B01">get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}</name> <fsummary>Gets the currently used options.</fsummary> <type> <v>OptionItems = all | [option_item()]</v> @@ -223,8 +223,8 @@ </func> <func> - <name>info() -> list()</name> - <name>info(Profile) -> list()</name> + <name since="OTP R15B02">info() -> list()</name> + <name since="OTP R15B02">info(Profile) -> list()</name> <fsummary>Produces a list of miscellaneous information.</fsummary> <type> <v>Profile = profile() | pid()</v> @@ -239,8 +239,8 @@ <func> - <name>reset_cookies() -> void()</name> - <name>reset_cookies(Profile) -> void()</name> + <name since="OTP R13B04">reset_cookies() -> void()</name> + <name since="OTP R13B04">reset_cookies(Profile) -> void()</name> <fsummary>Resets the cookie database.</fsummary> <type> <v>Profile = profile() | pid()</v> @@ -254,8 +254,8 @@ </func> <func> - <name>request(Url) -> </name> - <name>request(Url, Profile) -> {ok, Result} | {error, Reason}</name> + <name since="OTP R13B04">request(Url) -> </name> + <name since="OTP R13B04">request(Url, Profile) -> {ok, Result} | {error, Reason}</name> <fsummary>Sends a get HTTP request.</fsummary> <type> <v>Url = url()</v> @@ -272,8 +272,8 @@ </func> <func> - <name>request(Method, Request, HTTPOptions, Options) -></name> - <name>request(Method, Request, HTTPOptions, Options, Profile) -> {ok, Result} | {ok, saved_to_file} | {error, Reason}</name> + <name since="OTP R13B04">request(Method, Request, HTTPOptions, Options) -></name> + <name since="OTP R13B04">request(Method, Request, HTTPOptions, Options, Profile) -> {ok, Result} | {ok, saved_to_file} | {error, Reason}</name> <fsummary>Sends an HTTP request.</fsummary> <type> @@ -521,8 +521,8 @@ <func> - <name>set_options(Options) -> </name> - <name>set_options(Options, Profile) -> ok | {error, Reason}</name> + <name since="OTP R13B04">set_options(Options) -> </name> + <name since="OTP R13B04">set_options(Options, Profile) -> ok | {error, Reason}</name> <fsummary>Sets options to be used for subsequent requests.</fsummary> <type> <v>Options = [Option]</v> @@ -639,8 +639,8 @@ </func> <func> - <name>store_cookies(SetCookieHeaders, Url) -> </name> - <name>store_cookies(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name> + <name since="OTP R14B02">store_cookies(SetCookieHeaders, Url) -> </name> + <name since="OTP R14B02">store_cookies(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name> <fsummary>Saves the cookies defined in <c>SetCookieHeaders</c> in the client profile cookie database.</fsummary> <type> @@ -658,7 +658,7 @@ </func> <func> - <name>stream_next(Pid) -> ok</name> + <name since="OTP R13B04">stream_next(Pid) -> ok</name> <fsummary>Triggers the next message to be streamed, that is, the same behavior as active one for sockets. </fsummary> @@ -676,8 +676,8 @@ </func> <func> - <name>which_cookies() -> cookies()</name> - <name>which_cookies(Profile) -> cookies()</name> + <name since="OTP R13B04">which_cookies() -> cookies()</name> + <name since="OTP R13B04">which_cookies(Profile) -> cookies()</name> <fsummary>Dumps the entire cookie database.</fsummary> <type> <v>Profile = profile() | pid()</v> @@ -695,8 +695,8 @@ </func> <func> - <name>which_sessions() -> session_info()</name> - <name>which_sessions(Profile) -> session_info()</name> + <name since="OTP R15B02">which_sessions() -> session_info()</name> + <name since="OTP R15B02">which_sessions(Profile) -> session_info()</name> <fsummary>Produces a slightly processed dump of the sessions database.</fsummary> <type> <v>Profile = profile() | pid()</v> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 2c70c2b050..66369e8df9 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -29,7 +29,7 @@ <rev>2.2</rev> <file>httpd.sgml</file> </header> - <module>httpd</module> + <module since="">httpd</module> <modulesummary> HTTP server API </modulesummary> @@ -874,8 +874,8 @@ text/plain asc txt</pre> <funcs> <func> - <name>info(Pid) -></name> - <name>info(Pid, Properties) -> [{Option, Value}]</name> + <name since="">info(Pid) -></name> + <name since="">info(Pid, Properties) -> [{Option, Value}]</name> <fsummary>Fetches information about the HTTP server.</fsummary> <type> <v>Properties = [property()]</v> @@ -899,10 +899,10 @@ text/plain asc txt</pre> </func> <func> - <name>info(Address, Port) -> </name> - <name>info(Address, Port, Profile) -> </name> - <name>info(Address, Port, Profile, Properties) -> [{Option, Value}] </name> - <name>info(Address, Port, Properties) -> [{Option, Value}] </name> + <name since="">info(Address, Port) -> </name> + <name since="">info(Address, Port, Profile) -> </name> + <name since="OTP 18.0">info(Address, Port, Profile, Properties) -> [{Option, Value}] </name> + <name since="">info(Address, Port, Properties) -> [{Option, Value}] </name> <fsummary>Fetches information about the HTTP server.</fsummary> <type> <v>Address = ip_address()</v> @@ -927,7 +927,7 @@ text/plain asc txt</pre> </func> <func> - <name>reload_config(Config, Mode) -> ok | {error, Reason}</name> + <name since="">reload_config(Config, Mode) -> ok | {error, Reason}</name> <fsummary>Reloads the HTTP server configuration without restarting the server.</fsummary> <type> @@ -1051,7 +1051,7 @@ text/plain asc txt</pre> </section> <funcs> <func> - <name>Module:do(ModData)-> {proceed, OldData} | {proceed, NewData} | {break, NewData} | done</name> + <name since="">Module:do(ModData)-> {proceed, OldData} | {proceed, NewData} | {break, NewData} | done</name> <fsummary>Called for each request to the web server.</fsummary> <type> <v>OldData = list()</v> @@ -1105,7 +1105,7 @@ text/plain asc txt</pre> </func> <func> - <name>Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason}</name> + <name since="">Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason}</name> <fsummary>Converts a line in an Apache-like config file to an <c>{Option, Value}</c> tuple.</fsummary> <type> @@ -1128,7 +1128,7 @@ text/plain asc txt</pre> </func> <func> - <name>Module:remove(ConfigDB) -> ok | {error, Reason} </name> + <name since="">Module:remove(ConfigDB) -> ok | {error, Reason} </name> <fsummary>Callback function that is called when the web server is closed.</fsummary> <type> <v>ConfigDB = ets_table()</v> @@ -1143,7 +1143,7 @@ text/plain asc txt</pre> </func> <func> - <name>Module:store({Option, Value}, Config)-> {ok, {Option, NewValue}} | {error, Reason}</name> + <name since="">Module:store({Option, Value}, Config)-> {ok, {Option, NewValue}} | {error, Reason}</name> <fsummary>Checks the validity of the configuration options.</fsummary> <type> <v>Line = string()</v> @@ -1171,7 +1171,7 @@ text/plain asc txt</pre> </section> <funcs> <func> - <name>parse_query(QueryString) -> [{Key,Value}]</name> + <name since="">parse_query(QueryString) -> [{Key,Value}]</name> <fsummary>Parses incoming data to <c>erl</c> and <c>eval</c> scripts.</fsummary> <type> <v>QueryString = string()</v> diff --git a/lib/inets/doc/src/httpd_custom_api.xml b/lib/inets/doc/src/httpd_custom_api.xml index d2e5441895..2c0f92ff83 100644 --- a/lib/inets/doc/src/httpd_custom_api.xml +++ b/lib/inets/doc/src/httpd_custom_api.xml @@ -25,7 +25,7 @@ <title>httpd_custom_api</title> <file>httpd_custom_api.xml</file> </header> - <module>httpd_custom_api</module> + <module since="OTP 17.5.6">httpd_custom_api</module> <modulesummary>Behaviour with optional callbacks to customize the inets HTTP server.</modulesummary> <description> <p> The module implementing this behaviour shall be supplied to to the servers @@ -34,7 +34,7 @@ </description> <funcs> <func> - <name>response_default_headers() -> [Header] </name> + <name since="OTP 18.1.1">response_default_headers() -> [Header] </name> <fsummary>Provide default headers for the HTTP servers responses.</fsummary> <type> <v>Header = {HeaderName :: string(), HeaderValue::string()}</v> @@ -48,7 +48,7 @@ </func> <func> - <name>response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name> + <name since="OTP 17.5.6">response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name> <fsummary>Filter and possible alter HTTP response headers.</fsummary> <type> <v>Header = {HeaderName :: string(), HeaderValue::string()}</v> @@ -61,7 +61,7 @@ </func> <func> - <name>request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name> + <name since="OTP 17.5.6">request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name> <fsummary>Filter and possible alter HTTP request headers.</fsummary> <type> <v>Header = {HeaderName :: string(), HeaderValue::string()}</v> diff --git a/lib/inets/doc/src/httpd_socket.xml b/lib/inets/doc/src/httpd_socket.xml index d3aa82a540..22ead06f38 100644 --- a/lib/inets/doc/src/httpd_socket.xml +++ b/lib/inets/doc/src/httpd_socket.xml @@ -29,7 +29,7 @@ <rev>2.2</rev> <file>httpd_socket.sgml</file> </header> - <module>httpd_socket</module> + <module since="">httpd_socket</module> <modulesummary>Communication utility functions to be used by the Erlang web server API programmer.</modulesummary> <description> @@ -43,7 +43,7 @@ <funcs> <func> - <name>deliver(SocketType, Socket, Data) -> Result</name> + <name since="">deliver(SocketType, Socket, Data) -> Result</name> <fsummary>Sends binary data over socket.</fsummary> <type> <v>SocketType = socket_type()</v> @@ -63,7 +63,7 @@ </func> <func> - <name>peername(SocketType,Socket) -> {Port,IPAddress}</name> + <name since="">peername(SocketType,Socket) -> {Port,IPAddress}</name> <fsummary>Returns the port and IP address of the remote socket.</fsummary> <type> <v>SocketType = socket_type()</v> @@ -81,7 +81,7 @@ </func> <func> - <name>resolve() -> HostName</name> + <name since="">resolve() -> HostName</name> <fsummary>Returns the official name of the current host.</fsummary> <type> <v>HostName = string()</v> diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 220a2ede35..e0f947f860 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -29,7 +29,7 @@ <rev>2.2</rev> <file>httpd_util.sgml</file> </header> - <module>httpd_util</module> + <module since="">httpd_util</module> <modulesummary>Miscellaneous utility functions to be used when implementing Erlang web server API modules.</modulesummary> <description> @@ -41,12 +41,11 @@ <funcs> <func> - <name>convert_request_date(DateString) -> ErlDate|bad_date</name> + <name since="">convert_request_date(DateString) -> ErlDate|bad_date</name> <fsummary>Converts the date to the Erlang date format.</fsummary> <type> <v>DateString = string()</v> - <v>ErlDate = {{Year,Month,Date},{Hour,Min,Sec}}</v> - <v>Year = Month = Date = Hour = Min = Sec = integer()</v> + <v>ErlDate = calendar:datetime() </v> </type> <desc> <p><c>convert_request_date/1</c> converts <c>DateString</c> to @@ -57,7 +56,7 @@ </func> <func> - <name>create_etag(FileInfo) -> Etag</name> + <name since="">create_etag(FileInfo) -> Etag</name> <fsummary>Calculates the Etag for a file.</fsummary> <type> <v>FileInfo = file_info()</v> @@ -71,7 +70,7 @@ </func> <func> - <name>day(NthDayOfWeek) -> DayOfWeek</name> + <name since="">day(NthDayOfWeek) -> DayOfWeek</name> <fsummary>Converts the day of the week (integer [1-7]) to an abbreviated string.</fsummary> <type> @@ -87,7 +86,7 @@ </func> <func> - <name>decode_hex(HexValue) -> DecValue</name> + <name since="">decode_hex(HexValue) -> DecValue</name> <fsummary>Converts a hexadecimal value into its decimal equivalent.</fsummary> <type> <v>HexValue = DecValue = string()</v> @@ -99,7 +98,7 @@ </func> <func> - <name>flatlength(NestedList) -> Size</name> + <name since="">flatlength(NestedList) -> Size</name> <fsummary>Computes the size of a possibly nested list.</fsummary> <type> <v>NestedList = list()</v> @@ -112,7 +111,7 @@ </func> <func> - <name>hexlist_to_integer(HexString) -> Number</name> + <name since="">hexlist_to_integer(HexString) -> Number</name> <fsummary>Converts a hexadecimal string to an integer.</fsummary> <type> <v>Number = integer()</v> @@ -125,7 +124,7 @@ </func> <func> - <name>integer_to_hexlist(Number) -> HexString</name> + <name since="">integer_to_hexlist(Number) -> HexString</name> <fsummary>Converts an integer to a hexadecimal string.</fsummary> <type> <v>Number = integer()</v> @@ -138,8 +137,8 @@ </func> <func> - <name>lookup(ETSTable,Key) -> Result</name> - <name>lookup(ETSTable,Key,Undefined) -> Result</name> + <name since="">lookup(ETSTable,Key) -> Result</name> + <name since="">lookup(ETSTable,Key,Undefined) -> Result</name> <fsummary>Extracts the first value associated with a <c>Key</c> in an ETS table.</fsummary> <type> @@ -160,8 +159,8 @@ </func> <func> - <name>lookup_mime(ConfigDB,Suffix)</name> - <name>lookup_mime(ConfigDB,Suffix,Undefined) -> MimeType</name> + <name since="">lookup_mime(ConfigDB,Suffix)</name> + <name since="">lookup_mime(ConfigDB,Suffix,Undefined) -> MimeType</name> <fsummary>Returns the MIME type associated with a specific file suffix.</fsummary> <type> <v>ConfigDB = ets_table()</v> @@ -179,8 +178,8 @@ </func> <func> - <name>lookup_mime_default(ConfigDB,Suffix)</name> - <name>lookup_mime_default(ConfigDB,Suffix,Undefined) -> MimeType</name> + <name since="">lookup_mime_default(ConfigDB,Suffix)</name> + <name since="">lookup_mime_default(ConfigDB,Suffix,Undefined) -> MimeType</name> <fsummary>Returns the MIME type associated with a specific file suffix or the value of the DefaultType.</fsummary> <type> @@ -201,7 +200,7 @@ </func> <func> - <name>message(StatusCode,PhraseArgs,ConfigDB) -> Message</name> + <name since="">message(StatusCode,PhraseArgs,ConfigDB) -> Message</name> <fsummary>Returns an informative HTTP 1.1 status string in HTML.</fsummary> <type> <v>StatusCode = 301 | 400 | 403 | 404 | 500 | 501 | 504</v> @@ -236,7 +235,7 @@ </func> <func> - <name>month(NthMonth) -> Month</name> + <name since="">month(NthMonth) -> Month</name> <fsummary>Converts the month as an integer (1-12) to an abbreviated string.</fsummary> <type> <v>NthMonth = 1-12</v> @@ -250,7 +249,7 @@ </func> <func> - <name>multi_lookup(ETSTable,Key) -> Result</name> + <name since="">multi_lookup(ETSTable,Key) -> Result</name> <fsummary>Extracts the values associated with a key in an ETS table.</fsummary> <type> <v>ETSTable = ets_table()</v> @@ -265,7 +264,7 @@ </func> <func> - <name>reason_phrase(StatusCode) -> Description</name> + <name since="">reason_phrase(StatusCode) -> Description</name> <fsummary>Returns the description of an HTTP 1.1 status code.</fsummary> <type> <v>StatusCode = 100| 200 | 201 | 202 | 204 | 205 | 206 | 300 | 301 | 302 | 303 | 304 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 410 411 | 412 | 413 | 414 415 | 416 | 417 | 500 | 501 | 502 | 503 | 504 | 505</v> @@ -280,11 +279,11 @@ </func> <func> - <name>rfc1123_date() -> RFC1123Date</name> - <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date</name> + <name since="">rfc1123_date() -> RFC1123Date</name> + <name since="">rfc1123_date(Date) -> RFC1123Date</name> <fsummary>Returns the current date in RFC 1123 format.</fsummary> <type> - <v>YYYY = MM = DD = Hour = Min = Sec = integer()</v> + <v> Date = calendar:datetime()</v> <v>RFC1123Date = string()</v> </type> <desc> @@ -295,7 +294,7 @@ </func> <func> - <name>split(String,RegExp,N) -> SplitRes</name> + <name since="">split(String,RegExp,N) -> SplitRes</name> <fsummary>Splits a string in N chunks using a regular expression.</fsummary> <type> <v>String = RegExp = string()</v> @@ -313,7 +312,7 @@ </func> <func> - <name>split_script_path(RequestLine) -> Splitted</name> + <name since="">split_script_path(RequestLine) -> Splitted</name> <fsummary>Splits a <c>RequestLine</c> in a file reference to an executable, and a <c>QueryString</c> or a <c>PathInfo</c>string.</fsummary> <type> @@ -330,7 +329,7 @@ </func> <func> - <name>split_path(RequestLine) -> {Path,QueryStringOrPathInfo}</name> + <name since="">split_path(RequestLine) -> {Path,QueryStringOrPathInfo}</name> <fsummary>Splits a <c>RequestLine</c> in a file reference, and a <c>QueryString</c> or a <c>PathInfo</c> string.</fsummary> <type> @@ -356,7 +355,7 @@ </func> <func> - <name>strip(String) -> Stripped</name> + <name since="">strip(String) -> Stripped</name> <fsummary>Returns <c>String</c> where the leading and trailing space tabs are removed.</fsummary> <type> @@ -370,7 +369,7 @@ </func> <func> - <name>suffix(FileName) -> Suffix</name> + <name since="">suffix(FileName) -> Suffix</name> <fsummary>Extracts the file suffix from a given filename.</fsummary> <type> <v>FileName = Suffix = string()</v> diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml index 9b0ffaad5e..176af3137a 100644 --- a/lib/inets/doc/src/inets.xml +++ b/lib/inets/doc/src/inets.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>inets</module> + <module since="">inets</module> <modulesummary>The Inets services API.</modulesummary> <description> <p>This module provides the most basic API to the @@ -51,7 +51,7 @@ <funcs> <func> - <name>services() -> [{Service, Pid}]</name> + <name since="">services() -> [{Service, Pid}]</name> <fsummary>Returns a list of currently running services.</fsummary> <type> <v>Service = service()</v> @@ -68,7 +68,7 @@ </func> <func> - <name>services_info() -> [{Service, Pid, Info}]</name> + <name since="">services_info() -> [{Service, Pid, Info}]</name> <fsummary>Returns a list of currently running services where each service is described by an <c>[{Option, Value}]</c> list.</fsummary> @@ -91,7 +91,7 @@ </func> <func> - <name>service_names() -> [Service] </name> + <name since="">service_names() -> [Service] </name> <fsummary>Returns a list of available service names.</fsummary> <type> <v>Service = service()</v> @@ -104,8 +104,8 @@ </func> <func> - <name>start() -> </name> - <name>start(Type) -> ok | {error, Reason}</name> + <name since="">start() -> </name> + <name since="">start(Type) -> ok | {error, Reason}</name> <fsummary>Starts the <c>Inets</c> application.</fsummary> <type> <v>Type = permanent | transient | temporary</v> @@ -120,8 +120,8 @@ </func> <func> - <name>start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason}</name> - <name>start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason}</name> + <name since="">start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason}</name> + <name since="">start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason}</name> <fsummary>Dynamically starts an <c>Inets</c> service after the <c>Inets</c> application has been started.</fsummary> <type> @@ -156,7 +156,7 @@ </func> <func> - <name>stop() -> ok </name> + <name since="">stop() -> ok </name> <fsummary>Stops the <c>Inets</c> application.</fsummary> <desc> <p>Stops the <c>Inets</c> application. See also @@ -167,7 +167,7 @@ </func> <func> - <name>stop(Service, Reference) -> ok | {error, Reason} </name> + <name since="">stop(Service, Reference) -> ok | {error, Reason} </name> <fsummary>Stops a started service of the <c>Inets</c> application or takes down a <c>stand_alone </c>service gracefully.</fsummary> <type> diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml index 6ae19700a5..ff57d49d08 100644 --- a/lib/inets/doc/src/mod_alias.xml +++ b/lib/inets/doc/src/mod_alias.xml @@ -29,7 +29,7 @@ <rev>2.2</rev> <file>mod_alias.sgml</file> </header> - <module>mod_alias</module> + <module since="">mod_alias</module> <modulesummary>URL aliasing.</modulesummary> <description> <p>Erlang web server internal API for handling of, for example, @@ -40,7 +40,7 @@ <funcs> <func> - <name>default_index(ConfigDB, Path) -> NewPath</name> + <name since="">default_index(ConfigDB, Path) -> NewPath</name> <fsummary>Returns a new path with the default resource or file appended.</fsummary> <type> <v>ConfigDB = config_db()</v> @@ -64,7 +64,7 @@ </func> <func> - <name>path(PathData, ConfigDB, RequestURI) -> Path</name> + <name since="">path(PathData, ConfigDB, RequestURI) -> Path</name> <fsummary>Returns the file path to a URL.</fsummary> <type> <v>PathData = interaction_data()</v> @@ -89,7 +89,7 @@ </func> <func> - <name>real_name(ConfigDB, RequestURI, Aliases) -> Ret</name> + <name since="">real_name(ConfigDB, RequestURI, Aliases) -> Ret</name> <fsummary>Expands a request URI using <c>Aliases</c> config directives.</fsummary> <type> <v>ConfigDB = config_db()</v> @@ -120,7 +120,7 @@ </func> <func> - <name>real_script_name(ConfigDB, RequestURI, ScriptAliases) -> Ret</name> + <name since="">real_script_name(ConfigDB, RequestURI, ScriptAliases) -> Ret</name> <fsummary>Expands a request URI using <c>ScriptAliases</c> config directives.</fsummary> <type> diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml index c4f844622b..ad864ca4d1 100644 --- a/lib/inets/doc/src/mod_auth.xml +++ b/lib/inets/doc/src/mod_auth.xml @@ -29,7 +29,7 @@ <rev>2.3</rev> <file>mod_auth.sgml</file> </header> - <module>mod_auth</module> + <module since="">mod_auth</module> <modulesummary>User authentication using text files, Dets, or Mnesia database.</modulesummary> <description> <p>This module provides for basic user authentication using @@ -38,9 +38,9 @@ <funcs> <func> - <name>add_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> - <name>add_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> - <name>add_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name> + <name since="">add_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> + <name since="">add_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> + <name since="">add_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name> <fsummary>Adds a user to a group.</fsummary> <type> <v>GroupName = string()</v> @@ -65,9 +65,9 @@ </func> <func> - <name>add_user(UserName, Options) -> true| {error, Reason}</name> - <name>add_user(UserName, Password, UserData, Port, Dir) -> true | {error, Reason}</name> - <name>add_user(UserName, Password, UserData, Address, Port, Dir) -> true | {error, Reason}</name> + <name since="">add_user(UserName, Options) -> true| {error, Reason}</name> + <name since="">add_user(UserName, Password, UserData, Port, Dir) -> true | {error, Reason}</name> + <name since="">add_user(UserName, Password, UserData, Address, Port, Dir) -> true | {error, Reason}</name> <fsummary>Adds a user to the user database.</fsummary> <type> <v>UserName = string()</v> @@ -92,8 +92,8 @@ </func> <func> - <name>delete_group(GroupName, Options) -> true | {error,Reason} <name>delete_group(GroupName, Port, Dir) -> true | {error, Reason}</name> - <name>delete_group(GroupName, Address, Port, Dir) -> true | {error, Reason}</name> + <name since="">delete_group(GroupName, Options) -> true | {error,Reason} <name>delete_group(GroupName, Port, Dir) -> true | {error, Reason}</name> + <name since="">delete_group(GroupName, Address, Port, Dir) -> true | {error, Reason}</name> <fsummary>Deletes a group.</fsummary> <type> <v>Options = [Option]</v> @@ -115,9 +115,9 @@ </func> <func> - <name>delete_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> - <name>delete_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> - <name>delete_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name> + <name since="">delete_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> + <name since="">delete_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> + <name since="">delete_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name> <fsummary>Removes a user from a group.</fsummary> <type> <v>GroupName = string()</v> @@ -141,9 +141,9 @@ </func> <func> - <name>delete_user(UserName,Options) -> true | {error, Reason}</name> - <name>delete_user(UserName, Port, Dir) -> true | {error, Reason}</name> - <name>delete_user(UserName, Address, Port, Dir) -> true | {error, Reason}</name> + <name since="">delete_user(UserName,Options) -> true | {error, Reason}</name> + <name since="">delete_user(UserName, Port, Dir) -> true | {error, Reason}</name> + <name since="">delete_user(UserName, Address, Port, Dir) -> true | {error, Reason}</name> <fsummary>Deletes a user from the user database.</fsummary> <type> <v>UserName = string()</v> @@ -166,9 +166,9 @@ </func> <func> - <name>get_user(UserName,Options) -> {ok, #httpd_user} |{error, Reason}</name> - <name>get_user(UserName, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name> - <name>get_user(UserName, Address, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name> + <name since="">get_user(UserName,Options) -> {ok, #httpd_user} |{error, Reason}</name> + <name since="">get_user(UserName, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name> + <name since="">get_user(UserName, Address, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name> <fsummary>Returns a user from the user database.</fsummary> <type> <v>UserName = string()</v> @@ -190,9 +190,9 @@ </func> <func> - <name>list_groups(Options) -> {ok, Groups} | {error, Reason}</name> - <name>list_groups(Port, Dir) -> {ok, Groups} | {error, Reason}</name> - <name>list_groups(Address, Port, Dir) -> {ok, Groups} | {error, Reason}</name> + <name since="">list_groups(Options) -> {ok, Groups} | {error, Reason}</name> + <name since="">list_groups(Port, Dir) -> {ok, Groups} | {error, Reason}</name> + <name since="">list_groups(Address, Port, Dir) -> {ok, Groups} | {error, Reason}</name> <fsummary>Lists all the groups.</fsummary> <type> <v>Options = [Option]</v> @@ -214,9 +214,9 @@ </func> <func> - <name>list_group_members(GroupName, Options) -> {ok, Users} | {error, Reason}</name> - <name>list_group_members(GroupName, Port, Dir) -> {ok, Users} | {error, Reason}</name> - <name>list_group_members(GroupName, Address, Port, Dir) -> {ok, Users} | {error, Reason}</name> + <name since="">list_group_members(GroupName, Options) -> {ok, Users} | {error, Reason}</name> + <name since="">list_group_members(GroupName, Port, Dir) -> {ok, Users} | {error, Reason}</name> + <name since="">list_group_members(GroupName, Address, Port, Dir) -> {ok, Users} | {error, Reason}</name> <fsummary>Lists the members of a group.</fsummary> <type> <v>GroupName = string()</v> @@ -240,9 +240,9 @@ </func> <func> - <name>list_users(Options) -> {ok, Users} | {error, Reason}</name> - <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name> - <name>list_users(Address, Port, Dir) -> {ok, Users} | {error, Reason}</name> + <name since="">list_users(Options) -> {ok, Users} | {error, Reason}</name> + <name since="OTP R14B01">list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name> + <name since="">list_users(Address, Port, Dir) -> {ok, Users} | {error, Reason}</name> <fsummary>Lists users in the user database.</fsummary> <type> <v>Options = [Option]</v> @@ -264,8 +264,8 @@ </func> <func> - <name>update_password(Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> - <name>update_password(Address,Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> + <name since="">update_password(Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> + <name since="">update_password(Address,Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> <fsummary>Changes <c>AuthAcessPassword</c>.</fsummary> <type> <v>Port = integer()</v> diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index ede7dc8f7d..bc5f98068f 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -25,7 +25,7 @@ <title>mod_esi</title> <file>mod_esi.sgml</file> </header> - <module>mod_esi</module> + <module since="">mod_esi</module> <modulesummary>Erlang Server Interface</modulesummary> <description> <p>This module defines the Erlang Server Interface (ESI) API. @@ -88,7 +88,7 @@ <funcs> <func> - <name>deliver(SessionID, Data) -> ok | {error, Reason}</name> + <name since="">deliver(SessionID, Data) -> ok | {error, Reason}</name> <fsummary>Sends <c>Data</c> back to client.</fsummary> <type> <v>SessionID = term()</v> @@ -121,7 +121,7 @@ <funcs> <func> - <name>Module:Function(SessionID, Env, Input)-> {continue, State} | _ </name> + <name since="">Module:Function(SessionID, Env, Input)-> {continue, State} | _ </name> <fsummary>Creates a dynamic web page and returns it chunk by chunk to the server process by calling <c>mod_esi:deliver/2</c>.</fsummary> <type> @@ -179,7 +179,7 @@ </func> <func> - <name>Module:Function(Env, Input)-> Response </name> + <name since="">Module:Function(Env, Input)-> Response </name> <fsummary>Creates a dynamic web page and returns it as a list. This function is deprecated and is only kept for backwards compatibility.</fsummary> <type> diff --git a/lib/inets/doc/src/mod_security.xml b/lib/inets/doc/src/mod_security.xml index 6f3f3c048a..c26d7468c2 100644 --- a/lib/inets/doc/src/mod_security.xml +++ b/lib/inets/doc/src/mod_security.xml @@ -29,7 +29,7 @@ <rev>1.0</rev> <file>mod_security.sgml</file> </header> - <module>mod_security</module> + <module since="">mod_security</module> <modulesummary>Security Audit and Trailing Functionality</modulesummary> <description> <p>Security Audit and Trailing Functionality</p> @@ -37,8 +37,8 @@ <funcs> <func> - <name>block_user(User, Port, Dir, Seconds) -> true | {error, Reason}</name> - <name>block_user(User, Address, Port, Dir, Seconds) -> true | {error, Reason}</name> + <name since="">block_user(User, Port, Dir, Seconds) -> true | {error, Reason}</name> + <name since="">block_user(User, Address, Port, Dir, Seconds) -> true | {error, Reason}</name> <fsummary>Blocks a user from access to a directory for a certain amount of time.</fsummary> <type> <v>User = string()</v> @@ -56,10 +56,10 @@ </func> <func> - <name>list_auth_users(Port) -> Users | []</name> - <name>list_auth_users(Address, Port) -> Users | []</name> - <name>list_auth_users(Port, Dir) -> Users | []</name> - <name>list_auth_users(Address, Port, Dir) -> Users | []</name> + <name since="">list_auth_users(Port) -> Users | []</name> + <name since="">list_auth_users(Address, Port) -> Users | []</name> + <name since="">list_auth_users(Port, Dir) -> Users | []</name> + <name since="">list_auth_users(Address, Port, Dir) -> Users | []</name> <fsummary>Lists users that have authenticated within the <c>SecurityAuthTimeout</c> time for a given address (if specified), port number, and directory (if specified).</fsummary> @@ -77,10 +77,10 @@ </desc> </func> <func> - <name>list_blocked_users(Port) -> Users | []</name> - <name>list_blocked_users(Address, Port) -> Users | []</name> - <name>list_blocked_users(Port, Dir) -> Users | []</name> - <name>list_blocked_users(Address, Port, Dir) -> Users | []</name> + <name since="">list_blocked_users(Port) -> Users | []</name> + <name since="">list_blocked_users(Address, Port) -> Users | []</name> + <name since="">list_blocked_users(Port, Dir) -> Users | []</name> + <name since="">list_blocked_users(Address, Port, Dir) -> Users | []</name> <fsummary>Lists users that are currently blocked from access to a specified port number, for a given address (if specified).</fsummary> <type> @@ -97,10 +97,10 @@ </func> <func> - <name>unblock_user(User, Port) -> true | {error, Reason}</name> - <name>unblock_user(User, Address, Port) -> true | {error, Reason}</name> - <name>unblock_user(User, Port, Dir) -> true | {error, Reason}</name> - <name>unblock_user(User, Address, Port, Dir) -> true | {error, Reason}</name> + <name since="">unblock_user(User, Port) -> true | {error, Reason}</name> + <name since="">unblock_user(User, Address, Port) -> true | {error, Reason}</name> + <name since="">unblock_user(User, Port, Dir) -> true | {error, Reason}</name> + <name since="">unblock_user(User, Address, Port, Dir) -> true | {error, Reason}</name> <fsummary>Removes a blocked user from the block list.</fsummary> <type> <v>User = string()</v> @@ -129,8 +129,8 @@ <funcs> <func> - <name>Module:event(What, Port, Dir, Data) -> ignored</name> - <name>Module:event(What, Address, Port, Dir, Data) -> ignored</name> + <name since="OTP 18.1">Module:event(What, Port, Dir, Data) -> ignored</name> + <name since="OTP 18.1">Module:event(What, Address, Port, Dir, Data) -> ignored</name> <fsummary>Called whenever an event occurs in <c>mod_security</c>.</fsummary> <type> <v>What = atom()</v> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 12f5acb2e9..3624c6e3d7 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,7 +33,47 @@ <file>notes.xml</file> </header> - <section><title>Inets 7.0.3</title> + <section><title>Inets 7.0.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug that causes a crash in http client when using + hostnames (e.g. localhost) with the the option + ipv6_host_with_brackets set to true.</p> + <p> + This change also fixes a regression: httpc:request fails + with connection error (nxdomain) if option + ipv6_host_with_brackets set to true and host component of + the URI is an IPv6 address.</p> + <p> + Own Id: OTP-15554 Aux Id: ERIERL-289 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 7.0.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Make sure ipv6 addresses with brackets in URIs are + converted correctly before passing to lower level + functions like gen_tcp and ssl functions. Could cause + connection to fail.</p> + <p> + Own Id: OTP-15544 Aux Id: ERIERL-289 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 7.0.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 1bf5d25c98..8d443a1477 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -809,7 +809,7 @@ connect_and_send_first_request(Address, Request, #state{options = Options0} = St SocketType = socket_type(Request), ConnTimeout = (Request#request.settings)#http_options.connect_timeout, Options = handle_unix_socket_options(Request, Options0), - case connect(SocketType, Address, Options, ConnTimeout) of + case connect(SocketType, format_address(Address), Options, ConnTimeout) of {ok, Socket} -> ClientClose = httpc_request:is_client_closing( @@ -1738,4 +1738,8 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) -> {stacktrace, Stacktrace}]}} end. - +format_address({[$[|T], Port}) -> + {ok, Address} = inet:parse_address(string:strip(T, right, $])), + {Address, Port}; +format_address(HostPort) -> + HostPort. diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 52c05a7974..921161dce1 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 7.0.3 +INETS_VSN = 7.0.5 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 38c7b5acf1..5170502581 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>application</module> + <module since="">application</module> <modulesummary>Generic OTP application functions</modulesummary> <description> <p>In OTP, <em>application</em> denotes a component implementing @@ -67,8 +67,8 @@ </datatypes> <funcs> <func> - <name name="ensure_all_started" arity="1"/> - <name name="ensure_all_started" arity="2"/> + <name name="ensure_all_started" arity="1" since="OTP R16B02"/> + <name name="ensure_all_started" arity="2" since="OTP R16B02"/> <fsummary>Load and start an application and its dependencies, recursively.</fsummary> <desc> <p>Equivalent to calling @@ -85,8 +85,8 @@ </desc> </func> <func> - <name name="ensure_started" arity="1"/> - <name name="ensure_started" arity="2"/> + <name name="ensure_started" arity="1" since="OTP R16B01"/> + <name name="ensure_started" arity="2" since="OTP R16B01"/> <fsummary>Load and start an application.</fsummary> <desc> <p>Equivalent to @@ -95,8 +95,8 @@ </desc> </func> <func> - <name name="get_all_env" arity="0"/> - <name name="get_all_env" arity="1"/> + <name name="get_all_env" arity="0" since=""/> + <name name="get_all_env" arity="1" since=""/> <fsummary>Get the configuration parameters for an application.</fsummary> <desc> <p>Returns the configuration parameters and their values for @@ -108,8 +108,8 @@ </desc> </func> <func> - <name name="get_all_key" arity="0"/> - <name name="get_all_key" arity="1"/> + <name name="get_all_key" arity="0" since=""/> + <name name="get_all_key" arity="1" since=""/> <fsummary>Get the application specification keys.</fsummary> <desc> <p>Returns the application specification keys and their values @@ -122,8 +122,8 @@ </desc> </func> <func> - <name name="get_application" arity="0"/> - <name name="get_application" arity="1"/> + <name name="get_application" arity="0" since=""/> + <name name="get_application" arity="1" since=""/> <fsummary>Get the name of an application containing a certain process or module.</fsummary> <desc> <p>Returns the name of the application to which the process @@ -136,8 +136,8 @@ </desc> </func> <func> - <name name="get_env" arity="1"/> - <name name="get_env" arity="2"/> + <name name="get_env" arity="1" since=""/> + <name name="get_env" arity="2" since=""/> <fsummary>Get the value of a configuration parameter.</fsummary> <desc> <p>Returns the value of configuration parameter <c><anno>Par</anno></c> @@ -153,7 +153,7 @@ </desc> </func> <func> - <name name="get_env" arity="3"/> + <name name="get_env" arity="3" since="OTP R16B"/> <fsummary>Get the value of a configuration parameter using a default.</fsummary> <desc> <p>Works like <seealso marker="#get_env/2"><c>get_env/2</c></seealso> but returns @@ -162,8 +162,8 @@ </desc> </func> <func> - <name name="get_key" arity="1"/> - <name name="get_key" arity="2"/> + <name name="get_key" arity="1" since=""/> + <name name="get_key" arity="2" since=""/> <fsummary>Get the value of an application specification key.</fsummary> <desc> <p>Returns the value of the application specification key @@ -180,8 +180,8 @@ </desc> </func> <func> - <name name="load" arity="1"/> - <name name="load" arity="2"/> + <name name="load" arity="1" since=""/> + <name name="load" arity="2" since=""/> <fsummary>Load an application.</fsummary> <type name="application_spec"/> <type name="application_opt"/> @@ -226,7 +226,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="loaded_applications" arity="0"/> + <name name="loaded_applications" arity="0" since=""/> <fsummary>Get the currently loaded applications.</fsummary> <desc> <p>Returns a list with information about the applications, and included @@ -238,7 +238,42 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="permit" arity="2"/> + <name name="set_env" arity="1" since="OTP @OTP-15642@"/> + <name name="set_env" arity="2" since="OTP @OTP-15642@"/> + <fsummary>Sets the configuration parameters of multiple applications.</fsummary> + <desc> + <p>Sets the configuration <c><anno>Config</anno></c> for multiple + applications. It is equivalent to calling <c>set_env/4</c> on + each application individially, except it is more efficient. + The given <c><anno>Config</anno></c> is validated before the + configuration is set.</p> + <p><c>set_env/2</c> uses the standard <c>gen_server</c> time-out + value (5000 ms). Option <c>timeout</c> can be specified + if another time-out value is useful, for example, in situations + where the application controller is heavily loaded.</p> + <p>Option <c>persistent</c> can be set to <c>true</c> + to guarantee that parameters set with <c>set_env/2</c> + are not overridden by those defined in the application resource + file on load. This means that persistent values will stick after the application + is loaded and also on application reload.</p> + <p>If an application is given more than once or if an application + has the same key given more than once, the behaviour is undefined + and a warning message will be logged. In future releases, an error + will be raised.</p> + <p><c>set_env/1</c> is equivalent to <c>set_env(Config, [])</c>.</p> + <warning> + <p>Use this function only if you know what you are doing, + that is, on your own applications. It is very + application-dependent and + configuration parameter-dependent when and how often + the value is read by the application. Careless use + of this function can put the application in a + weird, inconsistent, and malfunctioning state.</p> + </warning> + </desc> + </func> + <func> + <name name="permit" arity="2" since=""/> <fsummary>Change the permission for an application to run at a node.</fsummary> <desc> <p>Changes the permission for <c><anno>Application</anno></c> to run at @@ -271,8 +306,8 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="set_env" arity="3"/> - <name name="set_env" arity="4"/> + <name name="set_env" arity="3" since=""/> + <name name="set_env" arity="4" since=""/> <fsummary>Set the value of a configuration parameter.</fsummary> <desc> <p>Sets the value of configuration parameter <c><anno>Par</anno></c> for @@ -302,8 +337,8 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="start" arity="1"/> - <name name="start" arity="2"/> + <name name="start" arity="1" since=""/> + <name name="start" arity="2" since=""/> <fsummary>Load and start an application.</fsummary> <desc> <p>Starts <c><anno>Application</anno></c>. If it is not loaded, @@ -353,7 +388,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="start_type" arity="0"/> + <name name="start_type" arity="0" since=""/> <fsummary>Get the start type of an ongoing application startup.</fsummary> <desc> <p>This function is intended to be called by a process belonging @@ -370,7 +405,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="stop" arity="1"/> + <name name="stop" arity="1" since=""/> <fsummary>Stop an application.</fsummary> <desc> <p>Stops <c><anno>Application</anno></c>. The application master calls @@ -399,7 +434,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="takeover" arity="2"/> + <name name="takeover" arity="2" since=""/> <fsummary>Take over a distributed application.</fsummary> <desc> <p>Takes over the distributed application @@ -424,7 +459,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="unload" arity="1"/> + <name name="unload" arity="1" since=""/> <fsummary>Unload an application.</fsummary> <desc> <p>Unloads the application specification for <c><anno>Application</anno></c> @@ -435,8 +470,8 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="unset_env" arity="2"/> - <name name="unset_env" arity="3"/> + <name name="unset_env" arity="2" since=""/> + <name name="unset_env" arity="3" since=""/> <fsummary>Unset the value of a configuration parameter.</fsummary> <desc> <p>Removes the configuration parameter <c><anno>Par</anno></c> and its value @@ -459,8 +494,8 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name name="which_applications" arity="0"/> - <name name="which_applications" arity="1"/> + <name name="which_applications" arity="0" since=""/> + <name name="which_applications" arity="1" since=""/> <fsummary>Get the currently running applications.</fsummary> <desc> <p>Returns a list with information about the applications that @@ -484,7 +519,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </section> <funcs> <func> - <name>Module:start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}</name> + <name since="">Module:start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}</name> <fsummary>Start an application.</fsummary> <type> <v>StartType = <seealso marker="#type-start_type"><c>start_type()</c></seealso></v> @@ -526,7 +561,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>Module:start_phase(Phase, StartType, PhaseArgs) -> ok | {error, Reason}</name> + <name since="">Module:start_phase(Phase, StartType, PhaseArgs) -> ok | {error, Reason}</name> <fsummary>Extended start of an application.</fsummary> <type> <v>Phase = atom()</v> @@ -551,7 +586,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>Module:prep_stop(State) -> NewState</name> + <name since="">Module:prep_stop(State) -> NewState</name> <fsummary>Prepare an application for termination.</fsummary> <type> <v>State = NewState = term()</v> @@ -569,7 +604,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>Module:stop(State)</name> + <name since="">Module:stop(State)</name> <fsummary>Clean up after termination of an application.</fsummary> <type> <v>State = term()</v> @@ -585,7 +620,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>Module:config_change(Changed, New, Removed) -> ok</name> + <name since="">Module:config_change(Changed, New, Removed) -> ok</name> <fsummary>Update the configuration parameters for an application.</fsummary> <type> <v>Changed = [{Par,Val}]</v> diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml index 5901446960..a57da18de9 100644 --- a/lib/kernel/doc/src/auth.xml +++ b/lib/kernel/doc/src/auth.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>auth</module> + <module since="">auth</module> <modulesummary>Erlang network authentication server.</modulesummary> <description> <p>This module is deprecated. For a description of the Magic @@ -42,7 +42,7 @@ </datatypes> <funcs> <func> - <name name="cookie" arity="0"/> + <name name="cookie" arity="0" since=""/> <fsummary>Magic cookie for local node (deprecated).</fsummary> <desc> <p>Use @@ -51,7 +51,7 @@ </desc> </func> <func> - <name name="cookie" arity="1"/> + <name name="cookie" arity="1" since=""/> <fsummary>Set the magic for the local node (deprecated).</fsummary> <type_desc variable="TheCookie"> The cookie can also be specified as a list with a single atom element. @@ -63,7 +63,7 @@ </desc> </func> <func> - <name name="is_auth" arity="1"/> + <name name="is_auth" arity="1" since=""/> <fsummary>Status of communication authorization (deprecated).</fsummary> <desc> <p>Returns <c>yes</c> if communication with <c><anno>Node</anno></c> is @@ -76,7 +76,7 @@ </desc> </func> <func> - <name>node_cookie([Node, Cookie]) -> yes | no</name> + <name since="">node_cookie([Node, Cookie]) -> yes | no</name> <fsummary>Set the magic cookie for a node and verify authorization (deprecated).</fsummary> <type> <v>Node = node()</v> @@ -88,7 +88,7 @@ </desc> </func> <func> - <name name="node_cookie" arity="2"/> + <name name="node_cookie" arity="2" since=""/> <fsummary>Set the magic cookie for a node and verify authorization (deprecated).</fsummary> <desc> <p>Sets the magic cookie of <c><anno>Node</anno></c> to diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index aff3e8133c..85178da930 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>code</module> + <module since="">code</module> <modulesummary>Erlang code server.</modulesummary> <description> <p>This module contains the interface to the Erlang @@ -322,7 +322,7 @@ zip:create("mnesia-4.4.7.ez", <funcs> <func> - <name name="set_path" arity="1"/> + <name name="set_path" arity="1" since=""/> <fsummary>Set the code server search path.</fsummary> <desc> <p>Sets the code path to the list of directories <c><anno>Path</anno></c>.</p> @@ -336,15 +336,15 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="get_path" arity="0"/> + <name name="get_path" arity="0" since=""/> <fsummary>Return the code server search path.</fsummary> <desc> <p>Returns the code path.</p> </desc> </func> <func> - <name name="add_path" arity="1"/> - <name name="add_pathz" arity="1"/> + <name name="add_path" arity="1" since=""/> + <name name="add_pathz" arity="1" since=""/> <fsummary>Add a directory to the end of the code path.</fsummary> <type name="add_path_ret"/> <desc> @@ -357,7 +357,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="add_patha" arity="1"/> + <name name="add_patha" arity="1" since=""/> <fsummary>Add a directory to the beginning of the code path.</fsummary> <type name="add_path_ret"/> <desc> @@ -370,8 +370,8 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="add_paths" arity="1"/> - <name name="add_pathsz" arity="1"/> + <name name="add_paths" arity="1" since=""/> + <name name="add_pathsz" arity="1" since=""/> <fsummary>Add directories to the end of the code path.</fsummary> <desc> <p>Adds the directories in <c><anno>Dirs</anno></c> to the end of the code @@ -381,7 +381,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="add_pathsa" arity="1"/> + <name name="add_pathsa" arity="1" since=""/> <fsummary>Add directories to the beginning of the code path.</fsummary> <desc> <p>Traverses <c><anno>Dirs</anno></c> and adds @@ -397,7 +397,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="del_path" arity="1"/> + <name name="del_path" arity="1" since=""/> <fsummary>Delete a directory from the code path.</fsummary> <desc> <p>Deletes a directory from the code path. The argument can be @@ -417,7 +417,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="replace_path" arity="2"/> + <name name="replace_path" arity="2" since=""/> <fsummary>Replace a directory with another in the code path.</fsummary> <desc> <p>Replaces an old occurrence of a directory @@ -441,7 +441,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="load_file" arity="1"/> + <name name="load_file" arity="1" since=""/> <fsummary>Load a module.</fsummary> <type name="load_ret"/> <desc> @@ -460,7 +460,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="load_abs" arity="1"/> + <name name="load_abs" arity="1" since=""/> <fsummary>Load a module, residing in a specified file.</fsummary> <type name="load_ret"/> <type name="loaded_filename"/> @@ -477,7 +477,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="ensure_loaded" arity="1"/> + <name name="ensure_loaded" arity="1" since=""/> <fsummary>Ensure that a module is loaded.</fsummary> <desc> <p>Tries to load a module in the same way as @@ -489,7 +489,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="load_binary" arity="3"/> + <name name="load_binary" arity="3" since=""/> <fsummary>Load object code for a module.</fsummary> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> @@ -507,7 +507,7 @@ zip:create("mnesia-4.4.7.ez", </desc> </func> <func> - <name name="atomic_load" arity="1"/> + <name name="atomic_load" arity="1" since="OTP 19.0"/> <fsummary>Load a list of modules atomically</fsummary> <desc> <p>Tries to load all of the modules in the list @@ -566,7 +566,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="prepare_loading" arity="1"/> + <name name="prepare_loading" arity="1" since="OTP 19.0"/> <fsummary>Prepare a list of modules atomically</fsummary> <desc> <p>Prepares to load the modules in the list @@ -598,7 +598,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="finish_loading" arity="1"/> + <name name="finish_loading" arity="1" since="OTP 19.0"/> <fsummary>Finish loading a list of prepared modules atomically</fsummary> <desc> <p>Tries to load code for all modules that have been previously @@ -627,7 +627,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="ensure_modules_loaded" arity="1"/> + <name name="ensure_modules_loaded" arity="1" since="OTP 19.0"/> <fsummary>Ensure that a list of modules is loaded</fsummary> <desc> <p>Tries to load any modules not already loaded in the list @@ -639,7 +639,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="delete" arity="1"/> + <name name="delete" arity="1" since=""/> <fsummary>Remove current code for a module.</fsummary> <desc> <p>Removes the current code for <c><anno>Module</anno></c>, that is, @@ -652,7 +652,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="purge" arity="1"/> + <name name="purge" arity="1" since=""/> <fsummary>Remove old code for a module.</fsummary> <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code @@ -668,7 +668,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="soft_purge" arity="1"/> + <name name="soft_purge" arity="1" since=""/> <fsummary>Remove old code for a module, unless no process uses it.</fsummary> <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code @@ -683,7 +683,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="is_loaded" arity="1"/> + <name name="is_loaded" arity="1" since=""/> <fsummary>Check if a module is loaded.</fsummary> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> @@ -702,7 +702,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="all_loaded" arity="0"/> + <name name="all_loaded" arity="0" since=""/> <fsummary>Get all loaded modules.</fsummary> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> @@ -716,7 +716,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="which" arity="1"/> + <name name="which" arity="1" since=""/> <fsummary>The object code file of a module.</fsummary> <type name="loaded_ret_atoms"/> <desc> @@ -731,7 +731,7 @@ ok = code:finish_loading(Prepared), </desc> </func> <func> - <name name="get_object_code" arity="1"/> + <name name="get_object_code" arity="1" since=""/> <fsummary>Gets the object code for a module.</fsummary> <desc> <p>Searches the code path for the object code of module @@ -750,7 +750,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="root_dir" arity="0"/> + <name name="root_dir" arity="0" since=""/> <fsummary>Root directory of Erlang/OTP.</fsummary> <desc> <p>Returns the root directory of Erlang/OTP, which is @@ -762,7 +762,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="lib_dir" arity="0"/> + <name name="lib_dir" arity="0" since=""/> <fsummary>Library directory of Erlang/OTP.</fsummary> <desc> <p>Returns the library directory, <c>$OTPROOT/lib</c>, where @@ -774,7 +774,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="lib_dir" arity="1"/> + <name name="lib_dir" arity="1" since=""/> <fsummary>Library directory for an application.</fsummary> <desc> <p>Returns the path @@ -807,7 +807,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="lib_dir" arity="2"/> + <name name="lib_dir" arity="2" since=""/> <fsummary>Subdirectory for an application.</fsummary> <desc> <p>Returns the path to a subdirectory directly under the top @@ -827,7 +827,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="compiler_dir" arity="0"/> + <name name="compiler_dir" arity="0" since=""/> <fsummary>Library directory for the compiler.</fsummary> <desc> <p>Returns the compiler library directory. Equivalent to @@ -835,7 +835,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="priv_dir" arity="1"/> + <name name="priv_dir" arity="1" since=""/> <fsummary>Priv directory for an application.</fsummary> <desc> <p>Returns the path to the <c>priv</c> directory in an @@ -846,7 +846,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="objfile_extension" arity="0"/> + <name name="objfile_extension" arity="0" since=""/> <fsummary>Object code file extension.</fsummary> <desc> <p>Returns the object code file extension corresponding to @@ -854,7 +854,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="stick_dir" arity="1"/> + <name name="stick_dir" arity="1" since=""/> <fsummary>Mark a directory as sticky.</fsummary> <desc> <p>Marks <c><anno>Dir</anno></c> as sticky.</p> @@ -862,7 +862,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="unstick_dir" arity="1"/> + <name name="unstick_dir" arity="1" since=""/> <fsummary>Remove a sticky directory mark.</fsummary> <desc> <p>Unsticks a directory that is marked as @@ -871,7 +871,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="is_sticky" arity="1"/> + <name name="is_sticky" arity="1" since=""/> <fsummary>Test if a module is sticky.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Module</anno></c> is the @@ -882,7 +882,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="where_is_file" arity="1"/> + <name name="where_is_file" arity="1" since=""/> <fsummary>Full name of a file located in the code path.</fsummary> <desc> <p>Searches the code path for <c><anno>Filename</anno></c>, a file of @@ -893,7 +893,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="clash" arity="0"/> + <name name="clash" arity="0" since=""/> <fsummary>Search for modules with identical names.</fsummary> <desc> <p>Searches all directories in the code path for module names with @@ -901,7 +901,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="module_status" arity="1"/> + <name name="module_status" arity="1" since="OTP 20.0"/> <fsummary>Return the status of the module in relation to object file on disk.</fsummary> <desc> <p>Returns:</p> @@ -934,7 +934,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="modified_modules" arity="0"/> + <name name="modified_modules" arity="0" since="OTP 20.0"/> <fsummary>Return a list of all modules modified on disk.</fsummary> <desc> <p>Returns the list of all currently loaded modules for which @@ -943,7 +943,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name name="is_module_native" arity="1"/> + <name name="is_module_native" arity="1" since=""/> <fsummary>Test if a module has native code.</fsummary> <desc> <p>Returns:</p> @@ -961,7 +961,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> - <name name="get_mode" arity="0"/> + <name name="get_mode" arity="0" since="OTP R16B"/> <fsummary>The mode of the code server.</fsummary> <desc> <p>Returns an atom describing the mode of the code server: diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 884cb32c0c..e308b06f3c 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -34,7 +34,7 @@ <rev>D</rev> <file>disk_log.sgml</file> </header> - <module>disk_log</module> + <module since="">disk_log</module> <modulesummary>A disk-based term logging facility.</modulesummary> <description> <p><c>disk_log</c> is a disk-based term logger that enables @@ -238,7 +238,7 @@ </datatypes> <funcs> <func> - <name name="accessible_logs" arity="0"/> + <name name="accessible_logs" arity="0" since=""/> <fsummary>Return the accessible disk logs on the current node.</fsummary> <desc> <p>Returns the names of the disk logs accessible on the current node. @@ -248,8 +248,8 @@ </desc> </func> <func> - <name name="alog" arity="2"/> - <name name="balog" arity="2"/> + <name name="alog" arity="2" since=""/> + <name name="balog" arity="2" since=""/> <fsummary>Asynchronously log an item on to a disk log.</fsummary> <type variable="Log"/> <type variable="Term" name_i="1"/> @@ -275,8 +275,8 @@ </desc> </func> <func> - <name name="alog_terms" arity="2"/> - <name name="balog_terms" arity="2"/> + <name name="alog_terms" arity="2" since=""/> + <name name="balog_terms" arity="2" since=""/> <fsummary>Asynchronously log many items on to a disk log.</fsummary> <type variable="Log"/> <type variable="TermList" name_i="1"/> @@ -303,8 +303,8 @@ </desc> </func> <func> - <name name="block" arity="1"/> - <name name="block" arity="2"/> + <name name="block" arity="1" since=""/> + <name name="block" arity="2" since=""/> <fsummary>Block a disk log.</fsummary> <type name="block_error_rsn"/> <desc> @@ -330,21 +330,21 @@ </desc> </func> <func> - <name name="change_header" arity="2"/> + <name name="change_header" arity="2" since=""/> <fsummary>Change option head or head_func for an owner of a disk log.</fsummary> <desc> <p>Changes the value of option <c>head</c> or <c>head_func</c> for an owner of a disk log.</p> </desc> </func> <func> - <name name="change_notify" arity="3"/> + <name name="change_notify" arity="3" since=""/> <fsummary>Change option notify for an owner of a disk log.</fsummary> <desc> <p>Changes the value of option <c>notify</c> for an owner of a disk log. </p> </desc> </func> <func> - <name name="change_size" arity="2"/> + <name name="change_size" arity="2" since=""/> <fsummary>Change the size of an open disk log.</fsummary> <desc> <p>Changes the size of an open log. @@ -384,10 +384,10 @@ </desc> </func> <func> - <name name="chunk" arity="2"/> - <name name="chunk" arity="3"/> - <name name="bchunk" arity="2"/> - <name name="bchunk" arity="3"/> + <name name="chunk" arity="2" since=""/> + <name name="chunk" arity="3" since=""/> + <name name="bchunk" arity="2" since=""/> + <name name="bchunk" arity="3" since=""/> <fsummary>Read a chunk of items written to a disk log.</fsummary> <type variable="Log"/> <type variable="Continuation"/> @@ -447,7 +447,7 @@ </desc> </func> <func> - <name name="chunk_info" arity="1"/> + <name name="chunk_info" arity="1" since=""/> <fsummary>Return information about a chunk continuation of a disk log.</fsummary> <desc> <p>Returns the pair <c>{node, <anno>Node</anno>}</c>, @@ -457,7 +457,7 @@ </desc> </func> <func> - <name name="chunk_step" arity="3"/> + <name name="chunk_step" arity="3" since=""/> <fsummary>Step forward or backward among the wrap log files of a disk log.</fsummary> <desc> <p>Can be used with <c>chunk/2,3</c> and <c>bchunk/2,3</c> @@ -480,7 +480,7 @@ </desc> </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a disk log.</fsummary> <type name="close_error_rsn"/> <desc> @@ -505,7 +505,7 @@ </desc> </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Return an English description of a disk log error reply.</fsummary> <desc> <p>Given the error returned by any function in this module, @@ -517,7 +517,7 @@ </desc> </func> <func> - <name name="inc_wrap_file" arity="1"/> + <name name="inc_wrap_file" arity="1" since=""/> <fsummary>Change to the next wrap log file of a disk log.</fsummary> <type name="inc_wrap_error_rsn"/> <type name="invalid_header"/> @@ -534,7 +534,7 @@ </desc> </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since=""/> <fsummary>Return information about a disk log.</fsummary> <type name="dlog_info"/> <desc> @@ -685,8 +685,8 @@ </desc> </func> <func> - <name name="lclose" arity="1"/> - <name name="lclose" arity="2"/> + <name name="lclose" arity="1" since=""/> + <name name="lclose" arity="2" since=""/> <fsummary>Close a disk log on one node.</fsummary> <type name="lclose_error_rsn"/> <desc> @@ -704,8 +704,8 @@ </desc> </func> <func> - <name name="log" arity="2"/> - <name name="blog" arity="2"/> + <name name="log" arity="2" since=""/> + <name name="blog" arity="2" since=""/> <fsummary>Log an item onto a disk log.</fsummary> <type variable="Log"/> <type variable="Term" name_i="1"/> @@ -739,8 +739,8 @@ </desc> </func> <func> - <name name="log_terms" arity="2"/> - <name name="blog_terms" arity="2"/> + <name name="log_terms" arity="2" since=""/> + <name name="blog_terms" arity="2" since=""/> <fsummary>Log many items onto a disk log.</fsummary> <type variable="Log"/> <type variable="TermList" name_i="1"/> @@ -768,7 +768,7 @@ </desc> </func> <func> - <name name="open" arity="1"/> + <name name="open" arity="1" since=""/> <fsummary>Open a disk log file.</fsummary> <type name="dlog_options"/> <type name="dlog_option"/> @@ -1041,7 +1041,7 @@ </desc> </func> <func> - <name name="pid2name" arity="1"/> + <name name="pid2name" arity="1" since=""/> <fsummary>Return the name of the disk log handled by a pid.</fsummary> <desc> <p>Returns the log name @@ -1053,9 +1053,9 @@ </desc> </func> <func> - <name name="reopen" arity="2"/> - <name name="reopen" arity="3"/> - <name name="breopen" arity="3"/> + <name name="reopen" arity="2" since=""/> + <name name="reopen" arity="3" since=""/> + <name name="breopen" arity="3" since=""/> <fsummary>Reopen a disk log and save the old log.</fsummary> <type variable="Log"/> <type variable="File" name_i="1"/> @@ -1087,7 +1087,7 @@ </desc> </func> <func> - <name name="sync" arity="1"/> + <name name="sync" arity="1" since=""/> <fsummary>Flush the contents of a disk log to the disk.</fsummary> <type name="sync_error_rsn"/> <desc> @@ -1097,9 +1097,9 @@ </desc> </func> <func> - <name name="truncate" arity="1"/> - <name name="truncate" arity="2"/> - <name name="btruncate" arity="2"/> + <name name="truncate" arity="1" since=""/> + <name name="truncate" arity="2" since=""/> + <name name="btruncate" arity="2" since=""/> <fsummary>Truncate a disk log.</fsummary> <type variable="Log"/> <type variable="Head" name_i="2"/> @@ -1129,7 +1129,7 @@ </desc> </func> <func> - <name name="unblock" arity="1"/> + <name name="unblock" arity="1" since=""/> <fsummary>Unblock a disk log.</fsummary> <type name="unblock_error_rsn"/> <desc> diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml index 4109251387..89f9855c49 100644 --- a/lib/kernel/doc/src/erl_boot_server.xml +++ b/lib/kernel/doc/src/erl_boot_server.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>erl_boot_server</module> + <module since="">erl_boot_server</module> <modulesummary>Boot server for other Erlang machines.</modulesummary> <description> <p>This server is used to assist diskless Erlang nodes that fetch @@ -52,14 +52,14 @@ </description> <funcs> <func> - <name name="add_slave" arity="1"/> + <name name="add_slave" arity="1" since=""/> <fsummary>Add a slave to the list of allowed slaves.</fsummary> <desc> <p>Adds a <c><anno>Slave</anno></c> node to the list of allowed slave hosts.</p> </desc> </func> <func> - <name name="delete_slave" arity="1"/> + <name name="delete_slave" arity="1" since=""/> <fsummary>Delete a slave from the list of allowed slaves.</fsummary> <desc> <p>Deletes a <c><anno>Slave</anno></c> node from the list of allowed slave @@ -67,7 +67,7 @@ </desc> </func> <func> - <name name="start" arity="1"/> + <name name="start" arity="1" since=""/> <fsummary>Start the boot server.</fsummary> <desc> <p>Starts the boot server. <c><anno>Slaves</anno></c> is a list of @@ -76,7 +76,7 @@ </desc> </func> <func> - <name name="start_link" arity="1"/> + <name name="start_link" arity="1" since=""/> <fsummary>Start the boot server and link to the the caller.</fsummary> <desc> <p>Starts the boot server and links to the caller. This function @@ -85,7 +85,7 @@ </desc> </func> <func> - <name name="which_slaves" arity="0"/> + <name name="which_slaves" arity="0" since=""/> <fsummary>Return the current list of allowed slave hosts.</fsummary> <desc> <p>Returns the current list of allowed slave hosts.</p> diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml index 75114e015c..f2d5e1b397 100644 --- a/lib/kernel/doc/src/erl_ddll.xml +++ b/lib/kernel/doc/src/erl_ddll.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>erl_ddll</module> + <module since="">erl_ddll</module> <modulesummary>Dynamic driver loader and linker.</modulesummary> <description> <p>This module provides an interface for loading @@ -196,7 +196,7 @@ </datatypes> <funcs> <func> - <name name="demonitor" arity="1"/> + <name name="demonitor" arity="1" since=""/> <fsummary>Remove a monitor for a driver.</fsummary> <desc> <p>Removes a driver monitor in much the same way as @@ -212,7 +212,7 @@ </desc> </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Format an error descriptor.</fsummary> <desc> <p>Takes an <c><anno>ErrorDesc</anno></c> returned by load, unload, or @@ -229,7 +229,7 @@ </desc> </func> <func> - <name name="info" arity="0"/> + <name name="info" arity="0" since=""/> <fsummary>Retrieve information about all drivers.</fsummary> <desc> <p>Returns a list of tuples <c>{<anno>DriverName</anno>, <anno>InfoList</anno>}</c>, @@ -240,7 +240,7 @@ </desc> </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since=""/> <fsummary>Retrieve information about one driver.</fsummary> <desc> <p>Returns a list of tuples <c>{<anno>Tag</anno>, <anno>Value</anno>}</c>, @@ -266,7 +266,7 @@ </desc> </func> <func> - <name name="info" arity="2"/> + <name name="info" arity="2" since=""/> <fsummary>Retrieve specific information about one driver.</fsummary> <desc> <p>Returns specific information about one aspect of a driver. @@ -328,7 +328,7 @@ </desc> </func> <func> - <name name="load" arity="2"/> + <name name="load" arity="2" since=""/> <fsummary>Load a driver.</fsummary> <desc> <p>Loads and links the dynamic driver <c><anno>Name</anno></c>. @@ -390,7 +390,7 @@ </desc> </func> <func> - <name name="load_driver" arity="2"/> + <name name="load_driver" arity="2" since=""/> <fsummary>Load a driver.</fsummary> <desc> <p>Works essentially as <c>load/2</c>, but loads the driver @@ -413,7 +413,7 @@ </desc> </func> <func> - <name name="loaded_drivers" arity="0"/> + <name name="loaded_drivers" arity="0" since=""/> <fsummary>List loaded drivers.</fsummary> <desc> <p>Returns a list of all the available drivers, both @@ -425,7 +425,7 @@ </desc> </func> <func> - <name name="monitor" arity="2"/> + <name name="monitor" arity="2" since=""/> <fsummary>Create a monitor for a driver.</fsummary> <desc> <p>Creates a driver monitor and works in many @@ -588,7 +588,7 @@ </desc> </func> <func> - <name name="reload" arity="2"/> + <name name="reload" arity="2" since=""/> <fsummary>Replace a driver.</fsummary> <desc> <p>Reloads the driver named <c><anno>Name</anno></c> from a possibly @@ -626,7 +626,7 @@ </desc> </func> <func> - <name name="reload_driver" arity="2"/> + <name name="reload_driver" arity="2" since=""/> <fsummary>Replace a driver.</fsummary> <desc> <p>Works exactly as <seealso marker="#reload/2"><c>reload/2</c></seealso>, @@ -644,7 +644,7 @@ </desc> </func> <func> - <name name="try_load" arity="3"/> + <name name="try_load" arity="3" since=""/> <fsummary>Load a driver.</fsummary> <desc> <p>Provides more control than the @@ -931,7 +931,7 @@ </desc> </func> <func> - <name name="try_unload" arity="2"/> + <name name="try_unload" arity="2" since=""/> <fsummary>Unload a driver.</fsummary> <desc> <p>This is the low-level function to unload (or decrement @@ -1116,7 +1116,7 @@ </desc> </func> <func> - <name name="unload" arity="1"/> + <name name="unload" arity="1" since=""/> <fsummary>Unload a driver.</fsummary> <desc> <p>Unloads, or at least dereferences the driver named @@ -1143,7 +1143,7 @@ </desc> </func> <func> - <name name="unload_driver" arity="1"/> + <name name="unload_driver" arity="1" since=""/> <fsummary>Unload a driver.</fsummary> <desc> <p>Unloads, or at least dereferences the driver named diff --git a/lib/kernel/doc/src/erl_epmd.xml b/lib/kernel/doc/src/erl_epmd.xml index 8b076cd2d7..2adbf11a28 100644 --- a/lib/kernel/doc/src/erl_epmd.xml +++ b/lib/kernel/doc/src/erl_epmd.xml @@ -28,7 +28,7 @@ <date>2018-02-19</date> <rev>A</rev> </header> - <module>erl_epmd</module> + <module since="OTP R14B">erl_epmd</module> <modulesummary> Erlang interface towards epmd </modulesummary> @@ -41,7 +41,7 @@ <funcs> <func> - <name name="start_link" arity="0"/> + <name name="start_link" arity="0" since="OTP 21.0"/> <fsummary>Callback for erl_distribution supervisor.</fsummary> <desc> <p>This function is invoked as this module is added as a child of the @@ -50,8 +50,8 @@ </func> <func> - <name name="register_node" arity="2"/> - <name name="register_node" arity="3"/> + <name name="register_node" arity="2" since="OTP 21.0"/> + <name name="register_node" arity="3" since="OTP 21.0"/> <fsummary>Registers the node with <c>epmd</c>.</fsummary> <desc> <p>Registers the node with <c>epmd</c> and tells epmd what port will be @@ -62,8 +62,8 @@ </func> <func> - <name name="port_please" arity="2"/> - <name name="port_please" arity="3"/> + <name name="port_please" arity="2" since="OTP 21.0"/> + <name name="port_please" arity="3" since="OTP 21.0"/> <fsummary>Returns the port number for a given node.</fsummary> <desc> <p>Requests the distribution port for the given node of an EPMD @@ -73,7 +73,7 @@ </func> <func> - <name name="address_please" arity="3"/> + <name name="address_please" arity="3" since="OTP 21.0"/> <fsummary>Returns address and port.</fsummary> <desc> <p>Called by the distribution module. Resolves the <c>Host</c> to an IP @@ -84,7 +84,7 @@ </func> <func> - <name name="names" arity="1"/> + <name name="names" arity="1" since="OTP 21.0"/> <fsummary>Names of Erlang nodes at a host.</fsummary> <desc> <p>Called by <seealso marker="net_adm"><c>net_adm:names/0</c></seealso>. diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml index e5639487dc..eb01e87aee 100644 --- a/lib/kernel/doc/src/error_handler.xml +++ b/lib/kernel/doc/src/error_handler.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>error_handler</module> + <module since="">error_handler</module> <modulesummary>Default system error handler.</modulesummary> <description> <p>This module defines what happens when certain types @@ -38,7 +38,7 @@ </description> <funcs> <func> - <name name="raise_undef_exception" arity="3"/> + <name name="raise_undef_exception" arity="3" since="OTP R16B"/> <fsummary>Raise an undef exception.</fsummary> <type_desc variable="Args"> A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> @@ -51,7 +51,7 @@ </desc> </func> <func> - <name name="undefined_function" arity="3"/> + <name name="undefined_function" arity="3" since=""/> <fsummary>Called when an undefined function is encountered.</fsummary> <type_desc variable="Args"> A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> @@ -93,7 +93,7 @@ </desc> </func> <func> - <name name="undefined_lambda" arity="3"/> + <name name="undefined_lambda" arity="3" since=""/> <fsummary>Called when an undefined lambda (fun) is encountered.</fsummary> <type_desc variable="Args"> A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index c3d68fd79f..c170b4fa34 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>error_logger</module> + <module since="">error_logger</module> <modulesummary>Erlang error logger.</modulesummary> <description> @@ -76,8 +76,8 @@ </datatypes> <funcs> <func> - <name name="add_report_handler" arity="1"/> - <name name="add_report_handler" arity="2"/> + <name name="add_report_handler" arity="1" since=""/> + <name name="add_report_handler" arity="2" since=""/> <fsummary>Add an event handler to the error logger.</fsummary> <desc> <p>Adds a new event handler to the error logger. The event @@ -96,7 +96,7 @@ </desc> </func> <func> - <name name="delete_report_handler" arity="1"/> + <name name="delete_report_handler" arity="1" since=""/> <fsummary>Delete an event handler from the error logger.</fsummary> <desc> <p>Deletes an event handler from the error logger by calling @@ -108,9 +108,9 @@ </desc> </func> <func> - <name name="error_msg" arity="1"/> - <name name="error_msg" arity="2"/> - <name name="format" arity="2"/> + <name name="error_msg" arity="1" since=""/> + <name name="error_msg" arity="2" since=""/> + <name name="format" arity="2" since=""/> <fsummary>Log a standard error event.</fsummary> <desc> <p>Log a standard error event. The <c><anno>Format</anno></c> @@ -142,7 +142,7 @@ ok</pre> </desc> </func> <func> - <name name="error_report" arity="1"/> + <name name="error_report" arity="1" since=""/> <fsummary>Log a standard error event.</fsummary> <desc> <p>Log a standard error event. Error logger forwards the event @@ -169,7 +169,7 @@ ok</pre> </desc> </func> <func> - <name name="error_report" arity="2"/> + <name name="error_report" arity="2" since=""/> <fsummary>Log a user-defined error event.</fsummary> <desc> <p>Log a user-defined error event. Error logger forwards the @@ -191,7 +191,7 @@ ok</pre> </desc> </func> <func> - <name name="get_format_depth" arity="0"/> + <name name="get_format_depth" arity="0" since="OTP 20.0"/> <fsummary>Get the value of the Kernel application variable <c>error_logger_format_depth</c>.</fsummary> <desc> @@ -211,8 +211,8 @@ ok</pre> </desc> </func> <func> - <name name="info_msg" arity="1"/> - <name name="info_msg" arity="2"/> + <name name="info_msg" arity="1" since=""/> + <name name="info_msg" arity="2" since=""/> <fsummary>Log a standard information event.</fsummary> <desc> <p>Log a standard information event. The <c><anno>Format</anno></c> @@ -244,7 +244,7 @@ ok</pre> </desc> </func> <func> - <name name="info_report" arity="1"/> + <name name="info_report" arity="1" since=""/> <fsummary>Log a standard information event.</fsummary> <desc> <p>Log a standard information event. Error logger forwards the @@ -271,7 +271,7 @@ ok</pre> </desc> </func> <func> - <name name="info_report" arity="2"/> + <name name="info_report" arity="2" since=""/> <fsummary>Log a user-defined information event.</fsummary> <desc> <p>Log a user-defined information event. Error logger forwards @@ -294,9 +294,9 @@ ok</pre> </desc> </func> <func> - <name name="logfile" arity="1" clause_i="1"/> - <name name="logfile" arity="1" clause_i="2"/> - <name name="logfile" arity="1" clause_i="3"/> + <name name="logfile" arity="1" clause_i="1" since=""/> + <name name="logfile" arity="1" clause_i="2" since=""/> + <name name="logfile" arity="1" clause_i="3" since=""/> <fsummary>Enable or disable error printouts to a file.</fsummary> <type variable="Filename"/> <type variable="OpenReason" name_i="1"/> @@ -346,7 +346,7 @@ ok</pre> </desc> </func> <func> - <name name="tty" arity="1"/> + <name name="tty" arity="1" since=""/> <fsummary>Enable or disable printouts to the terminal.</fsummary> <desc> <p>Enables (<c><anno>Flag</anno> == true</c>) or disables @@ -363,7 +363,7 @@ ok</pre> </desc> </func> <func> - <name name="warning_map" arity="0"/> + <name name="warning_map" arity="0" since=""/> <fsummary>Return the current mapping for warning events.</fsummary> <desc> <p>Returns the current mapping for warning events. Events sent @@ -400,8 +400,8 @@ ok</pre> </desc> </func> <func> - <name name="warning_msg" arity="1"/> - <name name="warning_msg" arity="2"/> + <name name="warning_msg" arity="1" since=""/> + <name name="warning_msg" arity="2" since=""/> <fsummary>Log a standard warning event.</fsummary> <desc> <p>Log a standard warning event. The <c><anno>Format</anno></c> @@ -429,7 +429,7 @@ ok</pre> </desc> </func> <func> - <name name="warning_report" arity="1"/> + <name name="warning_report" arity="1" since=""/> <fsummary>Log a standard warning event.</fsummary> <desc> <p>Log a standard warning event. Error logger forwards the event @@ -446,7 +446,7 @@ ok</pre> </desc> </func> <func> - <name name="warning_report" arity="2"/> + <name name="warning_report" arity="2" since=""/> <fsummary>Log a user-defined warning event.</fsummary> <desc> <p>Log a user-defined warning event. Error logger forwards the diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 9acaf6b41e..fc25e83d40 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>file</module> + <module since="">file</module> <modulesummary>File interface module.</modulesummary> <description> <p>This module provides an interface to the file system.</p> @@ -186,7 +186,7 @@ <funcs> <func> - <name name="advise" arity="4"/> + <name name="advise" arity="4" since="OTP R14B"/> <fsummary>Predeclare an access pattern for file data.</fsummary> <type name="posix_file_advise"/> <desc> @@ -197,7 +197,7 @@ </desc> </func> <func> - <name name="allocate" arity="3"/> + <name name="allocate" arity="3" since="OTP R16B"/> <fsummary>Allocate file space.</fsummary> <desc> <p><c>allocate/3</c> can be used to preallocate space for a file.</p> @@ -209,7 +209,7 @@ </desc> </func> <func> - <name name="change_group" arity="2"/> + <name name="change_group" arity="2" since=""/> <fsummary>Change group of a file.</fsummary> <desc> <p>Changes group of a file. See @@ -217,7 +217,7 @@ </desc> </func> <func> - <name name="change_mode" arity="2"/> + <name name="change_mode" arity="2" since="OTP R14B"/> <fsummary>Change permissions of a file.</fsummary> <desc> <p>Changes permissions of a file. See @@ -225,7 +225,7 @@ </desc> </func> <func> - <name name="change_owner" arity="2"/> + <name name="change_owner" arity="2" since=""/> <fsummary>Change owner of a file.</fsummary> <desc> <p>Changes owner of a file. See @@ -233,7 +233,7 @@ </desc> </func> <func> - <name name="change_owner" arity="3"/> + <name name="change_owner" arity="3" since=""/> <fsummary>Change owner and group of a file.</fsummary> <desc> <p>Changes owner and group of a file. See @@ -241,7 +241,7 @@ </desc> </func> <func> - <name name="change_time" arity="2"/> + <name name="change_time" arity="2" since=""/> <fsummary>Change the modification time of a file.</fsummary> <desc> <p>Changes the modification and access times of a file. See @@ -249,7 +249,7 @@ </desc> </func> <func> - <name name="change_time" arity="3"/> + <name name="change_time" arity="3" since=""/> <fsummary>Change the modification and last access time of a file.</fsummary> <desc> <p>Changes the modification and last access times of a file. See @@ -257,7 +257,7 @@ </desc> </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a file.</fsummary> <desc> <p>Closes the file referenced by <c><anno>IoDevice</anno></c>. It mostly @@ -270,7 +270,7 @@ </desc> </func> <func> - <name name="consult" arity="1"/> + <name name="consult" arity="1" since=""/> <fsummary>Read Erlang terms from a file.</fsummary> <desc> <p>Reads Erlang terms, separated by '.', from @@ -308,8 +308,8 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="copy" arity="2"/> - <name name="copy" arity="3"/> + <name name="copy" arity="2" since=""/> + <name name="copy" arity="3" since=""/> <fsummary>Copy file contents.</fsummary> <desc> <p>Copies <c><anno>ByteCount</anno></c> bytes from @@ -346,7 +346,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="datasync" arity="1"/> + <name name="datasync" arity="1" since="OTP R14B"/> <fsummary>Synchronize the in-memory data of a file, ignoring most of its metadata, with that on the physical medium.</fsummary> <desc> <p>Ensures that any buffers kept by the operating system @@ -369,7 +369,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="del_dir" arity="1"/> + <name name="del_dir" arity="1" since=""/> <fsummary>Delete a directory.</fsummary> <desc> <p>Tries to delete directory <c><anno>Dir</anno></c>. @@ -405,7 +405,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="delete" arity="1"/> + <name name="delete" arity="1" since=""/> <fsummary>Delete a file.</fsummary> <desc> <p>Tries to delete file <c><anno>Filename</anno></c>. @@ -442,7 +442,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="eval" arity="1"/> + <name name="eval" arity="1" since=""/> <fsummary>Evaluate Erlang expressions in a file.</fsummary> <desc> <p>Reads and evaluates Erlang expressions, separated by '.' (or @@ -476,7 +476,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="eval" arity="2"/> + <name name="eval" arity="2" since=""/> <fsummary>Evaluate Erlang expressions in a file.</fsummary> <desc> <p>The same as <c>eval/1</c>, but the variable bindings @@ -486,7 +486,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Return a descriptive string for an error reason.</fsummary> <desc> <p>Given the error reason returned by any function in this @@ -494,7 +494,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="get_cwd" arity="0"/> + <name name="get_cwd" arity="0" since=""/> <fsummary>Get the current working directory.</fsummary> <desc> <p>Returns <c>{ok, <anno>Dir</anno>}</c>, where <c><anno>Dir</anno></c> @@ -516,7 +516,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="get_cwd" arity="1"/> + <name name="get_cwd" arity="1" since=""/> <fsummary>Get the current working directory for the specified drive.</fsummary> <desc> <p>Returns <c>{ok, <anno>Dir</anno>}</c> or @@ -547,7 +547,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="list_dir" arity="1"/> + <name name="list_dir" arity="1" since=""/> <fsummary>List files in a directory.</fsummary> <desc> <p>Lists all files in a directory, <em>except</em> files @@ -578,7 +578,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="list_dir_all" arity="1"/> + <name name="list_dir_all" arity="1" since="OTP R16B"/> <fsummary>List all files in a directory.</fsummary> <desc> <p><marker id="list_dir_all"/>Lists all the files in a directory, @@ -603,7 +603,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="make_dir" arity="1"/> + <name name="make_dir" arity="1" since=""/> <fsummary>Make a directory.</fsummary> <desc> <p>Tries to create directory <c><anno>Dir</anno></c>. Missing parent @@ -637,7 +637,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="make_link" arity="2"/> + <name name="make_link" arity="2" since=""/> <fsummary>Make a hard link to a file.</fsummary> <desc> <p>Makes a hard link from <c><anno>Existing</anno></c> to @@ -666,7 +666,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="make_symlink" arity="2"/> + <name name="make_symlink" arity="2" since=""/> <fsummary>Make a symbolic link to a file or directory.</fsummary> <desc> <p>Creates a symbolic link <c><anno>New</anno></c> to @@ -702,7 +702,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="native_name_encoding" arity="0"/> + <name name="native_name_encoding" arity="0" since="OTP R14B01"/> <fsummary>Return the configured filename encoding of the VM.</fsummary> <desc> <p><marker id="native_name_encoding"/>Returns @@ -714,7 +714,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="open" arity="2"/> + <name name="open" arity="2" since=""/> <fsummary>Open a file.</fsummary> <desc> <p>Opens file <c><anno>File</anno></c> in the mode determined @@ -997,7 +997,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="path_consult" arity="2"/> + <name name="path_consult" arity="2" since=""/> <fsummary>Read Erlang terms from a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory @@ -1039,7 +1039,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="path_eval" arity="2"/> + <name name="path_eval" arity="2" since=""/> <fsummary>Evaluate Erlang expressions in a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory @@ -1085,7 +1085,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="path_open" arity="3"/> + <name name="path_open" arity="3" since=""/> <fsummary>Open a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory @@ -1114,7 +1114,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="path_script" arity="2"/> + <name name="path_script" arity="2" since=""/> <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory @@ -1158,7 +1158,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="path_script" arity="3"/> + <name name="path_script" arity="3" since=""/> <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>The same as <c>path_script/2</c> but the variable bindings @@ -1168,7 +1168,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="pid2name" arity="1"/> + <name name="pid2name" arity="1" since=""/> <fsummary>Return the name of the file handled by a pid.</fsummary> <desc> <p>If <c><anno>Pid</anno></c> is an I/O device, that is, a pid returned from @@ -1193,7 +1193,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="position" arity="2"/> + <name name="position" arity="2" since=""/> <fsummary>Set position in a file.</fsummary> <desc> <p>Sets the position of the file referenced by <c><anno>IoDevice</anno></c> @@ -1245,7 +1245,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="pread" arity="2"/> + <name name="pread" arity="2" since=""/> <fsummary>Read from a file at certain positions.</fsummary> <desc> <p>Performs a sequence of <c>pread/3</c> in one operation, @@ -1263,7 +1263,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="pread" arity="3"/> + <name name="pread" arity="3" since=""/> <fsummary>Read from a file at a certain position.</fsummary> <desc> <p>Combines <c>position/2</c> and <c>read/2</c> in one @@ -1283,7 +1283,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="pwrite" arity="2"/> + <name name="pwrite" arity="2" since=""/> <fsummary>Write to a file at certain positions.</fsummary> <desc> <p>Performs a sequence of <c>pwrite/3</c> in one operation, @@ -1298,7 +1298,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="pwrite" arity="3"/> + <name name="pwrite" arity="3" since=""/> <fsummary>Write to a file at a certain position.</fsummary> <desc> <p>Combines <c>position/2</c> and <c>write/2</c> in one @@ -1317,7 +1317,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read" arity="2"/> + <name name="read" arity="2" since=""/> <fsummary>Read from a file.</fsummary> <desc> <p>Reads <c><anno>Number</anno></c> bytes/characters from the file @@ -1371,7 +1371,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read_file" arity="1"/> + <name name="read_file" arity="1" since=""/> <fsummary>Read a file.</fsummary> <desc> <p>Returns <c>{ok, <anno>Binary</anno>}</c>, where @@ -1407,8 +1407,8 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read_file_info" arity="1"/> - <name name="read_file_info" arity="2"/> + <name name="read_file_info" arity="1" since=""/> + <name name="read_file_info" arity="2" since="OTP R15B"/> <fsummary>Retrieve information about a file.</fsummary> <desc> <p>Retrieves information about a file. Returns @@ -1562,7 +1562,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read_line" arity="1"/> + <name name="read_line" arity="1" since=""/> <fsummary>Read a line from a file.</fsummary> <desc> <p>Reads a line of bytes/characters from the file referenced by @@ -1619,7 +1619,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read_link" arity="1"/> + <name name="read_link" arity="1" since=""/> <fsummary>See what a link is pointing to.</fsummary> <desc> <p><marker id="read_link_all"/>Returns @@ -1649,7 +1649,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read_link_all" arity="1"/> + <name name="read_link_all" arity="1" since="OTP R16B"/> <fsummary>See what a link is pointing to.</fsummary> <desc> <p>Returns <c>{ok, <anno>Filename</anno>}</c> if @@ -1677,8 +1677,8 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="read_link_info" arity="1"/> - <name name="read_link_info" arity="2"/> + <name name="read_link_info" arity="1" since=""/> + <name name="read_link_info" arity="2" since="OTP R15B"/> <fsummary>Retrieve information about a link or file.</fsummary> <desc> <p>Works like @@ -1699,7 +1699,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="rename" arity="2"/> + <name name="rename" arity="2" since=""/> <fsummary>Rename a file.</fsummary> <desc> <p>Tries to rename the file <c><anno>Source</anno></c> to @@ -1762,7 +1762,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="script" arity="1"/> + <name name="script" arity="1" since=""/> <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>Reads and evaluates Erlang expressions, separated by '.' (or @@ -1797,7 +1797,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="script" arity="2"/> + <name name="script" arity="2" since=""/> <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>The same as <c>script/1</c> but the variable bindings @@ -1807,7 +1807,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="sendfile" arity="2"/> + <name name="sendfile" arity="2" since="OTP R15B"/> <fsummary>Send a file to a socket.</fsummary> <desc> <p>Sends the file <c>Filename</c> to <c>Socket</c>. @@ -1816,7 +1816,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="sendfile" arity="5"/> + <name name="sendfile" arity="5" since="OTP R15B"/> <fsummary>Send a file to a socket.</fsummary> <type name="sendfile_option"/> <desc> @@ -1843,7 +1843,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="set_cwd" arity="1"/> + <name name="set_cwd" arity="1" since=""/> <fsummary>Set the current working directory.</fsummary> <desc> <p>Sets the current working directory of the file server to @@ -1890,7 +1890,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="sync" arity="1"/> + <name name="sync" arity="1" since=""/> <fsummary>Synchronize the in-memory state of a file with that on the physical medium.</fsummary> <desc> <p>Ensures that any buffers kept by the operating system @@ -1906,7 +1906,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="truncate" arity="1"/> + <name name="truncate" arity="1" since=""/> <fsummary>Truncate a file.</fsummary> <desc> <p>Truncates the file referenced by <c><anno>IoDevice</anno></c> at @@ -1915,7 +1915,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="write" arity="2"/> + <name name="write" arity="2" since=""/> <fsummary>Write to a file.</fsummary> <desc> <p>Writes <c><anno>Bytes</anno></c> to the file referenced by @@ -1941,7 +1941,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="write_file" arity="2"/> + <name name="write_file" arity="2" since=""/> <fsummary>Write a file.</fsummary> <desc> <p>Writes the contents of the <c>iodata</c> term <c><anno>Bytes</anno></c> @@ -1978,7 +1978,7 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="write_file" arity="3"/> + <name name="write_file" arity="3" since=""/> <fsummary>Write a file.</fsummary> <desc> <p>Same as <c>write_file/2</c>, but takes a third argument @@ -1989,8 +1989,8 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name name="write_file_info" arity="2"/> - <name name="write_file_info" arity="3"/> + <name name="write_file_info" arity="2" since=""/> + <name name="write_file_info" arity="3" since="OTP R15B"/> <fsummary>Change file information.</fsummary> <desc> <p>Changes file information. Returns <c>ok</c> if successful, diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 1e08b25f66..f70d6c24db 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>gen_sctp.xml</file> </header> - <module>gen_sctp</module> + <module since="">gen_sctp</module> <modulesummary>Functions for communicating with sockets using the SCTP protocol.</modulesummary> <description> @@ -100,7 +100,7 @@ <funcs> <func> - <name name="abort" arity="2"/> + <name name="abort" arity="2" since=""/> <fsummary>Abnormally terminate the association specified by <c>Assoc</c>, without flushing of unsent data.</fsummary> <desc> @@ -113,7 +113,7 @@ </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close the socket and all associations on it.</fsummary> <desc> <p>Closes the socket and all associations on it. The unsent @@ -128,7 +128,7 @@ </func> <func> - <name name="connect" arity="4"/> + <name name="connect" arity="4" since=""/> <fsummary>Same as <c>connect(Socket, Addr, Port, Opts, infinity)</c>.</fsummary> <desc> <p>Same as <c>connect(<anno>Socket</anno>, <anno>Addr</anno>, @@ -137,7 +137,7 @@ </func> <func> - <name name="connect" arity="5"/> + <name name="connect" arity="5" since=""/> <fsummary>Establish a new association for socket <c>Socket</c>, with a peer (SCTP server socket).</fsummary> <desc> @@ -213,7 +213,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="connect_init" arity="4"/> + <name name="connect_init" arity="4" since="OTP R13B04"/> <fsummary>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>..</fsummary> <desc> <p>Same as <c>connect_init(<anno>Socket</anno>, <anno>Addr</anno>, @@ -222,7 +222,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="connect_init" arity="5"/> + <name name="connect_init" arity="5" since="OTP R13B04"/> <fsummary>Initiate a new association for socket <c>Socket</c>, with a peer (SCTP server socket).</fsummary> <desc> @@ -248,7 +248,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="controlling_process" arity="2"/> + <name name="controlling_process" arity="2" since=""/> <fsummary>Assign a new controlling process pid to the socket.</fsummary> <desc> <p>Assigns a new controlling process <c><anno>Pid</anno></c> to @@ -259,7 +259,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="eof" arity="2"/> + <name name="eof" arity="2" since=""/> <fsummary>Gracefully terminate the association specified by <c>Assoc</c>, with flushing of all unsent data.</fsummary> <desc> @@ -272,7 +272,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="error_string" arity="1"/> + <name name="error_string" arity="1" since=""/> <fsummary>Translate an SCTP error number into a string.</fsummary> <desc> <p>Translates an SCTP error number from, for example, @@ -283,8 +283,8 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="listen" arity="2" clause_i="1"/> - <name name="listen" arity="2" clause_i="2"/> + <name name="listen" arity="2" clause_i="1" since=""/> + <name name="listen" arity="2" clause_i="2" since="OTP R15B"/> <fsummary>Set up a socket to listen.</fsummary> <desc> <p>Sets up a socket to listen on the IP address and port number @@ -300,10 +300,10 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="open" arity="0"/> - <name name="open" arity="1" clause_i="1"/> - <name name="open" arity="1" clause_i="2"/> - <name name="open" arity="2"/> + <name name="open" arity="0" since=""/> + <name name="open" arity="1" clause_i="1" since=""/> + <name name="open" arity="1" clause_i="2" since=""/> + <name name="open" arity="2" since=""/> <fsummary>Create an SCTP socket and binds it to local addresses.</fsummary> <desc> <p>Creates an SCTP socket and binds it to the local addresses @@ -366,7 +366,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="peeloff" arity="2"/> + <name name="peeloff" arity="2" since="OTP R15B"/> <fsummary>Peel off a type <c>stream</c> socket from a type <c>seqpacket</c> one.</fsummary> <desc> @@ -387,8 +387,8 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="recv" arity="1"/> - <name name="recv" arity="2"/> + <name name="recv" arity="1" since=""/> + <name name="recv" arity="2" since=""/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receives the <c><anno>Data</anno></c> message from any association @@ -532,7 +532,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="send" arity="3"/> + <name name="send" arity="3" since=""/> <fsummary>Send a message using an <c>#sctp_sndrcvinfo{}</c>record.</fsummary> <desc> <p>Sends the <c><anno>Data</anno></c> message with all sending @@ -547,7 +547,7 @@ connect(Socket, Ip, Port>, </func> <func> - <name name="send" arity="4"/> + <name name="send" arity="4" since=""/> <fsummary>Send a message over an existing association and specified stream.</fsummary> <desc> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 24d63693fd..fc16473393 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -27,7 +27,7 @@ <date>1997-10-24</date> <rev>A</rev> </header> - <module>gen_tcp</module> + <module since="">gen_tcp</module> <modulesummary>Interface to TCP/IP sockets.</modulesummary> <description> <p>This module provides functions for communicating @@ -116,8 +116,8 @@ do_recv(Sock, Bs) -> <funcs> <func> - <name name="accept" arity="1"/> - <name name="accept" arity="2"/> + <name name="accept" arity="1" since=""/> + <name name="accept" arity="2" since=""/> <fsummary>Accept an incoming connection request on a listening socket.</fsummary> <type_desc variable="ListenSocket">Returned by <seealso marker="#listen/2"><c>listen/2</c></seealso>. @@ -163,7 +163,7 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a TCP socket.</fsummary> <desc> <p>Closes a TCP socket.</p> @@ -188,8 +188,8 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="connect" arity="3"/> - <name name="connect" arity="4"/> + <name name="connect" arity="3" since=""/> + <name name="connect" arity="4" since=""/> <fsummary>Connect to a TCP port.</fsummary> <desc> <p>Connects to a server on TCP port <c><anno>Port</anno></c> on the host @@ -268,7 +268,7 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="controlling_process" arity="2"/> + <name name="controlling_process" arity="2" since=""/> <fsummary>Change controlling process of a socket.</fsummary> <desc> <p>Assigns a new controlling process <c><anno>Pid</anno></c> to @@ -292,7 +292,7 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="listen" arity="2"/> + <name name="listen" arity="2" since=""/> <fsummary>Set up a socket to listen on a port.</fsummary> <desc> <p>Sets up a socket to listen on port <c><anno>Port</anno></c> on @@ -349,8 +349,8 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="recv" arity="2"/> - <name name="recv" arity="3"/> + <name name="recv" arity="2" since=""/> + <name name="recv" arity="3" since=""/> <fsummary>Receive a packet from a passive socket.</fsummary> <type_desc variable="HttpPacket">See the description of <c>HttpPacket</c> in @@ -375,7 +375,7 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="send" arity="2"/> + <name name="send" arity="2" since=""/> <fsummary>Send a packet.</fsummary> <desc> <p>Sends a packet on a socket.</p> @@ -386,7 +386,7 @@ do_recv(Sock, Bs) -> </func> <func> - <name name="shutdown" arity="2"/> + <name name="shutdown" arity="2" since=""/> <fsummary>Asynchronously close a socket.</fsummary> <desc> <p>Closes a socket in one or two directions.</p> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 840ca3c188..d20fc1fdfd 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -28,7 +28,7 @@ <date>1997-12-03</date> <rev>A</rev> </header> - <module>gen_udp</module> + <module since="">gen_udp</module> <modulesummary>Interface to UDP sockets.</modulesummary> <description> <p>This module provides functions for communicating @@ -53,7 +53,7 @@ <funcs> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a UDP socket.</fsummary> <desc> <p>Closes a UDP socket.</p> @@ -61,7 +61,7 @@ </func> <func> - <name name="controlling_process" arity="2"/> + <name name="controlling_process" arity="2" since=""/> <fsummary>Change controlling process of a socket.</fsummary> <desc> <p>Assigns a new controlling process <c><anno>Pid</anno></c> to @@ -77,8 +77,8 @@ </func> <func> - <name name="open" arity="1"/> - <name name="open" arity="2"/> + <name name="open" arity="1" since=""/> + <name name="open" arity="2" since=""/> <fsummary>Associate a UDP port number with the process calling it.</fsummary> <desc> <p>Associates a UDP port number (<c><anno>Port</anno></c>) with the @@ -189,8 +189,8 @@ </func> <func> - <name name="recv" arity="2"/> - <name name="recv" arity="3"/> + <name name="recv" arity="2" since=""/> + <name name="recv" arity="3" since=""/> <fsummary>Receive a packet from a passive socket.</fsummary> <desc> <p> @@ -213,7 +213,7 @@ </func> <func> - <name name="send" arity="4"/> + <name name="send" arity="4" since=""/> <fsummary>Send a packet.</fsummary> <desc> <p> diff --git a/lib/kernel/doc/src/global.xml b/lib/kernel/doc/src/global.xml index 4442741f54..dfe71de5ce 100644 --- a/lib/kernel/doc/src/global.xml +++ b/lib/kernel/doc/src/global.xml @@ -28,7 +28,7 @@ <date>1997-11-17</date> <rev></rev> </header> - <module>global</module> + <module since="">global</module> <modulesummary>A global name registration facility.</modulesummary> <description> <p>This module consists of the following services:</p> @@ -100,8 +100,8 @@ <funcs> <func> - <name name="del_lock" arity="1"/> - <name name="del_lock" arity="2"/> + <name name="del_lock" arity="1" since=""/> + <name name="del_lock" arity="2" since=""/> <fsummary>Delete a lock.</fsummary> <desc> <p>Deletes the lock <c><anno>Id</anno></c> synchronously.</p> @@ -109,7 +109,7 @@ </func> <func> - <name name="notify_all_name" arity="3"/> + <name name="notify_all_name" arity="3" since=""/> <fsummary>Name resolving function that notifies both pids.</fsummary> <desc> <p>Can be used as a name resolving function for @@ -123,7 +123,7 @@ </func> <func> - <name name="random_exit_name" arity="3"/> + <name name="random_exit_name" arity="3" since=""/> <fsummary>Name resolving function that kills one pid.</fsummary> <desc> <p>Can be used as a name resolving function for @@ -136,7 +136,7 @@ </func> <func> - <name name="random_notify_name" arity="3"/> + <name name="random_notify_name" arity="3" since=""/> <fsummary>Name resolving function that notifies one pid.</fsummary> <desc> <p>Can be used as a name resolving function for @@ -150,8 +150,8 @@ </func> <func> - <name name="re_register_name" arity="2"/> - <name name="re_register_name" arity="3"/> + <name name="re_register_name" arity="2" since=""/> + <name name="re_register_name" arity="3" since=""/> <fsummary>Atomically re-register a name.</fsummary> <type name="method"/> <type_desc name="method">{<c>Module</c>, <c>Function</c>} @@ -167,8 +167,8 @@ </func> <func> - <name name="register_name" arity="2"/> - <name name="register_name" arity="3"/> + <name name="register_name" arity="2" since=""/> + <name name="register_name" arity="3" since=""/> <fsummary>Globally register a name for a pid.</fsummary> <type name="method"/> <type_desc name="method">{<c>Module</c>, <c>Function</c>} is also @@ -221,7 +221,7 @@ </func> <func> - <name name="registered_names" arity="0"/> + <name name="registered_names" arity="0" since=""/> <fsummary>All globally registered names.</fsummary> <desc> <p>Returns a list of all globally registered names.</p> @@ -229,7 +229,7 @@ </func> <func> - <name name="send" arity="2"/> + <name name="send" arity="2" since=""/> <fsummary>Send a message to a globally registered pid.</fsummary> <desc> <p>Sends message <c><anno>Msg</anno></c> to the pid globally registered @@ -241,9 +241,9 @@ </func> <func> - <name name="set_lock" arity="1"/> - <name name="set_lock" arity="2"/> - <name name="set_lock" arity="3"/> + <name name="set_lock" arity="1" since=""/> + <name name="set_lock" arity="2" since=""/> + <name name="set_lock" arity="3" since=""/> <fsummary>Set a lock on the specified nodes.</fsummary> <type name="id"/> <type name="retries"/> @@ -287,7 +287,7 @@ </func> <func> - <name name="sync" arity="0"/> + <name name="sync" arity="0" since=""/> <fsummary>Synchronize the global name server.</fsummary> <desc> <p>Synchronizes the global name server with all nodes known to @@ -302,9 +302,9 @@ </func> <func> - <name name="trans" arity="2"/> - <name name="trans" arity="3"/> - <name name="trans" arity="4"/> + <name name="trans" arity="2" since=""/> + <name name="trans" arity="3" since=""/> + <name name="trans" arity="4" since=""/> <fsummary>Micro transaction facility.</fsummary> <type name="retries"/> <type name="trans_fun"/> @@ -322,7 +322,7 @@ </func> <func> - <name name="unregister_name" arity="1"/> + <name name="unregister_name" arity="1" since=""/> <fsummary>Remove a globally registered name for a pid.</fsummary> <desc> <p>Removes the globally registered name <c><anno>Name</anno></c> from @@ -331,7 +331,7 @@ </func> <func> - <name name="whereis_name" arity="1"/> + <name name="whereis_name" arity="1" since=""/> <fsummary>Get the pid with a specified globally registered name.</fsummary> <desc> <p>Returns the pid with the globally registered name diff --git a/lib/kernel/doc/src/global_group.xml b/lib/kernel/doc/src/global_group.xml index 8f947b9adf..74d15cd476 100644 --- a/lib/kernel/doc/src/global_group.xml +++ b/lib/kernel/doc/src/global_group.xml @@ -28,7 +28,7 @@ <date>1998-12-18</date> <rev>B</rev> </header> - <module>global_group</module> + <module since="">global_group</module> <modulesummary>Grouping nodes to global name registration groups.</modulesummary> <description> <p>This module makes it possible to partition the nodes of a @@ -105,7 +105,7 @@ <funcs> <func> - <name name="global_groups" arity="0"/> + <name name="global_groups" arity="0" since=""/> <fsummary>Return the global group names.</fsummary> <desc> <p>Returns a tuple containing the name of the global group that @@ -116,7 +116,7 @@ </func> <func> - <name name="info" arity="0"/> + <name name="info" arity="0" since=""/> <fsummary>Information about global groups.</fsummary> <type name="info_item"/> <type name="sync_state"/> @@ -173,7 +173,7 @@ </func> <func> - <name name="monitor_nodes" arity="1"/> + <name name="monitor_nodes" arity="1" since=""/> <fsummary>Subscribe to node status changes.</fsummary> <desc> <p>Depending on <c><anno>Flag</anno></c>, the calling process @@ -187,7 +187,7 @@ </func> <func> - <name name="own_nodes" arity="0"/> + <name name="own_nodes" arity="0" since=""/> <fsummary>Return the group nodes.</fsummary> <desc> <p>Returns the names of all group nodes, regardless of their @@ -196,7 +196,7 @@ </func> <func> - <name name="registered_names" arity="1"/> + <name name="registered_names" arity="1" since=""/> <fsummary>Return globally registered names.</fsummary> <desc> <p>Returns a list of all names that are globally registered @@ -205,8 +205,8 @@ </func> <func> - <name name="send" arity="2"/> - <name name="send" arity="3"/> + <name name="send" arity="2" since=""/> + <name name="send" arity="3" since=""/> <fsummary>Send a message to a globally registered pid.</fsummary> <desc> <p>Searches for <c><anno>Name</anno></c>, globally registered on @@ -224,7 +224,7 @@ </func> <func> - <name name="sync" arity="0"/> + <name name="sync" arity="0" since=""/> <fsummary>Synchronize the group nodes.</fsummary> <desc> <p>Synchronizes the group nodes, that is, the global name @@ -242,8 +242,8 @@ </func> <func> - <name name="whereis_name" arity="1"/> - <name name="whereis_name" arity="2"/> + <name name="whereis_name" arity="1" since=""/> + <name name="whereis_name" arity="2" since=""/> <fsummary>Get the pid with a specified globally registered name.</fsummary> <desc> <p>Searches for <c><anno>Name</anno></c>, globally registered on diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index ad1a2ffeb9..4243b1ffe8 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -28,7 +28,7 @@ <date>1998-01-28</date> <rev>A</rev> </header> - <module>heart</module> + <module since="">heart</module> <modulesummary>Heartbeat monitoring of an Erlang runtime system.</modulesummary> <description> <p>This modules contains the interface to the <c>heart</c> process. @@ -119,7 +119,7 @@ <funcs> <func> - <name name="set_cmd" arity="1"/> + <name name="set_cmd" arity="1" since=""/> <fsummary>Set a temporary reboot command.</fsummary> <desc> <p>Sets a temporary reboot command. This command is used if @@ -136,7 +136,7 @@ </func> <func> - <name name="clear_cmd" arity="0"/> + <name name="clear_cmd" arity="0" since=""/> <fsummary>Clear the temporary boot command.</fsummary> <desc> <p>Clears the temporary boot command. If the system terminates, @@ -145,7 +145,7 @@ </func> <func> - <name name="get_cmd" arity="0"/> + <name name="get_cmd" arity="0" since=""/> <fsummary>Get the temporary reboot command.</fsummary> <desc> <p>Gets the temporary reboot command. If the command is cleared, @@ -154,7 +154,7 @@ </func> <func> - <name name="set_callback" arity="2"/> + <name name="set_callback" arity="2" since="OTP 18.3"/> <fsummary>Set a validation callback</fsummary> <desc> <p> This validation callback will be executed before any @@ -166,14 +166,14 @@ </desc> </func> <func> - <name name="clear_callback" arity="0"/> + <name name="clear_callback" arity="0" since="OTP 18.3"/> <fsummary>Clear the validation callback</fsummary> <desc> <p>Removes the validation callback call before heartbeats.</p> </desc> </func> <func> - <name name="get_callback" arity="0"/> + <name name="get_callback" arity="0" since="OTP 18.3"/> <fsummary>Get the validation callback</fsummary> <desc> <p>Get the validation callback. If the callback is cleared, <c>none</c> will be returned.</p> @@ -181,7 +181,7 @@ </func> <func> - <name name="set_options" arity="1"/> + <name name="set_options" arity="1" since="OTP 18.3"/> <fsummary>Set a list of options</fsummary> <desc> <p> Valid options <c>set_options</c> are: </p> @@ -199,7 +199,7 @@ </desc> </func> <func> - <name name="get_options" arity="0"/> + <name name="get_options" arity="0" since="OTP 18.3"/> <fsummary>Get the temporary reboot command</fsummary> <desc> <p>Returns <c>{ok, Options}</c> where <c>Options</c> is a list of current options enabled for heart. diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 87b08e4e36..104c698591 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -28,7 +28,7 @@ <date>1998-02-04</date> <rev>A</rev> </header> - <module>inet</module> + <module since="">inet</module> <modulesummary>Access to TCP/IP protocols.</modulesummary> <description> <p>This module provides access to TCP/IP protocols.</p> @@ -298,7 +298,7 @@ fe80::204:acff:fe17:bf38 <funcs> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a socket of any type.</fsummary> <desc> <p>Closes a socket of any type.</p> @@ -306,7 +306,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Return a descriptive string for an error reason.</fsummary> <desc> <p>Returns a diagnostic error string. For possible POSIX values and @@ -316,7 +316,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="get_rc" arity="0"/> + <name name="get_rc" arity="0" since=""/> <fsummary>Return a list of IP configuration parameters.</fsummary> <desc> <p> @@ -335,7 +335,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="getaddr" arity="2"/> + <name name="getaddr" arity="2" since=""/> <fsummary>Return the IP address for a host.</fsummary> <desc> <p>Returns the IP address for <c><anno>Host</anno></c> as a tuple of @@ -345,7 +345,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="getaddrs" arity="2"/> + <name name="getaddrs" arity="2" since=""/> <fsummary>Return the IP addresses for a host.</fsummary> <desc> <p>Returns a list of all IP addresses for <c><anno>Host</anno></c>. @@ -355,7 +355,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="gethostbyaddr" arity="1"/> + <name name="gethostbyaddr" arity="1" since=""/> <fsummary>Return a hostent record for the host with the specified address.</fsummary> <desc> @@ -364,7 +364,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="gethostbyname" arity="1"/> + <name name="gethostbyname" arity="1" since=""/> <fsummary>Return a hostent record for the host with the specified name. </fsummary> <desc> @@ -376,7 +376,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="gethostbyname" arity="2"/> + <name name="gethostbyname" arity="2" since=""/> <fsummary>Return a hostent record for the host with the specified name. </fsummary> <desc> @@ -386,7 +386,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="gethostname" arity="0"/> + <name name="gethostname" arity="0" since=""/> <fsummary>Return the local hostname.</fsummary> <desc> <p>Returns the local hostname. Never fails.</p> @@ -394,7 +394,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="getifaddrs" arity="0"/> + <name name="getifaddrs" arity="0" since="OTP R14B01"/> <fsummary>Return a list of interfaces and their addresses.</fsummary> <desc> <p> @@ -416,7 +416,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name>getifaddrs(Opts) -> + <name since="OTP 21.2">getifaddrs(Opts) -> {ok, [{Ifname, Ifopts}]} | {error, Posix} </name> <fsummary>Return a list of interfaces and their addresses.</fsummary> @@ -459,7 +459,7 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name name="getopts" arity="2"/> + <name name="getopts" arity="2" since=""/> <fsummary>Get one or more options for a socket.</fsummary> <desc> <p>Gets one or more options for a socket. For a list of available @@ -529,8 +529,8 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="getstat" arity="1"/> - <name name="getstat" arity="2"/> + <name name="getstat" arity="1" since=""/> + <name name="getstat" arity="2" since=""/> <fsummary>Get one or more statistic options for a socket.</fsummary> <type name="stat_option"/> <desc> @@ -586,9 +586,9 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="i" arity="0" /> - <name name="i" arity="1" /> - <name name="i" arity="2" /> + <name name="i" arity="0" since="OTP 21.0"/> + <name name="i" arity="1" since="OTP 21.0"/> + <name name="i" arity="2" since="OTP 21.0"/> <fsummary>Displays information and statistics about sockets on the terminal</fsummary> <desc> <p> @@ -641,7 +641,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="ntoa" arity="1" /> + <name name="ntoa" arity="1" since="OTP R16B02"/> <fsummary>Convert IPv6/IPV4 address to ASCII.</fsummary> <desc> <p>Parses an @@ -651,7 +651,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="parse_address" arity="1" /> + <name name="parse_address" arity="1" since="OTP R16B"/> <fsummary>Parse an IPv4 or IPv6 address.</fsummary> <desc> <p>Parses an IPv4 or IPv6 address string and returns an @@ -662,7 +662,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="parse_ipv4_address" arity="1" /> + <name name="parse_ipv4_address" arity="1" since="OTP R16B"/> <fsummary>Parse an IPv4 address.</fsummary> <desc> <p>Parses an IPv4 address string and returns an @@ -672,7 +672,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="parse_ipv4strict_address" arity="1" /> + <name name="parse_ipv4strict_address" arity="1" since="OTP R16B"/> <fsummary>Parse an IPv4 address strict.</fsummary> <desc> <p>Parses an IPv4 address string containing four fields, that is, @@ -683,7 +683,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="parse_ipv6_address" arity="1" /> + <name name="parse_ipv6_address" arity="1" since="OTP R16B"/> <fsummary>Parse an IPv6 address.</fsummary> <desc> <p>Parses an IPv6 address string and returns an @@ -694,7 +694,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="parse_ipv6strict_address" arity="1" /> + <name name="parse_ipv6strict_address" arity="1" since="OTP R16B"/> <fsummary>Parse an IPv6 address strict.</fsummary> <desc> <p>Parses an IPv6 address string and returns an @@ -704,7 +704,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="ipv4_mapped_ipv6_address" arity="1" /> + <name name="ipv4_mapped_ipv6_address" arity="1" since="OTP 21.0"/> <fsummary>Convert to and from IPv4-mapped IPv6 address.</fsummary> <desc> <p> @@ -717,7 +717,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="parse_strict_address" arity="1" /> + <name name="parse_strict_address" arity="1" since="OTP R16B"/> <fsummary>Parse an IPv4 or IPv6 address strict.</fsummary> <desc> <p>Parses an IPv4 or IPv6 address string and returns an @@ -728,7 +728,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="peername" arity="1"/> + <name name="peername" arity="1" since=""/> <fsummary>Return the address and port for the other end of a connection. </fsummary> <desc> @@ -741,7 +741,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="peernames" arity="1"/> + <name name="peernames" arity="1" since="OTP R16B03"/> <fsummary>Return all address/port numbers for the other end of a connection.</fsummary> <desc> @@ -755,7 +755,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="peernames" arity="2"/> + <name name="peernames" arity="2" since="OTP R16B03"/> <fsummary>Return all address/port numbers for the other end of a connection.</fsummary> <desc> @@ -774,7 +774,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="port" arity="1"/> + <name name="port" arity="1" since=""/> <fsummary>Return the local port number for a socket.</fsummary> <desc> <p>Returns the local port number for a socket.</p> @@ -782,7 +782,7 @@ get_tcpi_sacked(Sock) -> </func> <func> - <name name="setopts" arity="2"/> + <name name="setopts" arity="2" since=""/> <fsummary>Set one or more options for a socket.</fsummary> <desc> <p>Sets one or more options for a socket.</p> @@ -1486,7 +1486,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> </func> <func> - <name name="sockname" arity="1"/> + <name name="sockname" arity="1" since=""/> <fsummary>Return the local address and port number for a socket. </fsummary> <desc> @@ -1499,7 +1499,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> </func> <func> - <name name="socknames" arity="1"/> + <name name="socknames" arity="1" since="OTP R16B03"/> <fsummary>Return all local address/port numbers for a socket.</fsummary> <desc> <p>Equivalent to @@ -1509,7 +1509,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> </func> <func> - <name name="socknames" arity="2"/> + <name name="socknames" arity="2" since="OTP R16B03"/> <fsummary>Return all local address/port numbers for a socket.</fsummary> <desc> <p>Returns a list of all local address/port number pairs for a socket diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index 351d86a93a..1904e371f7 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -28,7 +28,7 @@ <date>2009-09-11</date> <rev>A</rev> </header> - <module>inet_res</module> + <module since="">inet_res</module> <modulesummary>A rudimentary DNS client.</modulesummary> <description> <p>This module performs DNS name resolving to recursive name servers.</p> @@ -185,8 +185,8 @@ inet_dns:record_type(_) -> undefined.</pre> <funcs> <func> - <name name="getbyname" arity="2"/> - <name name="getbyname" arity="3"/> + <name name="getbyname" arity="2" since=""/> + <name name="getbyname" arity="3" since=""/> <fsummary>Resolve a DNS record of the specified type for the specified host.</fsummary> <desc> @@ -205,8 +205,8 @@ inet_dns:record_type(_) -> undefined.</pre> </func> <func> - <name name="gethostbyaddr" arity="1"/> - <name name="gethostbyaddr" arity="2"/> + <name name="gethostbyaddr" arity="1" since=""/> + <name name="gethostbyaddr" arity="2" since=""/> <fsummary>Return a hostent record for the host with the specified address.</fsummary> <desc> @@ -217,9 +217,9 @@ inet_dns:record_type(_) -> undefined.</pre> </func> <func> - <name name="gethostbyname" arity="1"/> - <name name="gethostbyname" arity="2"/> - <name name="gethostbyname" arity="3"/> + <name name="gethostbyname" arity="1" since=""/> + <name name="gethostbyname" arity="2" since=""/> + <name name="gethostbyname" arity="3" since=""/> <fsummary>Return a hostent record for the host with the specified name. </fsummary> <desc> @@ -235,9 +235,9 @@ inet_dns:record_type(_) -> undefined.</pre> </func> <func> - <name name="lookup" arity="3"/> - <name name="lookup" arity="4"/> - <name name="lookup" arity="5"/> + <name name="lookup" arity="3" since=""/> + <name name="lookup" arity="4" since=""/> + <name name="lookup" arity="5" since=""/> <fsummary>Resolve the DNS data for the record of the specified type and class for the specified name.</fsummary> <desc> @@ -257,9 +257,9 @@ inet_dns:record_type(_) -> undefined.</pre> </func> <func> - <name name="resolve" arity="3"/> - <name name="resolve" arity="4"/> - <name name="resolve" arity="5"/> + <name name="resolve" arity="3" since=""/> + <name name="resolve" arity="4" since=""/> + <name name="resolve" arity="5" since=""/> <fsummary>Resolve a DNS record of the specified type and class for the specified name.</fsummary> <desc> @@ -326,9 +326,9 @@ example_lookup(Name, Class, Type) -> <funcs> <func> - <name name="nslookup" arity="3"/> - <name name="nslookup" arity="4" clause_i="1"/> - <name name="nslookup" arity="4" clause_i="2"/> + <name name="nslookup" arity="3" since=""/> + <name name="nslookup" arity="4" clause_i="1" since=""/> + <name name="nslookup" arity="4" clause_i="2" since=""/> <fsummary>Resolve a DNS record of the specified type and class for the specified name.</fsummary> <type variable="Name"/> @@ -344,8 +344,8 @@ example_lookup(Name, Class, Type) -> </func> <func> - <name name="nnslookup" arity="4"/> - <name name="nnslookup" arity="5"/> + <name name="nnslookup" arity="4" since=""/> + <name name="nnslookup" arity="5" since=""/> <fsummary>Resolve a DNS record of the specified type and class for the specified name.</fsummary> <desc> diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 556f25f96a..e6448e144e 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>logger.xml</file> </header> - <module>logger</module> + <module since="OTP 21.0">logger</module> <modulesummary>API module for Logger, the standard logging facility in Erlang/OTP.</modulesummary> @@ -190,7 +190,7 @@ logger:error("error happened because: ~p", [Reason]). % Without macro <list> <item><c>pid => self()</c></item> <item><c>gl => group_leader()</c></item> - <item><c>time => erlang:system_time(microsecond)</c></item> + <item><c>time => logger:timestamp()</c></item> </list> <p>When a log macro is used, Logger also inserts location information:</p> @@ -245,6 +245,12 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </desc> </datatype> <datatype> + <name name="olp_config"/> + <desc> + <p></p> + </desc> + </datatype> + <datatype> <name name="primary_config"/> <desc> <p>Primary configuration data for Logger. The following @@ -282,8 +288,8 @@ logger:error("error happened because: ~p", [Reason]). % Without macro <name name="timestamp"/> <desc> <p>A timestamp produced - with <seealso marker="erts:erlang#system_time-1"> - <c>erlang:system_time(microsecond)</c></seealso>.</p> + with <seealso marker="#timestamp-0"> + <c>logger:timestamp()</c></seealso>.</p> </desc> </datatype> </datatypes> @@ -334,9 +340,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </section> <funcs> <func> - <name>emergency(StringOrReport[,Metadata])</name> - <name>emergency(Format,Args[,Metadata])</name> - <name>emergency(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">emergency(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">emergency(Format,Args[,Metadata])</name> + <name since="OTP 21.0">emergency(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>emergency</c>.</fsummary> <desc> <p>Equivalent to @@ -345,9 +351,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>alert(StringOrReport[,Metadata])</name> - <name>alert(Format,Args[,Metadata])</name> - <name>alert(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">alert(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">alert(Format,Args[,Metadata])</name> + <name since="OTP 21.0">alert(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>alert</c>.</fsummary> <desc> <p>Equivalent to @@ -356,9 +362,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>critical(StringOrReport[,Metadata])</name> - <name>critical(Format,Args[,Metadata])</name> - <name>critical(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">critical(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">critical(Format,Args[,Metadata])</name> + <name since="OTP 21.0">critical(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>critical</c>.</fsummary> <desc> <p>Equivalent to @@ -367,9 +373,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>error(StringOrReport[,Metadata])</name> - <name>error(Format,Args[,Metadata])</name> - <name>error(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">error(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">error(Format,Args[,Metadata])</name> + <name since="OTP 21.0">error(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>error</c>.</fsummary> <desc> <p>Equivalent to @@ -378,9 +384,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>warning(StringOrReport[,Metadata])</name> - <name>warning(Format,Args[,Metadata])</name> - <name>warning(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">warning(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">warning(Format,Args[,Metadata])</name> + <name since="OTP 21.0">warning(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>warning</c>.</fsummary> <desc> <p>Equivalent to @@ -389,9 +395,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>notice(StringOrReport[,Metadata])</name> - <name>notice(Format,Args[,Metadata])</name> - <name>notice(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">notice(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">notice(Format,Args[,Metadata])</name> + <name since="OTP 21.0">notice(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>notice</c>.</fsummary> <desc> <p>Equivalent to @@ -400,9 +406,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>info(StringOrReport[,Metadata])</name> - <name>info(Format,Args[,Metadata])</name> - <name>info(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">info(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">info(Format,Args[,Metadata])</name> + <name since="OTP 21.0">info(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>info</c>.</fsummary> <desc> <p>Equivalent to @@ -411,9 +417,9 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name>debug(StringOrReport[,Metadata])</name> - <name>debug(Format,Args[,Metadata])</name> - <name>debug(Fun,FunArgs[,Metadata])</name> + <name since="OTP 21.0">debug(StringOrReport[,Metadata])</name> + <name since="OTP 21.0">debug(Format,Args[,Metadata])</name> + <name since="OTP 21.0">debug(Fun,FunArgs[,Metadata])</name> <fsummary>Logs the given message as level <c>debug</c>.</fsummary> <desc> <p>Equivalent to @@ -422,12 +428,12 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name name="log" arity="2"/> - <name name="log" arity="3" clause_i="1"/> - <name name="log" arity="3" clause_i="2"/> - <name name="log" arity="3" clause_i="3"/> - <name name="log" arity="4" clause_i="1"/> - <name name="log" arity="4" clause_i="2"/> + <name name="log" arity="2" since="OTP 21.0"/> + <name name="log" arity="3" clause_i="1" since="OTP 21.0"/> + <name name="log" arity="3" clause_i="2" since="OTP 21.0"/> + <name name="log" arity="3" clause_i="3" since="OTP 21.0"/> + <name name="log" arity="4" clause_i="1" since="OTP 21.0"/> + <name name="log" arity="4" clause_i="2" since="OTP 21.0"/> <fsummary>Logs the given message.</fsummary> <type variable="Level"/> <type variable="StringOrReport" name_i="1"/> @@ -448,7 +454,7 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </section> <funcs> <func> - <name name="add_handler" arity="3"/> + <name name="add_handler" arity="3" since="OTP 21.0"/> <fsummary>Add a handler with the given configuration.</fsummary> <desc> <p>Add a handler with the given configuration.</p> @@ -459,7 +465,7 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name name="add_handler_filter" arity="3"/> + <name name="add_handler_filter" arity="3" since="OTP 21.0"/> <fsummary>Add a filter to the specified handler.</fsummary> <desc> <p>Add a filter to the specified handler.</p> @@ -500,7 +506,7 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name name="add_handlers" arity="1" clause_i="1"/> + <name name="add_handlers" arity="1" clause_i="1" since="OTP 21.0"/> <fsummary>Set up log handlers from the application's configuration parameters.</fsummary> <desc> @@ -510,7 +516,7 @@ logger:error("error happened because: ~p", [Reason]). % Without macro </func> <func> - <name name="add_handlers" arity="1" clause_i="2"/> + <name name="add_handlers" arity="1" clause_i="2" since="OTP 21.0"/> <fsummary>Setup logger handlers.</fsummary> <type name="config_handler"/> <desc> @@ -553,7 +559,7 @@ start(_, []) -> </func> <func> - <name name="add_primary_filter" arity="2"/> + <name name="add_primary_filter" arity="2" since="OTP 21.0"/> <fsummary>Add a primary filter to Logger.</fsummary> <desc> <p>Add a primary filter to Logger.</p> @@ -594,16 +600,16 @@ start(_, []) -> </func> <func> - <name name="get_config" arity="0"/> + <name name="get_config" arity="0" since="OTP 21.0"/> <fsummary>Look up the current Logger configuration</fsummary> <desc> - <p>Look up all current Logger configuration, including primary - and handler configuration, and module level settings.</p> + <p>Look up all current Logger configuration, including primary, + handler, and proxy configuration, and module level settings.</p> </desc> </func> <func> - <name name="get_handler_config" arity="0"/> + <name name="get_handler_config" arity="0" since="OTP 21.0"/> <fsummary>Look up the current configuration for all handlers.</fsummary> <desc> <p>Look up the current configuration for all handlers.</p> @@ -611,7 +617,7 @@ start(_, []) -> </func> <func> - <name name="get_handler_config" arity="1"/> + <name name="get_handler_config" arity="1" since="OTP 21.0"/> <fsummary>Look up the current configuration for the given handler.</fsummary> <desc> @@ -620,7 +626,7 @@ start(_, []) -> </func> <func> - <name name="get_handler_ids" arity="0"/> + <name name="get_handler_ids" arity="0" since="OTP 21.0"/> <fsummary>Look up the identities for all installed handlers.</fsummary> <desc> <p>Look up the identities for all installed handlers.</p> @@ -628,7 +634,7 @@ start(_, []) -> </func> <func> - <name name="get_primary_config" arity="0"/> + <name name="get_primary_config" arity="0" since="OTP 21.0"/> <fsummary>Look up the current primary configuration for Logger.</fsummary> <desc> <p>Look up the current primary configuration for Logger.</p> @@ -636,7 +642,18 @@ start(_, []) -> </func> <func> - <name name="get_module_level" arity="0"/> + <name name="get_proxy_config" arity="0" since="OTP 21.3"/> + <fsummary>Look up the current configuration for the Logger proxy.</fsummary> + <desc> + <p>Look up the current configuration for the Logger proxy.</p> + <p>For more information about the proxy, see + section <seealso marker="logger_chapter#proxy">Logger + Proxy</seealso> in the Kernel User's Guide.</p> + </desc> + </func> + + <func> + <name name="get_module_level" arity="0" since="OTP 21.0"/> <fsummary>Look up all current module levels.</fsummary> <desc> <p>Look up all current module levels. Returns a list @@ -648,7 +665,7 @@ start(_, []) -> </func> <func> - <name name="get_module_level" arity="1"/> + <name name="get_module_level" arity="1" since="OTP 21.0"/> <fsummary>Look up the current level for the given modules.</fsummary> <desc> <p>Look up the current level for the given modules. Returns a @@ -660,7 +677,7 @@ start(_, []) -> </func> <func> - <name name="get_process_metadata" arity="0"/> + <name name="get_process_metadata" arity="0" since="OTP 21.0"/> <fsummary>Retrieve data set with set_process_metadata/1.</fsummary> <desc> <p>Retrieve data set @@ -672,7 +689,16 @@ start(_, []) -> </func> <func> - <name name="remove_handler" arity="1"/> + <name name="i" arity="0" since="OTP 21.3"/> + <name name="i" arity="1" since="OTP 21.3"/> + <fsummary>Pretty print the Logger configuration.</fsummary> + <desc> + <p>Pretty print the Logger configuration.</p> + </desc> + </func> + + <func> + <name name="remove_handler" arity="1" since="OTP 21.0"/> <fsummary>Remove the handler with the specified identity.</fsummary> <desc> <p>Remove the handler identified by <c><anno>HandlerId</anno></c>.</p> @@ -680,7 +706,7 @@ start(_, []) -> </func> <func> - <name name="remove_handler_filter" arity="2"/> + <name name="remove_handler_filter" arity="2" since="OTP 21.0"/> <fsummary>Remove a filter from the specified handler.</fsummary> <desc> <p>Remove the filter identified @@ -690,7 +716,7 @@ start(_, []) -> </func> <func> - <name name="remove_primary_filter" arity="1"/> + <name name="remove_primary_filter" arity="1" since="OTP 21.0"/> <fsummary>Remove a primary filter from Logger.</fsummary> <desc> <p>Remove the primary filter identified @@ -699,7 +725,7 @@ start(_, []) -> </func> <func> - <name name="set_application_level" arity="2"/> + <name name="set_application_level" arity="2" since="OTP 21.1"/> <fsummary>Set the log level for all modules in the specified application.</fsummary> <desc> <p>Set the log level for all the modules of the specified application.</p> @@ -710,7 +736,7 @@ start(_, []) -> </func> <func> - <name name="set_handler_config" arity="2"/> + <name name="set_handler_config" arity="2" since="OTP 21.0"/> <fsummary>Set configuration data for the specified handler.</fsummary> <desc> <p>Set configuration data for the specified handler. This @@ -731,11 +757,11 @@ start(_, []) -> </func> <func> - <name name="set_handler_config" arity="3" clause_i="1"/> - <name name="set_handler_config" arity="3" clause_i="2"/> - <name name="set_handler_config" arity="3" clause_i="3"/> - <name name="set_handler_config" arity="3" clause_i="4"/> - <name name="set_handler_config" arity="3" clause_i="5"/> + <name name="set_handler_config" arity="3" clause_i="1" since="OTP 21.0"/> + <name name="set_handler_config" arity="3" clause_i="2" since="OTP 21.0"/> + <name name="set_handler_config" arity="3" clause_i="3" since="OTP 21.0"/> + <name name="set_handler_config" arity="3" clause_i="4" since="OTP 21.0"/> + <name name="set_handler_config" arity="3" clause_i="5" since="OTP 21.0"/> <fsummary>Add or update configuration data for the specified handler.</fsummary> <type variable="HandlerId"/> @@ -767,7 +793,7 @@ start(_, []) -> </func> <func> - <name name="set_primary_config" arity="1"/> + <name name="set_primary_config" arity="1" since="OTP 21.0"/> <fsummary>Set primary configuration data for Logger.</fsummary> <desc> <p>Set primary configuration data for Logger. This @@ -785,9 +811,9 @@ start(_, []) -> </func> <func> - <name name="set_primary_config" arity="2" clause_i="1"/> - <name name="set_primary_config" arity="2" clause_i="2"/> - <name name="set_primary_config" arity="2" clause_i="3"/> + <name name="set_primary_config" arity="2" clause_i="1" since="OTP 21.0"/> + <name name="set_primary_config" arity="2" clause_i="2" since="OTP 21.0"/> + <name name="set_primary_config" arity="2" clause_i="3" since="OTP 21.0"/> <fsummary>Add or update primary configuration data for Logger.</fsummary> <type variable="Level" name_i="1"/> <type variable="FilterDefault" name_i="2"/> @@ -801,7 +827,28 @@ start(_, []) -> </func> <func> - <name name="set_module_level" arity="2"/> + <name name="set_proxy_config" arity="1" since="OTP 21.3"/> + <fsummary>Set configuration data for the Logger proxy.</fsummary> + <desc> + <p>Set configuration data for the Logger proxy. This + overwrites the current proxy configuration. Keys that are not + specified in the <c><anno>Config</anno></c> map gets default + values.</p> + <p>To modify the existing configuration, + use <seealso marker="#update_proxy_config-1"> + <c>update_proxy_config/1</c></seealso>, or, if a more + complex merge is needed, read the current configuration + with <seealso marker="#get_proxy_config-0"><c>get_proxy_config/0</c> + </seealso>, then do the merge before writing the new + configuration back with this function.</p> + <p>For more information about the proxy, see + section <seealso marker="logger_chapter#proxy">Logger + Proxy</seealso> in the Kernel User's Guide.</p> + </desc> + </func> + + <func> + <name name="set_module_level" arity="2" since="OTP 21.0"/> <fsummary>Set the log level for the specified modules.</fsummary> <desc> <p>Set the log level for the specified modules.</p> @@ -841,7 +888,7 @@ start(_, []) -> </func> <func> - <name name="set_process_metadata" arity="1"/> + <name name="set_process_metadata" arity="1" since="OTP 21.0"/> <fsummary>Set metadata to use when logging from current process.</fsummary> <desc> <p>Set metadata which Logger shall automatically insert in @@ -860,7 +907,7 @@ start(_, []) -> </func> <func> - <name name="unset_application_level" arity="1"/> + <name name="unset_application_level" arity="1" since="OTP 21.1"/> <fsummary>Unset the log level for all modules in the specified application.</fsummary> <desc> <p>Unset the log level for all the modules of the specified application.</p> @@ -871,7 +918,7 @@ start(_, []) -> </func> <func> - <name name="unset_module_level" arity="0"/> + <name name="unset_module_level" arity="0" since="OTP 21.0"/> <fsummary>Remove module specific log settings for all modules.</fsummary> <desc> <p>Remove module specific log settings. After this, the @@ -880,7 +927,7 @@ start(_, []) -> </func> <func> - <name name="unset_module_level" arity="1"/> + <name name="unset_module_level" arity="1" since="OTP 21.0"/> <fsummary>Remove module specific log settings for the given modules.</fsummary> <desc> @@ -890,7 +937,7 @@ start(_, []) -> </func> <func> - <name name="unset_process_metadata" arity="0"/> + <name name="unset_process_metadata" arity="0" since="OTP 21.0"/> <fsummary>Delete data set with set_process_metadata/1.</fsummary> <desc> <p>Delete data set @@ -902,7 +949,7 @@ start(_, []) -> </func> <func> - <name name="update_formatter_config" arity="2"/> + <name name="update_formatter_config" arity="2" since="OTP 21.0"/> <fsummary>Update the formatter configuration for the specified handler.</fsummary> <desc> <p>Update the formatter configuration for the specified handler.</p> @@ -917,7 +964,7 @@ start(_, []) -> </func> <func> - <name name="update_formatter_config" arity="3"/> + <name name="update_formatter_config" arity="3" since="OTP 21.0"/> <fsummary>Update the formatter configuration for the specified handler.</fsummary> <desc> <p>Update the formatter configuration for the specified handler.</p> @@ -928,7 +975,7 @@ start(_, []) -> </func> <func> - <name name="update_handler_config" arity="2"/> + <name name="update_handler_config" arity="2" since="OTP 21.0"/> <fsummary>Update configuration data for the specified handler.</fsummary> <desc> <p>Update configuration data for the specified handler. This function @@ -944,11 +991,11 @@ logger:set_handler_config(HandlerId, maps:merge(Old, Config)). </func> <func> - <name name="update_handler_config" arity="3" clause_i="1"/> - <name name="update_handler_config" arity="3" clause_i="2"/> - <name name="update_handler_config" arity="3" clause_i="3"/> - <name name="update_handler_config" arity="3" clause_i="4"/> - <name name="update_handler_config" arity="3" clause_i="5"/> + <name name="update_handler_config" arity="3" clause_i="1" since="OTP 21.2"/> + <name name="update_handler_config" arity="3" clause_i="2" since="OTP 21.2"/> + <name name="update_handler_config" arity="3" clause_i="3" since="OTP 21.2"/> + <name name="update_handler_config" arity="3" clause_i="4" since="OTP 21.2"/> + <name name="update_handler_config" arity="3" clause_i="5" since="OTP 21.2"/> <fsummary>Add or update configuration data for the specified handler.</fsummary> <type variable="HandlerId"/> @@ -980,7 +1027,7 @@ logger:set_handler_config(HandlerId, maps:merge(Old, Config)). </func> <func> - <name name="update_primary_config" arity="1"/> + <name name="update_primary_config" arity="1" since="OTP 21.0"/> <fsummary>Update primary configuration data for Logger.</fsummary> <desc> <p>Update primary configuration data for Logger. This function @@ -996,7 +1043,7 @@ logger:set_primary_config(maps:merge(Old, Config)). </func> <func> - <name name="update_process_metadata" arity="1"/> + <name name="update_process_metadata" arity="1" since="OTP 21.0"/> <fsummary>Set or update metadata to use when logging from current process.</fsummary> <desc> @@ -1013,6 +1060,25 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </seealso>.</p> </desc> </func> + + <func> + <name name="update_proxy_config" arity="1" since="OTP 21.3"/> + <fsummary>Update configuration data for the Logger proxy.</fsummary> + <desc> + <p>Update configuration data for the Logger proxy. This function + behaves as if it was implemented as follows:</p> + <code type="erl"> +Old = logger:get_proxy_config(), +logger:set_proxy_config(maps:merge(Old, Config)). + </code> + <p>To overwrite the existing configuration without any merge, + use <seealso marker="#set_proxy_config-1"><c>set_proxy_config/1</c> + </seealso>.</p> + <p>For more information about the proxy, see + section <seealso marker="logger_chapter#proxy">Logger + Proxy</seealso> in the Kernel User's Guide.</p> + </desc> + </func> </funcs> <section> @@ -1021,7 +1087,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </section> <funcs> <func> - <name name="compare_levels" arity="2"/> + <name name="compare_levels" arity="2" since="OTP 21.0"/> <fsummary>Compare the severity of two log levels.</fsummary> <desc> <p>Compare the severity of two log levels. Returns <c>gt</c> @@ -1032,7 +1098,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </func> <func> - <name name="format_report" arity="1"/> + <name name="format_report" arity="1" since="OTP 21.0"/> <fsummary>Convert a log message on report form to {Format, Args}.</fsummary> <desc> <p>Convert a log message on report form to <c>{Format, @@ -1051,6 +1117,24 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). a key-value list before formatting as such.</p> </desc> </func> + + <func> + <name name="timestamp" arity="0" since="OTP @OTP-15625@"/> + <fsummary>Return a timestamp to insert in meta data for a log + event.</fsummary> + <desc> + <p>Return a timestamp that can be inserted as the <c>time</c> + field in the meta data for a log event. It is produced with + <seealso marker="kernel:os#system_time-1"> + <c>os:system_time(microsecond)</c></seealso>.</p> + <p>Notice that Logger automatically inserts a timestamp in the + meta data unless it already exists. This function is + exported for the rare case when the timestamp must be taken + at a different point in time than when the log event is + issued.</p> + </desc> + </func> + </funcs> <section> @@ -1062,7 +1146,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). <funcs> <func> - <name>HModule:adding_handler(Config1) -> {ok, Config2} | {error, + <name since="OTP 21.0">HModule:adding_handler(Config1) -> {ok, Config2} | {error, Reason}</name> <fsummary>An instance of this handler is about to be added.</fsummary> <type> @@ -1088,7 +1172,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </func> <func> - <name>HModule:changing_config(SetOrUpdate, OldConfig, NewConfig) -> {ok, Config} | {error, Reason}</name> + <name since="OTP 21.2">HModule:changing_config(SetOrUpdate, OldConfig, NewConfig) -> {ok, Config} | {error, Reason}</name> <fsummary>The configuration for this handler is about to change.</fsummary> <type> <v>SetOrUpdate = set | update</v> @@ -1126,7 +1210,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </func> <func> - <name>HModule:filter_config(Config) -> FilteredConfig</name> + <name since="OTP 21.2">HModule:filter_config(Config) -> FilteredConfig</name> <fsummary>Remove internal data from configuration.</fsummary> <type> <v>Config = FilteredConfig = @@ -1146,7 +1230,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </func> <func> - <name>HModule:log(LogEvent, Config) -> void()</name> + <name since="OTP 21.0">HModule:log(LogEvent, Config) -> void()</name> <fsummary>Log the given log event.</fsummary> <type> <v>LogEvent = @@ -1169,7 +1253,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </func> <func> - <name>HModule:removing_handler(Config) -> ok</name> + <name since="OTP 21.0">HModule:removing_handler(Config) -> ok</name> <fsummary>The given handler is about to be removed.</fsummary> <type> <v>Config = @@ -1197,7 +1281,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). <funcs> <func> - <name>FModule:check_config(FConfig) -> ok | {error, Reason}</name> + <name since="OTP 21.0">FModule:check_config(FConfig) -> ok | {error, Reason}</name> <fsummary>Validate the given formatter configuration.</fsummary> <type> <v>FConfig = @@ -1228,7 +1312,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). </desc> </func> <func> - <name>FModule:format(LogEvent, FConfig) -> FormattedLogEntry</name> + <name since="OTP 21.0">FModule:format(LogEvent, FConfig) -> FormattedLogEntry</name> <fsummary>Format the given log event.</fsummary> <type> <v>LogEvent = diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 458e61cef5..5ed7397135 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -693,8 +693,10 @@ logger:debug(#{got => connection_request, id => Id, state => State}, with <seealso marker="#logger_sasl_compatible"> <c>logger_sasl_compatible</c></seealso>.</p> <p>With this parameter, you can modify or disable the default - handler, add custom handlers and primary logger filters, and - set log levels per module.</p> + handler, add custom handlers and primary logger filters, set + log levels per module, and modify + the <seealso marker="#proxy">proxy</seealso> + configuration.</p> <p><c>Config</c> is any (zero or more) of the following:</p> <taglist> <tag><c>{handler, default, undefined}</c></tag> @@ -746,6 +748,14 @@ logger:debug(#{got => connection_request, id => Id, state => State}, <p>for each <c>Module</c>.</p> <p>Multiple entries of this type are allowed.</p> </item> + <tag><c>{proxy, ProxyConfig}</c></tag> + <item> + <p>Sets the proxy configuration, equivalent to calling</p> + <pre><seealso marker="logger#set_proxy_config/1"> + logger:set_proxy_config(ProxyConfig) + </seealso></pre> + <p>Only one entry of this type is allowed.</p> + </item> </taglist> <p>See section <seealso marker="#config_examples">Configuration @@ -1334,9 +1344,50 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, </section> <section> + <marker id="proxy"/> + <title>Logger Proxy</title> + <p>The Logger proxy is an Erlang process which is part of the + Kernel application's supervision tree. During startup, the proxy + process registers itself as the <c>system_logger</c>, meaning + that log events produced by the emulator are sent to this + process.</p> + <p>When a log event is issued on a process which has its group + leader on a remote node, Logger automatically forwards the log + event to the group leader's node. To achieve this, it first + sends the log event as an Erlang message from the original + client process to the proxy on the local node, and the proxy in + turn forwards the event to the proxy on the remote node.</p> + <p>When receiving a log event, either from the emulator or from a + remote node, the proxy calls the Logger API to log the event.</p> + <p>The proxy process is overload protected in the same way as + described in + section <seealso marker="#overload_protection">Protecting the + Handler from Overload</seealso>, but with the following default + values:</p> + <code> + #{sync_mode_qlen => 500, + drop_mode_qlen => 1000, + flush_qlen => 5000, + burst_limit_enable => false, + overload_kill_enable => false}</code> + <p>For log events from the emulator, synchronous message passing + mode is not applicable, since all messages are passed + asynchronously by the emulator. Drop mode is achieved by setting + the <c>system_logger</c> to <c>undefined</c>, forcing the + emulator to drop events until it is set back to the proxy pid + again.</p> + <p>The proxy uses <seealso marker="erts:erlang#send_nosuspend/2"> + <c>erlang:send_nosuspend/2</c></seealso> when sending log + events to a remote node. If the message could not be sent + without suspending the sender, it is dropped. This is to avoid + blocking the proxy process.</p> + </section> + + <section> <title>See Also</title> <p> <seealso marker="disk_log"><c>disk_log(3)</c></seealso>, + <seealso marker="erts:erlang"><c>erlang(3)</c></seealso>, <seealso marker="error_logger"><c>error_logger(3)</c></seealso>, <seealso marker="logger"><c>logger(3)</c></seealso>, <seealso marker="logger_disk_log_h"><c>logger_disk_log_h(3)</c></seealso>, diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index d9b941a0a9..5b2374690e 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>logger_disk_log_h.xml</file> </header> - <module>logger_disk_log_h</module> + <module since="OTP 21.0">logger_disk_log_h</module> <modulesummary>A disk_log based handler for Logger</modulesummary> <description> @@ -148,7 +148,7 @@ erl -kernel logger '[{handler,default,logger_disk_log_h, <funcs> <func> - <name name="filesync" arity="1" clause_i="1"/> + <name name="filesync" arity="1" clause_i="1" since="OTP 21.0"/> <fsummary>Writes buffered data to disk.</fsummary> <desc> <p>Write buffered data to disk.</p> diff --git a/lib/kernel/doc/src/logger_filters.xml b/lib/kernel/doc/src/logger_filters.xml index 90f1fcc270..0a02342864 100644 --- a/lib/kernel/doc/src/logger_filters.xml +++ b/lib/kernel/doc/src/logger_filters.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>logger_filters.xml</file> </header> - <module>logger_filters</module> + <module since="OTP 21.0">logger_filters</module> <modulesummary>Filters to use with Logger.</modulesummary> <description> @@ -51,7 +51,7 @@ <funcs> <func> - <name name="domain" arity="2"/> + <name name="domain" arity="2" since="OTP 21.0"/> <fsummary>Filter log events based on the domain field in metadata.</fsummary> <desc> @@ -152,7 +152,7 @@ ok</code> </func> <func> - <name name="level" arity="2"/> + <name name="level" arity="2" since="OTP 21.0"/> <fsummary>Filter log events based on the log level.</fsummary> <desc> <p>This filter provides a way of filtering log events based @@ -212,7 +212,7 @@ ok</code> </func> <func> - <name name="progress" arity="2"/> + <name name="progress" arity="2" since="OTP 21.0"/> <fsummary>Filter progress reports from supervisor and application_controller.</fsummary> <desc> <p>This filter matches all progress reports @@ -227,7 +227,7 @@ ok</code> </func> <func> - <name name="remote_gl" arity="2"/> + <name name="remote_gl" arity="2" since="OTP 21.0"/> <fsummary>Filter events with group leader on remote node.</fsummary> <desc> <p>This filter matches all events originating from a process diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index d066e263df..6dc83d24e1 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>logger_formatter.xml</file> </header> - <module>logger_formatter</module> + <module since="OTP 21.0">logger_formatter</module> <modulesummary>Default formatter for Logger.</modulesummary> <description> @@ -289,7 +289,7 @@ exit_reason: "It crashed"</code> <funcs> <func> - <name name="check_config" arity="1"/> + <name name="check_config" arity="1" since="OTP 21.0"/> <fsummary>Validates the given formatter configuration.</fsummary> <desc> <p>The function is called by Logger when the formatter @@ -310,7 +310,7 @@ exit_reason: "It crashed"</code> </desc> </func> <func> - <name name="format" arity="2"/> + <name name="format" arity="2" since="OTP 21.0"/> <fsummary>Formats the given message.</fsummary> <desc> <p>This the formatter callback function to be called from diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index e156f5719b..fcd180abd6 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>logger_std_h.xml</file> </header> - <module>logger_std_h</module> + <module since="OTP 21.0">logger_std_h</module> <modulesummary>Standard handler for Logger.</modulesummary> <description> @@ -121,7 +121,7 @@ erl -kernel logger '[{handler,default,logger_std_h, <funcs> <func> - <name name="filesync" arity="1" clause_i="1"/> + <name name="filesync" arity="1" clause_i="1" since="OTP 21.0"/> <fsummary>Writes buffered data to disk.</fsummary> <desc> <p>Write buffered data to disk.</p> diff --git a/lib/kernel/doc/src/net_adm.xml b/lib/kernel/doc/src/net_adm.xml index 6957a3b5e4..c3e1619f1b 100644 --- a/lib/kernel/doc/src/net_adm.xml +++ b/lib/kernel/doc/src/net_adm.xml @@ -28,7 +28,7 @@ <date>1996-09-10</date> <rev>A</rev> </header> - <module>net_adm</module> + <module since="">net_adm</module> <modulesummary>Various Erlang net administration routines.</modulesummary> <description> <p>This module contains various network utility functions.</p> @@ -36,7 +36,7 @@ <funcs> <func> - <name name="dns_hostname" arity="1"/> + <name name="dns_hostname" arity="1" since=""/> <fsummary>Official name of a host.</fsummary> <desc> <p>Returns the official name of <c><anno>Host</anno></c>, or @@ -46,7 +46,7 @@ </func> <func> - <name name="host_file" arity="0"/> + <name name="host_file" arity="0" since=""/> <fsummary>Read file <c>.hosts.erlang</c>.</fsummary> <desc> <p>Reads file <c>.hosts.erlang</c>, see section @@ -58,7 +58,7 @@ </func> <func> - <name name="localhost" arity="0"/> + <name name="localhost" arity="0" since=""/> <fsummary>Name of the local host.</fsummary> <desc> <p>Returns the name of the local host. If Erlang was started @@ -68,8 +68,8 @@ </func> <func> - <name name="names" arity="0"/> - <name name="names" arity="1"/> + <name name="names" arity="0" since=""/> + <name name="names" arity="1" since=""/> <fsummary>Names of Erlang nodes at a host.</fsummary> <desc> <p>Similar to <c>epmd -names</c>, see @@ -86,7 +86,7 @@ </func> <func> - <name name="ping" arity="1"/> + <name name="ping" arity="1" since=""/> <fsummary>Set up a connection to a node.</fsummary> <desc> <p>Sets up a connection to <c><anno>Node</anno></c>. Returns @@ -95,8 +95,8 @@ </func> <func> - <name name="world" arity="0"/> - <name name="world" arity="1"/> + <name name="world" arity="0" since=""/> + <name name="world" arity="1" since=""/> <fsummary>Lookup and connect to all nodes at all hosts in <c>.hosts.erlang</c>.</fsummary> <type name="verbosity"/> @@ -117,8 +117,8 @@ </func> <func> - <name name="world_list" arity="1"/> - <name name="world_list" arity="2"/> + <name name="world_list" arity="1" since=""/> + <name name="world_list" arity="2" since=""/> <fsummary>Lookup and connect to all nodes at specified hosts.</fsummary> <type name="verbosity"/> <desc> diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index bfbe7a6470..419d3cad84 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -28,7 +28,7 @@ <date>1996-09-10</date> <rev>A</rev> </header> - <module>net_kernel</module> + <module since="">net_kernel</module> <modulesummary>Erlang networking kernel.</modulesummary> <description> <p>The net kernel is a system process, registered as @@ -81,7 +81,7 @@ $ <input>erl -sname foobar</input></pre> <funcs> <func> - <name name="allow" arity="1"/> + <name name="allow" arity="1" since=""/> <fsummary>Permit access to a specified set of nodes</fsummary> <desc> <p>Permits access to the specified set of nodes.</p> @@ -98,7 +98,7 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="connect_node" arity="1"/> + <name name="connect_node" arity="1" since=""/> <fsummary>Establish a connection to a node.</fsummary> <desc> <p>Establishes a connection to <c><anno>Node</anno></c>. Returns @@ -110,7 +110,7 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="get_net_ticktime" arity="0"/> + <name name="get_net_ticktime" arity="0" since=""/> <fsummary>Get <c>net_ticktime</c>.</fsummary> <desc> <p>Gets <c>net_ticktime</c> (see @@ -131,7 +131,7 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="getopts" arity="2"/> + <name name="getopts" arity="2" since="OTP 19.1"/> <fsummary>Get distribution socket options.</fsummary> <desc> <p>Get one or more options for the distribution socket @@ -146,8 +146,8 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="monitor_nodes" arity="1"/> - <name name="monitor_nodes" arity="2"/> + <name name="monitor_nodes" arity="1" since=""/> + <name name="monitor_nodes" arity="2" since=""/> <fsummary>Subscribe to node status change messages.</fsummary> <desc> <p>The calling process subscribes or unsubscribes to node @@ -267,8 +267,8 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="set_net_ticktime" arity="1"/> - <name name="set_net_ticktime" arity="2"/> + <name name="set_net_ticktime" arity="1" since=""/> + <name name="set_net_ticktime" arity="2" since=""/> <fsummary>Set <c>net_ticktime</c>.</fsummary> <desc> <p>Sets <c>net_ticktime</c> (see @@ -324,7 +324,7 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="setopts" arity="2"/> + <name name="setopts" arity="2" since="OTP 19.1"/> <fsummary>Set distribution socket options.</fsummary> <desc> <p>Set one or more options for distribution sockets. @@ -345,9 +345,9 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name>start([Name]) -> {ok, pid()} | {error, Reason}</name> - <name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name> - <name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name> + <name since="">start([Name]) -> {ok, pid()} | {error, Reason}</name> + <name since="">start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name> + <name since="">start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name> <fsummary>Turn an Erlang runtime system into a distributed node.</fsummary> <type> <v>Name = atom()</v> @@ -364,7 +364,7 @@ $ <input>erl -sname foobar</input></pre> </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary>Turn a node into a non-distributed Erlang runtime system.</fsummary> <desc> <p>Turns a distributed node into a non-distributed node. For diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index c95e615c6b..0500e4cfb3 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>os</module> + <module since="">os</module> <modulesummary>Operating system-specific functions.</modulesummary> <description> <p>The functions in this module are operating system-specific. @@ -134,8 +134,8 @@ <funcs> <func> - <name name="cmd" arity="1"/> - <name name="cmd" arity="2"/> + <name name="cmd" arity="1" since=""/> + <name name="cmd" arity="2" since="OTP 20.2.3"/> <fsummary>Execute a command in a shell of the target OS.</fsummary> <desc> <p>Executes <c><anno>Command</anno></c> in a command shell of the @@ -173,8 +173,8 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="find_executable" arity="1"/> - <name name="find_executable" arity="2"/> + <name name="find_executable" arity="1" since=""/> + <name name="find_executable" arity="2" since=""/> <fsummary>Absolute filename of a program.</fsummary> <desc> <p>These two functions look up an executable program, with the @@ -190,7 +190,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="getenv" arity="0"/> + <name name="getenv" arity="0" since=""/> <fsummary>List all environment variables.</fsummary> <desc> <p>Returns a list of all environment variables. @@ -205,7 +205,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="getenv" arity="1"/> + <name name="getenv" arity="1" since=""/> <fsummary>Get the value of an environment variable.</fsummary> <desc> <p>Returns the <c><anno>Value</anno></c> of the environment variable @@ -220,7 +220,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="getenv" arity="2"/> + <name name="getenv" arity="2" since="OTP 18.0"/> <fsummary>Get the value of an environment variable.</fsummary> <desc> <p>Returns the <c><anno>Value</anno></c> of the environment variable @@ -235,7 +235,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="getpid" arity="0"/> + <name name="getpid" arity="0" since=""/> <fsummary>Return the process identifier of the emulator process.</fsummary> <desc> @@ -251,7 +251,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="putenv" arity="2"/> + <name name="putenv" arity="2" since=""/> <fsummary>Set a new value for an environment variable.</fsummary> <desc> <p>Sets a new <c><anno>Value</anno></c> for environment variable @@ -277,7 +277,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="set_signal" arity="2"/> + <name name="set_signal" arity="2" since="OTP 20.0"/> <fsummary>Enables or disables handling of OS signals.</fsummary> <desc> <p>Enables or disables OS signals.</p> @@ -304,7 +304,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="system_time" arity="0"/> + <name name="system_time" arity="0" since="OTP 18.0"/> <fsummary>Current OS system time.</fsummary> <desc> <p>Returns the current @@ -317,7 +317,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="system_time" arity="1"/> + <name name="system_time" arity="1" since="OTP 18.0"/> <fsummary>Current OS system time.</fsummary> <desc> <p>Returns the current @@ -332,7 +332,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </func> <func> - <name name="timestamp" arity="0"/> + <name name="timestamp" arity="0" since=""/> <fsummary>Current OS system time on the <c>erlang:timestamp/0</c> format.</fsummary> <type_desc variable="Timestamp">Timestamp = {MegaSecs, Secs, MicroSecs}</type_desc> <desc> @@ -373,7 +373,7 @@ calendar:now_to_universal_time(TS), </func> <func> - <name name="perf_counter" arity="0"/> + <name name="perf_counter" arity="0" since="OTP 19.0"/> <fsummary>Returns a performance counter</fsummary> <desc> <p>Returns the current performance counter value in <c>perf_counter</c> @@ -383,7 +383,7 @@ calendar:now_to_universal_time(TS), </desc> </func> <func> - <name name="perf_counter" arity="1"/> + <name name="perf_counter" arity="1" since="OTP 19.0"/> <fsummary>Returns a performance counter</fsummary> <desc><p>Returns a performance counter that can be used as a very fast and high resolution timestamp. This counter is read directly from the hardware or operating @@ -397,7 +397,7 @@ calendar:now_to_universal_time(TS), </desc> </func> <func> - <name name="type" arity="0"/> + <name name="type" arity="0" since=""/> <fsummary>Return the OS family and, in some cases, the OS name of the current OS.</fsummary> <desc> @@ -417,7 +417,7 @@ calendar:now_to_universal_time(TS), </func> <func> - <name name="unsetenv" arity="1"/> + <name name="unsetenv" arity="1" since="OTP R16B03"/> <fsummary>Delete an environment variable.</fsummary> <desc> <p>Deletes the environment variable <c><anno>VarName</anno></c>.</p> @@ -429,7 +429,7 @@ calendar:now_to_universal_time(TS), </func> <func> - <name name="version" arity="0"/> + <name name="version" arity="0" since=""/> <fsummary>Return the OS versions.</fsummary> <desc> <p>Returns the OS version. diff --git a/lib/kernel/doc/src/pg2.xml b/lib/kernel/doc/src/pg2.xml index 0631b317b4..058d711756 100644 --- a/lib/kernel/doc/src/pg2.xml +++ b/lib/kernel/doc/src/pg2.xml @@ -32,7 +32,7 @@ <rev>A2</rev> <file>pg2.xml</file> </header> - <module>pg2</module> + <module since="">pg2</module> <modulesummary>Distributed named process groups.</modulesummary> <description> <p>This module implements process groups. Each message can be sent @@ -66,7 +66,7 @@ <funcs> <func> - <name name="create" arity="1"/> + <name name="create" arity="1" since=""/> <fsummary>Create a new, empty process group.</fsummary> <desc> <p>Creates a new, empty process group. The group is globally @@ -75,7 +75,7 @@ </func> <func> - <name name="delete" arity="1"/> + <name name="delete" arity="1" since=""/> <fsummary>Delete a process group.</fsummary> <desc> <p>Deletes a process group.</p> @@ -83,7 +83,7 @@ </func> <func> - <name name="get_closest_pid" arity="1"/> + <name name="get_closest_pid" arity="1" since=""/> <fsummary>Common dispatch function.</fsummary> <desc> <p>A useful dispatch function that can be used from @@ -93,7 +93,7 @@ </func> <func> - <name name="get_local_members" arity="1"/> + <name name="get_local_members" arity="1" since=""/> <fsummary>Return all local processes in a group.</fsummary> <desc> <p>Returns all processes running on the local node in the @@ -104,7 +104,7 @@ </func> <func> - <name name="get_members" arity="1"/> + <name name="get_members" arity="1" since=""/> <fsummary>Return all processes in a group.</fsummary> <desc> <p>Returns all processes in the group <c>Name</c>. This @@ -114,7 +114,7 @@ </func> <func> - <name name="join" arity="2"/> + <name name="join" arity="2" since=""/> <fsummary>Join a process to a group.</fsummary> <desc> <p>Joins the process <c>Pid</c> to the group <c>Name</c>. @@ -124,7 +124,7 @@ </func> <func> - <name name="leave" arity="2"/> + <name name="leave" arity="2" since=""/> <fsummary>Make a process leave a group.</fsummary> <desc> <p>Makes the process <c>Pid</c> leave the group <c>Name</c>. @@ -134,8 +134,8 @@ </func> <func> - <name name="start" arity="0"/> - <name name="start_link" arity="0"/> + <name name="start" arity="0" since=""/> + <name name="start_link" arity="0" since=""/> <fsummary>Start the <c>pg2</c> server.</fsummary> <desc> <p>Starts the <c>pg2</c> server. Normally, the server does not need @@ -149,7 +149,7 @@ </func> <func> - <name name="which_groups" arity="0"/> + <name name="which_groups" arity="0" since=""/> <fsummary>Return a list of all known groups.</fsummary> <desc> <p>Returns a list of all known groups.</p> diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index fab616e630..c55454506e 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -28,7 +28,7 @@ <date>1996-09-10</date> <rev>A</rev> </header> - <module>rpc</module> + <module since="">rpc</module> <modulesummary>Remote Procedure Call services.</modulesummary> <description> <p>This module contains services similar to Remote @@ -51,7 +51,7 @@ <funcs> <func> - <name name="abcast" arity="2"/> + <name name="abcast" arity="2" since=""/> <fsummary>Broadcast a message asynchronously to a registered process on all nodes.</fsummary> <desc> @@ -61,7 +61,7 @@ </func> <func> - <name name="abcast" arity="3"/> + <name name="abcast" arity="3" since=""/> <fsummary>Broadcast a message asynchronously to a registered process on specific nodes.</fsummary> <desc> @@ -72,7 +72,7 @@ </func> <func> - <name name="async_call" arity="4"/> + <name name="async_call" arity="4" since=""/> <fsummary>Evaluate a function call on a node, asynchronous version.</fsummary> <desc> @@ -98,7 +98,7 @@ </func> <func> - <name name="block_call" arity="4"/> + <name name="block_call" arity="4" since=""/> <fsummary>Evaluate a function call on a node in the RPC server's context.</fsummary> <desc> @@ -115,7 +115,7 @@ </func> <func> - <name name="block_call" arity="5"/> + <name name="block_call" arity="5" since=""/> <fsummary>Evaluate a function call on a node in the RPC server's context.</fsummary> <desc> @@ -127,7 +127,7 @@ </func> <func> - <name name="call" arity="4"/> + <name name="call" arity="4" since=""/> <fsummary>Evaluate a function call on a node.</fsummary> <desc> <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, @@ -138,7 +138,7 @@ </func> <func> - <name name="call" arity="5"/> + <name name="call" arity="5" since=""/> <fsummary>Evaluate a function call on a node.</fsummary> <desc> <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, @@ -158,7 +158,7 @@ </func> <func> - <name name="cast" arity="4"/> + <name name="cast" arity="4" since=""/> <fsummary>Run a function on a node ignoring the result.</fsummary> <desc> <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, @@ -171,7 +171,7 @@ </func> <func> - <name name="eval_everywhere" arity="3"/> + <name name="eval_everywhere" arity="3" since=""/> <fsummary>Run a function on all nodes, ignoring the result.</fsummary> <desc> <p>Equivalent to <c>eval_everywhere([node()|nodes()], @@ -181,7 +181,7 @@ </func> <func> - <name name="eval_everywhere" arity="4"/> + <name name="eval_everywhere" arity="4" since=""/> <fsummary>Run a function on specific nodes, ignoring the result.</fsummary> <desc> @@ -192,7 +192,7 @@ </func> <func> - <name name="multi_server_call" arity="2"/> + <name name="multi_server_call" arity="2" since=""/> <fsummary>Interact with the servers on a number of nodes.</fsummary> <desc> <p>Equivalent to <c>multi_server_call([node()|nodes()], @@ -201,7 +201,7 @@ </func> <func> - <name name="multi_server_call" arity="3"/> + <name name="multi_server_call" arity="3" since=""/> <fsummary>Interact with the servers on a number of nodes.</fsummary> <desc> <p>Can be used when interacting with servers called @@ -224,7 +224,7 @@ </func> <func> - <name name="multicall" arity="3"/> + <name name="multicall" arity="3" since=""/> <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, @@ -233,7 +233,7 @@ </func> <func> - <name name="multicall" arity="4" clause_i="1"/> + <name name="multicall" arity="4" clause_i="1" since=""/> <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> <p>Equivalent to <c>multicall(<anno>Nodes</anno>, <anno>Module</anno>, @@ -242,7 +242,7 @@ </func> <func> - <name name="multicall" arity="4" clause_i="2"/> + <name name="multicall" arity="4" clause_i="2" since=""/> <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, @@ -252,7 +252,7 @@ </func> <func> - <name name="multicall" arity="5"/> + <name name="multicall" arity="5" since=""/> <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> <p>In contrast to an RPC, a multicall is an RPC that is sent @@ -288,7 +288,7 @@ </func> <func> - <name name="nb_yield" arity="1"/> + <name name="nb_yield" arity="1" since=""/> <fsummary>Deliver the result of evaluating a function call on a node (non-blocking).</fsummary> <desc> @@ -297,7 +297,7 @@ </func> <func> - <name name="nb_yield" arity="2"/> + <name name="nb_yield" arity="2" since=""/> <fsummary>Deliver the result of evaluating a function call on a node (non-blocking).</fsummary> <desc> @@ -315,7 +315,7 @@ </func> <func> - <name name="parallel_eval" arity="1"/> + <name name="parallel_eval" arity="1" since=""/> <fsummary>Evaluate many function calls on all nodes in parallel.</fsummary> <desc> @@ -328,7 +328,7 @@ </func> <func> - <name name="pinfo" arity="1"/> + <name name="pinfo" arity="1" since=""/> <fsummary>Information about a process.</fsummary> <desc> <p>Location transparent version of the BIF @@ -337,8 +337,8 @@ </func> <func> - <name name="pinfo" arity="2" clause_i="1"/> - <name name="pinfo" arity="2" clause_i="2"/> + <name name="pinfo" arity="2" clause_i="1" since=""/> + <name name="pinfo" arity="2" clause_i="2" since=""/> <fsummary>Information about a process.</fsummary> <desc> <p>Location transparent version of the BIF @@ -347,7 +347,7 @@ </func> <func> - <name name="pmap" arity="3"/> + <name name="pmap" arity="3" since=""/> <fsummary>Parallel evaluation of mapping a function over a list.</fsummary> <desc> @@ -360,7 +360,7 @@ </func> <func> - <name name="sbcast" arity="2"/> + <name name="sbcast" arity="2" since=""/> <fsummary>Broadcast a message synchronously to a registered process on all nodes.</fsummary> <desc> @@ -370,7 +370,7 @@ </func> <func> - <name name="sbcast" arity="3"/> + <name name="sbcast" arity="3" since=""/> <fsummary>Broadcast a message synchronously to a registered process on specific nodes.</fsummary> <desc> @@ -391,7 +391,7 @@ </func> <func> - <name name="server_call" arity="4"/> + <name name="server_call" arity="4" since=""/> <fsummary>Interact with a server on a node.</fsummary> <desc> <p>Can be used when interacting with a server called @@ -410,7 +410,7 @@ </func> <func> - <name name="yield" arity="1"/> + <name name="yield" arity="1" since=""/> <fsummary>Deliver the result of evaluating a function call on a node (blocking).</fsummary> <desc> diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index 1a4a74419a..aa29223dd0 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -28,7 +28,7 @@ <date>1998-04-16</date> <rev>A</rev> </header> - <module>seq_trace</module> + <module since="">seq_trace</module> <modulesummary>Sequential tracing of messages.</modulesummary> <description> <p>Sequential tracing makes it possible to trace all messages @@ -51,7 +51,7 @@ </datatypes> <funcs> <func> - <name name="set_token" arity="1"/> + <name name="set_token" arity="1" since=""/> <fsummary>Set the trace token</fsummary> <desc> <p>Sets the trace token for the calling process to <c><anno>Token</anno></c>. @@ -71,7 +71,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="set_token" arity="2"/> + <name name="set_token" arity="2" since=""/> <fsummary>Set a component of the trace token</fsummary> <type name="component"/> <type name="flag"/> @@ -158,7 +158,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="get_token" arity="0"/> + <name name="get_token" arity="0" since=""/> <fsummary>Return the value of the trace token</fsummary> <desc> <p>Returns the value of the trace token for the calling process. @@ -169,7 +169,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="get_token" arity="1"/> + <name name="get_token" arity="1" since=""/> <fsummary>Return the value of a trace token component</fsummary> <type name="component"/> <type name="flag"/> @@ -182,7 +182,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="print" arity="1"/> + <name name="print" arity="1" since=""/> <fsummary>Put the Erlang term <c>TraceInfo</c>into the sequential trace output</fsummary> <desc> <p>Puts the Erlang term <c><anno>TraceInfo</anno></c> into the sequential @@ -192,7 +192,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="print" arity="2"/> + <name name="print" arity="2" since=""/> <fsummary>Put the Erlang term <c>TraceInfo</c>into the sequential trace output</fsummary> <desc> <p>Same as <c>print/1</c> with the additional condition that @@ -201,7 +201,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="reset_trace" arity="0"/> + <name name="reset_trace" arity="0" since=""/> <fsummary>Stop all sequential tracing on the local node</fsummary> <desc> <p>Sets the trace token to empty for all processes on the @@ -213,7 +213,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="set_system_tracer" arity="1"/> + <name name="set_system_tracer" arity="1" since=""/> <fsummary>Set the system tracer</fsummary> <type name="tracer"/> <desc> @@ -227,7 +227,7 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name name="get_system_tracer" arity="0"/> + <name name="get_system_tracer" arity="0" since=""/> <fsummary>Return the pid() or port() of the current system tracer.</fsummary> <type name="tracer"/> <desc> diff --git a/lib/kernel/doc/src/wrap_log_reader.xml b/lib/kernel/doc/src/wrap_log_reader.xml index 7fb9c1c023..5f37e7ec5f 100644 --- a/lib/kernel/doc/src/wrap_log_reader.xml +++ b/lib/kernel/doc/src/wrap_log_reader.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>wrap_log_reader.sgml</file> </header> - <module>wrap_log_reader</module> + <module since="">wrap_log_reader</module> <modulesummary>A service to read internally formatted wrap disk logs. </modulesummary> <description> @@ -65,8 +65,8 @@ <funcs> <func> - <name name="chunk" arity="1"/> - <name name="chunk" arity="2"/> + <name name="chunk" arity="1" since=""/> + <name name="chunk" arity="2" since=""/> <fsummary>Read a chunk of objects written to a wrap log.</fsummary> <type name="chunk_ret"/> <desc> @@ -105,7 +105,7 @@ </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a log.</fsummary> <desc> <p>Closes a log file properly.</p> @@ -113,8 +113,8 @@ </func> <func> - <name name="open" arity="1"/> - <name name="open" arity="2"/> + <name name="open" arity="1" since=""/> + <name name="open" arity="2" since=""/> <fsummary>Open a log file.</fsummary> <type name="open_ret"/> <desc> diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 57f17defc8..3d1506ea08 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -118,6 +118,8 @@ MODULES = \ logger_h_common \ logger_filters \ logger_formatter \ + logger_olp \ + logger_proxy \ logger_server \ logger_simple_h \ logger_sup \ @@ -151,7 +153,7 @@ INTERNAL_HRL_FILES= application_master.hrl disk_log.hrl \ inet_dns.hrl inet_res.hrl \ inet_boot.hrl inet_config.hrl inet_int.hrl \ inet_dns_record_adts.hrl \ - logger_internal.hrl logger_h_common.hrl + logger_internal.hrl logger_olp.hrl logger_h_common.hrl ERL_FILES= $(MODULES:%=%.erl) @@ -279,6 +281,8 @@ $(EBIN)/logger_config.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_disk_log_h.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl ../include/file.hrl $(EBIN)/logger_filters.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_formatter.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_olp.beam: logger_olp.hrl logger_internal.hrl +$(EBIN)/logger_proxy.beam: logger_internal.hrl $(EBIN)/logger_server.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_simple_h.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_std_h.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl ../include/file.hrl diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl index bc6be2f8f5..5c2e981e4b 100644 --- a/lib/kernel/src/application.erl +++ b/lib/kernel/src/application.erl @@ -25,7 +25,7 @@ which_applications/0, which_applications/1, loaded_applications/0, permit/2]). -export([ensure_started/1, ensure_started/2]). --export([set_env/3, set_env/4, unset_env/2, unset_env/3]). +-export([set_env/1, set_env/2, set_env/3, set_env/4, unset_env/2, unset_env/3]). -export([get_env/1, get_env/2, get_env/3, get_all_env/0, get_all_env/1]). -export([get_key/1, get_key/2, get_all_key/0, get_all_key/1]). -export([get_application/0, get_application/1, info/0]). @@ -279,6 +279,26 @@ loaded_applications() -> info() -> application_controller:info(). +-spec set_env(Config) -> 'ok' when + Config :: [{Application, Env}], + Application :: atom(), + Env :: [{Par :: atom(), Val :: term()}]. + +set_env(Config) when is_list(Config) -> + set_env(Config, []). + +-spec set_env(Config, Opts) -> 'ok' when + Config :: [{Application, Env}], + Application :: atom(), + Env :: [{Par :: atom(), Val :: term()}], + Opts :: [{timeout, timeout()} | {persistent, boolean()}]. + +set_env(Config, Opts) when is_list(Config), is_list(Opts) -> + case application_controller:set_env(Config, Opts) of + ok -> ok; + {error, Msg} -> erlang:error({badarg, Msg}, [Config, Opts]) + end. + -spec set_env(Application, Par, Val) -> 'ok' when Application :: atom(), Par :: atom(), diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index a074d2e74b..9a8091fb2e 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -26,7 +26,7 @@ control_application/1, change_application_data/2, prep_config_change/0, config_change/1, which_applications/0, which_applications/1, - loaded_applications/0, info/0, + loaded_applications/0, info/0, set_env/2, get_pid_env/2, get_env/2, get_pid_all_env/1, get_all_env/1, get_pid_key/2, get_key/2, get_pid_all_key/1, get_all_key/1, get_master/1, get_application/1, get_application_module/1, @@ -345,9 +345,6 @@ get_all_env(AppName) -> map(fun([Key, Val]) -> {Key, Val} end, ets:match(ac_tab, {{env, AppName, '$1'}, '$2'})). - - - get_pid_key(Master, Key) -> case ets:match(ac_tab, {{application_master, '$1'}, Master}) of [[AppName]] -> get_key(AppName, Key); @@ -461,6 +458,15 @@ permit_application(ApplName, Flag) -> {permit_application, ApplName, Flag}, infinity). +set_env(Config, Opts) -> + case check_conf_data(Config) of + ok -> + Timeout = proplists:get_value(timeout, Opts, 5000), + gen_server:call(?AC, {set_env, Config, Opts}, Timeout); + + {error, _} = Error -> + Error + end. set_env(AppName, Key, Val) -> gen_server:call(?AC, {set_env, AppName, Key, Val, []}). @@ -528,19 +534,17 @@ check_conf_data([]) -> check_conf_data(ConfData) when is_list(ConfData) -> [Application | ConfDataRem] = ConfData, case Application of - {kernel, List} when is_list(List) -> - case check_para_kernel(List) of - ok -> - check_conf_data(ConfDataRem); - Error1 -> - Error1 - end; {AppName, List} when is_atom(AppName), is_list(List) -> - case check_para(List, atom_to_list(AppName)) of - ok -> - check_conf_data(ConfDataRem); - Error2 -> - Error2 + case lists:keymember(AppName, 1, ConfDataRem) of + true -> + ?LOG_WARNING("duplicate application config: " ++ atom_to_list(AppName)); + false -> + ok + end, + + case check_para(List, AppName) of + ok -> check_conf_data(ConfDataRem); + Error -> Error end; {AppName, List} when is_list(List) -> ErrMsg = "application: " @@ -553,36 +557,40 @@ check_conf_data(ConfData) when is_list(ConfData) -> ++ "; parameters must be a list", {error, ErrMsg}; Else -> - ErrMsg = "invalid application name: " ++ - lists:flatten(io_lib:format(" ~tp",[Else])), + ErrMsg = "invalid application config: " + ++ lists:flatten(io_lib:format("~tp",[Else])), {error, ErrMsg} end; check_conf_data(_ConfData) -> - {error, 'configuration must be a list ended by <dot><whitespace>'}. - + {error, "configuration must be a list ended by <dot><whitespace>"}. -%% Special check of distributed parameter for kernel -check_para_kernel([]) -> + +check_para([], _AppName) -> ok; -check_para_kernel([{distributed, Apps} | ParaList]) when is_list(Apps) -> - case check_distributed(Apps) of - {error, _ErrorMsg} = Error -> - Error; - _ -> - check_para_kernel(ParaList) +check_para([{Para, Val} | ParaList], AppName) when is_atom(Para) -> + case lists:keymember(Para, 1, ParaList) of + true -> + ?LOG_WARNING("application: " ++ atom_to_list(AppName) ++ + "; duplicate parameter: " ++ atom_to_list(Para)); + false -> + ok + end, + + case check_para_value(Para, Val, AppName) of + ok -> check_para(ParaList, AppName); + {error, _} = Error -> Error end; -check_para_kernel([{distributed, _Apps} | _ParaList]) -> - {error, "application: kernel; erroneous parameter: distributed"}; -check_para_kernel([{Para, _Val} | ParaList]) when is_atom(Para) -> - check_para_kernel(ParaList); -check_para_kernel([{Para, _Val} | _ParaList]) -> - {error, "application: kernel; invalid parameter: " ++ +check_para([{Para, _Val} | _ParaList], AppName) -> + {error, "application: " ++ atom_to_list(AppName) ++ "; invalid parameter name: " ++ lists:flatten(io_lib:format("~tp",[Para]))}; -check_para_kernel(Else) -> - {error, "application: kernel; invalid parameter list: " ++ +check_para([Else | _ParaList], AppName) -> + {error, "application: " ++ atom_to_list(AppName) ++ "; invalid parameter: " ++ lists:flatten(io_lib:format("~tp",[Else]))}. - +check_para_value(distributed, Apps, kernel) -> check_distributed(Apps); +check_para_value(_Para, _Val, _AppName) -> ok. + +%% Special check of distributed parameter for kernel check_distributed([]) -> ok; check_distributed([{App, List} | Apps]) when is_atom(App), is_list(List) -> @@ -595,18 +603,6 @@ check_distributed(_Else) -> {error, "application: kernel; erroneous parameter: distributed"}. -check_para([], _AppName) -> - ok; -check_para([{Para, _Val} | ParaList], AppName) when is_atom(Para) -> - check_para(ParaList, AppName); -check_para([{Para, _Val} | _ParaList], AppName) -> - {error, "application: " ++ AppName ++ "; invalid parameter: " ++ - lists:flatten(io_lib:format("~tp",[Para]))}; -check_para([Else | _ParaList], AppName) -> - {error, "application: " ++ AppName ++ "; invalid parameter: " ++ - lists:flatten(io_lib:format("~tp",[Else]))}. - - -type calls() :: 'info' | 'prep_config_change' | 'which_applications' | {'config_change' | 'control_application' | 'load_application' | 'start_type' | 'stop_application' | @@ -863,6 +859,16 @@ handle_call(which_applications, _From, S) -> end, S#state.running), {reply, Reply, S}; +handle_call({set_env, Config, Opts}, _From, S) -> + _ = [add_env(AppName, Env) || {AppName, Env} <- Config], + + case proplists:get_value(persistent, Opts, false) of + true -> + {reply, ok, S#state{conf_data = merge_env(S#state.conf_data, Config)}}; + false -> + {reply, ok, S} + end; + handle_call({set_env, AppName, Key, Val, Opts}, _From, S) -> ets:insert(ac_tab, {{env, AppName, Key}, Val}), case proplists:get_value(persistent, Opts, false) of diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 1b4a67ecb7..68e1205301 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1434,19 +1434,25 @@ all_loaded(Db) -> -spec error_msg(io:format(), [term()]) -> 'ok'. error_msg(Format, Args) -> + %% This is equal to calling logger:error/3 which we don't want to + %% do from code_server. We don't want to call logger:timestamp() + %% either. logger ! {log,error,Format,Args, #{pid=>self(), gl=>group_leader(), - time=>erlang:system_time(microsecond), + time=>os:system_time(microsecond), error_logger=>#{tag=>error}}}, ok. -spec info_msg(io:format(), [term()]) -> 'ok'. info_msg(Format, Args) -> + %% This is equal to calling logger:info/3 which we don't want to + %% do from code_server. We don't want to call logger:timestamp() + %% either. logger ! {log,info,Format,Args, #{pid=>self(), gl=>group_leader(), - time=>erlang:system_time(microsecond), + time=>os:system_time(microsecond), error_logger=>#{tag=>info_msg}}}, ok. diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index b7e8868911..7a14e2635c 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -77,8 +77,8 @@ stop() -> %% -spec port_please(Name, Host) -> {ok, Port, Version} | noport when - Name :: string(), - Host :: inet:ip_address(), + Name :: atom() | string(), + Host :: atom() | string() | inet:ip_address(), Port :: non_neg_integer(), Version :: non_neg_integer(). @@ -86,8 +86,8 @@ port_please(Node, Host) -> port_please(Node, Host, infinity). -spec port_please(Name, Host, Timeout) -> {ok, Port, Version} | noport when - Name :: string(), - Host :: inet:ip_address(), + Name :: atom() | string(), + Host :: atom() | string() | inet:ip_address(), Timeout :: non_neg_integer() | infinity, Port :: non_neg_integer(), Version :: non_neg_integer(). diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index fe073621c8..a1d9e8e215 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -68,6 +68,8 @@ logger_formatter, logger_h_common, logger_handler_watcher, + logger_olp, + logger_proxy, logger_server, logger_simple_h, logger_std_h, diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 6762998d4f..38bd2f481c 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -43,11 +43,14 @@ get_module_level/0, get_module_level/1, set_primary_config/1, set_primary_config/2, set_handler_config/2, set_handler_config/3, + set_proxy_config/1, update_primary_config/1, update_handler_config/2, update_handler_config/3, + update_proxy_config/1, update_formatter_config/2, update_formatter_config/3, get_primary_config/0, get_handler_config/1, get_handler_config/0, get_handler_ids/0, get_config/0, + get_proxy_config/0, add_handlers/1]). %% Private configuration @@ -57,6 +60,8 @@ -export([compare_levels/2]). -export([set_process_metadata/1, update_process_metadata/1, unset_process_metadata/0, get_process_metadata/0]). +-export([i/0, i/1]). +-export([timestamp/0]). %% Basic report formatting -export([format_report/1, format_otp_report/1]). @@ -122,6 +127,18 @@ {filters,log | stop,[{filter_id(),filter()}]} | {module_level,level(),[module()]}]. +-type olp_config() :: #{sync_mode_qlen => non_neg_integer(), + drop_mode_qlen => pos_integer(), + flush_qlen => pos_integer(), + burst_limit_enable => boolean(), + burst_limit_max_count => pos_integer(), + burst_limit_window_time => pos_integer(), + overload_kill_enable => boolean(), + overload_kill_qlen => pos_integer(), + overload_kill_mem_size => pos_integer(), + overload_kill_restart_after => + non_neg_integer() | infinity}. + -export_type([log_event/0, level/0, report/0, @@ -137,7 +154,9 @@ filter_arg/0, filter_return/0, config_handler/0, - formatter_config/0]). + formatter_config/0, + olp_config/0, + timestamp/0]). %%%----------------------------------------------------------------- %%% API @@ -337,6 +356,10 @@ internal_log(Level,Term) when is_atom(Level) -> erlang:display_string("Logger - "++ atom_to_list(Level) ++ ": "), erlang:display(Term). +-spec timestamp() -> timestamp(). +timestamp() -> + os:system_time(microsecond). + %%%----------------------------------------------------------------- %%% Configuration -spec add_primary_filter(FilterId,Filter) -> ok | {error,term()} when @@ -390,6 +413,7 @@ set_primary_config(Key,Value) -> set_primary_config(Config) -> logger_server:set_config(primary,Config). + -spec set_handler_config(HandlerId,level,Level) -> Return when HandlerId :: handler_id(), Level :: level() | all | none, @@ -419,6 +443,11 @@ set_handler_config(HandlerId,Key,Value) -> set_handler_config(HandlerId,Config) -> logger_server:set_config(HandlerId,Config). +-spec set_proxy_config(Config) -> ok | {error,term()} when + Config :: olp_config(). +set_proxy_config(Config) -> + logger_server:set_config(proxy,Config). + -spec update_primary_config(Config) -> ok | {error,term()} when Config :: primary_config(). update_primary_config(Config) -> @@ -453,6 +482,11 @@ update_handler_config(HandlerId,Key,Value) -> update_handler_config(HandlerId,Config) -> logger_server:update_config(HandlerId,Config). +-spec update_proxy_config(Config) -> ok | {error,term()} when + Config :: olp_config(). +update_proxy_config(Config) -> + logger_server:update_config(proxy,Config). + -spec get_primary_config() -> Config when Config :: primary_config(). get_primary_config() -> @@ -486,6 +520,12 @@ get_handler_ids() -> {ok,#{handlers:=HandlerIds}} = logger_config:get(?LOGGER_TABLE,primary), HandlerIds. +-spec get_proxy_config() -> Config when + Config :: olp_config(). +get_proxy_config() -> + {ok,Config} = logger_config:get(?LOGGER_TABLE,proxy), + Config. + -spec update_formatter_config(HandlerId,FormatterConfig) -> ok | {error,term()} when HandlerId :: handler_id(), @@ -606,12 +646,150 @@ unset_process_metadata() -> -spec get_config() -> #{primary=>primary_config(), handlers=>[handler_config()], + proxy=>olp_config(), module_levels=>[{module(),level() | all | none}]}. get_config() -> #{primary=>get_primary_config(), handlers=>get_handler_config(), + proxy=>get_proxy_config(), module_levels=>lists:keysort(1,get_module_level())}. +-spec i() -> ok. +i() -> + #{primary := Primary, + handlers := HandlerConfigs, + proxy := Proxy, + module_levels := Modules} = get_config(), + M = modifier(), + i_primary(Primary,M), + i_handlers(HandlerConfigs,M), + i_proxy(Proxy,M), + i_modules(Modules,M). + +-spec i(What) -> ok when + What :: primary | handlers | proxy | modules | handler_id(). +i(primary) -> + i_primary(get_primary_config(),modifier()); +i(handlers) -> + i_handlers(get_handler_config(),modifier()); +i(proxy) -> + i_proxy(get_proxy_config(),modifier()); +i(modules) -> + i_modules(get_module_level(),modifier()); +i(HandlerId) when is_atom(HandlerId) -> + case get_handler_config(HandlerId) of + {ok,HandlerConfig} -> + i_handlers([HandlerConfig],modifier()); + Error -> + Error + end; +i(What) -> + erlang:error(badarg,[What]). + + +i_primary(#{level := Level, + filters := Filters, + filter_default := FilterDefault}, + M) -> + io:format("Primary configuration: ~n",[]), + io:format(" Level: ~p~n",[Level]), + io:format(" Filter Default: ~p~n", [FilterDefault]), + io:format(" Filters: ~n", []), + print_filters(" ",Filters,M). + +i_handlers(HandlerConfigs,M) -> + io:format("Handler configuration: ~n", []), + print_handlers(HandlerConfigs,M). + +i_proxy(Proxy,M) -> + io:format("Proxy configuration: ~n", []), + print_custom(" ",Proxy,M). + +i_modules(Modules,M) -> + io:format("Level set per module: ~n", []), + print_module_levels(Modules,M). + +encoding() -> + case lists:keyfind(encoding, 1, io:getopts()) of + false -> latin1; + {encoding, Enc} -> Enc + end. + +modifier() -> + modifier(encoding()). + +modifier(latin1) -> ""; +modifier(_) -> "t". + +print_filters(Indent, {Id, {Fun, Arg}}, M) -> + io:format("~sId: ~"++M++"p~n" + "~s Fun: ~"++M++"p~n" + "~s Arg: ~"++M++"p~n", + [Indent, Id, Indent, Fun, Indent, Arg]); +print_filters(Indent,[],_M) -> + io:format("~s(none)~n",[Indent]); +print_filters(Indent,Filters,M) -> + [print_filters(Indent,Filter,M) || Filter <- Filters], + ok. + +print_handlers(#{id := Id, + module := Module, + level := Level, + filters := Filters, filter_default := FilterDefault, + formatter := {FormatterModule,FormatterConfig}} = Config, M) -> + io:format(" Id: ~"++M++"p~n" + " Module: ~p~n" + " Level: ~p~n" + " Formatter:~n" + " Module: ~p~n" + " Config:~n", + [Id, Module, Level, FormatterModule]), + print_custom(" ",FormatterConfig,M), + io:format(" Filter Default: ~p~n" + " Filters:~n", + [FilterDefault]), + print_filters(" ",Filters,M), + case maps:find(config,Config) of + {ok,HandlerConfig} -> + io:format(" Handler Config:~n"), + print_custom(" ",HandlerConfig,M); + error -> + ok + end, + MyKeys = [filter_default, filters, formatter, level, module, id, config], + case maps:without(MyKeys,Config) of + Empty when Empty==#{} -> + ok; + Unhandled -> + io:format(" Custom Config:~n"), + print_custom(" ",Unhandled,M) + end; +print_handlers([], _M) -> + io:format(" (none)~n"); +print_handlers(HandlerConfigs, M) -> + [print_handlers(HandlerConfig, M) || HandlerConfig <- HandlerConfigs], + ok. + +print_custom(Indent, {Key, Value}, M) -> + io:format("~s~"++M++"p: ~"++M++"p~n",[Indent,Key,Value]); +print_custom(Indent, Map, M) when is_map(Map) -> + print_custom(Indent,lists:keysort(1,maps:to_list(Map)), M); +print_custom(Indent, List, M) when is_list(List), is_tuple(hd(List)) -> + [print_custom(Indent, X, M) || X <- List], + ok; +print_custom(Indent, Value, M) -> + io:format("~s~"++M++"p~n",[Indent,Value]). + +print_module_levels({Module,Level},M) -> + io:format(" Module: ~"++M++"p~n" + " Level: ~p~n", + [Module,Level]); +print_module_levels([],_M) -> + io:format(" (none)~n"); +print_module_levels(Modules,M) -> + [print_module_levels(Module,M) || Module <- Modules], + ok. + -spec internal_init_logger() -> ok | {error,term()}. %% This function is responsible for config of the logger %% This is done before add_handlers because we want the @@ -672,6 +850,17 @@ init_kernel_handlers(Env) -> %% This function is responsible for resolving the handler config %% and then starting the correct handlers. This is done after the %% kernel supervisor tree has been started as it needs the logger_sup. +add_handlers(kernel) -> + Env = get_logger_env(kernel), + case get_proxy_opts(Env) of + undefined -> + add_handlers(kernel,Env); + Opts -> + case set_proxy_config(Opts) of + ok -> add_handlers(kernel,Env); + {error, Reason} -> {error,{bad_proxy_config,Reason}} + end + end; add_handlers(App) when is_atom(App) -> add_handlers(App,get_logger_env(App)); add_handlers(HandlerConfig) -> @@ -729,6 +918,8 @@ check_logger_config(kernel,[{filters,_,_}|Env]) -> check_logger_config(kernel,Env); check_logger_config(kernel,[{module_level,_,_}|Env]) -> check_logger_config(kernel,Env); +check_logger_config(kernel,[{proxy,_}|Env]) -> + check_logger_config(kernel,Env); check_logger_config(_,Bad) -> throw(Bad). @@ -784,6 +975,13 @@ get_primary_filters(Env) -> _ -> throw({multiple_filters,Env}) end. +get_proxy_opts(Env) -> + case [P || P={proxy,_} <- Env] of + [{proxy,Opts}] -> Opts; + [] -> undefined; + _ -> throw({multiple_proxies,Env}) + end. + %% This function looks at the kernel logger environment %% and updates it so that the correct logger is configured init_default_config(Type,Env) when Type==standard_io; @@ -880,30 +1078,30 @@ log_allowed(Location,Level,Msg,Meta0) when is_map(Meta0) -> maps:merge(Location,maps:merge(proc_meta(),Meta0))), case node(maps:get(gl,Meta)) of Node when Node=/=node() -> - log_remote(Node,Level,Msg,Meta), - do_log_allowed(Level,Msg,Meta); + log_remote(Node,Level,Msg,Meta); _ -> - do_log_allowed(Level,Msg,Meta) - end. + ok + end, + do_log_allowed(Level,Msg,Meta,tid()). -do_log_allowed(Level,{Format,Args}=Msg,Meta) +do_log_allowed(Level,{Format,Args}=Msg,Meta,Tid) when ?IS_LEVEL(Level), is_list(Format), is_list(Args), is_map(Meta) -> - logger_backend:log_allowed(#{level=>Level,msg=>Msg,meta=>Meta},tid()); -do_log_allowed(Level,Report,Meta) + logger_backend:log_allowed(#{level=>Level,msg=>Msg,meta=>Meta},Tid); +do_log_allowed(Level,Report,Meta,Tid) when ?IS_LEVEL(Level), ?IS_REPORT(Report), is_map(Meta) -> logger_backend:log_allowed(#{level=>Level,msg=>{report,Report},meta=>Meta}, - tid()); -do_log_allowed(Level,String,Meta) + Tid); +do_log_allowed(Level,String,Meta,Tid) when ?IS_LEVEL(Level), ?IS_STRING(String), is_map(Meta) -> logger_backend:log_allowed(#{level=>Level,msg=>{string,String},meta=>Meta}, - tid()). + Tid). tid() -> ets:whereis(?LOGGER_TABLE). @@ -913,7 +1111,7 @@ log_remote(Node,Level,Msg,Meta) -> log_remote(Node,{log,Level,Msg,Meta}). log_remote(Node,Request) -> - {logger,Node} ! Request, + logger_proxy:log({remote,Node,Request}), ok. add_default_metadata(Meta) -> @@ -937,7 +1135,7 @@ proc_meta() -> default(pid) -> self(); default(gl) -> group_leader(); -default(time) -> erlang:system_time(microsecond). +default(time) -> timestamp(). %% Remove everything upto and including this module from the stacktrace filter_stacktrace(Module,[{Module,_,_,_}|_]) -> diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 5e9faf332c..5024d20cfe 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -66,6 +66,8 @@ get(Tid,What) -> case ets:lookup(Tid,table_key(What)) of [{_,_,Config}] -> {ok,Config}; + [{_,Config}] when What=:=proxy -> + {ok,Config}; [] -> {error,{not_found,What}} end. @@ -79,10 +81,15 @@ get(Tid,What,Level) -> [Data] -> {ok,Data} end. +create(Tid,proxy,Config) -> + ets:insert(Tid,{table_key(proxy),Config}); create(Tid,What,Config) -> LevelInt = level_to_int(maps:get(level,Config)), ets:insert(Tid,{table_key(What),LevelInt,Config}). +set(Tid,proxy,Config) -> + ets:insert(Tid,{table_key(proxy),Config}), + ok; set(Tid,What,Config) -> LevelInt = level_to_int(maps:get(level,Config)), %% Should do this only if the level has actually changed. Possibly @@ -148,5 +155,6 @@ int_to_level(?LOG_ALL) -> all. %%%----------------------------------------------------------------- %%% Internal +table_key(proxy) -> ?PROXY_KEY; table_key(primary) -> ?PRIMARY_KEY; table_key(HandlerId) -> {?HANDLER_KEY,HandlerId}. diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 41e0d51a9d..47b39da900 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -24,7 +24,7 @@ -include("logger_h_common.hrl"). %%% API --export([info/1, filesync/1, reset/1]). +-export([filesync/1]). %% logger_h_common callbacks -export([init/2, check_config/4, reset_state/2, @@ -47,25 +47,6 @@ filesync(Name) -> logger_h_common:filesync(?MODULE,Name). -%%%----------------------------------------------------------------- -%%% --spec info(Name) -> Info | {error,Reason} when - Name :: atom(), - Info :: term(), - Reason :: handler_busy | {badarg,term()}. - -info(Name) -> - logger_h_common:info(?MODULE,Name). - -%%%----------------------------------------------------------------- -%%% --spec reset(Name) -> ok | {error,Reason} when - Name :: atom(), - Reason :: handler_busy | {badarg,term()}. - -reset(Name) -> - logger_h_common:reset(?MODULE,Name). - %%%=================================================================== %%% logger callbacks %%%=================================================================== diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 74a2d158fc..4b5e0a7dd0 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -24,11 +24,11 @@ -include("logger_internal.hrl"). %% API --export([start_link/1, info/2, filesync/2, reset/2]). +-export([filesync/2]). -%% gen_server and proc_lib callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +%% logger_olp callbacks +-export([init/1, handle_load/2, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3, notify/2, reset_state/1]). %% logger callbacks -export([log/2, adding_handler/1, removing_handler/1, changing_config/3, @@ -37,52 +37,45 @@ %% Library functions for handlers -export([error_notify/1]). -%%%----------------------------------------------------------------- --define(CONFIG_KEYS,[sync_mode_qlen, - drop_mode_qlen, - flush_qlen, - burst_limit_enable, - burst_limit_max_count, - burst_limit_window_time, - overload_kill_enable, - overload_kill_qlen, - overload_kill_mem_size, - overload_kill_restart_after, - filesync_repeat_interval]). --define(READ_ONLY_KEYS,[handler_pid,mode_tab]). +-define(OLP_KEYS,[sync_mode_qlen, + drop_mode_qlen, + flush_qlen, + burst_limit_enable, + burst_limit_max_count, + burst_limit_window_time, + overload_kill_enable, + overload_kill_qlen, + overload_kill_mem_size, + overload_kill_restart_after]). + +-define(COMMON_KEYS,[filesync_repeat_interval]). + +-define(READ_ONLY_KEYS,[olp]). %%%----------------------------------------------------------------- %%% API %% This function is called by the logger_sup supervisor -start_link(Args) -> - proc_lib:start_link(?MODULE,init,[Args]). - filesync(Module, Name) -> call(Module, Name, filesync). -info(Module, Name) -> - call(Module, Name, info). - -reset(Module, Name) -> - call(Module, Name, reset). - %%%----------------------------------------------------------------- %%% Handler being added adding_handler(#{id:=Name,module:=Module}=Config) -> HConfig0 = maps:get(config, Config, #{}), - HandlerConfig0 = maps:without(?CONFIG_KEYS,HConfig0), + HandlerConfig0 = maps:without(?OLP_KEYS++?COMMON_KEYS,HConfig0), case Module:check_config(Name,set,undefined,HandlerConfig0) of {ok,HandlerConfig} -> - ModifiedCommon = maps:with(?CONFIG_KEYS,HandlerConfig), - CommonConfig0 = maps:with(?CONFIG_KEYS,HConfig0), + ModifiedCommon = maps:with(?COMMON_KEYS,HandlerConfig), + CommonConfig0 = maps:with(?COMMON_KEYS,HConfig0), CommonConfig = maps:merge( maps:merge(get_default_config(), CommonConfig0), ModifiedCommon), case check_config(CommonConfig) of ok -> HConfig = maps:merge(CommonConfig,HandlerConfig), - start(Config#{config => HConfig}); + OlpOpts = maps:with(?OLP_KEYS,HConfig0), + start(OlpOpts, Config#{config => HConfig}); {error,Faulty} -> {error,{invalid_config,Module,Faulty}} end; @@ -92,11 +85,11 @@ adding_handler(#{id:=Name,module:=Module}=Config) -> %%%----------------------------------------------------------------- %%% Handler being removed -removing_handler(#{id:=Name, module:=Module}) -> +removing_handler(#{id:=Name, module:=Module, config:=#{olp:=Olp}}) -> case whereis(?name_to_reg_name(Module,Name)) of undefined -> ok; - Pid -> + _Pid -> %% We don't want to do supervisor:terminate_child here %% since we need to distinguish this explicit stop from a %% system termination in order to avoid circular attempts @@ -106,7 +99,7 @@ removing_handler(#{id:=Name, module:=Module}) -> %% the restart type is temporary, which means that the %% child specification is automatically removed from the %% supervisor when the process dies. - _ = gen_server:call(Pid, stop), + _ = logger_olp:stop(Olp), ok end. @@ -116,34 +109,52 @@ changing_config(SetOrUpdate, #{id:=Name,config:=OldHConfig,module:=Module}, NewConfig0) -> NewHConfig0 = maps:get(config, NewConfig0, #{}), - OldHandlerConfig = maps:without(?CONFIG_KEYS++?READ_ONLY_KEYS,OldHConfig), - NewHandlerConfig0 = maps:without(?CONFIG_KEYS++?READ_ONLY_KEYS,NewHConfig0), + NoHandlerKeys = ?OLP_KEYS++?COMMON_KEYS++?READ_ONLY_KEYS, + OldHandlerConfig = maps:without(NoHandlerKeys,OldHConfig), + NewHandlerConfig0 = maps:without(NoHandlerKeys,NewHConfig0), case Module:check_config(Name, SetOrUpdate, OldHandlerConfig,NewHandlerConfig0) of {ok, NewHandlerConfig} -> - ModifiedCommon = maps:with(?CONFIG_KEYS,NewHandlerConfig), - NewCommonConfig0 = maps:with(?CONFIG_KEYS,NewHConfig0), + ModifiedCommon = maps:with(?COMMON_KEYS,NewHandlerConfig), + NewCommonConfig0 = maps:with(?COMMON_KEYS,NewHConfig0), + OldCommonConfig = maps:with(?COMMON_KEYS,OldHConfig), CommonDefault = case SetOrUpdate of set -> get_default_config(); update -> - maps:with(?CONFIG_KEYS,OldHConfig) + OldCommonConfig end, NewCommonConfig = maps:merge( maps:merge(CommonDefault,NewCommonConfig0), ModifiedCommon), case check_config(NewCommonConfig) of ok -> - ReadOnly = maps:with(?READ_ONLY_KEYS,OldHConfig), - NewHConfig = maps:merge( - maps:merge(NewCommonConfig,NewHandlerConfig), - ReadOnly), - NewConfig = NewConfig0#{config=>NewHConfig}, - HPid = maps:get(handler_pid,OldHConfig), - case call(HPid, {change_config,NewConfig}) of - ok -> {ok,NewConfig}; - Error -> Error + OlpDefault = + case SetOrUpdate of + set -> + logger_olp:get_default_opts(); + update -> + maps:with(?OLP_KEYS,OldHConfig) + end, + Olp = maps:get(olp,OldHConfig), + NewOlpOpts = maps:merge(OlpDefault, + maps:with(?OLP_KEYS,NewHConfig0)), + case logger_olp:set_opts(Olp,NewOlpOpts) of + ok -> + maybe_set_repeated_filesync(Olp,OldCommonConfig, + NewCommonConfig), + ReadOnly = maps:with(?READ_ONLY_KEYS,OldHConfig), + NewHConfig = + maps:merge( + maps:merge( + maps:merge(NewCommonConfig,NewHandlerConfig), + ReadOnly), + NewOlpOpts), + NewConfig = NewConfig0#{config=>NewHConfig}, + {ok,NewConfig}; + Error -> + Error end; {error,Faulty} -> {error,{invalid_config,Module,Faulty}} @@ -158,14 +169,12 @@ changing_config(SetOrUpdate, LogEvent :: logger:log_event(), Config :: logger:handler_config(). -log(LogEvent, Config = #{id := Name, - config := #{handler_pid := HPid, - mode_tab := ModeTab}}) -> +log(LogEvent, Config = #{config := #{olp:=Olp}}) -> %% if the handler has crashed, we must drop this event %% and hope the handler restarts so we can try again - true = is_process_alive(HPid), + true = is_process_alive(logger_olp:get_pid(Olp)), Bin = log_to_binary(LogEvent, Config), - call_cast_or_drop(Name, HPid, ModeTab, Bin). + logger_olp:load(Olp,Bin). %%%----------------------------------------------------------------- %%% Remove internal fields from configuration @@ -180,18 +189,23 @@ filter_config(#{config:=HConfig}=Config) -> %%% %%% The handler process is linked to logger_sup, which is part of the %%% kernel application's supervision tree. -start(#{id := Name} = Config0) -> +start(OlpOpts0, #{id := Name, module:=Module, config:=HConfig} = Config0) -> + RegName = ?name_to_reg_name(Module,Name), ChildSpec = #{id => Name, - start => {?MODULE, start_link, [Config0]}, + start => {logger_olp, start_link, [RegName,?MODULE, + Config0, OlpOpts0]}, restart => temporary, shutdown => 2000, type => worker, modules => [?MODULE]}, case supervisor:start_child(logger_sup, ChildSpec) of - {ok,Pid,Config} -> + {ok,Pid,Olp} -> ok = logger_handler_watcher:register_handler(Name,Pid), - {ok,Config}; + OlpOpts = logger_olp:get_opts(Olp), + {ok,Config0#{config=>(maps:merge(HConfig,OlpOpts))#{olp=>Olp}}}; + {error,{Reason,Ch}} when is_tuple(Ch), element(1,Ch)==child -> + {error,Reason}; Error -> Error end. @@ -200,103 +214,50 @@ start(#{id := Name} = Config0) -> %%% gen_server callbacks %%%=================================================================== -init(#{id := Name, module := Module, - formatter := Formatter, config := HConfig0} = Config0) -> - RegName = ?name_to_reg_name(Module,Name), - register(RegName, self()), +init(#{id := Name, module := Module, config := HConfig}) -> process_flag(trap_exit, true), - process_flag(message_queue_data, off_heap), ?init_test_hooks(), - ?start_observation(Name), - case Module:init(Name, HConfig0) of + case Module:init(Name, HConfig) of {ok,HState} -> - try ets:new(Name, [public]) of - ModeTab -> - ?set_mode(ModeTab, async), - T0 = ?timestamp(), - HConfig = HConfig0#{handler_pid => self(), - mode_tab => ModeTab}, - Config = Config0#{config => HConfig}, - proc_lib:init_ack({ok,self(),Config}), - %% Storing common config in state to avoid copying - %% (sending) the config data for each log message - CommonConfig = maps:with(?CONFIG_KEYS,HConfig), - State = - ?merge_with_stats( - CommonConfig#{id => Name, - module => Module, - mode_tab => ModeTab, - mode => async, - ctrl_sync_count => - ?CONTROLLER_SYNC_INTERVAL, - last_qlen => 0, - last_log_ts => T0, - last_op => sync, - burst_win_ts => T0, - burst_msg_count => 0, - formatter => Formatter, - handler_state => HState}), - State1 = set_repeated_filesync(State), - unset_restart_flag(State1), - gen_server:enter_loop(?MODULE, [], State1) - catch - _:Error -> - unregister(RegName), - error_notify({init_handler,Name,Error}), - proc_lib:init_ack(Error) - end; + %% Storing common config in state to avoid copying + %% (sending) the config data for each log message + CommonConfig = maps:with(?COMMON_KEYS,HConfig), + State = CommonConfig#{id => Name, + module => Module, + ctrl_sync_count => + ?CONTROLLER_SYNC_INTERVAL, + last_op => sync, + handler_state => HState}, + State1 = set_repeated_filesync(State), + {ok,State1}; Error -> - unregister(RegName), - error_notify({init_handler,Name,Error}), - proc_lib:init_ack(Error) + Error end. -%% This is the synchronous log event. -handle_call({log, Bin}, _From, State) -> - {Result,State1} = do_log(Bin, call, State), - %% Result == ok | dropped - {reply,Result, State1}; +%% This is the log event. +handle_load(Bin, #{id:=Name, + module:=Module, + handler_state:=HandlerState, + ctrl_sync_count := CtrlSync}=State) -> + if CtrlSync==0 -> + {_,HS1} = Module:write(Name, sync, Bin, HandlerState), + State#{handler_state => HS1, + ctrl_sync_count => ?CONTROLLER_SYNC_INTERVAL, + last_op=>write}; + true -> + {_,HS1} = Module:write(Name, async, Bin, HandlerState), + State#{handler_state => HS1, + ctrl_sync_count => CtrlSync-1, + last_op=>write} + end. handle_call(filesync, _From, State = #{id := Name, module := Module, handler_state := HandlerState}) -> {Result,HandlerState1} = Module:filesync(Name,sync,HandlerState), - {reply, Result, State#{handler_state=>HandlerState1, last_op=>sync}}; - -handle_call({change_config, #{formatter:=Formatter, config:=NewHConfig}}, _From, - State = #{filesync_repeat_interval := FSyncInt0}) -> - %% In the future, if handler_state must be updated due to config - %% change, then we need to add a callback to Module here. - CommonConfig = maps:with(?CONFIG_KEYS,NewHConfig), - State1 = maps:merge(State, CommonConfig), - State2 = - case maps:get(filesync_repeat_interval, NewHConfig) of - FSyncInt0 -> - State1; - _FSyncInt1 -> - set_repeated_filesync(cancel_repeated_filesync(State1)) - end, - {reply, ok, State2#{formatter:=Formatter}}; - -handle_call(info, _From, State) -> - {reply, State, State}; - -handle_call(reset, _From, - #{id:=Name,module:=Module,handler_state:=HandlerState}=State) -> - State1 = ?merge_with_stats(State), - {reply, ok, State1#{last_qlen => 0, - last_log_ts => ?timestamp(), - handler_state => Module:reset_state(Name,HandlerState)}}; - -handle_call(stop, _From, State) -> - {stop, {shutdown,stopped}, ok, State}. - -%% This is the asynchronous log event. -handle_cast({log, Bin}, State) -> - {_,State1} = do_log(Bin, cast, State), - {noreply, State1}; + {reply, Result, State#{handler_state=>HandlerState1, last_op=>sync}}. %% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this %% clause gets called repeatedly by the handler. In order to @@ -319,168 +280,83 @@ handle_cast(repeated_filesync, {_,HS} = Module:filesync(Name, async, HandlerState), State#{handler_state => HS, last_op => sync} end, - {noreply,set_repeated_filesync(State1)}. + {noreply,set_repeated_filesync(State1)}; + +handle_cast({set_repeated_filesync,FSyncInt},State) -> + State1 = State#{filesync_repeat_interval=>FSyncInt}, + State2 = set_repeated_filesync(cancel_repeated_filesync(State1)), + {noreply, State2}. handle_info(Info, #{id := Name, module := Module, handler_state := HandlerState} = State) -> {noreply,State#{handler_state => Module:handle_info(Name,Info,HandlerState)}}. -terminate(Reason, State = #{id := Name, - module := Module, - handler_state := HandlerState}) -> +terminate(overloaded=Reason, #{id:=Name}=State) -> + _ = log_handler_info(Name,"Handler ~p overloaded and stopping",[Name],State), + do_terminate(Reason,State), + ConfigResult = logger:get_handler_config(Name), + case ConfigResult of + {ok,#{module:=Module}=HConfig0} -> + spawn(fun() -> logger:remove_handler(Name) end), + HConfig = try Module:filter_config(HConfig0) + catch _:_ -> HConfig0 + end, + {ok,fun() -> logger:add_handler(Name,Module,HConfig) end}; + Error -> + error_notify({Name,restart_impossible,Error}), + Error + end; +terminate(Reason, State) -> + do_terminate(Reason, State). + +do_terminate(Reason, State = #{id := Name, + module := Module, + handler_state := HandlerState}) -> _ = cancel_repeated_filesync(State), _ = Module:terminate(Name, Reason, HandlerState), - ok = stop_or_restart(Name, Reason, State), - unregister(?name_to_reg_name(Module, Name)), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. +reset_state(#{id:=Name, module:=Module, handler_state:=HandlerState} = State) -> + State#{handler_state=>Module:reset_state(Name, HandlerState)}. %%%----------------------------------------------------------------- %%% Internal functions call(Module, Name, Op) when is_atom(Name) -> - call(?name_to_reg_name(Module,Name), Op); + case logger_olp:call(?name_to_reg_name(Module,Name), Op) of + {error,busy} -> {error,handler_busy}; + Other -> Other + end; call(_, Name, Op) -> {error,{badarg,{Op,[Name]}}}. -call(Server, Msg) -> - try - gen_server:call(Server, Msg, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end. - -%% check for overload between every event (and set Mode to async, -%% sync or drop accordingly), but never flush the whole mailbox -%% before LogWindowSize events have been handled -do_log(Bin, CallOrCast, State = #{id:=Name, mode:=Mode0}) -> - T1 = ?timestamp(), - - %% check if the handler is getting overloaded, or if it's - %% recovering from overload (the check must be done for each - %% event to react quickly to large bursts of events and - %% to ensure that the handler can never end up in drop mode - %% with an empty mailbox, which would stop operation) - {Mode1,QLen,Mem,State1} = check_load(State), - - if (Mode1 == drop) andalso (Mode0 =/= drop) -> - log_handler_info(Name, "Handler ~p switched to drop mode", - [Name], State); - (Mode0 == drop) andalso ((Mode1 == async) orelse (Mode1 == sync)) -> - log_handler_info(Name, "Handler ~p switched to ~w mode", - [Name,Mode1], State); - true -> - ok - end, - - %% kill the handler if it can't keep up with the load - kill_if_choked(Name, QLen, Mem, State), - - if Mode1 == flush -> - flush(Name, QLen, T1, State1); - true -> - write(Name, Mode1, T1, Bin, CallOrCast, State1) - end. - -%% this clause is called by do_log/3 after an overload check -%% has been performed, where QLen > FlushQLen -flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> - %% flush messages in the mailbox (a limited number in - %% order to not cause long delays) - NewFlushed = flush_log_events(?FLUSH_MAX_N), - - %% write info in log about flushed messages +notify({mode_change,Mode0,Mode1},#{id:=Name}=State) -> + log_handler_info(Name,"Handler ~p switched from ~p to ~p mode", + [Name,Mode0,Mode1], State); +notify({flushed,Flushed},#{id:=Name}=State) -> log_handler_info(Name, "Handler ~p flushed ~w log events", - [Name,NewFlushed], State), - - %% because of the receive loop when flushing messages, the - %% handler will be scheduled out often and the mailbox could - %% grow very large, so we'd better check the queue again here - {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(Name,{max_qlen,_QLen1}), - - %% Add 1 for the current log event - ?observe(Name,{flushed,NewFlushed+1}), - - State1 = ?update_max_time(?diff_time(T1,_T0),State), - State2 = ?update_max_qlen(_QLen1,State1), - {dropped,?update_other(flushed,FLUSHED,NewFlushed, - State2#{mode => ?set_mode(ModeTab,async), - last_qlen => 0, - last_log_ts => T1})}. - -%% this clause is called to write to file -write(Name, Mode, T1, Bin, _CallOrCast, - State = #{module := Module, - handler_state := HandlerState, - mode_tab := ModeTab, - ctrl_sync_count := CtrlSync, - last_qlen := LastQLen, - last_log_ts := T0}) -> - %% check if we need to limit the number of writes - %% during a burst of log events - {DoWrite,State1} = limit_burst(State), - - %% only log synhrounously every ?CONTROLLER_SYNC_INTERVAL time, to - %% give the handler time between writes so it can keep up with - %% incoming messages - {Result,LastQLen1,HandlerState1} = - if DoWrite, CtrlSync == 0 -> - ?observe(Name,{_CallOrCast,1}), - {_,HS1} = Module:write(Name, sync, Bin, HandlerState), - {ok,element(2, process_info(self(), message_queue_len)),HS1}; - DoWrite -> - ?observe(Name,{_CallOrCast,1}), - {_,HS1} = Module:write(Name, async, Bin, HandlerState), - {ok,LastQLen,HS1}; - not DoWrite -> - ?observe(Name,{flushed,1}), - {dropped,LastQLen,HandlerState} - end, - - %% Check if the time since the previous log event is long enough - - %% and the queue length small enough - to assume the mailbox has - %% been emptied, and if so, do filesync operation and reset mode to - %% async. Note that this is the best we can do to detect an idle - %% handler without setting a timer after each log call/cast. If the - %% time between two consecutive log events is fast and no new - %% event comes in after the last one, idle state won't be detected! - Time = ?diff_time(T1,T0), - State2 = - if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso - (Time > ?IDLE_DETECT_TIME_USEC) -> - {_,HS2} = Module:filesync(Name,async,HandlerState), - State1#{mode => ?change_mode(ModeTab, Mode, async), - burst_msg_count => 0, - handler_state => HS2}; - true -> - State1#{mode => Mode, handler_state => HandlerState1} - end, - State3 = ?update_calls_or_casts(_CallOrCast,1,State2), - State4 = ?update_max_qlen(LastQLen1,State3), - State5 = - ?update_max_time(Time, - State4#{last_qlen := LastQLen1, - last_log_ts => T1, - last_op => write, - ctrl_sync_count => - if CtrlSync==0 -> ?CONTROLLER_SYNC_INTERVAL; - true -> CtrlSync-1 - end}), - {Result,State5}. + [Name,Flushed], State); +notify(restart,#{id:=Name}=State) -> + log_handler_info(Name, "Handler ~p restarted", [Name], State); +notify(idle,#{id:=Name,module:=Module,handler_state:=HandlerState}=State) -> + {_,HS} = Module:filesync(Name,async,HandlerState), + State#{handler_state=>HS, last_op=>sync}. log_handler_info(Name, Format, Args, #{module:=Module, - formatter:=Formatter, - handler_state:=HandlerState}) -> - Config = #{formatter=>Formatter}, - Meta = #{time=>erlang:system_time(microsecond)}, + handler_state:=HandlerState}=State) -> + Config = + case logger:get_handler_config(Name) of + {ok,Conf} -> Conf; + _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} + end, + Meta = #{time=>logger:timestamp()}, Bin = log_to_binary(#{level => notice, msg => {Format,Args}, meta => Meta}, Config), - _ = Module:write(Name, async, Bin, HandlerState), - ok. + {_,HS} = Module:write(Name, async, Bin, HandlerState), + State#{handler_state=>HS, last_op=>write}. %%%----------------------------------------------------------------- %%% Convert log data on any form to binary @@ -540,42 +416,8 @@ string_to_binary(String) -> %%%----------------------------------------------------------------- %%% Check that the configuration term is valid check_config(Config) when is_map(Config) -> - case check_common_config(maps:to_list(Config)) of - ok -> - case overload_levels_ok(Config) of - true -> - ok; - false -> - Faulty = maps:with([sync_mode_qlen, - drop_mode_qlen, - flush_qlen],Config), - {error,{invalid_levels,Faulty}} - end; - Error -> - Error - end. + check_common_config(maps:to_list(Config)). -check_common_config([{sync_mode_qlen,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{drop_mode_qlen,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{flush_qlen,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{burst_limit_enable,Bool}|Config]) when is_boolean(Bool) -> - check_common_config(Config); -check_common_config([{burst_limit_max_count,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{burst_limit_window_time,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{overload_kill_enable,Bool}|Config]) when is_boolean(Bool) -> - check_common_config(Config); -check_common_config([{overload_kill_qlen,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{overload_kill_mem_size,N}|Config]) when is_integer(N) -> - check_common_config(Config); -check_common_config([{overload_kill_restart_after,NorA}|Config]) - when is_integer(NorA); NorA == infinity -> - check_common_config(Config); check_common_config([{filesync_repeat_interval,NorA}|Config]) when is_integer(NorA); NorA == no_repeat -> check_common_config(Config); @@ -585,156 +427,7 @@ check_common_config([]) -> ok. get_default_config() -> - #{sync_mode_qlen => ?SYNC_MODE_QLEN, - drop_mode_qlen => ?DROP_MODE_QLEN, - flush_qlen => ?FLUSH_QLEN, - burst_limit_enable => ?BURST_LIMIT_ENABLE, - burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, - burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, - overload_kill_enable => ?OVERLOAD_KILL_ENABLE, - overload_kill_qlen => ?OVERLOAD_KILL_QLEN, - overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, - overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. - -%%%----------------------------------------------------------------- -%%% Overload Protection -call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> - %% If the handler process is getting overloaded, the log event - %% will be synchronous instead of asynchronous (slows down the - %% logging tempo of a process doing lots of logging. If the - %% handler is choked, drop mode is set and no event will be sent. - try ?get_mode(ModeTab) of - async -> - gen_server:cast(HandlerPid, {log,Bin}); - sync -> - case call(HandlerPid, {log,Bin}) of - ok -> - ok; - _Other -> - %% dropped or {error,handler_busy} - ?observe(_Name,{dropped,1}), - ok - end; - drop -> - ?observe(_Name,{dropped,1}) - catch - %% if the ETS table doesn't exist (maybe because of a - %% handler restart), we can only drop the event - _:_ -> ?observe(_Name,{dropped,1}) - end, - ok. - -set_restart_flag(#{id := Name, module := Module} = State) -> - log_handler_info(Name, "Handler ~p overloaded and stopping", [Name], State), - Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), - spawn(fun() -> - register(Flag, self()), - timer:sleep(infinity) - end), - ok. - -unset_restart_flag(#{id := Name, module := Module} = State) -> - Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), - case whereis(Flag) of - undefined -> - ok; - Pid -> - exit(Pid, kill), - log_handler_info(Name, "Handler ~p restarted", [Name], State) - end. - -check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, - sync_mode_qlen := SyncModeQLen, - drop_mode_qlen := DropModeQLen, - flush_qlen := FlushQLen}) -> - {_,Mem} = process_info(self(), memory), - ?observe(_Name,{max_mem,Mem}), - {_,QLen} = process_info(self(), message_queue_len), - ?observe(_Name,{max_qlen,QLen}), - %% When the handler process gets scheduled in, it's impossible - %% to predict the QLen. We could jump "up" arbitrarily from say - %% async to sync, async to drop, sync to flush, etc. However, when - %% the handler process manages the log events (without flushing), - %% one after the other, we will move "down" from drop to sync and - %% from sync to async. This way we don't risk getting stuck in - %% drop or sync mode with an empty mailbox. - {Mode1,_NewDrops,_NewFlushes} = - if - QLen >= FlushQLen -> - {flush, 0,1}; - QLen >= DropModeQLen -> - %% Note that drop mode will force log events to - %% be dropped on the client side (never sent get to - %% the handler). - IncDrops = if Mode == drop -> 0; true -> 1 end, - {?change_mode(ModeTab, Mode, drop), IncDrops,0}; - QLen >= SyncModeQLen -> - {?change_mode(ModeTab, Mode, sync), 0,0}; - true -> - {?change_mode(ModeTab, Mode, async), 0,0} - end, - State1 = ?update_other(drops,DROPS,_NewDrops,State), - {Mode1, QLen, Mem, - ?update_other(flushes,FLUSHES,_NewFlushes, - State1#{last_qlen => QLen})}. - -limit_burst(#{burst_limit_enable := false}=State) -> - {true,State}; -limit_burst(#{burst_win_ts := BurstWinT0, - burst_msg_count := BurstMsgCount, - burst_limit_window_time := BurstLimitWinTime, - burst_limit_max_count := BurstLimitMaxCnt} = State) -> - if (BurstMsgCount >= BurstLimitMaxCnt) -> - %% the limit for allowed messages has been reached - BurstWinT1 = ?timestamp(), - case ?diff_time(BurstWinT1,BurstWinT0) of - BurstCheckTime when BurstCheckTime < (BurstLimitWinTime*1000) -> - %% we're still within the burst time frame - {false,?update_other(burst_drops,BURSTS,1,State)}; - _BurstCheckTime -> - %% burst time frame passed, reset counters - {true,State#{burst_win_ts => BurstWinT1, - burst_msg_count => 0}} - end; - true -> - %% the limit for allowed messages not yet reached - {true,State#{burst_win_ts => BurstWinT0, - burst_msg_count => BurstMsgCount+1}} - end. - -kill_if_choked(Name, QLen, Mem, State = #{overload_kill_enable := KillIfOL, - overload_kill_qlen := OLKillQLen, - overload_kill_mem_size := OLKillMem}) -> - if KillIfOL andalso - ((QLen > OLKillQLen) orelse (Mem > OLKillMem)) -> - set_restart_flag(State), - exit({shutdown,{overloaded,Name,QLen,Mem}}); - true -> - ok - end. - -flush_log_events(Limit) -> - process_flag(priority, high), - Flushed = flush_log_events(0, Limit), - process_flag(priority, normal), - Flushed. - -flush_log_events(Limit, Limit) -> - Limit; -flush_log_events(N, Limit) -> - %% flush log events but leave other events, such as - %% filesync, info and change_config, so that these - %% have a chance to be processed even under heavy load - receive - {'$gen_cast',{log,_}} -> - flush_log_events(N+1, Limit); - {'$gen_call',{Pid,MRef},{log,_}} -> - Pid ! {MRef, dropped}, - flush_log_events(N+1, Limit) - after - 0 -> N - end. + #{filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. set_repeated_filesync(#{filesync_repeat_interval:=FSyncInt} = State) when is_integer(FSyncInt) -> @@ -752,51 +445,12 @@ cancel_repeated_filesync(State) -> error -> State end. - -stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, - #{overload_kill_restart_after := RestartAfter}) -> - %% If we're terminating because of an overload situation (see - %% kill_if_choked/4), we need to remove the handler and set a - %% restart timer. A separate process must perform this in order to - %% avoid deadlock. - HandlerPid = self(), - ConfigResult = logger:get_handler_config(Name), - RemoveAndRestart = - fun() -> - MRef = erlang:monitor(process, HandlerPid), - receive - {'DOWN',MRef,_,_,_} -> - ok - after 30000 -> - error_notify(Reason), - exit(HandlerPid, kill) - end, - case ConfigResult of - {ok,#{module:=HMod}=HConfig0} when is_integer(RestartAfter) -> - _ = logger:remove_handler(Name), - HConfig = try HMod:filter_config(HConfig0) - catch _:_ -> HConfig0 - end, - _ = timer:apply_after(RestartAfter, logger, add_handler, - [Name,HMod,HConfig]); - {ok,_} -> - _ = logger:remove_handler(Name); - {error,CfgReason} when is_integer(RestartAfter) -> - error_notify({Name,restart_impossible,CfgReason}); - {error,_} -> - ok - end - end, - spawn(RemoveAndRestart), - ok; -stop_or_restart(_Name, _Reason, _State) -> - ok. - -overload_levels_ok(HandlerConfig) -> - SMQL = maps:get(sync_mode_qlen, HandlerConfig, ?SYNC_MODE_QLEN), - DMQL = maps:get(drop_mode_qlen, HandlerConfig, ?DROP_MODE_QLEN), - FQL = maps:get(flush_qlen, HandlerConfig, ?FLUSH_QLEN), - (DMQL > 1) andalso (SMQL =< DMQL) andalso (DMQL =< FQL). - error_notify(Term) -> ?internal_log(error, Term). + +maybe_set_repeated_filesync(_Olp, + #{filesync_repeat_interval:=FSyncInt}, + #{filesync_repeat_interval:=FSyncInt}) -> + ok; +maybe_set_repeated_filesync(Olp,_,#{filesync_repeat_interval:=FSyncInt}) -> + logger_olp:cast(Olp,{set_repeated_filesync,FSyncInt}). diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index 261b0a6246..004a61d9d9 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -1,50 +1,22 @@ - -%%%----------------------------------------------------------------- -%%% Overload protection configuration - -%%! *** NOTE *** -%%! It's important that: -%%! SYNC_MODE_QLEN =< DROP_MODE_QLEN =< FLUSH_QLEN -%%! and that DROP_MODE_QLEN >= 2. -%%! Otherwise the handler could end up in drop mode with no new -%%! log requests to process. This would cause all future requests -%%! to be dropped (no switch to async mode would ever take place). - -%% This specifies the message_queue_len value where the log -%% requests switch from asynchronous casts to synchronous calls. --define(SYNC_MODE_QLEN, 10). -%% Above this message_queue_len, log requests will be dropped, -%% i.e. no log requests get sent to the handler process. --define(DROP_MODE_QLEN, 200). -%% Above this message_queue_len, the handler process will flush -%% its mailbox and only leave this number of messages in it. --define(FLUSH_QLEN, 1000). - -%% Never flush more than this number of messages in one go, -%% or the handler will be unresponsive for seconds (keep this -%% number as large as possible or the mailbox could grow large). --define(FLUSH_MAX_N, 5000). - -%% BURST_LIMIT_MAX_COUNT is the max number of log requests allowed -%% to be written within a BURST_LIMIT_WINDOW_TIME time frame. --define(BURST_LIMIT_ENABLE, true). --define(BURST_LIMIT_MAX_COUNT, 500). --define(BURST_LIMIT_WINDOW_TIME, 1000). - -%% This enables/disables the feature to automatically get the -%% handler terminated if it gets too loaded (and can't keep up). --define(OVERLOAD_KILL_ENABLE, false). -%% If the message_queue_len goes above this size even after -%% flushing has been performed, the handler is terminated. --define(OVERLOAD_KILL_QLEN, 20000). -%% If the memory usage exceeds this level --define(OVERLOAD_KILL_MEM_SIZE, 3000000). - -%% This is the default time that the handler will wait before -%% restarting and accepting new requests. The value 'infinity' -%% disables restarts. --define(OVERLOAD_KILL_RESTART_AFTER, 5000). -%%-define(OVERLOAD_KILL_RESTART_AFTER, infinity). +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2015. 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% +%% %% The handler sends asynchronous write requests to the process %% controlling the i/o device, but every once in this interval @@ -65,12 +37,6 @@ -define(FILESYNC_REPEAT_INTERVAL, 5000). %%-define(FILESYNC_REPEAT_INTERVAL, no_repeat). -%% This is the time after last message received that we think/hope -%% that the handler has an empty mailbox (no new log request has -%% come in). --define(IDLE_DETECT_TIME_MSEC, 100). --define(IDLE_DETECT_TIME_USEC, 100000). - %% Default disk log option values -define(DISK_LOG_TYPE, wrap). -define(DISK_LOG_MAX_NO_FILES, 10). @@ -83,43 +49,6 @@ list_to_atom(lists:concat([MODULE,"_",Name]))). %%%----------------------------------------------------------------- -%%% Overload protection macros - --define(timestamp(), erlang:monotonic_time(microsecond)). - --define(get_mode(Tid), - case ets:lookup(Tid, mode) of - [{mode,M}] -> M; - _ -> async - end). - --define(set_mode(Tid, M), - begin ets:insert(Tid, {mode,M}), M end). - --define(change_mode(Tid, M0, M1), - if M0 == M1 -> - M0; - true -> - ets:insert(Tid, {mode,M1}), - M1 - end). - --define(min(X1, X2), - if X2 == undefined -> X1; - X2 < X1 -> X2; - true -> X1 - end). - --define(max(X1, X2), - if - X2 == undefined -> X1; - X2 > X1 -> X2; - true -> X1 - end). - --define(diff_time(OS_T1, OS_T0), OS_T1-OS_T0). - -%%%----------------------------------------------------------------- %%% The test hook macros make it possible to observe and manipulate %%% internal handler functionality. When enabled, these macros will %%% slow down execution and therefore should not be include in code @@ -183,7 +112,6 @@ [{_,ERROR}] -> ERROR catch _:_ -> disk_log:sync(LOG) end). - -define(DEFAULT_CALL_TIMEOUT, 5000). -else. % DEFAULTS! -define(TEST_HOOKS_TAB, undefined). @@ -196,68 +124,4 @@ -define(file_datasync(DEVICE), file:datasync(DEVICE)). -define(disk_log_write(LOG, MODE, DATA), disk_log_write(LOG, MODE, DATA)). -define(disk_log_sync(LOG), disk_log:sync(LOG)). - -define(DEFAULT_CALL_TIMEOUT, 10000). --endif. - -%%%----------------------------------------------------------------- -%%% These macros enable statistics counters in the state of the -%%% handler which is useful for analysing the overload protection -%%% behaviour. These counters should not be included in code to be -%%% officially released (as some counters will grow very large -%%% over time). - -%%-define(SAVE_STATS, true). --ifdef(SAVE_STATS). - -define(merge_with_stats(STATE), - STATE#{flushes => 0, flushed => 0, drops => 0, - burst_drops => 0, casts => 0, calls => 0, - max_qlen => 0, max_time => 0}). - - -define(update_max_qlen(QLEN, STATE), - begin #{max_qlen := QLEN0} = STATE, - STATE#{max_qlen => ?max(QLEN0,QLEN)} end). - - -define(update_calls_or_casts(CALL_OR_CAST, INC, STATE), - case CALL_OR_CAST of - cast -> - #{casts := CASTS0} = STATE, - STATE#{casts => CASTS0+INC}; - call -> - #{calls := CALLS0} = STATE, - STATE#{calls => CALLS0+INC} - end). - - -define(update_max_time(TIME, STATE), - begin #{max_time := TIME0} = STATE, - STATE#{max_time => ?max(TIME0,TIME)} end). - - -define(update_other(OTHER, VAR, INCVAL, STATE), - begin #{OTHER := VAR} = STATE, - STATE#{OTHER => VAR+INCVAL} end). - --else. % DEFAULT! - -define(merge_with_stats(STATE), STATE). - -define(update_max_qlen(_QLEN, STATE), STATE). - -define(update_calls_or_casts(_CALL_OR_CAST, _INC, STATE), STATE). - -define(update_max_time(_TIME, STATE), STATE). - -define(update_other(_OTHER, _VAR, _INCVAL, STATE), STATE). --endif. - -%%%----------------------------------------------------------------- -%%% These macros enable callbacks that make it possible to analyse -%%% the overload protection behaviour from outside the handler -%%% process (including dropped requests on the client side). -%%% An external callback module (?OBSERVER_MOD) is required which -%%% is not part of the kernel application. For this reason, these -%%% callbacks should not be included in code to be officially released. - -%%-define(OBSERVER_MOD, logger_test). --ifdef(OBSERVER_MOD). - -define(start_observation(NAME), ?OBSERVER:start_observation(NAME)). - -define(observe(NAME,EVENT), ?OBSERVER:observe(NAME,EVENT)). - --else. % DEFAULT! - -define(start_observation(_NAME), ok). - -define(observe(_NAME,_EVENT), ok). -endif. -%%! <--- diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index d96a4ac78b..e53922e5d3 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -19,6 +19,7 @@ %% -include_lib("kernel/include/logger.hrl"). -define(LOGGER_TABLE,logger). +-define(PROXY_KEY,'$proxy_config$'). -define(PRIMARY_KEY,'$primary_config$'). -define(HANDLER_KEY,'$handler_config$'). -define(LOGGER_META_KEY,'$logger_metadata$'). @@ -40,12 +41,14 @@ -define(DEFAULT_LOGGER_CALL_TIMEOUT, infinity). --define(LOG_INTERNAL(Level,Report), +-define(LOG_INTERNAL(Level,Report),?DO_LOG_INTERNAL(Level,[Report])). +-define(LOG_INTERNAL(Level,Format,Args),?DO_LOG_INTERNAL(Level,[Format,Args])). +-define(DO_LOG_INTERNAL(Level,Data), case logger:allow(Level,?MODULE) of true -> %% Spawn this to avoid deadlocks - _ = spawn(logger,macro_log,[?LOCATION,Level,Report, - logger:add_default_metadata(#{})]), + _ = spawn(logger,macro_log,[?LOCATION,Level|Data]++ + [logger:add_default_metadata(#{})]), ok; false -> ok diff --git a/lib/kernel/src/logger_olp.erl b/lib/kernel/src/logger_olp.erl new file mode 100644 index 0000000000..009280a9c9 --- /dev/null +++ b/lib/kernel/src/logger_olp.erl @@ -0,0 +1,626 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_olp). +-behaviour(gen_server). + +-include("logger_olp.hrl"). +-include("logger_internal.hrl"). + +%% API +-export([start_link/4, load/2, info/1, reset/1, stop/1, restart/1, + set_opts/2, get_opts/1, get_default_opts/0, get_pid/1, + call/2, cast/2, get_ref/0, get_ref/1]). + +%% gen_server and proc_lib callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(OPT_KEYS,[sync_mode_qlen, + drop_mode_qlen, + flush_qlen, + burst_limit_enable, + burst_limit_max_count, + burst_limit_window_time, + overload_kill_enable, + overload_kill_qlen, + overload_kill_mem_size, + overload_kill_restart_after]). + +-export_type([olp_ref/0, options/0]). + +-opaque olp_ref() :: {atom(),pid(),ets:tid()}. + +-type options() :: logger:olp_config(). + +%%%----------------------------------------------------------------- +%%% API + +-spec start_link(Name,Module,Args,Options) -> {ok,Pid,Olp} | {error,Reason} when + Name :: atom(), + Module :: module(), + Args :: term(), + Options :: options(), + Pid :: pid(), + Olp :: olp_ref(), + Reason :: term(). +start_link(Name,Module,Args,Options0) when is_map(Options0) -> + Options = maps:merge(get_default_opts(),Options0), + case check_opts(Options) of + ok -> + proc_lib:start_link(?MODULE,init,[[Name,Module,Args,Options]]); + Error -> + Error + end. + +-spec load(Olp, Msg) -> ok when + Olp :: olp_ref(), + Msg :: term(). +load({_Name,Pid,ModeRef},Msg) -> + %% If the process is getting overloaded, the message will be + %% synchronous instead of asynchronous (slows down the tempo of a + %% process causing much load). If the process is choked, drop mode + %% is set and no message is sent. + try ?get_mode(ModeRef) of + async -> + gen_server:cast(Pid, {'$olp_load',Msg}); + sync -> + case call(Pid, {'$olp_load',Msg}) of + ok -> + ok; + _Other -> + %% dropped or {error,busy} + ?observe(_Name,{dropped,1}), + ok + end; + drop -> + ?observe(_Name,{dropped,1}) + catch + %% if the ETS table doesn't exist (maybe because of a + %% process restart), we can only drop the event + _:_ -> ?observe(_Name,{dropped,1}) + end, + ok. + +-spec info(Olp) -> map() | {error, busy} when + Olp :: atom() | pid() | olp_ref(). +info(Olp) -> + call(Olp, info). + +-spec reset(Olp) -> ok | {error, busy} when + Olp :: atom() | pid() | olp_ref(). +reset(Olp) -> + call(Olp, reset). + +-spec stop(Olp) -> ok when + Olp :: atom() | pid() | olp_ref(). +stop({_Name,Pid,_ModRef}) -> + stop(Pid); +stop(Pid) -> + _ = gen_server:call(Pid, stop), + ok. + +-spec set_opts(Olp, Opts) -> ok | {error,term()} | {error, busy} when + Olp :: atom() | pid() | olp_ref(), + Opts :: options(). +set_opts(Olp, Opts) -> + call(Olp, {set_opts,Opts}). + +-spec get_opts(Olp) -> options() | {error, busy} when + Olp :: atom() | pid() | olp_ref(). +get_opts(Olp) -> + call(Olp, get_opts). + +-spec get_default_opts() -> options(). +get_default_opts() -> + #{sync_mode_qlen => ?SYNC_MODE_QLEN, + drop_mode_qlen => ?DROP_MODE_QLEN, + flush_qlen => ?FLUSH_QLEN, + burst_limit_enable => ?BURST_LIMIT_ENABLE, + burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable => ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen => ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER}. + +-spec restart(fun(() -> any())) -> ok. +restart(Fun) -> + Result = + try Fun() + catch C:R:S -> + {error,{restart_failed,Fun,C,R,S}} + end, + ?LOG_INTERNAL(debug,[{logger_olp,restart}, + {result,Result}]), + ok. + +-spec get_ref() -> olp_ref(). +get_ref() -> + get(olp_ref). + +-spec get_ref(PidOrName) -> olp_ref() | {error, busy} when + PidOrName :: pid() | atom(). +get_ref(PidOrName) -> + call(PidOrName,get_ref). + +-spec get_pid(olp_ref()) -> pid(). +get_pid({_Name,Pid,_ModeRef}) -> + Pid. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([Name,Module,Args,Options]) -> + register(Name, self()), + process_flag(message_queue_data, off_heap), + + ?start_observation(Name), + + try ets:new(Name, [public]) of + ModeRef -> + OlpRef = {Name,self(),ModeRef}, + put(olp_ref,OlpRef), + try Module:init(Args) of + {ok,CBState} -> + ?set_mode(ModeRef, async), + T0 = ?timestamp(), + proc_lib:init_ack({ok,self(),OlpRef}), + %% Storing options in state to avoid copying + %% (sending) the option data with each message + State0 = ?merge_with_stats( + Options#{id => Name, + idle=> true, + module => Module, + mode_ref => ModeRef, + mode => async, + last_qlen => 0, + last_load_ts => T0, + burst_win_ts => T0, + burst_msg_count => 0, + cb_state => CBState}), + State = reset_restart_flag(State0), + gen_server:enter_loop(?MODULE, [], State); + Error -> + _ = ets:delete(ModeRef), + unregister(Name), + proc_lib:init_ack(Error) + catch + _:Error -> + _ = ets:delete(ModeRef), + unregister(Name), + proc_lib:init_ack(Error) + end + catch + _:Error -> + unregister(Name), + proc_lib:init_ack(Error) + end. + +%% This is the synchronous load event. +handle_call({'$olp_load', Msg}, _From, State) -> + {Result,State1} = do_load(Msg, call, State#{idle=>false}), + %% Result == ok | dropped + reply_return(Result,State1); + +handle_call(get_ref,_From,#{id:=Name,mode_ref:=ModeRef}=State) -> + reply_return({Name,self(),ModeRef},State); + +handle_call({set_opts,Opts0},_From,State) -> + Opts = maps:merge(maps:with(?OPT_KEYS,State),Opts0), + case check_opts(Opts) of + ok -> + reply_return(ok, maps:merge(State,Opts)); + Error -> + reply_return(Error, State) + end; + +handle_call(get_opts,_From,State) -> + reply_return(maps:with(?OPT_KEYS,State), State); + +handle_call(info, _From, State) -> + reply_return(State, State); + +handle_call(reset, _From, #{module:=Module,cb_state:=CBState}=State) -> + State1 = ?merge_with_stats(State), + CBState1 = try_callback_call(Module,reset_state,[CBState],CBState), + reply_return(ok, State1#{idle => true, + last_qlen => 0, + last_load_ts => ?timestamp(), + cb_state => CBState1}); + +handle_call(stop, _From, State) -> + {stop, {shutdown,stopped}, ok, State}; + +handle_call(Msg, From, #{module:=Module,cb_state:=CBState}=State) -> + case try_callback_call(Module,handle_call,[Msg, From, CBState]) of + {reply,Reply,CBState1} -> + reply_return(Reply,State#{cb_state=>CBState1}); + {noreply,CBState1} -> + noreply_return(State#{cb_state=>CBState1}); + {stop, Reason, Reply, CBState1} -> + {stop, Reason, Reply, State#{cb_state=>CBState1}}; + {stop, Reason, CBState1} -> + {stop, Reason, State#{cb_state=>CBState1}} + end. + +%% This is the asynchronous load event. +handle_cast({'$olp_load', Msg}, State) -> + {_Result,State1} = do_load(Msg, cast, State#{idle=>false}), + noreply_return(State1); + +handle_cast(Msg, #{module:=Module, cb_state:=CBState} = State) -> + case try_callback_call(Module,handle_cast,[Msg, CBState]) of + {noreply,CBState1} -> + noreply_return(State#{cb_state=>CBState1}); + {stop, Reason, CBState1} -> + {stop, Reason, State#{cb_state=>CBState1}} + end. + +handle_info(timeout, #{mode_ref:=_ModeRef, mode:=Mode} = State) -> + State1 = notify(idle,State), + State2 = maybe_notify_mode_change(async,State1), + {noreply, State2#{idle => true, + mode => ?change_mode(_ModeRef, Mode, async), + burst_msg_count => 0}}; +handle_info(Msg, #{module := Module, cb_state := CBState} = State) -> + case try_callback_call(Module,handle_info,[Msg, CBState]) of + {noreply,CBState1} -> + noreply_return(State#{cb_state=>CBState1}); + {stop, Reason, CBState1} -> + {stop, Reason, State#{cb_state=>CBState1}}; + {load,CBState1} -> + {_,State1} = do_load(Msg, cast, State#{idle=>false, + cb_state=>CBState1}), + noreply_return(State1) + end. + +terminate({shutdown,{overloaded,_QLen,_Mem}}, + #{id:=Name, module := Module, cb_state := CBState, + overload_kill_restart_after := RestartAfter} = State) -> + %% We're terminating because of an overload situation (see + %% kill_if_choked/3). + unregister(Name), %%!!!! to avoid error printout of callback crashed on stop + case try_callback_call(Module,terminate,[overloaded,CBState],ok) of + {ok,Fun} when is_function(Fun,0), is_integer(RestartAfter) -> + set_restart_flag(State), + _ = timer:apply_after(RestartAfter,?MODULE,restart,[Fun]), + ok; + _ -> + ok + end; +terminate(Reason, #{id:=Name, module:=Module, cb_state:=CBState}) -> + _ = try_callback_call(Module,terminate,[Reason,CBState],ok), + unregister(Name), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + +%%%----------------------------------------------------------------- +%%% Internal functions +-spec call(Olp, term()) -> term() | {error,busy} when + Olp :: atom() | pid() | olp_ref(). +call({_Name, Pid, _ModeRef},Msg) -> + call(Pid, Msg); +call(Server, Msg) -> + try + gen_server:call(Server, Msg) + catch + _:{timeout,_} -> {error,busy} + end. + +-spec cast(olp_ref(),term()) -> ok. +cast({_Name, Pid, _ModeRef},Msg) -> + gen_server:cast(Pid, Msg). + +%% check for overload between every event (and set Mode to async, +%% sync or drop accordingly), but never flush the whole mailbox +%% before LogWindowSize events have been handled +do_load(Msg, CallOrCast, State) -> + T1 = ?timestamp(), + State1 = ?update_time(T1,State), + + %% check if the process is getting overloaded, or if it's + %% recovering from overload (the check must be done for each + %% event to react quickly to large bursts of events and + %% to ensure that the handler can never end up in drop mode + %% with an empty mailbox, which would stop operation) + {Mode1,QLen,Mem,State2} = check_load(State1), + + %% kill the handler if it can't keep up with the load + kill_if_choked(QLen, Mem, State2), + + if Mode1 == flush -> + flush(T1, State2); + true -> + handle_load(Mode1, T1, Msg, CallOrCast, State2) + end. + +%% this function is called by do_load/3 after an overload check +%% has been performed, where QLen > FlushQLen +flush(T1, State=#{id := _Name, mode := Mode, last_load_ts := _T0, mode_ref := ModeRef}) -> + %% flush load messages in the mailbox (a limited number in order + %% to not cause long delays) + NewFlushed = flush_load(?FLUSH_MAX_N), + + %% write info in log about flushed messages + State1=notify({flushed,NewFlushed},State), + + %% because of the receive loop when flushing messages, the + %% handler will be scheduled out often and the mailbox could + %% grow very large, so we'd better check the queue again here + {_,QLen1} = process_info(self(), message_queue_len), + ?observe(_Name,{max_qlen,QLen1}), + + %% Add 1 for the current log event + ?observe(_Name,{flushed,NewFlushed+1}), + + State2 = ?update_max_time(?diff_time(T1,_T0),State1), + State3 = ?update_max_qlen(QLen1,State2), + State4 = maybe_notify_mode_change(async,State3), + {dropped,?update_other(flushed,FLUSHED,NewFlushed, + State4#{mode => ?change_mode(ModeRef,Mode,async), + last_qlen => QLen1, + last_load_ts => T1})}. + +%% this function is called to actually handle the message +handle_load(Mode, T1, Msg, _CallOrCast, + State = #{id := _Name, + module := Module, + cb_state := CBState, + last_qlen := LastQLen, + last_load_ts := _T0}) -> + %% check if we need to limit the number of writes + %% during a burst of log events + {DoWrite,State1} = limit_burst(State), + + {Result,LastQLen1,CBState1} = + if DoWrite -> + ?observe(_Name,{_CallOrCast,1}), + CBS = try_callback_call(Module,handle_load,[Msg,CBState]), + {ok,element(2, process_info(self(), message_queue_len)),CBS}; + true -> + ?observe(_Name,{flushed,1}), + {dropped,LastQLen,CBState} + end, + State2 = State1#{cb_state=>CBState1}, + + State3 = State2#{mode => Mode}, + State4 = ?update_calls_or_casts(_CallOrCast,1,State3), + State5 = ?update_max_qlen(LastQLen1,State4), + State6 = + ?update_max_time(?diff_time(T1,_T0), + State5#{last_qlen := LastQLen1, + last_load_ts => T1}), + State7 = case Result of + ok -> + S = ?update_freq(T1,State6), + ?update_other(writes,WRITES,1,S); + _ -> + State6 + end, + {Result,State7}. + + +%%%----------------------------------------------------------------- +%%% Check that the options are valid +check_opts(Options) when is_map(Options) -> + case do_check_opts(maps:to_list(Options)) of + ok -> + case overload_levels_ok(Options) of + true -> + ok; + false -> + Faulty = maps:with([sync_mode_qlen, + drop_mode_qlen, + flush_qlen],Options), + {error,{invalid_olp_levels,Faulty}} + end; + {error,Key,Value} -> + {error,{invalid_olp_config,#{Key=>Value}}} + end. + +do_check_opts([{sync_mode_qlen,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{drop_mode_qlen,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{flush_qlen,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{burst_limit_enable,Bool}|Options]) when is_boolean(Bool) -> + do_check_opts(Options); +do_check_opts([{burst_limit_max_count,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{burst_limit_window_time,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{overload_kill_enable,Bool}|Options]) when is_boolean(Bool) -> + do_check_opts(Options); +do_check_opts([{overload_kill_qlen,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{overload_kill_mem_size,N}|Options]) when is_integer(N) -> + do_check_opts(Options); +do_check_opts([{overload_kill_restart_after,NorA}|Options]) + when is_integer(NorA); NorA == infinity -> + do_check_opts(Options); +do_check_opts([{Key,Value}|_]) -> + {error,Key,Value}; +do_check_opts([]) -> + ok. + +set_restart_flag(#{id := Name, module := Module}) -> + Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), + spawn(fun() -> + register(Flag, self()), + timer:sleep(infinity) + end), + ok. + +reset_restart_flag(#{id := Name, module := Module} = State) -> + Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), + case whereis(Flag) of + undefined -> + State; + Pid -> + exit(Pid, kill), + notify(restart,State) + end. + +check_load(State = #{id:=_Name, mode_ref := ModeRef, mode := Mode, + sync_mode_qlen := SyncModeQLen, + drop_mode_qlen := DropModeQLen, + flush_qlen := FlushQLen}) -> + {_,Mem} = process_info(self(), memory), + ?observe(_Name,{max_mem,Mem}), + {_,QLen} = process_info(self(), message_queue_len), + ?observe(_Name,{max_qlen,QLen}), + %% When the handler process gets scheduled in, it's impossible + %% to predict the QLen. We could jump "up" arbitrarily from say + %% async to sync, async to drop, sync to flush, etc. However, when + %% the handler process manages the log events (without flushing), + %% one after the other, we will move "down" from drop to sync and + %% from sync to async. This way we don't risk getting stuck in + %% drop or sync mode with an empty mailbox. + {Mode1,_NewDrops,_NewFlushes} = + if + QLen >= FlushQLen -> + {flush, 0,1}; + QLen >= DropModeQLen -> + %% Note that drop mode will force load messages to + %% be dropped on the client side (never sent to + %% the olp process). + IncDrops = if Mode == drop -> 0; true -> 1 end, + {?change_mode(ModeRef, Mode, drop), IncDrops,0}; + QLen >= SyncModeQLen -> + {?change_mode(ModeRef, Mode, sync), 0,0}; + true -> + {?change_mode(ModeRef, Mode, async), 0,0} + end, + State1 = ?update_other(drops,DROPS,_NewDrops,State), + State2 = ?update_max_qlen(QLen,State1), + State3 = maybe_notify_mode_change(Mode1,State2), + {Mode1, QLen, Mem, + ?update_other(flushes,FLUSHES,_NewFlushes, + State3#{last_qlen => QLen})}. + +limit_burst(#{burst_limit_enable := false}=State) -> + {true,State}; +limit_burst(#{burst_win_ts := BurstWinT0, + burst_msg_count := BurstMsgCount, + burst_limit_window_time := BurstLimitWinTime, + burst_limit_max_count := BurstLimitMaxCnt} = State) -> + if (BurstMsgCount >= BurstLimitMaxCnt) -> + %% the limit for allowed messages has been reached + BurstWinT1 = ?timestamp(), + case ?diff_time(BurstWinT1,BurstWinT0) of + BurstCheckTime when BurstCheckTime < (BurstLimitWinTime*1000) -> + %% we're still within the burst time frame + {false,?update_other(burst_drops,BURSTS,1,State)}; + _BurstCheckTime -> + %% burst time frame passed, reset counters + {true,State#{burst_win_ts => BurstWinT1, + burst_msg_count => 0}} + end; + true -> + %% the limit for allowed messages not yet reached + {true,State#{burst_win_ts => BurstWinT0, + burst_msg_count => BurstMsgCount+1}} + end. + +kill_if_choked(QLen, Mem, #{overload_kill_enable := KillIfOL, + overload_kill_qlen := OLKillQLen, + overload_kill_mem_size := OLKillMem}) -> + if KillIfOL andalso + ((QLen > OLKillQLen) orelse (Mem > OLKillMem)) -> + exit({shutdown,{overloaded,QLen,Mem}}); + true -> + ok + end. + +flush_load(Limit) -> + process_flag(priority, high), + Flushed = flush_load(0, Limit), + process_flag(priority, normal), + Flushed. + +flush_load(Limit, Limit) -> + Limit; +flush_load(N, Limit) -> + %% flush log events but leave other events, such as info, reset + %% and stop, so that these have a chance to be processed even + %% under heavy load + receive + {'$gen_cast',{'$olp_load',_}} -> + flush_load(N+1, Limit); + {'$gen_call',{Pid,MRef},{'$olp_load',_}} -> + Pid ! {MRef, dropped}, + flush_load(N+1, Limit); + {log,_,_,_,_} -> + flush_load(N+1, Limit); + {log,_,_,_} -> + flush_load(N+1, Limit) + after + 0 -> N + end. + +overload_levels_ok(Options) -> + SMQL = maps:get(sync_mode_qlen, Options, ?SYNC_MODE_QLEN), + DMQL = maps:get(drop_mode_qlen, Options, ?DROP_MODE_QLEN), + FQL = maps:get(flush_qlen, Options, ?FLUSH_QLEN), + (DMQL > 1) andalso (SMQL =< DMQL) andalso (DMQL =< FQL). + +maybe_notify_mode_change(drop,#{mode:=Mode0}=State) + when Mode0=/=drop -> + notify({mode_change,Mode0,drop},State); +maybe_notify_mode_change(Mode1,#{mode:=drop}=State) + when Mode1==async; Mode1==sync -> + notify({mode_change,drop,Mode1},State); +maybe_notify_mode_change(_,State) -> + State. + +notify(Note,#{module:=Module,cb_state:=CBState}=State) -> + CBState1 = try_callback_call(Module,notify,[Note,CBState],CBState), + State#{cb_state=>CBState1}. + +try_callback_call(Module, Function, Args) -> + try_callback_call(Module, Function, Args, '$no_default_return'). + +try_callback_call(Module, Function, Args, DefRet) -> + try apply(Module, Function, Args) + catch + throw:R -> R; + error:undef:S when DefRet=/='$no_default_return' -> + case S of + [{Module,Function,Args,_}|_] -> + DefRet; + _ -> + erlang:raise(error,undef,S) + end + end. + +noreply_return(#{idle:=true}=State) -> + {noreply,State}; +noreply_return(#{idle:=false}=State) -> + {noreply,State,?IDLE_DETECT_TIME}. + +reply_return(Reply,#{idle:=true}=State) -> + {reply,Reply,State}; +reply_return(Reply,#{idle:=false}=State) -> + {reply,Reply,State,?IDLE_DETECT_TIME}. diff --git a/lib/kernel/src/logger_olp.hrl b/lib/kernel/src/logger_olp.hrl new file mode 100644 index 0000000000..9b4f5ebf27 --- /dev/null +++ b/lib/kernel/src/logger_olp.hrl @@ -0,0 +1,180 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2015. 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% +%% + +%%%----------------------------------------------------------------- +%%% Overload protection configuration + +%%! *** NOTE *** +%%! It's important that: +%%! SYNC_MODE_QLEN =< DROP_MODE_QLEN =< FLUSH_QLEN +%%! and that DROP_MODE_QLEN >= 2. +%%! Otherwise the process could end up in drop mode with no new +%%! log requests to process. This would cause all future requests +%%! to be dropped (no switch to async mode would ever take place). + +%% This specifies the message_queue_len value where the log +%% requests switch from asynchronous casts to synchronous calls. +-define(SYNC_MODE_QLEN, 10). +%% Above this message_queue_len, log requests will be dropped, +%% i.e. no log requests get sent to the process. +-define(DROP_MODE_QLEN, 200). +%% Above this message_queue_len, the process will flush its mailbox +%% and only leave this number of messages in it. +-define(FLUSH_QLEN, 1000). + +%% Never flush more than this number of messages in one go, or the +%% process will be unresponsive for seconds (keep this number as large +%% as possible or the mailbox could grow large). +-define(FLUSH_MAX_N, 5000). + +%% BURST_LIMIT_MAX_COUNT is the max number of log requests allowed +%% to be written within a BURST_LIMIT_WINDOW_TIME time frame. +-define(BURST_LIMIT_ENABLE, true). +-define(BURST_LIMIT_MAX_COUNT, 500). +-define(BURST_LIMIT_WINDOW_TIME, 1000). + +%% This enables/disables the feature to automatically terminate the +%% process if it gets too loaded (and can't keep up). +-define(OVERLOAD_KILL_ENABLE, false). +%% If the message_queue_len goes above this size even after +%% flushing has been performed, the process is terminated. +-define(OVERLOAD_KILL_QLEN, 20000). +%% If the memory usage exceeds this level, the process is terminated. +-define(OVERLOAD_KILL_MEM_SIZE, 3000000). + +%% This is the default time to wait before restarting and accepting +%% new requests. The value 'infinity' disables restarts. +-define(OVERLOAD_KILL_RESTART_AFTER, 5000). + +%% This is the time in milliseconds after last load message received +%% that we notify the callback about being idle. +-define(IDLE_DETECT_TIME, 100). + +%%%----------------------------------------------------------------- +%%% Overload protection macros + +-define(timestamp(), erlang:monotonic_time(microsecond)). + +-define(get_mode(Tid), + case ets:lookup(Tid, mode) of + [{mode,M}] -> M; + _ -> async + end). + +-define(set_mode(Tid, M), + begin ets:insert(Tid, {mode,M}), M end). + +-define(change_mode(Tid, M0, M1), + if M0 == M1 -> + M0; + true -> + ets:insert(Tid, {mode,M1}), + M1 + end). + +-define(max(X1, X2), + if + X2 == undefined -> X1; + X2 > X1 -> X2; + true -> X1 + end). + +-define(diff_time(OS_T1, OS_T0), OS_T1-OS_T0). + +%%%----------------------------------------------------------------- +%%% These macros enable statistics counters in the state of the +%%% process, which is useful for analysing the overload protection +%%% behaviour. These counters should not be included in code to be +%%% officially released (as some counters will grow very large over +%%% time). + +%% -define(SAVE_STATS, true). +-ifdef(SAVE_STATS). + -define(merge_with_stats(STATE), + begin + TIME = ?timestamp(), + STATE#{start => TIME, time => {TIME,0}, + flushes => 0, flushed => 0, drops => 0, + burst_drops => 0, casts => 0, calls => 0, + writes => 0, max_qlen => 0, max_time => 0, + freq => {TIME,0,0}} end). + + -define(update_max_qlen(QLEN, STATE), + begin #{max_qlen := QLEN0} = STATE, + STATE#{max_qlen => ?max(QLEN0,QLEN)} end). + + -define(update_calls_or_casts(CALL_OR_CAST, INC, STATE), + case CALL_OR_CAST of + cast -> + #{casts := CASTS0} = STATE, + STATE#{casts => CASTS0+INC}; + call -> + #{calls := CALLS0} = STATE, + STATE#{calls => CALLS0+INC} + end). + + -define(update_max_time(TIME, STATE), + begin #{max_time := TIME0} = STATE, + STATE#{max_time => ?max(TIME0,TIME)} end). + + -define(update_other(OTHER, VAR, INCVAL, STATE), + begin #{OTHER := VAR} = STATE, + STATE#{OTHER => VAR+INCVAL} end). + + -define(update_freq(TIME,STATE), + begin + case STATE of + #{freq := {START, 49, _}} -> + STATE#{freq => {TIME, 0, trunc(1000000*50/(?diff_time(TIME,START)))}}; + #{freq := {START, N, FREQ}} -> + STATE#{freq => {START, N+1, FREQ}} + end end). + + -define(update_time(TIME,STATE), + begin #{start := START} = STATE, + STATE#{time => {TIME,trunc((?diff_time(TIME,START))/1000000)}} end). + +-else. % DEFAULT! + -define(merge_with_stats(STATE), STATE). + -define(update_max_qlen(_QLEN, STATE), STATE). + -define(update_calls_or_casts(_CALL_OR_CAST, _INC, STATE), STATE). + -define(update_max_time(_TIME, STATE), STATE). + -define(update_other(_OTHER, _VAR, _INCVAL, STATE), STATE). + -define(update_freq(_TIME, STATE), STATE). + -define(update_time(_TIME, STATE), STATE). +-endif. + +%%%----------------------------------------------------------------- +%%% These macros enable callbacks that make it possible to analyse the +%%% overload protection behaviour from outside the process (including +%%% dropped requests on the client side). An external callback module +%%% (?OBSERVER_MOD) is required which is not part of the kernel +%%% application. For this reason, these callbacks should not be +%%% included in code to be officially released. + +%%-define(OBSERVER_MOD, logger_test). +-ifdef(OBSERVER_MOD). + -define(start_observation(NAME), ?OBSERVER:start_observation(NAME)). + -define(observe(NAME,EVENT), ?OBSERVER:observe(NAME,EVENT)). + +-else. % DEFAULT! + -define(start_observation(_NAME), ok). + -define(observe(_NAME,_EVENT), ok). +-endif. diff --git a/lib/kernel/src/logger_proxy.erl b/lib/kernel/src/logger_proxy.erl new file mode 100644 index 0000000000..24b293805c --- /dev/null +++ b/lib/kernel/src/logger_proxy.erl @@ -0,0 +1,165 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_proxy). + +%% API +-export([start_link/0, restart/0, log/1, child_spec/0, get_default_config/0]). + +%% logger_olp callbacks +-export([init/1, handle_load/2, handle_info/2, terminate/2, + notify/2]). + +-include("logger_internal.hrl"). + +-define(SERVER,?MODULE). + +%%%----------------------------------------------------------------- +%%% API +-spec log(RemoteLog) -> ok when + RemoteLog :: {remote,node(),LogEvent}, + LogEvent :: {log,Level,Format,Args,Meta} | + {log,Level,StringOrReport,Meta}, + Level :: logger:level(), + Format :: io:format(), + Args :: list(term()), + StringOrReport :: unicode:chardata() | logger:report(), + Meta :: logger:metadata(). +log(RemoteLog) -> + Olp = persistent_term:get(?MODULE), + case logger_olp:get_pid(Olp) =:= self() of + true -> + %% This happens when the log event comes from the + %% emulator, and the group leader is on a remote node. + _ = handle_load(RemoteLog, no_state), + ok; + false -> + logger_olp:load(Olp, RemoteLog) + end. + +%% Called by supervisor +-spec start_link() -> {ok,pid(),logger_olp:olp_ref()} | {error,term()}. +start_link() -> + %% Notice that sync_mode is only used when logging to remote node, + %% i.e. when the log/2 API function is called. + %% + %% When receiving log events from the emulator or from a remote + %% node, the log event is sent as a message to this process, and + %% thus received directly in handle_info/2. This means that the + %% mode (async/sync/drop) is not read before the message is + %% sent. Thus sync mode is never entered, and drop mode is + %% implemented by setting the system_logger flag to undefined (see + %% notify/2) + %% + %% Burst limit is disabled, since this is only a proxy and we + %% don't want to limit bursts twice (here and in the handler). + logger_olp:start_link(?SERVER,?MODULE,[],logger:get_proxy_config()). + +%% Fun used for restarting this process after it has been killed due +%% to overload (must set overload_kill_enable=>true in opts) +restart() -> + case supervisor:start_child(logger_sup, child_spec()) of + {ok,_Pid,Olp} -> + {ok,Olp}; + {error,{Reason,Ch}} when is_tuple(Ch), element(1,Ch)==child -> + {error,Reason}; + Error -> + Error + end. + +%% Called internally and by logger_sup +child_spec() -> + Name = ?SERVER, + #{id => Name, + start => {?MODULE, start_link, []}, + restart => temporary, + shutdown => 2000, + type => worker, + modules => [?MODULE]}. + +get_default_config() -> + OlpDefault = logger_olp:get_default_opts(), + OlpDefault#{sync_mode_qlen=>500, + drop_mode_qlen=>1000, + flush_qlen=>5000, + burst_limit_enable=>false}. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([]) -> + process_flag(trap_exit, true), + _ = erlang:system_flag(system_logger,self()), + persistent_term:put(?MODULE,logger_olp:get_ref()), + {ok,no_state}. + +%% Log event to send to the node where the group leader of it's client resides +handle_load({remote,Node,Log},State) -> + %% If the connection is overloaded (send_nosuspend returns false), + %% we drop the message. + _ = erlang:send_nosuspend({?SERVER,Node},Log), + State; +%% Log event to log on this node +handle_load({log,Level,Format,Args,Meta},State) -> + try_log([Level,Format,Args,Meta]), + State; +handle_load({log,Level,Report,Meta},State) -> + try_log([Level,Report,Meta]), + State. + +%% Log event sent to this process e.g. from the emulator - it is really load +handle_info(Log,State) when is_tuple(Log), element(1,Log)==log -> + {load,State}. + +terminate(overloaded, _State) -> + _ = erlang:system_flag(system_logger,undefined), + {ok,fun ?MODULE:restart/0}; +terminate(_Reason, _State) -> + _ = erlang:system_flag(system_logger,whereis(logger)), + ok. + +notify({mode_change,Mode0,Mode1},State) -> + _ = if Mode1=:=drop -> % entering drop mode + erlang:system_flag(system_logger,undefined); + Mode0=:=drop -> % leaving drop mode + erlang:system_flag(system_logger,self()); + true -> + ok + end, + ?LOG_INTERNAL(notice,"~w switched from ~w to ~w mode",[?MODULE,Mode0,Mode1]), + State; +notify({flushed,Flushed},State) -> + ?LOG_INTERNAL(notice, "~w flushed ~w log events",[?MODULE,Flushed]), + State; +notify(restart,State) -> + ?LOG_INTERNAL(notice, "~w restarted", [?MODULE]), + State; +notify(_Note,State) -> + State. + +%%%----------------------------------------------------------------- +%%% Internal functions +try_log(Args) -> + try apply(logger,log,Args) + catch C:R:S -> + ?LOG_INTERNAL(debug,[{?MODULE,log_failed}, + {log,Args}, + {reason,{C,R,S}}]) + end. diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index b7735dbcf7..722246e82c 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -22,8 +22,7 @@ -behaviour(gen_server). %% API --export([start_link/0, - add_handler/3, remove_handler/1, +-export([start_link/0, add_handler/3, remove_handler/1, add_filter/2, remove_filter/2, set_module_level/2, unset_module_level/0, unset_module_level/1, cache_module_level/1, @@ -43,7 +42,7 @@ -define(SERVER, logger). -define(LOGGER_SERVER_TAG, '$logger_cb_process'). --record(state, {tid, async_req, async_req_queue}). +-record(state, {tid, async_req, async_req_queue, remote_logger}). %%%=================================================================== %%% API @@ -155,6 +154,8 @@ init([]) -> process_flag(trap_exit, true), put(?LOGGER_SERVER_TAG,true), Tid = logger_config:new(?LOGGER_TABLE), + %% Store initial proxy config. logger_proxy reads config from here at startup. + logger_config:create(Tid,proxy,logger_proxy:get_default_config()), PrimaryConfig = maps:merge(default_config(primary), #{handlers=>[simple]}), logger_config:create(Tid,primary,PrimaryConfig), @@ -221,6 +222,24 @@ handle_call({add_filter,Id,Filter}, _From,#state{tid=Tid}=State) -> handle_call({remove_filter,Id,FilterId}, _From, #state{tid=Tid}=State) -> Reply = do_remove_filter(Tid,Id,FilterId), {reply,Reply,State}; +handle_call({change_config,SetOrUpd,proxy,Config0},_From,#state{tid=Tid}=State) -> + Default = + case SetOrUpd of + set -> + logger_proxy:get_default_config(); + update -> + {ok,OldConfig} = logger_config:get(Tid,proxy), + OldConfig + end, + Config = maps:merge(Default,Config0), + Reply = + case logger_olp:set_opts(logger_proxy,Config) of + ok -> + logger_config:set(Tid,proxy,Config); + Error -> + Error + end, + {reply,Reply,State}; handle_call({change_config,SetOrUpd,primary,Config0}, _From, #state{tid=Tid}=State) -> {ok,#{handlers:=Handlers}=OldConfig} = logger_config:get(Tid,primary), @@ -357,7 +376,7 @@ terminate(_Reason, _State) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -call(Request) -> +call(Request) when is_tuple(Request) -> Action = element(1,Request), case get(?LOGGER_SERVER_TAG) of true when @@ -369,6 +388,7 @@ call(Request) -> gen_server:call(?SERVER,Request,?DEFAULT_LOGGER_CALL_TIMEOUT) end. + do_add_filter(Tid,Id,{FId,_} = Filter) -> case logger_config:get(Tid,Id) of {ok,Config} -> @@ -413,11 +433,13 @@ default_config(Id,Module) -> sanity_check(Owner,Key,Value) -> sanity_check_1(Owner,[{Key,Value}]). -sanity_check(HandlerId,Config) when is_map(Config) -> - sanity_check_1(HandlerId,maps:to_list(Config)); +sanity_check(Owner,Config) when is_map(Config) -> + sanity_check_1(Owner,maps:to_list(Config)); sanity_check(_,Config) -> {error,{invalid_config,Config}}. +sanity_check_1(proxy,_Config) -> + ok; % Details are checked by logger_olp:set_opts/2 sanity_check_1(Owner,Config) when is_list(Config) -> try Type = get_type(Owner), diff --git a/lib/kernel/src/logger_simple_h.erl b/lib/kernel/src/logger_simple_h.erl index fe181722f3..a0d51dba25 100644 --- a/lib/kernel/src/logger_simple_h.erl +++ b/lib/kernel/src/logger_simple_h.erl @@ -69,7 +69,7 @@ log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) -> do_log( #{level=>error, msg=>{report,{error,simple_handler_process_dead}}, - meta=>#{time=>erlang:system_time(microsecond)}}), + meta=>#{time=>logger:timestamp()}}), do_log(Log); _ -> ?MODULE ! {log,Log} @@ -129,7 +129,7 @@ drop_msg(0) -> drop_msg(N) -> [#{level=>info, msg=>{"Simple handler buffer full, dropped ~w messages",[N]}, - meta=>#{time=>erlang:system_time(microsecond)}}]. + meta=>#{time=>logger:timestamp()}}]. %%%----------------------------------------------------------------- %%% Internal diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 63d1dbaba2..65f5b3876e 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -26,7 +26,7 @@ -include_lib("kernel/include/file.hrl"). %% API --export([info/1, filesync/1, reset/1]). +-export([filesync/1]). %% logger_h_common callbacks -export([init/2, check_config/4, reset_state/2, @@ -36,6 +36,8 @@ -export([log/2, adding_handler/1, removing_handler/1, changing_config/3, filter_config/1]). +-define(DEFAULT_CALL_TIMEOUT, 5000). + %%%=================================================================== %%% API %%%=================================================================== @@ -49,25 +51,6 @@ filesync(Name) -> logger_h_common:filesync(?MODULE,Name). -%%%----------------------------------------------------------------- -%%% --spec info(Name) -> Info | {error,Reason} when - Name :: atom(), - Info :: term(), - Reason :: handler_busy | {badarg,term()}. - -info(Name) -> - logger_h_common:info(?MODULE,Name). - -%%%----------------------------------------------------------------- -%%% --spec reset(Name) -> ok | {error,Reason} when - Name :: atom(), - Reason :: handler_busy | {badarg,term()}. - -reset(Name) -> - logger_h_common:reset(?MODULE,Name). - %%%=================================================================== %%% logger callbacks - just forward to logger_h_common %%%=================================================================== @@ -234,17 +217,24 @@ open_log_file(HandlerName, FileInfo) -> Error -> Error end. -do_open_log_file({file,File}) -> - do_open_log_file({file,File,[raw,append,delayed_write]}); +do_open_log_file({file,FileName}) -> + do_open_log_file({file,FileName,[raw,append,delayed_write]}); -do_open_log_file({file,File,[]}) -> - do_open_log_file({file,File,[raw,append,delayed_write]}); +do_open_log_file({file,FileName,[]}) -> + do_open_log_file({file,FileName,[raw,append,delayed_write]}); -do_open_log_file({file,File,Modes}) -> +do_open_log_file({file,FileName,Modes}) -> try - case filelib:ensure_dir(File) of + case filelib:ensure_dir(FileName) of ok -> - file:open(File, Modes); + case file:open(FileName, Modes) of + {ok, Fd} -> + {ok,#file_info{inode=INode}} = + file:read_file_info(FileName), + {ok, {Fd, INode}}; + Error -> + Error + end; Error -> Error end @@ -254,7 +244,7 @@ do_open_log_file({file,File,Modes}) -> close_log_file(Std) when Std == standard_io; Std == standard_error -> ok; -close_log_file(Fd) -> +close_log_file({Fd,_}) -> _ = file:datasync(Fd), _ = file:close(Fd). @@ -313,9 +303,9 @@ file_ctrl_init(HandlerName, FileInfo, Starter) when is_tuple(FileInfo) -> process_flag(message_queue_data, off_heap), FileName = element(2, FileInfo), case do_open_log_file(FileInfo) of - {ok,Fd} -> + {ok,File} -> Starter ! {self(),ok}, - file_ctrl_loop(Fd, FileName, false, ok, ok, HandlerName); + file_ctrl_loop(File, FileName, false, ok, ok, HandlerName); {error,Reason} -> Starter ! {self(),{error,{open_failed,FileName,Reason}}} end; @@ -323,39 +313,43 @@ file_ctrl_init(HandlerName, StdDev, Starter) -> Starter ! {self(),ok}, file_ctrl_loop(StdDev, StdDev, false, ok, ok, HandlerName). -file_ctrl_loop(Fd, DevName, Synced, +file_ctrl_loop(File, DevName, Synced, PrevWriteResult, PrevSyncResult, HandlerName) -> receive %% asynchronous event {log,Bin} -> - Fd1 = ensure(Fd, DevName), - Result = write_to_dev(Fd1, Bin, DevName, PrevWriteResult, HandlerName), - file_ctrl_loop(Fd1, DevName, false, + File1 = ensure(File, DevName), + Result = write_to_dev(File1, Bin, DevName, + PrevWriteResult, HandlerName), + file_ctrl_loop(File1, DevName, false, Result, PrevSyncResult, HandlerName); %% synchronous event {{log,Bin},{From,MRef}} -> - Fd1 = ensure(Fd, DevName), - Result = write_to_dev(Fd1, Bin, DevName, PrevWriteResult, HandlerName), + File1 = ensure(File, DevName), + Result = write_to_dev(File1, Bin, DevName, + PrevWriteResult, HandlerName), From ! {MRef,ok}, - file_ctrl_loop(Fd1, DevName, false, + file_ctrl_loop(File1, DevName, false, Result, PrevSyncResult, HandlerName); filesync -> - Fd1 = ensure(Fd, DevName), - Result = sync_dev(Fd1, DevName, Synced, PrevSyncResult, HandlerName), - file_ctrl_loop(Fd1, DevName, true, + File1 = ensure(File, DevName), + Result = sync_dev(File1, DevName, Synced, + PrevSyncResult, HandlerName), + file_ctrl_loop(File1, DevName, true, PrevWriteResult, Result, HandlerName); {filesync,{From,MRef}} -> - Fd1 = ensure(Fd, DevName), - Result = sync_dev(Fd1, DevName, Synced, PrevSyncResult, HandlerName), + File1 = ensure(File, DevName), + Result = sync_dev(File1, DevName, Synced, + PrevSyncResult, HandlerName), From ! {MRef,ok}, - file_ctrl_loop(Fd1, DevName, true, + file_ctrl_loop(File1, DevName, true, PrevWriteResult, Result, HandlerName); stop -> - _ = close_log_file(Fd), + _ = close_log_file(File), stopped end. @@ -364,16 +358,16 @@ file_ctrl_loop(Fd, DevName, Synced, %% logrotate) ensure(Fd,DevName) when is_atom(DevName) -> Fd; -ensure(Fd,FileName) -> +ensure({Fd,INode},FileName) -> case file:read_file_info(FileName) of - {ok,_} -> - Fd; + {ok,#file_info{inode=INode}} -> + {Fd,INode}; _ -> _ = file:close(Fd), _ = file:close(Fd), % delayed_write cause close not to close case do_open_log_file({file,FileName}) of - {ok,Fd1} -> - Fd1; + {ok,File} -> + File; Error -> exit({could_not_reopen_file,Error}) end @@ -382,13 +376,13 @@ ensure(Fd,FileName) -> write_to_dev(DevName, Bin, _DevName, _PrevWriteResult, _HandlerName) when is_atom(DevName) -> io:put_chars(DevName, Bin); -write_to_dev(Fd, Bin, FileName, PrevWriteResult, HandlerName) -> +write_to_dev({Fd,_}, Bin, FileName, PrevWriteResult, HandlerName) -> Result = ?file_write(Fd, Bin), maybe_notify_error(write,Result,PrevWriteResult,FileName,HandlerName). -sync_dev(_Fd, _FileName, true, PrevSyncResult, _HandlerName) -> +sync_dev(_, _FileName, true, PrevSyncResult, _HandlerName) -> PrevSyncResult; -sync_dev(Fd, FileName, false, PrevSyncResult, HandlerName) -> +sync_dev({Fd,_}, FileName, false, PrevSyncResult, HandlerName) -> Result = ?file_datasync(Fd), maybe_notify_error(filesync,Result,PrevSyncResult,FileName,HandlerName). diff --git a/lib/kernel/src/logger_sup.erl b/lib/kernel/src/logger_sup.erl index 3d6f482e20..9ea8558a16 100644 --- a/lib/kernel/src/logger_sup.erl +++ b/lib/kernel/src/logger_sup.erl @@ -50,7 +50,9 @@ init([]) -> start => {logger_handler_watcher, start_link, []}, shutdown => brutal_kill}, - {ok, {SupFlags, [Watcher]}}. + Proxy = logger_proxy:child_spec(), + + {ok, {SupFlags, [Watcher,Proxy]}}. %%%=================================================================== %%% Internal functions diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 14fe21e9de..4f9d7b3e5c 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -98,7 +98,7 @@ print(Label, Term) -> -spec reset_trace() -> 'true'. reset_trace() -> - erlang:system_flag(1, 0). + erlang:system_flag(reset_seq_trace, true). %% reset_trace(Pid) -> % this might be a useful function too diff --git a/lib/kernel/src/standard_error.erl b/lib/kernel/src/standard_error.erl index 5d649e5f94..ef5b532960 100644 --- a/lib/kernel/src/standard_error.erl +++ b/lib/kernel/src/standard_error.erl @@ -27,7 +27,8 @@ -define(PROCNAME_SUP, standard_error_sup). %% Defines for control ops --define(CTRL_OP_GET_WINSIZE,100). +-define(ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER, 16#018b0900). +-define(CTRL_OP_GET_WINSIZE, (100 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)). %% %% The basic server and start-up. diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl index 872e63ab53..0c9e1ea303 100644 --- a/lib/kernel/src/user.erl +++ b/lib/kernel/src/user.erl @@ -28,7 +28,8 @@ -define(NAME, user). %% Defines for control ops --define(CTRL_OP_GET_WINSIZE,100). +-define(ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER, 16#018b0900). +-define(CTRL_OP_GET_WINSIZE, (100 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)). %% %% The basic server and start-up. diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl index 9f914aa222..08286dd476 100644 --- a/lib/kernel/src/user_drv.erl +++ b/lib/kernel/src/user_drv.erl @@ -32,9 +32,10 @@ -define(OP_BEEP,4). -define(OP_PUTC_SYNC,5). % Control op --define(CTRL_OP_GET_WINSIZE,100). --define(CTRL_OP_GET_UNICODE_STATE,101). --define(CTRL_OP_SET_UNICODE_STATE,102). +-define(ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER, 16#018b0900). +-define(CTRL_OP_GET_WINSIZE, (100 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)). +-define(CTRL_OP_GET_UNICODE_STATE, (101 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)). +-define(CTRL_OP_SET_UNICODE_STATE, (102 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)). %% start() %% start(ArgumentList) diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 4a86265a4a..8a6ffe7e72 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -76,8 +76,11 @@ MODULES= \ logger_filters_SUITE \ logger_formatter_SUITE \ logger_legacy_SUITE \ + logger_olp_SUITE \ + logger_proxy_SUITE \ logger_simple_h_SUITE \ logger_std_h_SUITE \ + logger_stress_SUITE \ logger_test_lib \ os_SUITE \ pg2_SUITE \ diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 5c35b82207..94d7c17712 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -31,6 +31,7 @@ otp_3002/1, otp_3184/1, otp_4066/1, otp_4227/1, otp_5363/1, otp_5606/1, start_phases/1, get_key/1, get_env/1, + set_env/1, set_env_persistent/1, set_env_errors/1, permit_false_start_local/1, permit_false_start_dist/1, script_start/1, nodedown_start/1, init2973/0, loop2973/0, loop5606/1]). @@ -55,6 +56,7 @@ all() -> load_use_cache, ensure_started, {group, reported_bugs}, start_phases, script_start, nodedown_start, permit_false_start_local, permit_false_start_dist, get_key, get_env, ensure_all_started, + set_env, set_env_persistent, set_env_errors, {group, distr_changed}, config_change, shutdown_func, shutdown_timeout, shutdown_deadlock, config_relative_paths, persistent_env]. @@ -1944,6 +1946,101 @@ get_appls([_ | T], Res) -> get_appls([], Res) -> Res. +%% Test set_env/1. +set_env(Conf) when is_list(Conf) -> + ok = application:set_env([{appinc, [{own2, persist}, {not_in_app, persist}]}, + {unknown_app, [{key, persist}]}]), + + %% own_env1 and own2 are set in appinc + undefined = application:get_env(appinc, own_env1), + {ok, persist} = application:get_env(appinc, own2), + {ok, persist} = application:get_env(appinc, not_in_app), + {ok, persist} = application:get_env(unknown_app, key), + + ok = application:load(appinc()), + {ok, value1} = application:get_env(appinc, own_env1), + {ok, val2} = application:get_env(appinc, own2), + {ok, persist} = application:get_env(appinc, not_in_app), + {ok, persist} = application:get_env(unknown_app, key), + + %% On reload, values are lost + ok = application:unload(appinc), + ok = application:load(appinc()), + {ok, value1} = application:get_env(appinc, own_env1), + {ok, val2} = application:get_env(appinc, own2), + undefined = application:get_env(appinc, not_in_app), + + %% Clean up + ok = application:unload(appinc). + +%% Test set_env/2 with persistent true. +set_env_persistent(Conf) when is_list(Conf) -> + Opts = [{persistent, true}], + ok = application:set_env([{appinc, [{own2, persist}, {not_in_app, persist}]}, + {unknown_app, [{key, persist}]}], Opts), + + %% own_env1 and own2 are set in appinc + undefined = application:get_env(appinc, own_env1), + {ok, persist} = application:get_env(appinc, own2), + {ok, persist} = application:get_env(appinc, not_in_app), + {ok, persist} = application:get_env(unknown_app, key), + + ok = application:load(appinc()), + {ok, value1} = application:get_env(appinc, own_env1), + {ok, persist} = application:get_env(appinc, own2), + {ok, persist} = application:get_env(appinc, not_in_app), + {ok, persist} = application:get_env(unknown_app, key), + + %% On reload, values are not lost + ok = application:unload(appinc), + ok = application:load(appinc()), + {ok, value1} = application:get_env(appinc, own_env1), + {ok, persist} = application:get_env(appinc, own2), + {ok, persist} = application:get_env(appinc, not_in_app), + + %% Clean up + ok = application:unload(appinc). + +set_env_errors(Conf) when is_list(Conf) -> + "application: 1; application name must be an atom" = + badarg_msg(fun() -> application:set_env([{1, []}]) end), + + "application: foo; parameters must be a list" = + badarg_msg(fun() -> application:set_env([{foo, bar}]) end), + + "invalid application config: foo_bar" = + badarg_msg(fun() -> application:set_env([foo_bar]) end), + + "application: foo; invalid parameter name: 1" = + badarg_msg(fun() -> application:set_env([{foo, [{1, 2}]}]) end), + + "application: foo; invalid parameter: config" = + badarg_msg(fun() -> application:set_env([{foo, [config]}]) end), + + "application: kernel; erroneous parameter: distributed" = + badarg_msg(fun() -> application:set_env([{kernel, [{distributed, config}]}]) end), + + %% This will raise in the future + ct:capture_start(), + _ = application:set_env([{foo, []}, {foo, []}]), + timer:sleep(100), + ct:capture_stop(), + [_ | _] = string:find(ct:capture_get(), "duplicate application config: foo"), + + ct:capture_start(), + _ = application:set_env([{foo, [{bar, baz}, {bar, bat}]}]), + timer:sleep(100), + ct:capture_stop(), + [_ | _] = string:find(ct:capture_get(), "application: foo; duplicate parameter: bar"), + + ok. + +badarg_msg(Fun) -> + try Fun() of + _ -> ct:fail(try_succeeded) + catch + error:{badarg, Msg} -> Msg + end. %% Test set_env/4 and unset_env/3 with persistent true. persistent_env(Conf) when is_list(Conf) -> diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 244bd7e2a0..52edfaee29 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -53,7 +53,7 @@ active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1, - otp_12242/1]). + otp_12242/1, delay_send_error/1]). %% Internal exports. -export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, @@ -97,7 +97,7 @@ all() -> active_once_closed, send_timeout, send_timeout_active, otp_7731, wrapping_oct, zombie_sockets, otp_7816, otp_8102, otp_9389, - otp_12242]. + otp_12242, delay_send_error]. groups() -> []. @@ -3427,3 +3427,32 @@ otp_12242(Addr) when tuple_size(Addr) =:= 4 -> wait(Mref) -> receive {'DOWN',Mref,_,_,Reason} -> Reason end. + +%% OTP-15536 +%% Test that send error works correctly for delay_send +delay_send_error(Config) -> + {ok, LS} = gen_tcp:listen(0, [{reuseaddr, true}, {packet, 1}, {active, false}]), + {ok,{{0,0,0,0},PortNum}}=inet:sockname(LS), + P = spawn_link( + fun() -> + {ok, S} = gen_tcp:accept(LS), + receive die -> gen_tcp:close(S) end + end), + erlang:monitor(process, P), + {ok, S} = gen_tcp:connect("localhost", PortNum, + [{packet, 1}, {active, false}, {delay_send, true}]), + + %% Do a couple of sends first to see that it works + ok = gen_tcp:send(S, "hello"), + ok = gen_tcp:send(S, "hello"), + ok = gen_tcp:send(S, "hello"), + + %% Make the receiver close + P ! die, + receive _Down -> ok end, + + ok = gen_tcp:send(S, "hello"), + timer:sleep(500), %% Sleep in order for delay_send to have time to trigger + + %% This used to result in a double free + {error, closed} = gen_tcp:send(S, "hello"). diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 6a006cdc01..4f90260f98 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -295,7 +295,7 @@ is_real_system(KernelVsn, StdlibVsn) -> %% before restart. %% ------------------------------------------------ many_restarts() -> - [{timetrap,{minutes,8}}]. + [{timetrap,{minutes,16}}]. many_restarts(Config) when is_list(Config) -> {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), @@ -315,7 +315,7 @@ loop_restart(N,Node,EHPid) -> loose_node:stop(Node), ct:fail(not_stopping) end, - ok = wait_for(30, Node, EHPid), + ok = wait_for(60, Node, EHPid), loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[logger])). wait_for(0,Node,_) -> @@ -367,7 +367,8 @@ restart(Config) when is_list(Config) -> SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), io:format("SysProcs0=~p~n", [SysProcs0]), [InitPid, PurgerPid, LitCollectorPid, - DirtySigNPid, DirtySigHPid, DirtySigMPid] = SysProcs0, + DirtySigNPid, DirtySigHPid, DirtySigMPid, + PrimFilePid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -385,7 +386,8 @@ restart(Config) when is_list(Config) -> SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), io:format("SysProcs1=~p~n", [SysProcs1]), [InitPid1, PurgerPid1, LitCollectorPid1, - DirtySigNPid1, DirtySigHPid1, DirtySigMPid1] = SysProcs1, + DirtySigNPid1, DirtySigHPid1, DirtySigMPid1, + PrimFilePid1] = SysProcs1, %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), @@ -411,6 +413,10 @@ restart(Config) when is_list(Config) -> DirtySigMP = pid_to_list(DirtySigMPid), DirtySigMP = pid_to_list(DirtySigMPid1), + %% and same prim_file helper process! + PrimFileP = pid_to_list(PrimFilePid), + PrimFileP = pid_to_list(PrimFilePid1), + NewProcs0 = rpc:call(Node, erlang, processes, []), NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of @@ -437,7 +443,8 @@ restart(Config) when is_list(Config) -> literal_collector, dirty_sig_handler_normal, dirty_sig_handler_high, - dirty_sig_handler_max}). + dirty_sig_handler_max, + prim_file}). find_system_processes() -> find_system_procs(processes(), #sys_procs{}). @@ -448,7 +455,8 @@ find_system_procs([], SysProcs) -> SysProcs#sys_procs.literal_collector, SysProcs#sys_procs.dirty_sig_handler_normal, SysProcs#sys_procs.dirty_sig_handler_high, - SysProcs#sys_procs.dirty_sig_handler_max]; + SysProcs#sys_procs.dirty_sig_handler_max, + SysProcs#sys_procs.prim_file]; find_system_procs([P|Ps], SysProcs) -> case process_info(P, [initial_call, priority]) of [{initial_call,{otp_ring0,start,2}},_] -> @@ -472,6 +480,9 @@ find_system_procs([P|Ps], SysProcs) -> {priority,max}] -> undefined = SysProcs#sys_procs.dirty_sig_handler_max, find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_max = P}); + [{initial_call,{prim_file,start,0}},_] -> + undefined = SysProcs#sys_procs.prim_file, + find_system_procs(Ps, SysProcs#sys_procs{prim_file = P}); _ -> find_system_procs(Ps, SysProcs) end. diff --git a/lib/kernel/test/kernel_bench.spec b/lib/kernel/test/kernel_bench.spec index 4de133f21b..898ceb59e0 100644 --- a/lib/kernel/test/kernel_bench.spec +++ b/lib/kernel/test/kernel_bench.spec @@ -1,2 +1,3 @@ {groups,"../kernel_test",zlib_SUITE,[bench]}. {groups,"../kernel_test",file_SUITE,[bench]}. +{suites,"../kernel_test",[logger_stress_SUITE]}. diff --git a/lib/kernel/test/logger.cover b/lib/kernel/test/logger.cover index 960bc0abff..9691aa295e 100644 --- a/lib/kernel/test/logger.cover +++ b/lib/kernel/test/logger.cover @@ -4,9 +4,12 @@ logger_backend, logger_config, logger_disk_log_h, - logger_h_common, logger_filters, logger_formatter, + logger_handler_watcher, + logger_h_common, + logger_olp, + logger_proxy, logger_server, logger_simple_h, logger_std_h, diff --git a/lib/kernel/test/logger.spec b/lib/kernel/test/logger.spec index 1ab90b3e93..3aec37951d 100644 --- a/lib/kernel/test/logger.spec +++ b/lib/kernel/test/logger.spec @@ -7,5 +7,7 @@ logger_filters_SUITE, logger_formatter_SUITE, logger_legacy_SUITE, + logger_olp_SUITE, + logger_proxy_SUITE, logger_simple_h_SUITE, logger_std_h_SUITE]}. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index d831d0d108..70bb775db8 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -101,7 +101,8 @@ all() -> compare_levels, process_metadata, app_config, - kernel_config]. + kernel_config, + pretty_print]. start_stop(_Config) -> S = whereis(logger), @@ -898,14 +899,14 @@ process_metadata(_Config) -> undefined = logger:get_process_metadata(), {error,badarg} = ?TRY(logger:set_process_metadata(bad)), ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), - Time = erlang:system_time(microsecond), + Time = logger:timestamp(), ProcMeta = #{time=>Time,line=>0,custom=>proc}, ok = logger:set_process_metadata(ProcMeta), S1 = ?str, ?LOG_NOTICE(S1,#{custom=>macro}), check_logged(notice,S1,#{time=>Time,line=>0,custom=>macro}), - Time2 = erlang:system_time(microsecond), + Time2 = logger:timestamp(), S2 = ?str, ?LOG_NOTICE(S2,#{time=>Time2,line=>1,custom=>macro}), check_logged(notice,S2,#{time=>Time2,line=>1,custom=>macro}), @@ -1141,6 +1142,61 @@ kernel_config(Config) -> ok. +pretty_print(Config) -> + ok = logger:add_handler(?FUNCTION_NAME,logger_std_h,#{}), + ok = logger:set_module_level([module1,module2],debug), + + ct:capture_start(), + logger:i(), + ct:capture_stop(), + I0 = ct:capture_get(), + + ct:capture_start(), + logger:i(primary), + ct:capture_stop(), + IPrim = ct:capture_get(), + + ct:capture_start(), + logger:i(handlers), + ct:capture_stop(), + IHs = ct:capture_get(), + + ct:capture_start(), + logger:i(proxy), + ct:capture_stop(), + IProxy = ct:capture_get(), + + ct:capture_start(), + logger:i(modules), + ct:capture_stop(), + IMs = ct:capture_get(), + + I02 = lists:append([IPrim,IHs,IProxy,IMs]), + %% ct:log("~p~n",[I0]), + %% ct:log("~p~n",[I02]), + I0 = I02, + + ct:capture_start(), + logger:i(handlers), + ct:capture_stop(), + IHs = ct:capture_get(), + + Ids = logger:get_handler_ids(), + IHs2 = + lists:append( + [begin + ct:capture_start(), + logger:i(Id), + ct:capture_stop(), + [_|IH] = ct:capture_get(), + IH + end || Id <- Ids]), + + %% ct:log("~p~n",[IHs]), + %% ct:log("~p~n",[["Handler configuration: \n"|IHs2]]), + IHs = ["Handler configuration: \n"|IHs2], + ok. + %%%----------------------------------------------------------------- %%% Internal check_logged(Level,Format,Args,Meta) -> diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 87b8250781..9bbec42de8 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -24,6 +24,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/logger.hrl"). -include_lib("kernel/src/logger_internal.hrl"). +-include_lib("kernel/src/logger_olp.hrl"). -include_lib("kernel/src/logger_h_common.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("kernel/include/file.hrl"). @@ -97,7 +98,6 @@ all() -> formatter_fail, config_fail, bad_input, - info_and_reset, reconfig, sync, disk_log_full, @@ -306,9 +306,9 @@ logging(cleanup, _Config) -> filter_config(_Config) -> ok = logger:add_handler(?MODULE,logger_disk_log_h,#{}), {ok,#{config:=HConfig}=Config} = logger:get_handler_config(?MODULE), - HConfig = maps:without([handler_pid,mode_tab],HConfig), + HConfig = maps:without([olp],HConfig), - FakeFullHConfig = HConfig#{handler_pid=>self(),mode_tab=>erlang:make_ref()}, + FakeFullHConfig = HConfig#{olp=>{regname,self(),erlang:make_ref()}}, #{config:=HConfig} = logger_disk_log_h:filter_config(Config#{config=>FakeFullHConfig}), ok. @@ -351,9 +351,7 @@ errors(Config) -> %% Read-only fields may (accidentially) be included in the change, %% but it won't take effect {ok,C} = logger:get_handler_config(Name1), - ok = logger:set_handler_config(Name1,config, - #{handler_pid=>self(), - mode_tab=>erlang:make_ref()}), + ok = logger:set_handler_config(Name1,config,#{olp=>dummyvalue}), {ok,C} = logger:get_handler_config(Name1), @@ -419,19 +417,16 @@ config_fail(_Config) -> filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{handler_not_added,{invalid_config,logger_disk_log_h, - {invalid_levels,#{drop_mode_qlen:=1}}}}} = + {error,{handler_not_added,{invalid_olp_levels,#{drop_mode_qlen:=1}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{drop_mode_qlen=>1}}), - {error,{handler_not_added,{invalid_config,logger_disk_log_h, - {invalid_levels,#{sync_mode_qlen:=43, - drop_mode_qlen:=42}}}}} = + {error,{handler_not_added,{invalid_olp_levels,#{sync_mode_qlen:=43, + drop_mode_qlen:=42}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{sync_mode_qlen=>43, drop_mode_qlen=>42}}), - {error,{handler_not_added,{invalid_config,logger_disk_log_h, - {invalid_levels,#{drop_mode_qlen:=43, - flush_qlen:=42}}}}} = + {error,{handler_not_added,{invalid_olp_levels,#{drop_mode_qlen:=43, + flush_qlen:=42}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{drop_mode_qlen=>43, flush_qlen=>42}}), @@ -445,7 +440,7 @@ config_fail(_Config) -> #{max_no_files=>2}), %% incorrect values of OP params {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), - {error,{invalid_config,logger_disk_log_h,{invalid_levels,_}}} = + {error,{invalid_olp_levels,_}} = logger:update_handler_config(?MODULE,config, HConfig#{sync_mode_qlen=>100, flush_qlen=>99}), @@ -459,18 +454,7 @@ config_fail(cleanup,_Config) -> bad_input(_Config) -> {error,{badarg,{filesync,["BadType"]}}} = - logger_disk_log_h:filesync("BadType"), - {error,{badarg,{info,["BadType"]}}} = logger_disk_log_h:info("BadType"), - {error,{badarg,{reset,["BadType"]}}} = logger_disk_log_h:reset("BadType"). - -info_and_reset(_Config) -> - ok = logger:add_handler(?MODULE,logger_disk_log_h, - #{filter_default=>log, - formatter=>{?MODULE,self()}}), - #{id := ?MODULE} = logger_disk_log_h:info(?MODULE), - ok = logger_disk_log_h:reset(?MODULE). -info_and_reset(cleanup,_Config) -> - logger:remove_handler(?MODULE). + logger_disk_log_h:filesync("BadType"). reconfig(Config) -> Dir = ?config(priv_dir,Config), @@ -479,7 +463,7 @@ reconfig(Config) -> #{filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), - #{id := ?MODULE, + #{%id := ?MODULE, sync_mode_qlen := ?SYNC_MODE_QLEN, drop_mode_qlen := ?DROP_MODE_QLEN, flush_qlen := ?FLUSH_QLEN, @@ -490,13 +474,14 @@ reconfig(Config) -> overload_kill_qlen := ?OVERLOAD_KILL_QLEN, overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL, - handler_state := - #{log_opts := #{type := ?DISK_LOG_TYPE, - max_no_files := ?DISK_LOG_MAX_NO_FILES, - max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, - file := DiskLogFile}}} = - logger_disk_log_h:info(?MODULE), + cb_state := + #{handler_state := + #{log_opts := #{type := ?DISK_LOG_TYPE, + max_no_files := ?DISK_LOG_MAX_NO_FILES, + max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, + file := DiskLogFile}}, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL}} = + logger_olp:info(h_proc_name()), {ok,#{config := #{sync_mode_qlen := ?SYNC_MODE_QLEN, drop_mode_qlen := ?DROP_MODE_QLEN, @@ -527,7 +512,7 @@ reconfig(Config) -> overload_kill_restart_after => infinity, filesync_repeat_interval => no_repeat}, ok = logger:set_handler_config(?MODULE, config, HConfig1), - #{id := ?MODULE, + #{%id := ?MODULE, sync_mode_qlen := 1, drop_mode_qlen := 2, flush_qlen := 3, @@ -538,8 +523,8 @@ reconfig(Config) -> overload_kill_qlen := 100000, overload_kill_mem_size := 10000000, overload_kill_restart_after := infinity, - filesync_repeat_interval := no_repeat} = - logger_disk_log_h:info(?MODULE), + cb_state := #{filesync_repeat_interval := no_repeat}} = + logger_olp:info(h_proc_name()), {ok,#{config:=HConfig1}} = logger:get_handler_config(?MODULE), ok = logger:update_handler_config(?MODULE, config, @@ -577,12 +562,13 @@ reconfig(Config) -> max_no_files => 1, max_no_bytes => 1024, file => File}}), - #{handler_state := - #{log_opts := #{type := halt, - max_no_files := 1, - max_no_bytes := 1024, - file := File}}} = - logger_disk_log_h:info(?MODULE), + #{cb_state := + #{handler_state := + #{log_opts := #{type := halt, + max_no_files := 1, + max_no_bytes := 1024, + file := File}}}} = + logger_olp:info(h_proc_name()), {ok,#{config := #{type := halt, max_no_files := 1, @@ -650,13 +636,8 @@ sync(Config) -> {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), HConfig1 = HConfig#{filesync_repeat_interval => no_repeat}, ok = logger:update_handler_config(?MODULE, config, HConfig1), - no_repeat = maps:get(filesync_repeat_interval, - logger_disk_log_h:info(?MODULE)), - %% The following timer is to make sure the time from last log - %% ("first") to next ("second") is long enough, so the a flush is - %% triggered by the idle timeout between "fourth" and "fifth". - timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + maps:get(cb_state,logger_olp:info(h_proc_name()))), start_tracer([{logger_disk_log_h,disk_log_write,3}, {disk_log,sync,1}], @@ -666,10 +647,10 @@ sync(Config) -> {disk_log,sync}]), logger:notice("second", ?domain), - timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + timer:sleep(?IDLE_DETECT_TIME*2), logger:notice("third", ?domain), %% wait for automatic disk_log_sync - check_tracer(?IDLE_DETECT_TIME_MSEC*2), + check_tracer(?IDLE_DETECT_TIME*2), try_read_file(Log, {ok,<<"first\nsecond\nthird\n">>}, 1000), @@ -678,14 +659,15 @@ sync(Config) -> WaitT = 4500, OneSync = {logger_h_common,handle_cast,repeated_filesync}, %% receive 1 repeated_filesync per sec - start_tracer([{logger_h_common,handle_cast,2}], + start_tracer([{{logger_h_common,handle_cast,2}, + [{[repeated_filesync,'_'],[],[{message,{caller}}]}]}], [OneSync || _ <- lists:seq(1, trunc(WaitT/SyncInt))]), HConfig2 = HConfig#{filesync_repeat_interval => SyncInt}, ok = logger:update_handler_config(?MODULE, config, HConfig2), SyncInt = maps:get(filesync_repeat_interval, - logger_disk_log_h:info(?MODULE)), + maps:get(cb_state,logger_olp:info(h_proc_name()))), timer:sleep(WaitT), HConfig3 = HConfig#{filesync_repeat_interval => no_repeat}, ok = logger:update_handler_config(?MODULE, config, HConfig3), @@ -803,7 +785,7 @@ disk_log_full(cleanup, _Config) -> dbg:stop_clear(), logger:remove_handler(?MODULE). -disk_log_events(Config) -> +disk_log_events(_Config) -> Node = node(), Log = ?MODULE, ok = logger:add_handler(?MODULE, @@ -860,10 +842,12 @@ write_failure(Config) -> rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), rpc:call(Node, ?MODULE, set_result, [disk_log_write,ok]), - HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), - ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, - maps:get(handler_state,HState))]), - + HState = rpc:call(Node, logger_olp, info, [h_proc_name(?STANDARD_HANDLER)]), + LogOpts = maps:get(log_opts, + maps:get(handler_state, + maps:get(cb_state,HState))), + ct:pal("LogOpts = ~p", [LogOpts]), + %% ?check and ?check_no_log in this test only check for internal log events ok = log_on_remote_node(Node, "Logged1"), rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), @@ -914,15 +898,16 @@ sync_failure(Config) -> rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), - HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), - LogOpts = maps:get(log_opts, maps:get(handler_state,HState)), + HState = rpc:call(Node, logger_olp, info, [h_proc_name(?STANDARD_HANDLER)]), + LogOpts = maps:get(log_opts, maps:get(handler_state, + maps:get(cb_state,HState))), SyncInt = 500, ok = rpc:call(Node, logger, update_handler_config, [?STANDARD_HANDLER, config, #{filesync_repeat_interval => SyncInt}]), - Info = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), - SyncInt = maps:get(filesync_repeat_interval, Info), + Info = rpc:call(Node, logger_olp, info, [h_proc_name(?STANDARD_HANDLER)]), + SyncInt = maps:get(filesync_repeat_interval, maps:get(cb_state, Info)), ok = log_on_remote_node(Node, "Logged1"), ?check_no_log, @@ -1198,7 +1183,7 @@ qlen_kill_new(Config) -> receive {'DOWN', MRef, _, _, Info} -> case Info of - {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + {shutdown,{overloaded,QLen,Mem}} -> ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); killed -> ct:pal("Slow shutdown, handler process was killed!", []) @@ -1208,7 +1193,7 @@ qlen_kill_new(Config) -> ok after 5000 -> - Info = logger_disk_log_h:info(?MODULE), + Info = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info]), ct:fail("Handler not dead! It should not have survived this!") end. @@ -1235,7 +1220,7 @@ mem_kill_new(Config) -> receive {'DOWN', MRef, _, _, Info} -> case Info of - {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + {shutdown,{overloaded,QLen,Mem}} -> ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); killed -> ct:pal("Slow shutdown, handler process was killed!", []) @@ -1245,7 +1230,7 @@ mem_kill_new(Config) -> ok after 5000 -> - Info = logger_disk_log_h:info(?MODULE), + Info = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info]), ct:fail("Handler not dead! It should not have survived this!") end. @@ -1271,7 +1256,7 @@ restart_after(Config) -> ok after 5000 -> - Info1 = logger_std_h:info(?MODULE), + Info1 = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info1]), ct:fail("Handler not dead! It should not have survived this!") end, @@ -1295,7 +1280,7 @@ restart_after(Config) -> ok after 5000 -> - Info2 = logger_std_h:info(?MODULE), + Info2 = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info2]), ct:fail("Handler not dead! It should not have survived this!") end, @@ -1316,11 +1301,15 @@ handler_requests_under_load(Config) -> flush_qlen => 2000, burst_limit_enable => false}}, ok = logger:update_handler_config(?MODULE, NewHConfig), - Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, - {info,[]}, - {reset,[]}, - {change_config,[]}]) - end), + Pid = spawn_link( + fun() -> send_requests(1,[{logger_disk_log_h,filesync,[?MODULE],[]}, + {logger_olp,info,[h_proc_name()],[]}, + {logger_olp,reset,[h_proc_name()],[]}, + {logger,update_handler_config, + [?MODULE, config, + #{overload_kill_enable => false}], + []}]) + end), Procs = 100, Sent = Procs * send_burst({n,5000}, {spawn,Procs,10}, {chars,79}, notice), Pid ! {self(),finish}, @@ -1332,29 +1321,22 @@ handler_requests_under_load(Config) -> [E || E <- Res, is_tuple(E) andalso (element(1,E) == error)] end, - Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], - NoOfReqs = lists:foldl(fun({_,Res}, N) -> N + length(Res) end, 0, ReqResult), + Errors = [{Func,FindError(Res)} || {_,Func,_,Res} <- ReqResult], + NoOfReqs = lists:foldl(fun({_,_,_,Res}, N) -> N + length(Res) end, + 0, ReqResult), ct:pal("~w requests made. Errors: ~n~p", [NoOfReqs,Errors]), ok = file_delete(Log). handler_requests_under_load(cleanup, _Config) -> ok = stop_handler(?MODULE). -send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> +send_requests(TO, Reqs = [{Mod,Func,Args,Res}|Rs]) -> receive {From,finish} -> From ! {self(),Reqs} after TO -> - Result = - case Req of - change_config -> - logger:update_handler_config(HName, logger_disk_log_h, - #{overload_kill_enable => - false}); - Func -> - logger_disk_log_h:Func(HName) - end, - send_requests(HName, TO, Rs ++ [{Req,[Result|Res]}]) + Result = apply(Mod,Func,Args), + send_requests(TO, Rs ++ [{Mod,Func,Args,[Result|Res]}]) end. %%%----------------------------------------------------------------- @@ -1472,15 +1454,6 @@ format(Msg,Tag) -> erlang:display(Error), exit(Error). -remove(Handler, LogName) -> - logger_disk_log_h:remove(Handler, LogName), - HState = #{log_names := Logs} = logger_disk_log_h:info(), - false = maps:is_key(LogName, HState), - false = lists:member(LogName, Logs), - false = logger_config:exist(?LOGGER_TABLE, LogName), - {error,no_such_log} = disk_log:info(LogName), - ok. - start_and_add(Name, Config, LogOpts) -> HConfig = maps:get(config, Config, #{}), HConfig1 = maps:merge(HConfig, LogOpts), @@ -1607,7 +1580,9 @@ start_tracer(Trace,Expected) -> ok. tpl([{M,F,A}|Trace]) -> - {ok,Match} = dbg:tpl(M,F,A,c), + tpl([{{M,F,A},c}|Trace]); +tpl([{{M,F,A},MS}|Trace]) -> + {ok,Match} = dbg:tpl(M,F,A,MS), case lists:keyfind(matched,1,Match) of {_,_,1} -> ok; diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index e8d1a313dc..9d2ad11be8 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -59,7 +59,8 @@ groups() -> logger_undefined, logger_many_handlers_default_first, logger_many_handlers_default_last, - logger_many_handlers_default_last_broken_filter + logger_many_handlers_default_last_broken_filter, + logger_proxy ]}, {bad,[],[bad_error_logger, bad_level, @@ -541,6 +542,19 @@ logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> ok. +logger_proxy(Config) -> + %% assume current node runs with default settings + DefOpts = logger_olp:get_opts(logger_proxy), + {ok,_,Node} = setup(Config, + [{logger,[{proxy,#{sync_mode_qlen=>0, + drop_mode_qlen=>2}}]}]), + Expected = DefOpts#{sync_mode_qlen:=0, + drop_mode_qlen:=2}, + Expected = rpc:call(Node,logger_olp,get_opts,[logger_proxy]), + Expected = rpc:call(Node,logger,get_proxy_config,[]), + + ok. + sasl_compatible_false(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,_,Node} = setup(Config, diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 8c13f0f908..83e3e6c40a 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -867,7 +867,7 @@ my_try(Fun) -> try Fun() catch C:R:S -> {C,R,hd(S)} end. timestamp() -> - erlang:system_time(microsecond). + logger:timestamp(). %% necessary? add_time(#{time:=_}=Meta) -> diff --git a/lib/kernel/test/logger_olp_SUITE.erl b/lib/kernel/test/logger_olp_SUITE.erl new file mode 100644 index 0000000000..ea3eec89f5 --- /dev/null +++ b/lib/kernel/test/logger_olp_SUITE.erl @@ -0,0 +1,90 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_olp_SUITE). + +-compile(export_all). + +-include_lib("kernel/src/logger_olp.hrl"). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [idle_timer]. + +%%%----------------------------------------------------------------- +%%% Test cases +idle_timer(_Config) -> + {ok,_Pid,Olp} = logger_olp:start_link(?MODULE,?MODULE,self(),#{}), + [logger_olp:load(Olp,{msg,N}) || N<-lists:seq(1,3)], + timer:sleep(?IDLE_DETECT_TIME*2), + [{load,{msg,1}}, + {load,{msg,2}}, + {load,{msg,3}}, + {notify,idle}] = test_server:messages_get(), + logger_olp:cast(Olp,hello), + timer:sleep(?IDLE_DETECT_TIME*2), + [{cast,hello}] = test_server:messages_get(), + ok. +idle_timer(cleanup,_Config) -> + unlink(whereis(?MODULE)), + logger_olp:stop(?MODULE), + ok. + +%%%----------------------------------------------------------------- +%%% Olp callbacks +init(P) -> + {ok,P}. + +handle_load(M,P) -> + P ! {load,M}, + P. + +handle_cast(M,P) -> + P ! {cast,M}, + {noreply,P}. + +notify(N,P) -> + P ! {notify,N}, + P. diff --git a/lib/kernel/test/logger_proxy_SUITE.erl b/lib/kernel/test/logger_proxy_SUITE.erl new file mode 100644 index 0000000000..777531e4ed --- /dev/null +++ b/lib/kernel/test/logger_proxy_SUITE.erl @@ -0,0 +1,274 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_proxy_SUITE). + +-compile(export_all). + +%% -include_lib("common_test/include/ct.hrl"). +%% -include_lib("kernel/include/logger.hrl"). +%% -include_lib("kernel/src/logger_internal.hrl"). + +%% -define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ +%% ":"++integer_to_list(?LINE)). +%% -define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). +%% -define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). + +%% -define(MY_LOC(N),#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, +%% file=>?FILE, line=>?LINE-N}). + +%% -define(TRY(X), my_try(fun() -> X end)). + + +-define(HNAME,list_to_atom(lists:concat([?MODULE,"_",?FUNCTION_NAME]))). +-define(LOC,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY},line=>?LINE}). +-define(ENSURE_TIME,5000). + +suite() -> + [{timetrap,{seconds,30}}, + {ct_hooks,[logger_test_lib]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [basic, + emulator, + remote, + remote_emulator, + config, + restart_after, + terminate]. + +%%%----------------------------------------------------------------- +%%% Test cases +basic(_Config) -> + ok = logger:add_handler(?HNAME,?MODULE,#{config=>self()}), + logger_proxy ! {log,notice,"Log from: ~p; ~p",[?FUNCTION_NAME,?LINE],L1=?LOC}, + ok = ensure(L1), + logger_proxy ! {log,notice,[{test_case,?FUNCTION_NAME},{line,?LINE}],L2=?LOC}, + ok = ensure(L2), + logger_proxy:log({remote,node(),{log,notice, + "Log from: ~p; ~p", + [?FUNCTION_NAME,?LINE], + L3=?LOC}}), + ok = ensure(L3), + logger_proxy:log({remote,node(),{log,notice, + [{test_case,?FUNCTION_NAME}, + {line,?LINE}], + L4=?LOC}}), + ok = ensure(L4), + ok. +basic(cleanup,_Config) -> + ok = logger:remove_handler(?HNAME). + +emulator(_Config) -> + ok = logger:add_handler(?HNAME,?MODULE,#{config=>self()}), + Pid = spawn(fun() -> erlang:error(some_reason) end), + ok = ensure(#{pid=>Pid}), + ok. +emulator(cleanup,_Config) -> + ok = logger:remove_handler(?HNAME). + +remote(Config) -> + {ok,_,Node} = logger_test_lib:setup(Config,[{logger,[{proxy,#{}}]}]), + ok = logger:add_handler(?HNAME,?MODULE,#{config=>self()}), + L1 = ?LOC, spawn(Node,fun() -> logger:notice("Log from ~p; ~p",[?FUNCTION_NAME,?LINE],L1) end), + ok = ensure(L1), + L2 = ?LOC, spawn(Node,fun() -> logger:notice([{test_case,?FUNCTION_NAME},{line,?LINE}],L2) end), + ok = ensure(L2), + ok. +remote(cleanup,_Config) -> + ok = logger:remove_handler(?HNAME). + +remote_emulator(Config) -> + {ok,_,Node} = logger_test_lib:setup(Config,[{logger,[{proxy,#{}}]}]), + ok = logger:add_handler(?HNAME,?MODULE,#{config=>self()}), + Pid = spawn(Node,fun() -> erlang:error(some_reason) end), + ok = ensure(#{pid=>Pid}), + ok. +remote_emulator(cleanup,_Config) -> + ok = logger:remove_handler(?HNAME). + +config(_Config) -> + C1 = #{sync_mode_qlen:=SQ, + drop_mode_qlen:=DQ} = logger:get_proxy_config(), + C1 = logger_olp:get_opts(logger_proxy), + + %% Update the existing config with these two values + SQ1 = SQ+1, + DQ1 = DQ+1, + ok = logger:update_proxy_config(#{sync_mode_qlen=>SQ1, + drop_mode_qlen=>DQ1}), + C2 = logger:get_proxy_config(), % reads from ets table + C2 = logger_olp:get_opts(logger_proxy), % ensure consistency with process opts + C2 = C1#{sync_mode_qlen:=SQ1, + drop_mode_qlen:=DQ1}, + + %% Update the existing again with only one value + SQ2 = SQ+2, + ok = logger:update_proxy_config(#{sync_mode_qlen=>SQ2}), + C3 = logger:get_proxy_config(), + C3 = logger_olp:get_opts(logger_proxy), + C3 = C2#{sync_mode_qlen:=SQ2}, + + %% Set the config, i.e. merge with defaults + ok = logger:set_proxy_config(#{sync_mode_qlen=>SQ1}), + C4 = logger:get_proxy_config(), + C4 = logger_olp:get_opts(logger_proxy), + C4 = C1#{sync_mode_qlen:=SQ1}, + + %% Reset to default + ok = logger:set_proxy_config(#{}), + C5 = logger:get_proxy_config(), + C5 = logger_olp:get_opts(logger_proxy), + C5 = logger_proxy:get_default_config(), + + %% Errors + {error,{invalid_olp_config,_}} = + logger:set_proxy_config(#{faulty_key=>1}), + {error,{invalid_olp_config,_}} = + logger:set_proxy_config(#{sync_mode_qlen=>infinity}), + {error,{invalid_config,[]}} = logger:set_proxy_config([]), + + {error,{invalid_olp_config,_}} = + logger:update_proxy_config(#{faulty_key=>1}), + {error,{invalid_olp_config,_}} = + logger:update_proxy_config(#{sync_mode_qlen=>infinity}), + {error,{invalid_config,[]}} = logger:update_proxy_config([]), + + C5 = logger:get_proxy_config(), + C5 = logger_olp:get_opts(logger_proxy), + + ok. +config(cleanup,_Config) -> + _ = logger:set_logger_proxy(logger_proxy:get_default_config()), + ok. + +restart_after(_Config) -> + Restart = 3000, + ok = logger:update_proxy_config(#{overload_kill_enable => true, + overload_kill_qlen => 10, + overload_kill_restart_after => Restart}), + Proxy = whereis(logger_proxy), + Proxy = erlang:system_info(system_logger), + ProxyConfig = logger:get_proxy_config(), + ProxyConfig = logger_olp:get_opts(logger_proxy), + + Ref = erlang:monitor(process,Proxy), + spawn(fun() -> + [logger_proxy ! {log,debug, + [{test_case,?FUNCTION_NAME}, + {line,?LINE}], + ?LOC} || _ <- lists:seq(1,100)] + end), + receive + {'DOWN',Ref,_,_,_Reason} -> + undefined = erlang:system_info(system_logger), + timer:sleep(Restart), + poll_restarted(10) + after 5000 -> + ct:fail(proxy_not_terminated) + end, + + Proxy1 = whereis(logger_proxy), + Proxy1 = erlang:system_info(system_logger), + ProxyConfig = logger:get_proxy_config(), + ProxyConfig = logger_olp:get_opts(logger_proxy), + + ok. +restart_after(cleanup,_Config) -> + _ = logger:set_logger_proxy(logger_proxy:get_default_config()), + ok. + +%% Test that system_logger flag is set to logger process if +%% logger_proxy terminates for other reason than overloaded. +terminate(_Config) -> + Logger = whereis(logger), + Proxy = whereis(logger_proxy), + Proxy = erlang:system_info(system_logger), + ProxyConfig = logger:get_proxy_config(), + ProxyConfig = logger_olp:get_opts(logger_proxy), + + Ref = erlang:monitor(process,Proxy), + ok = logger_olp:stop(Proxy), + receive + {'DOWN',Ref,_,_,_Reason} -> + Logger = erlang:system_info(system_logger), + logger_proxy:restart(), + poll_restarted(10) + after 5000 -> + ct:fail(proxy_not_terminated) + end, + + Proxy1 = whereis(logger_proxy), + Proxy1 = erlang:system_info(system_logger), + ProxyConfig = logger:get_proxy_config(), + ProxyConfig = logger_olp:get_opts(logger_proxy), + + ok. + +%%%----------------------------------------------------------------- +%%% Internal functions + +poll_restarted(0) -> + ct:fail(proxy_not_restarted); +poll_restarted(N) -> + timer:sleep(1000), + case whereis(logger_proxy) of + undefined -> + poll_restarted(N-1); + _Pid -> + ok + end. + +%% Logger handler callback +log(#{meta:=Meta},#{config:=Pid}) -> + Pid ! {logged,Meta}. + +%% Check that the log from the logger callback function log/2 is received +ensure(Match) -> + receive {logged,Meta} -> + case maps:with(maps:keys(Match),Meta) of + Match -> ok; + _NoMatch -> {error,Match,Meta,test_server:messages_get()} + end + after ?ENSURE_TIME -> {error,Match,test_server:messages_get()} + end. diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index eb17a6d857..b2c2c8ba67 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -25,10 +25,15 @@ -include_lib("kernel/include/logger.hrl"). -include_lib("kernel/src/logger_internal.hrl"). -include_lib("kernel/src/logger_h_common.hrl"). +-include_lib("kernel/src/logger_olp.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("kernel/include/file.hrl"). --define(check_no_log, [] = test_server:messages_get()). +-define(check_no_log, + begin + timer:sleep(?IDLE_DETECT_TIME*2), + [] = test_server:messages_get() + end). -define(check(Expected), receive {log,Expected} -> @@ -115,7 +120,6 @@ all() -> crash_std_h_to_file, crash_std_h_to_disk_log, bad_input, - info_and_reset, reconfig, file_opts, sync, @@ -137,7 +141,8 @@ all() -> mem_kill_std, restart_after, handler_requests_under_load, - recreate_deleted_log + recreate_deleted_log, + reopen_changed_log ]. add_remove_instance_tty(_Config) -> @@ -209,9 +214,9 @@ default_formatter(_Config) -> filter_config(_Config) -> ok = logger:add_handler(?MODULE,logger_std_h,#{}), {ok,#{config:=HConfig}=Config} = logger:get_handler_config(?MODULE), - HConfig = maps:without([handler_pid,mode_tab],HConfig), + HConfig = maps:without([olp],HConfig), - FakeFullHConfig = HConfig#{handler_pid=>self(),mode_tab=>erlang:make_ref()}, + FakeFullHConfig = HConfig#{olp=>{regname,self(),erlang:make_ref()}}, #{config:=HConfig} = logger_std_h:filter_config(Config#{config=>FakeFullHConfig}), ok. @@ -246,13 +251,13 @@ errors(Config) -> _ -> NoDir = lists:concat(["/",?MODULE,"_dir"]), {error, - {handler_not_added,{{open_failed,NoDir,eacces},_}}} = + {handler_not_added,{open_failed,NoDir,eacces}}} = logger:add_handler(myh2,logger_std_h, #{config=>#{type=>{file,NoDir}}}) end, {error, - {handler_not_added,{{open_failed,Log,_},_}}} = + {handler_not_added,{open_failed,Log,_}}} = logger:add_handler(myh3,logger_std_h, #{config=>#{type=>{file,Log,[bad_file_opt]}}}), @@ -320,19 +325,16 @@ config_fail(_Config) -> #{config => #{restart_type => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{handler_not_added,{invalid_config,logger_std_h, - {invalid_levels,#{drop_mode_qlen:=1}}}}} = + {error,{handler_not_added,{invalid_olp_levels,#{drop_mode_qlen:=1}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{drop_mode_qlen=>1}}), - {error,{handler_not_added,{invalid_config,logger_std_h, - {invalid_levels,#{sync_mode_qlen:=43, - drop_mode_qlen:=42}}}}} = + {error,{handler_not_added,{invalid_olp_levels,#{sync_mode_qlen:=43, + drop_mode_qlen:=42}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{sync_mode_qlen=>43, drop_mode_qlen=>42}}), - {error,{handler_not_added,{invalid_config,logger_std_h, - {invalid_levels,#{drop_mode_qlen:=43, - flush_qlen:=42}}}}} = + {error,{handler_not_added,{invalid_olp_levels,#{drop_mode_qlen:=43, + flush_qlen:=42}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{drop_mode_qlen=>43, flush_qlen=>42}}), @@ -344,7 +346,7 @@ config_fail(_Config) -> logger:set_handler_config(?MODULE,config, #{type=>{file,"file"}}), - {error,{invalid_config,logger_std_h,{invalid_levels,_}}} = + {error,{invalid_olp_levels,_}} = logger:set_handler_config(?MODULE,config, #{sync_mode_qlen=>100, flush_qlen=>99}), @@ -355,9 +357,7 @@ config_fail(_Config) -> %% Read-only fields may (accidentially) be included in the change, %% but it won't take effect {ok,C} = logger:get_handler_config(?MODULE), - ok = logger:set_handler_config(?MODULE,config, - #{handler_pid=>self(), - mode_tab=>erlang:make_ref()}), + ok = logger:set_handler_config(?MODULE,config,#{olp=>dummyvalue}), {ok,C} = logger:get_handler_config(?MODULE), ok. @@ -425,10 +425,13 @@ crash_std_h(Config,Func,Var,Type,Log) -> %% logger would send the log event to the logger process here instead %% of logging it itself. log_on_remote_node(Node,Msg) -> + Pid = self(), _ = spawn_link(Node, fun() -> erlang:group_leader(whereis(user),self()), - logger:notice(Msg) + logger:notice(Msg), + Pid ! done end), + receive done -> ok end, ok. @@ -456,14 +459,7 @@ sync_and_read(Node,file,Log) -> end. bad_input(_Config) -> - {error,{badarg,{filesync,["BadType"]}}} = logger_std_h:filesync("BadType"), - {error,{badarg,{info,["BadType"]}}} = logger_std_h:info("BadType"), - {error,{badarg,{reset,["BadType"]}}} = logger_std_h:reset("BadType"). - - -info_and_reset(_Config) -> - #{id := ?STANDARD_HANDLER} = logger_std_h:info(?STANDARD_HANDLER), - ok = logger_std_h:reset(?STANDARD_HANDLER). + {error,{badarg,{filesync,["BadType"]}}} = logger_std_h:filesync("BadType"). reconfig(Config) -> Dir = ?config(priv_dir,Config), @@ -473,9 +469,10 @@ reconfig(Config) -> filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), - #{id := ?MODULE, - handler_state := #{type := standard_io, - file_ctrl_pid := FileCtrlPid}, + #{%id := ?MODULE, + cb_state:=#{handler_state := #{type := standard_io, + file_ctrl_pid := FileCtrlPid}, + filesync_repeat_interval := no_repeat}, sync_mode_qlen := ?SYNC_MODE_QLEN, drop_mode_qlen := ?DROP_MODE_QLEN, flush_qlen := ?FLUSH_QLEN, @@ -485,9 +482,8 @@ reconfig(Config) -> overload_kill_enable := ?OVERLOAD_KILL_ENABLE, overload_kill_qlen := ?OVERLOAD_KILL_QLEN, overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, - overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval := no_repeat} = DefaultInfo = - logger_std_h:info(?MODULE), + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER} = + logger_olp:info(h_proc_name()), {ok, #{config:= @@ -518,9 +514,10 @@ reconfig(Config) -> overload_kill_mem_size => 10000000, overload_kill_restart_after => infinity, filesync_repeat_interval => 5000}), - #{id := ?MODULE, - handler_state := #{type := standard_io, - file_ctrl_pid := FileCtrlPid}, + #{%id := ?MODULE, + cb_state := #{handler_state := #{type := standard_io, + file_ctrl_pid := FileCtrlPid}, + filesync_repeat_interval := no_repeat}, sync_mode_qlen := 1, drop_mode_qlen := 2, flush_qlen := 3, @@ -530,8 +527,7 @@ reconfig(Config) -> overload_kill_enable := true, overload_kill_qlen := 100000, overload_kill_mem_size := 10000000, - overload_kill_restart_after := infinity, - filesync_repeat_interval := no_repeat} = Info = logger_std_h:info(?MODULE), + overload_kill_restart_after := infinity} = logger_olp:info(h_proc_name()), {ok,#{config := #{type := standard_io, @@ -613,7 +609,7 @@ file_opts(Config) -> Log = filename:join(Dir, lists:concat([?FUNCTION_NAME,".log"])), BadFileOpts = [raw], BadType = {file,Log,BadFileOpts}, - {error,{handler_not_added,{{open_failed,Log,enoent},_}}} = + {error,{handler_not_added,{open_failed,Log,enoent}}} = logger:add_handler(?MODULE, logger_std_h, #{config => #{type => BadType}}), @@ -626,7 +622,9 @@ file_opts(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), - #{handler_state := #{type := OkType}} = logger_std_h:info(?MODULE), + #{cb_state := #{handler_state := #{type := OkType}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{type := OkType}}} = logger:get_handler_config(?MODULE), logger:notice(M1=?msg,?domain), ?check(M1), B1 = ?bin(M1), @@ -675,11 +673,8 @@ sync(Config) -> %% a filesync is still performed when handler goes idle ok = logger:update_handler_config(?MODULE, config, #{filesync_repeat_interval => no_repeat}), - no_repeat = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), - %% The following timer is to make sure the time from last log - %% ("second") to next ("third") is long enough, so the a flush is - %% triggered by the idle timeout between "thrid" and "fourth". - timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + no_repeat = maps:get(filesync_repeat_interval, + maps:get(cb_state, logger_olp:info(h_proc_name()))), start_tracer([{logger_std_h, write_to_dev, 5}, {file, datasync, 1}], [{logger_std_h, write_to_dev, <<"third\n">>}, @@ -688,22 +683,24 @@ sync(Config) -> {file,datasync}]), logger:notice("third", ?domain), %% wait for automatic filesync - timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + timer:sleep(?IDLE_DETECT_TIME*2), logger:notice("fourth", ?domain), %% wait for automatic filesync - check_tracer(?IDLE_DETECT_TIME_MSEC*2), + check_tracer(?IDLE_DETECT_TIME*2), %% switch repeated filesync on and verify that the looping works SyncInt = 1000, WaitT = 4500, OneSync = {logger_h_common,handle_cast,repeated_filesync}, %% receive 1 repeated_filesync per sec - start_tracer([{logger_h_common,handle_cast,2}], + start_tracer([{{logger_h_common,handle_cast,2}, + [{[repeated_filesync,'_'],[],[]}]}], [OneSync || _ <- lists:seq(1, trunc(WaitT/SyncInt))]), ok = logger:update_handler_config(?MODULE, config, #{filesync_repeat_interval => SyncInt}), - SyncInt = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), + SyncInt = maps:get(filesync_repeat_interval, + maps:get(cb_state,logger_olp:info(h_proc_name()))), timer:sleep(WaitT), ok = logger:update_handler_config(?MODULE, config, #{filesync_repeat_interval => no_repeat}), @@ -764,8 +761,6 @@ sync_failure(Config) -> ok = rpc:call(Node, logger, update_handler_config, [?STANDARD_HANDLER, config, #{filesync_repeat_interval => SyncInt}]), - Info = rpc:call(Node, logger_std_h, info, [?STANDARD_HANDLER]), - SyncInt = maps:get(filesync_repeat_interval, Info), ok = log_on_remote_node(Node, "Logged1"), ?check_no_log, @@ -1095,7 +1090,7 @@ qlen_kill_new(Config) -> receive {'DOWN', MRef, _, _, Info} -> case Info of - {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + {shutdown,{overloaded,QLen,Mem}} -> ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); killed -> ct:pal("Slow shutdown, handler process was killed!", []) @@ -1105,7 +1100,7 @@ qlen_kill_new(Config) -> ok after 5000 -> - Info = logger_std_h:info(?MODULE), + Info = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info]), ct:fail("Handler not dead! It should not have survived this!") end. @@ -1146,7 +1141,7 @@ mem_kill_new(Config) -> receive {'DOWN', MRef, _, _, Info} -> case Info of - {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + {shutdown,{overloaded,QLen,Mem}} -> ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); killed -> ct:pal("Slow shutdown, handler process was killed!", []) @@ -1156,7 +1151,7 @@ mem_kill_new(Config) -> ok after 5000 -> - Info = logger_std_h:info(?MODULE), + Info = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info]), ct:fail("Handler not dead! It should not have survived this!") end. @@ -1187,7 +1182,7 @@ restart_after(Config) -> ok after 5000 -> - Info1 = logger_std_h:info(?MODULE), + Info1 = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info1]), ct:fail("Handler not dead! It should not have survived this!") end, @@ -1212,7 +1207,7 @@ restart_after(Config) -> ok after 5000 -> - Info2 = logger_std_h:info(?MODULE), + Info2 = logger_olp:info(h_proc_name()), ct:pal("Handler state = ~p", [Info2]), ct:fail("Handler not dead! It should not have survived this!") end, @@ -1234,11 +1229,15 @@ handler_requests_under_load(Config) -> flush_qlen => 2000, burst_limit_enable => false}}, ok = logger:update_handler_config(?MODULE, NewHConfig), - Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, - {info,[]}, - {reset,[]}, - {change_config,[]}]) - end), + Pid = spawn_link( + fun() -> send_requests(1,[{logger_std_h,filesync,[?MODULE],[]}, + {logger_olp,info,[h_proc_name()],[]}, + {logger_olp,reset,[h_proc_name()],[]}, + {logger,update_handler_config, + [?MODULE, config, + #{overload_kill_enable => false}], + []}]) + end), Sent = send_burst({t,10000}, seq, {chars,79}, notice), Pid ! {self(),finish}, ReqResult = receive {Pid,Result} -> Result end, @@ -1249,8 +1248,9 @@ handler_requests_under_load(Config) -> [E || E <- Res, is_tuple(E) andalso (element(1,E) == error)] end, - Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], - NoOfReqs = lists:foldl(fun({_,Res}, N) -> N + length(Res) end, 0, ReqResult), + Errors = [{Func,FindError(Res)} || {_,Func,_,Res} <- ReqResult], + NoOfReqs = lists:foldl(fun({_,_,_,Res}, N) -> N + length(Res) end, + 0, ReqResult), ct:pal("~w requests made. Errors: ~n~p", [NoOfReqs,Errors]), ok = file_delete(Log). handler_requests_under_load(cleanup, _Config) -> @@ -1270,24 +1270,31 @@ recreate_deleted_log(Config) -> recreate_deleted_log(cleanup, _Config) -> ok = stop_handler(?MODULE). +reopen_changed_log(Config) -> + {Log,_HConfig,_StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + logger:notice("first",?domain), + logger_std_h:filesync(?MODULE), + ok = file:rename(Log,Log++".old"), + ok = file:write_file(Log,""), + logger:notice("second",?domain), + logger_std_h:filesync(?MODULE), + {ok,<<"first\n">>} = file:read_file(Log++".old"), + {ok,<<"second\n">>} = file:read_file(Log), + ok. +reopen_changed_log(cleanup, _Config) -> + ok = stop_handler(?MODULE). + %%%----------------------------------------------------------------- %%% -send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> +send_requests(TO, Reqs = [{Mod,Func,Args,Res}|Rs]) -> receive {From,finish} -> From ! {self(),Reqs} after TO -> - Result = - case Req of - change_config -> - logger:update_handler_config(HName, config, - #{overload_kill_enable => - false}); - Func -> - logger_std_h:Func(HName) - end, - send_requests(HName, TO, Rs ++ [{Req,[Result|Res]}]) + Result = apply(Mod,Func,Args), + send_requests(TO, Rs ++ [{Mod,Func,Args,[Result|Res]}]) end. @@ -1624,7 +1631,8 @@ start_tracer(Trace,Expected) -> Pid = self(), FileCtrlPid = maps:get(file_ctrl_pid, maps:get(handler_state, - logger_std_h:info(?MODULE))), + maps:get(cb_state, + logger_olp:info(h_proc_name())))), dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), dbg:p(whereis(h_proc_name()),[c]), dbg:p(FileCtrlPid,[c]), @@ -1632,7 +1640,9 @@ start_tracer(Trace,Expected) -> ok. tpl([{M,F,A}|Trace]) -> - {ok,Match} = dbg:tpl(M,F,A,[]), + tpl([{{M,F,A},[]}|Trace]); +tpl([{{M,F,A},MS}|Trace]) -> + {ok,Match} = dbg:tpl(M,F,A,MS), case lists:keyfind(matched,1,Match) of {_,_,1} -> ok; diff --git a/lib/kernel/test/logger_stress_SUITE.erl b/lib/kernel/test/logger_stress_SUITE.erl new file mode 100644 index 0000000000..4072e8c86a --- /dev/null +++ b/lib/kernel/test/logger_stress_SUITE.erl @@ -0,0 +1,456 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_stress_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_h_common.hrl"). + +-ifdef(SAVE_STATS). + -define(COLLECT_STATS(_All_,_Procs_), + ct:pal("~p",[stats(_All_,_Procs_)])). +-else. + -define(COLLECT_STATS(_All_,_Procs__), ok). +-endif. + +-define(TEST_DURATION,120). % seconds + +suite() -> + [{timetrap,{minutes,3}}, + {ct_hooks,[logger_test_lib]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [allow_events, + reject_events, + std_handler, + disk_log_handler, + emulator_events, + remote_events, + remote_to_disk_log, + remote_emulator_events, + remote_emulator_to_disk_log]. + +%%%----------------------------------------------------------------- +%%% Test cases +%%%----------------------------------------------------------------- +%% Time from log macro call to handler callback +allow_events(Config) -> + {ok,_,Node} = + logger_test_lib:setup(Config, + [{logger, + [{handler,default,?MODULE,#{}}]}, + {logger_level,notice}]), + N = 100000, + {T,_} = timer:tc(fun() -> rpc:call(Node,?MODULE,nlogs,[N]) end), + IOPS = N * 1000/T, % log events allowed per millisecond + ct_event:notify(#event{name = benchmark_data, + data = [{value,IOPS}]}), + {comment,io_lib:format("~.2f accepted events pr millisecond", + [IOPS])}. + +%% Time from log macro call to reject (log level) +reject_events(Config) -> + {ok,_,Node} = + logger_test_lib:setup(Config, + [{logger, + [{handler,default,?MODULE,#{}}]}, + {logger_level,error}]), + N = 1000000, + {T,_} = timer:tc(fun() -> rpc:call(Node,?MODULE,nlogs,[N]) end), + IOPS = N * 1000/T, % log events rejected per millisecond + ct_event:notify(#event{name = benchmark_data, + data = [{value,IOPS}]}), + {comment,io_lib:format("~.2f rejected events pr millisecond", + [IOPS])}. + +%% Cascading failure that produce gen_server and proc_lib reports - +%% how many of the produced log events are actually written to a log +%% with logger_std_h file handler. +std_handler(Config) -> + {ok,_,Node} = + logger_test_lib:setup(Config, + [{logger, + [{handler,default,logger_std_h, + #{config=>#{type=>{file,"default.log"}}}}]}]), + + cascade({Node,{logger_backend,log_allowed,2},[]}, + {Node,{logger_std_h,write,4},[{default,logger_std_h_default}]}, + fun otp_cascading/0). +std_handler(cleanup,_Config) -> + _ = file:delete("default.log"), + ok. + +%% Cascading failure that produce gen_server and proc_lib reports - +%% how many of the produced log events are actually written to a log +%% with logger_disk_log_h wrap file handler. +disk_log_handler(Config) -> + {ok,_,Node} = + logger_test_lib:setup(Config, + [{logger, + [{handler,default,logger_disk_log_h,#{}}]}]), + cascade({Node,{logger_backend,log_allowed,2},[]}, + {Node,{logger_disk_log_h,write,4}, + [{default,logger_disk_log_h_default}]}, + fun otp_cascading/0). +disk_log_handler(cleanup,_Config) -> + Files = filelib:wildcard("default.log.*"), + [_ = file:delete(F) || F <- Files], + ok. + +%% Cascading failure that produce log events from the emulator - how +%% many of the produced log events pass through the proxy. +emulator_events(Config) -> + {ok,_,Node} = + logger_test_lib:setup(Config, + [{logger, + [{handler,default,?MODULE,#{}}]}]), + cascade({Node,{?MODULE,producer,0},[]}, + {Node,{?MODULE,log,2},[{proxy,logger_proxy}]}, + fun em_cascading/0). + +%% Cascading failure that produce gen_server and proc_lib reports on +%% remote node - how many of the produced log events pass through the +%% proxy. +remote_events(Config) -> + {ok,_,Node1} = + logger_test_lib:setup([{postfix,1}|Config], + [{logger, + [{handler,default,?MODULE,#{}}]}]), + {ok,_,Node2} = + logger_test_lib:setup([{postfix,2}|Config],[]), + cascade({Node2,{logger_backend,log_allowed,2},[{remote_proxy,logger_proxy}]}, + {Node1,{?MODULE,log,2},[{local_proxy,logger_proxy}]}, + fun otp_cascading/0). + +%% Cascading failure that produce gen_server and proc_lib reports on +%% remote node - how many of the produced log events are actually +%% written to a log with logger_disk_log_h wrap file handler. +remote_to_disk_log(Config) -> + {ok,_,Node1} = + logger_test_lib:setup([{postfix,1}|Config], + [{logger, + [{handler,default,logger_disk_log_h,#{}}]}]), + {ok,_,Node2} = + logger_test_lib:setup([{postfix,2}|Config],[]), + cascade({Node2,{logger_backend,log_allowed,2},[{remote_proxy,logger_proxy}]}, + {Node1,{logger_disk_log_h,write,4}, + [{local_proxy,logger_proxy}, + {local_default,logger_disk_log_h_default}]}, + fun otp_cascading/0). +remote_to_disk_log(cleanup,_Config) -> + Files = filelib:wildcard("default.log.*"), + [_ = file:delete(F) || F <- Files], + ok. + +%% Cascading failure that produce log events from the emulator on +%% remote node - how many of the produced log events pass through the +%% proxy. +remote_emulator_events(Config) -> + {ok,_,Node1} = + logger_test_lib:setup([{postfix,1}|Config], + [{logger, + [{handler,default,?MODULE,#{}}]}]), + {ok,_,Node2} = + logger_test_lib:setup([{postfix,2}|Config],[]), + cascade({Node2,{?MODULE,producer,0},[{remote_proxy,logger_proxy}]}, + {Node1,{?MODULE,log,2},[{local_proxy,logger_proxy}]}, + fun em_cascading/0). + +%% Cascading failure that produce log events from the emulator on +%% remote node - how many of the produced log events are actually +%% written to a log with logger_disk_log_h wrap file handler. +remote_emulator_to_disk_log(Config) -> + {ok,_,Node1} = + logger_test_lib:setup([{postfix,1}|Config], + [{logger, + [{handler,default,logger_disk_log_h,#{}}]}]), + {ok,_,Node2} = + logger_test_lib:setup([{postfix,2}|Config],[]), + cascade({Node2,{?MODULE,producer,0},[{remote_proxy,logger_proxy}]}, + {Node1,{logger_disk_log_h,write,4}, + [{local_proxy,logger_proxy}, + {local_default,logger_disk_log_h_default}]}, + fun em_cascading/0). +remote_emulator_to_disk_log(cleanup,_Config) -> + Files = filelib:wildcard("default.log.*"), + [_ = file:delete(F) || F <- Files], + ok. + +%%%----------------------------------------------------------------- +%%% Internal functions +nlogs(N) -> + group_leader(whereis(user),self()), + Str = "\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "[\\]^_`abcdefghijklmnopqr", + [?LOG_NOTICE(Str) || _ <- lists:seq(1,N)], + ok. + +%% cascade(ProducerInfo,ConsumerInfo,TestFun) +cascade({PNode,PMFA,_PStatProcs},{CNode,CMFA,_CStatProcs},TestFun) -> + Tab = ets:new(counter,[set,public]), + ets:insert(Tab,{producer,0}), + ets:insert(Tab,{consumer,0}), + dbg:tracer(process,{fun tracer/2,{Tab,PNode,CNode}}), + dbg:n(PNode), + dbg:n(CNode), + dbg:cn(node()), + dbg:p(all,[call,arity]), + dbg:tpl(PMFA,[]), + dbg:tpl(CMFA,[]), + + Pid = rpc:call(CNode,?MODULE,wrap_test,[PNode,TestFun]), + MRef = erlang:monitor(process,Pid), + TO = ?TEST_DURATION*1000, + receive {'DOWN',MRef,_,_,Reason} -> + ct:fail({remote_pid_down,Reason}) + after TO -> + All = ets:lookup_element(Tab,producer,2), + Written = ets:lookup_element(Tab,consumer,2), + dbg:stop_clear(), + ?COLLECT_STATS(All, + [{PNode,P,Id} || {Id,P} <- _PStatProcs] ++ + [{CNode,P,Id} || {Id,P} <- _CStatProcs]), + Ratio = Written/All * 100, + ct_event:notify(#event{name = benchmark_data, + data = [{value,Ratio}]}), + {comment,io_lib:format("~p % (~p written, ~p produced)", + [round(Ratio),Written,All])} + end. + +wrap_test(Fun) -> + wrap_test(node(),Fun). +wrap_test(Node,Fun) -> + reset(), + group_leader(whereis(user),self()), + rpc:call(Node,?MODULE,do_fun,[Fun]). + +do_fun(Fun) -> + reset(), + Fun(). + +reset() -> + reset([logger_std_h_default, logger_disk_log_h_default, logger_proxy]). +reset([P|Ps]) -> + is_pid(whereis(P)) andalso logger_olp:reset(P), + reset(Ps); +reset([]) -> + ok. + + +tracer({trace,_,call,{?MODULE,producer,_}},{Tab,_PNode,_CNode}=S) -> + ets:update_counter(Tab,producer,1), + S; +tracer({trace,Pid,call,{logger_backend,log_allowed,_}},{Tab,PNode,_CNode}=S) when node(Pid)=:=PNode -> + ets:update_counter(Tab,producer,1), + S; +tracer({trace,_,call,{?MODULE,log,_}},{Tab,_PNode,_CNode}=S) -> + ets:update_counter(Tab,consumer,1), + S; +tracer({trace,_,call,{_,write,_}},{Tab,_PNode,_CNode}=S) -> + ets:update_counter(Tab,consumer,1), + S; +tracer(_,S) -> + S. + + +%%%----------------------------------------------------------------- +%%% Collect statistics +-define(STAT_KEYS, + [burst_drops, + calls, + casts, + drops, + flushed, + flushes, + freq, + last_qlen, + max_qlen, + time, + writes]). +-define(EVENT_KEYS, + [calls,casts,flushed]). + +stats(All,Procs) -> + NI = [{Id,rpc:call(N,logger_olp,info,[P])} || {N,P,Id}<-Procs], + [{all,All}|[stats(Id,I,All) || {Id,I} <- NI]]. + +stats(Id,Info,All) -> + S = maps:with(?STAT_KEYS,Info), + AllOnProc = lists:sum(maps:values(maps:with(?EVENT_KEYS,S))), + if All>0 -> + Writes = maps:get(writes,S), + {_,ActiveTime} = maps:get(time,S), + Rate = round(100*Writes/All), + RateOnProc = + if AllOnProc>0 -> + round(100*Writes/AllOnProc); + true -> + 0 + end, + AvFreq = + if ActiveTime>0 -> + round(Writes/ActiveTime); + true -> + 0 + end, + {Id, + {stats,S}, + {rate,Rate}, + {rate_on_proc,RateOnProc}, + {av_freq,AvFreq}}; + true -> + {Id,none} + end. + +%%%----------------------------------------------------------------- +%%% Spawn a lot of processes that crash repeatedly, causing a lot of +%%% error reports from the emulator. +em_cascading() -> + spawn(fun() -> super() end). + +super() -> + process_flag(trap_exit,true), + spawn_link(fun server/0), + [spawn_link(fun client/0) || _<-lists:seq(1,10000)], + super_loop(). + +super_loop() -> + receive + {'EXIT',_,server} -> + spawn_link(fun server/0), + super_loop(); + {'EXIT',_,_} -> + _L = lists:sum(lists:seq(1,10000)), + spawn_link(fun client/0), + super_loop() + end. + +client() -> + receive + after 1 -> + case whereis(server) of + Pid when is_pid(Pid) -> + ok; + undefined -> + producer(), + erlang:error(some_exception) + end + end, + client(). + +server() -> + register(server,self()), + receive + after 3000 -> + exit(server) + end. + + +%%%----------------------------------------------------------------- +%%% Create a supervisor tree with processes that crash repeatedly, +%%% causing a lot of supervisor reports and crashreports +otp_cascading() -> + {ok,Pid} = supervisor:start_link({local,otp_super}, ?MODULE, [otp_super]), + unlink(Pid), + Pid. + +otp_server_sup() -> + supervisor:start_link({local,otp_server_sup},?MODULE,[otp_server_sup]). + +otp_client_sup(N) -> + supervisor:start_link({local,otp_client_sup},?MODULE,[otp_client_sup,N]). + +otp_server() -> + gen_server:start_link({local,otp_server},?MODULE,[otp_server],[]). + +otp_client() -> + gen_server:start_link(?MODULE,[otp_client],[]). + +init([otp_super]) -> + {ok, {{one_for_one, 200, 10}, + [{client_sup, + {?MODULE, otp_client_sup, [10000]}, + permanent, 1000, supervisor, [?MODULE]}, + {server_sup, + {?MODULE, otp_server_sup, []}, + permanent, 1000, supervisor, [?MODULE]} + ]}}; +init([otp_server_sup]) -> + {ok, {{one_for_one, 2, 10}, + [{server, + {?MODULE, otp_server, []}, + permanent, 1000, worker, [?MODULE]} + ]}}; +init([otp_client_sup,N]) -> + spawn(fun() -> + [supervisor:start_child(otp_client_sup,[]) + || _ <- lists:seq(1,N)] + end), + {ok, {{simple_one_for_one, N*10, 1}, + [{client, + {?MODULE, otp_client, []}, + permanent, 1000, worker, [?MODULE]} + ]}}; +init([otp_server]) -> + {ok, server, 10000}; +init([otp_client]) -> + {ok, client,1}. + +handle_info(timeout, client) -> + true = is_pid(whereis(otp_server)), + {noreply,client,1}; +handle_info(timeout, server) -> + exit(self(), some_error). + +%%%----------------------------------------------------------------- +%%% Logger callbacks +log(_LogEvent,_Config) -> + ok. + +%%%----------------------------------------------------------------- +%%% Function to trace on for counting produced emulator messages +producer() -> + ok. diff --git a/lib/kernel/test/logger_test_lib.erl b/lib/kernel/test/logger_test_lib.erl index 81eb9ce5eb..be4bc427fb 100644 --- a/lib/kernel/test/logger_test_lib.erl +++ b/lib/kernel/test/logger_test_lib.erl @@ -28,11 +28,17 @@ post_end_per_testcase/5, post_end_per_suite/3]). setup(Config,Vars) -> + Postfix = case proplists:get_value(postfix, Config) of + undefined -> ""; + P -> ["_",P] + end, FuncStr = lists:concat([proplists:get_value(suite, Config), "_", - proplists:get_value(tc, Config)]), + proplists:get_value(tc, Config)| + Postfix]), ConfigFileName = filename:join(proplists:get_value(priv_dir, Config), FuncStr), file:write_file(ConfigFileName ++ ".config", io_lib:format("[{kernel, ~p}].",[Vars])), - case test_server:start_node(proplists:get_value(tc, Config), slave, + Sname = lists:concat([proplists:get_value(tc,Config)|Postfix]), + case test_server:start_node(Sname, slave, [{args, ["-pa ",filename:dirname(code:which(?MODULE)), " -boot start_sasl -kernel start_timer true " "-config ",ConfigFileName]}]) of diff --git a/lib/megaco/doc/src/megaco.xml b/lib/megaco/doc/src/megaco.xml index d4a7451bfc..c7bcdfcd6f 100644 --- a/lib/megaco/doc/src/megaco.xml +++ b/lib/megaco/doc/src/megaco.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco.xml</file> </header> - <module>megaco</module> + <module since="">megaco</module> <modulesummary>Main API of the Megaco application</modulesummary> <description> <p>Interface module for the Megaco application</p> @@ -135,7 +135,7 @@ megaco_incr_timer() = #megaco_incr_timer{} <funcs> <func> - <name>start() -> ok | {error, Reason}</name> + <name since="">start() -> ok | {error, Reason}</name> <fsummary>Starts the Megaco application</fsummary> <type> <v>Reason = term()</v> @@ -153,7 +153,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>stop() -> ok | {error, Reason}</name> + <name since="">stop() -> ok | {error, Reason}</name> <fsummary>Stops the Megaco application</fsummary> <type> <v>Reason = term()</v> @@ -166,7 +166,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>start_user(UserMid, Config) -> ok | {error, Reason}</name> + <name since="">start_user(UserMid, Config) -> ok | {error, Reason}</name> <fsummary>Initial configuration of a user</fsummary> <type> <v>UserMid = megaco_mid()</v> @@ -188,7 +188,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>stop_user(UserMid) -> ok | {error, Reason}</name> + <name since="">stop_user(UserMid) -> ok | {error, Reason}</name> <fsummary>Delete the configuration of a user</fsummary> <type> <v>UserMid = megaco_mid()</v> @@ -203,8 +203,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>user_info(UserMid) -> [{Item, Value}]</name> - <name>user_info(UserMid, Item) -> Value | exit(Reason)</name> + <name since="">user_info(UserMid) -> [{Item, Value}]</name> + <name since="">user_info(UserMid, Item) -> Value | exit(Reason)</name> <fsummary>Lookup user information</fsummary> <type> <v>Handle = user_info_handle()</v> @@ -703,7 +703,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>update_user_info(UserMid, Item, Value) -> ok | {error, Reason}</name> + <name since="">update_user_info(UserMid, Item, Value) -> ok | {error, Reason}</name> <fsummary>Update information about a user</fsummary> <type> <v>UserMid = megaco_mid() </v> @@ -721,8 +721,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>conn_info(ConnHandle) -> [{Item, Value}]</name> - <name>conn_info(ConnHandle, Item) -> Value | exit(Reason)</name> + <name since="">conn_info(ConnHandle) -> [{Item, Value}]</name> + <name since="">conn_info(ConnHandle, Item) -> Value | exit(Reason)</name> <fsummary>Lookup information about an active connection</fsummary> <type> <v>ConnHandle = #megaco_conn_handle{}</v> @@ -1222,7 +1222,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>update_conn_info(ConnHandle, Item, Value) -> ok | {error, Reason}</name> + <name since="">update_conn_info(ConnHandle, Item, Value) -> ok | {error, Reason}</name> <fsummary>Update information about an active connection</fsummary> <type> <v>ConnHandle = #megaco_conn_handle{}</v> @@ -1241,8 +1241,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>system_info() -> [{Item, Value}] | exit(Reason)</name> - <name>system_info(Item) -> Value | exit(Reason)</name> + <name since="">system_info() -> [{Item, Value}] | exit(Reason)</name> + <name since="">system_info(Item) -> Value | exit(Reason)</name> <fsummary>Lookup system information</fsummary> <type> <v>Item = system_info_item()</v> @@ -1289,7 +1289,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>info() -> Info</name> + <name since="">info() -> Info</name> <fsummary>All the information of the application</fsummary> <type> <v>Info = [{Key, Value}]</v> @@ -1311,8 +1311,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid) -> {ok, ConnHandle} | {error, Reason}</name> - <name>connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid, Extra) -> {ok, ConnHandle} | {error, Reason}</name> + <name since="">connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid) -> {ok, ConnHandle} | {error, Reason}</name> + <name since="">connect(ReceiveHandle, RemoteMid, SendHandle, ControlPid, Extra) -> {ok, ConnHandle} | {error, Reason}</name> <fsummary>Establish a "virtual" connection</fsummary> <type> <v>ReceiveHandle = #megaco_receive_handle{}</v> @@ -1436,7 +1436,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>disconnect(ConnHandle, DiscoReason) -> ok | {error, ErrReason}</name> + <name since="">disconnect(ConnHandle, DiscoReason) -> ok | {error, ErrReason}</name> <fsummary>Tear down a "virtual" connection</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -1454,7 +1454,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>call(ConnHandle, Actions, Options) -> {ProtocolVersion, UserReply}</name> + <name since="">call(ConnHandle, Actions, Options) -> {ProtocolVersion, UserReply}</name> <fsummary>Sends one or more transaction request(s) and waits for the reply</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -1545,7 +1545,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>cast(ConnHandle, Actions, Options) -> ok | {error, Reason}</name> + <name since="">cast(ConnHandle, Actions, Options) -> ok | {error, Reason}</name> <fsummary>Sends one or more transaction request(s) but does NOT wait for a reply</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -1582,7 +1582,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>encode_actions(ConnHandle, Actions, Options) -> {ok, BinOrBins} | {error, Reason}</name> + <name since="">encode_actions(ConnHandle, Actions, Options) -> {ok, BinOrBins} | {error, Reason}</name> <fsummary>Encode action requests for one or more transaction request(s)</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -1607,9 +1607,9 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>token_tag2string(Tag) -> Result</name> - <name>token_tag2string(Tag, EncoderMod) -> Result</name> - <name>token_tag2string(Tag, EncoderMod, Version) -> Result</name> + <name since="">token_tag2string(Tag) -> Result</name> + <name since="">token_tag2string(Tag, EncoderMod) -> Result</name> + <name since="">token_tag2string(Tag, EncoderMod, Version) -> Result</name> <fsummary>Convert a token tag to a string</fsummary> <type> <v>Tag = atom()</v> @@ -1635,7 +1635,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>cancel(ConnHandle, CancelReason) -> ok | {error, ErrReason}</name> + <name since="">cancel(ConnHandle, CancelReason) -> ok | {error, ErrReason}</name> <fsummary>Cancel all outstanding messages for this connection</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -1655,8 +1655,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) -> ok</name> - <name>process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg, Extra) -> ok</name> + <name since="">process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) -> ok</name> + <name since="">process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg, Extra) -> ok</name> <fsummary>Process a received message</fsummary> <type> <v>ReceiveHandle = #megaco_receive_handle{}</v> @@ -1755,8 +1755,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) -> ok</name> - <name>receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg, Extra) -> ok</name> + <name since="">receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) -> ok</name> + <name since="">receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg, Extra) -> ok</name> <fsummary>Process a received message</fsummary> <type> <v>ReceiveHandle = #megaco_receive_handle{}</v> @@ -1783,7 +1783,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>parse_digit_map(DigitMapBody) -> {ok, ParsedDigitMap} | {error, Reason}</name> + <name since="">parse_digit_map(DigitMapBody) -> {ok, ParsedDigitMap} | {error, Reason}</name> <fsummary>Parses a digit map body</fsummary> <type> <v>DigitMapBody = string()</v> @@ -1802,8 +1802,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>eval_digit_map(DigitMap) -> {ok, MatchResult} | {error, Reason}</name> - <name>eval_digit_map(DigitMap, Timers) -> {ok, MatchResult} | {error, Reason}</name> + <name since="">eval_digit_map(DigitMap) -> {ok, MatchResult} | {error, Reason}</name> + <name since="">eval_digit_map(DigitMap, Timers) -> {ok, MatchResult} | {error, Reason}</name> <fsummary>Collect digit map letters according to the digit map</fsummary> <type> <v>DigitMap = #'DigitMapValue'{} | parsed_digit_map()</v> @@ -1839,7 +1839,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>report_digit_event(DigitMapEvalPid, Events) -> ok | {error, Reason}</name> + <name since="">report_digit_event(DigitMapEvalPid, Events) -> ok | {error, Reason}</name> <fsummary>Send one or more events to the event collector process</fsummary> <type> <v>DigitMapEvalPid = pid()</v> @@ -1866,7 +1866,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>test_digit_event(DigitMap, Events) -> {ok, Kind, Letters} | {error, Reason}</name> + <name since="">test_digit_event(DigitMap, Events) -> {ok, Kind, Letters} | {error, Reason}</name> <fsummary>Feed digit map collector with events and return the result</fsummary> <type> <v>DigitMap = #'DigitMapValue'{} | parsed_digit_map()</v> @@ -1900,7 +1900,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>encode_sdp(SDP) -> {ok, PP} | {error, Reason}</name> + <name since="">encode_sdp(SDP) -> {ok, PP} | {error, Reason}</name> <fsummary>Encode an SDP construct</fsummary> <type> <v>SDP = sdp_property_parm() | sdp_property_group() | sdp_property_groups() | asn1_NOVALUE</v> @@ -1929,7 +1929,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>decode_sdp(PP) -> {ok, SDP} | {error, Reason}</name> + <name since="">decode_sdp(PP) -> {ok, SDP} | {error, Reason}</name> <fsummary>Decode an property parameter construct</fsummary> <type> <v>PP = property_parm() | property_group() | property_groups() | asn1_NOVALUE</v> @@ -1969,7 +1969,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>get_sdp_record_from_PropertGroup(Type, PG) -> [sdp()]</name> + <name since="">get_sdp_record_from_PropertGroup(Type, PG) -> [sdp()]</name> <fsummary>Get all sdp records of a certain type from a property group</fsummary> <type> <v>Type = v | c | m | o | a | b | t | r | z | k | s | i | u | e | p</v> @@ -1986,8 +1986,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>versions1() -> {ok, VersionInfo} | {error, Reason}</name> - <name>versions2() -> {ok, Info} | {error, Reason}</name> + <name since="">versions1() -> {ok, VersionInfo} | {error, Reason}</name> + <name since="">versions2() -> {ok, Info} | {error, Reason}</name> <fsummary>Retreive various system and application info</fsummary> <type> <v>VersionInfo = [version_info()]</v> @@ -2007,8 +2007,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>print_version_info() -> void()</name> - <name>print_version_info(VersionInfo) -> void()</name> + <name since="">print_version_info() -> void()</name> + <name since="">print_version_info(VersionInfo) -> void()</name> <fsummary>Formated print of result of the versions functions</fsummary> <type> <v>VersionInfo = [version_info()]</v> @@ -2029,7 +2029,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>enable_trace(Level, Destination) -> void()</name> + <name since="">enable_trace(Level, Destination) -> void()</name> <fsummary>Start megaco tracing</fsummary> <type> <v>Level = max | min | 0 <= integer() <= 100</v> @@ -2057,7 +2057,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>disable_trace() -> void()</name> + <name since="">disable_trace() -> void()</name> <fsummary>Stop megaco tracing</fsummary> <desc> <p>This function is used to stop megaco tracing.</p> @@ -2065,7 +2065,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </desc> </func> <func> - <name>set_trace(Level) -> void()</name> + <name since="">set_trace(Level) -> void()</name> <fsummary>Change megaco trace level</fsummary> <type> <v>Level = max | min | 0 <= integer() <= 100</v> @@ -2081,10 +2081,10 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>get_stats() -> {ok, TotalStats} | {error, Reason}</name> - <name>get_stats(GlobalCounter) -> {ok, CounterStats} | {error, Reason}</name> - <name>get_stats(ConnHandle) -> {ok, ConnHandleStats} | {error, Reason}</name> - <name>get_stats(ConnHandle, Counter) -> {ok, integer()} | {error, Reason}</name> + <name since="">get_stats() -> {ok, TotalStats} | {error, Reason}</name> + <name since="">get_stats(GlobalCounter) -> {ok, CounterStats} | {error, Reason}</name> + <name since="">get_stats(ConnHandle) -> {ok, ConnHandleStats} | {error, Reason}</name> + <name since="">get_stats(ConnHandle, Counter) -> {ok, integer()} | {error, Reason}</name> <fsummary></fsummary> <type> <v>TotalStats = [total_stats()]</v> @@ -2110,8 +2110,8 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>reset_stats() -> void()</name> - <name>reset_stats(ConnHandle) -> void()</name> + <name since="">reset_stats() -> void()</name> + <name since="">reset_stats(ConnHandle) -> void()</name> <fsummary></fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -2123,7 +2123,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>test_request(ConnHandle, Version, EncodingMod, EncodingConfig, Actions) -> {MegaMsg, EncodeRes}</name> + <name since="">test_request(ConnHandle, Version, EncodingMod, EncodingConfig, Actions) -> {MegaMsg, EncodeRes}</name> <fsummary>Tests if the Actions argument is correct</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -2150,7 +2150,7 @@ megaco_incr_timer() = #megaco_incr_timer{} </func> <func> - <name>test_reply(ConnHandle, Version, EncodingMod, EncodingConfig, Reply) -> {MegaMsg, EncodeRes}</name> + <name since="">test_reply(ConnHandle, Version, EncodingMod, EncodingConfig, Reply) -> {MegaMsg, EncodeRes}</name> <fsummary>Tests if the Reply argument is correct</fsummary> <type> <v>ConnHandle = conn_handle()</v> diff --git a/lib/megaco/doc/src/megaco_codec_meas.xml b/lib/megaco/doc/src/megaco_codec_meas.xml index 13cc3eb834..5184fe392e 100644 --- a/lib/megaco/doc/src/megaco_codec_meas.xml +++ b/lib/megaco/doc/src/megaco_codec_meas.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_codec_meas.xml</file> </header> - <module>megaco_codec_meas</module> + <module since="">megaco_codec_meas</module> <modulesummary>This module implements a simple megaco codec measurement tool.</modulesummary> <description> <p>This module implements a simple megaco codec measurement tool.</p> @@ -43,8 +43,8 @@ <funcs> <func> - <name>start() -> void()</name> - <name>start(MessagePackage) -> void()</name> + <name since="">start() -> void()</name> + <name since="">start(MessagePackage) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackageRaw = message_package()</v> diff --git a/lib/megaco/doc/src/megaco_codec_mstone1.xml b/lib/megaco/doc/src/megaco_codec_mstone1.xml index 2ff959a648..507a790c71 100644 --- a/lib/megaco/doc/src/megaco_codec_mstone1.xml +++ b/lib/megaco/doc/src/megaco_codec_mstone1.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_codec_mstone1.xml</file> </header> - <module>megaco_codec_mstone1</module> + <module since="">megaco_codec_mstone1</module> <modulesummary>This module implements a simple megaco codec-based performance tool.</modulesummary> <description> <p>This module implements the <em>mstone1</em> tool, @@ -44,9 +44,9 @@ <funcs> <func> - <name>start() -> void()</name> - <name>start(MessagePackage) -> void()</name> - <name>start(MessagePackage, Factor) -> void()</name> + <name since="">start() -> void()</name> + <name since="">start(MessagePackage) -> void()</name> + <name since="">start(MessagePackage, Factor) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackage = message_package()</v> @@ -63,9 +63,9 @@ </func> <func> - <name>start_flex() -> void()</name> - <name>start_flex(MessagePackage) -> void()</name> - <name>start_flex(MessagePackage, Factor) -> void()</name> + <name since="">start_flex() -> void()</name> + <name since="">start_flex(MessagePackage) -> void()</name> + <name since="">start_flex(MessagePackage, Factor) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackage = message_package()</v> @@ -83,9 +83,9 @@ </func> <func> - <name>start_only_drv() -> void()</name> - <name>start_only_drv(MessagePackage) -> void()</name> - <name>start_only_drv(MessagePackage, Factor) -> void()</name> + <name since="">start_only_drv() -> void()</name> + <name since="">start_only_drv(MessagePackage) -> void()</name> + <name since="">start_only_drv(MessagePackage, Factor) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackage = message_package()</v> @@ -105,9 +105,9 @@ </func> <func> - <name>start_no_drv() -> void()</name> - <name>start_no_drv(MessagePackage) -> void()</name> - <name>start_no_drv(MessagePackage, Factor) -> void()</name> + <name since="">start_no_drv() -> void()</name> + <name since="">start_no_drv(MessagePackage) -> void()</name> + <name since="">start_no_drv(MessagePackage, Factor) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackage = message_package()</v> diff --git a/lib/megaco/doc/src/megaco_codec_mstone2.xml b/lib/megaco/doc/src/megaco_codec_mstone2.xml index 3da30d4f99..03990f5c3d 100644 --- a/lib/megaco/doc/src/megaco_codec_mstone2.xml +++ b/lib/megaco/doc/src/megaco_codec_mstone2.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_codec_mstone2.xml</file> </header> - <module>megaco_codec_mstone2</module> + <module since="">megaco_codec_mstone2</module> <modulesummary>This module implements a simple megaco codec-based performance tool.</modulesummary> <description> <p>This module implements the <em>mstone2</em> tool, @@ -44,8 +44,8 @@ <funcs> <func> - <name>start() -> void()</name> - <name>start(MessagePackage) -> void()</name> + <name since="">start() -> void()</name> + <name since="">start(MessagePackage) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackage = message_package()</v> diff --git a/lib/megaco/doc/src/megaco_codec_transform.xml b/lib/megaco/doc/src/megaco_codec_transform.xml index 26b83c3799..392868fdfa 100644 --- a/lib/megaco/doc/src/megaco_codec_transform.xml +++ b/lib/megaco/doc/src/megaco_codec_transform.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_codec_transform.xml</file> </header> - <module>megaco_codec_transform</module> + <module since="">megaco_codec_transform</module> <modulesummary>Megaco message transformation utility.</modulesummary> <description> @@ -45,8 +45,8 @@ <funcs> <func> - <name>export_messages() -> void()</name> - <name>export_messages(MessagePackage) -> void()</name> + <name since="">export_messages() -> void()</name> + <name since="">export_messages(MessagePackage) -> void()</name> <fsummary></fsummary> <type> <v>MessagePackage = atom()</v> diff --git a/lib/megaco/doc/src/megaco_edist_compress.xml b/lib/megaco/doc/src/megaco_edist_compress.xml index d5c7c7224d..16443e469c 100644 --- a/lib/megaco/doc/src/megaco_edist_compress.xml +++ b/lib/megaco/doc/src/megaco_edist_compress.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_edist_compress.xml</file> </header> - <module>megaco_edist_compress</module> + <module since="">megaco_edist_compress</module> <modulesummary>Megaco erlang dist compress behaviour.</modulesummary> <description> <p>The following functions should be exported from a @@ -40,7 +40,7 @@ </description> <funcs> <func> - <name>Module:encode(R, Version) -> T</name> + <name since="">Module:encode(R, Version) -> T</name> <fsummary>Encode (compress) a megaco component.</fsummary> <type> <v>R = megaco_message() | transaction() | action_reply() | action_request() | command_request()</v> @@ -53,7 +53,7 @@ </desc> </func> <func> - <name>Module:decode(T, Version) -> R</name> + <name since="">Module:decode(T, Version) -> R</name> <fsummary>Decode (decompress) a megaco component.</fsummary> <type> <v>T = term()</v> diff --git a/lib/megaco/doc/src/megaco_encoder.xml b/lib/megaco/doc/src/megaco_encoder.xml index 13c6ed324b..cc8270440b 100644 --- a/lib/megaco/doc/src/megaco_encoder.xml +++ b/lib/megaco/doc/src/megaco_encoder.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_encoder.xml</file> </header> - <module>megaco_encoder</module> + <module since="">megaco_encoder</module> <modulesummary>Megaco encoder behaviour.</modulesummary> <description> <p>The following functions should be exported from a @@ -64,7 +64,7 @@ action_reply() = #'ActionReply'{} <funcs> <func> - <name>Module:encode_message(EncodingConfig, Version, Message) -> {ok, Bin} | Error</name> + <name since="">Module:encode_message(EncodingConfig, Version, Message) -> {ok, Bin} | Error</name> <fsummary>Encode a megaco message.</fsummary> <type> <v>EncodingConfig = list()</v> @@ -81,7 +81,7 @@ action_reply() = #'ActionReply'{} </func> <func> - <name>Module:decode_message(EncodingConfig, Version, Bin) -> {ok, Message} | Error</name> + <name since="">Module:decode_message(EncodingConfig, Version, Bin) -> {ok, Message} | Error</name> <fsummary>Decode a megaco message.</fsummary> <type> <v>EncodingConfig = list()</v> @@ -104,7 +104,7 @@ action_reply() = #'ActionReply'{} </func> <func> - <name>Module:decode_mini_message(EncodingConfig, Version, Bin) -> {ok, Message} | Error</name> + <name since="">Module:decode_mini_message(EncodingConfig, Version, Bin) -> {ok, Message} | Error</name> <fsummary>Perform a minimal decode of a megaco message.</fsummary> <type> <v>EncodingConfig = list()</v> @@ -129,7 +129,7 @@ action_reply() = #'ActionReply'{} </func> <func> - <name>Module:encode_transaction(EncodingConfig, Version, Transaction) -> OK | Error</name> + <name since="">Module:encode_transaction(EncodingConfig, Version, Transaction) -> OK | Error</name> <fsummary>Encode a megaco transaction.</fsummary> <type> <v>EncodingConfig = list()</v> @@ -155,7 +155,7 @@ action_reply() = #'ActionReply'{} </func> <func> - <name>Module:encode_action_requests(EncodingConfig, Version, ARs) -> OK | Error</name> + <name since="">Module:encode_action_requests(EncodingConfig, Version, ARs) -> OK | Error</name> <fsummary>Encode megaco action requests.</fsummary> <type> <v>EncodingConfig = list()</v> @@ -181,7 +181,7 @@ action_reply() = #'ActionReply'{} </func> <func> - <name>Module:encode_action_reply(EncodingConfig, Version, AR) -> OK | Error</name> + <name since="">Module:encode_action_reply(EncodingConfig, Version, AR) -> OK | Error</name> <fsummary>Encode a megaco action reply.</fsummary> <type> <v>EncodingConfig = list()</v> diff --git a/lib/megaco/doc/src/megaco_flex_scanner.xml b/lib/megaco/doc/src/megaco_flex_scanner.xml index 0856f3f429..121a7fbcff 100644 --- a/lib/megaco/doc/src/megaco_flex_scanner.xml +++ b/lib/megaco/doc/src/megaco_flex_scanner.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_flex_scanner.xml</file> </header> - <module>megaco_flex_scanner</module> + <module since="">megaco_flex_scanner</module> <modulesummary>Interface module to the flex scanner linked in driver.</modulesummary> <description> <p>This module contains the public interface to the flex scanner @@ -72,7 +72,7 @@ megaco_version() = integer() >= 1 <funcs> <func> - <name>start() -> {ok, PortOrPorts} | {error, Reason}</name> + <name since="">start() -> {ok, PortOrPorts} | {error, Reason}</name> <fsummary></fsummary> <type> <v>PortOrPorts = megaco_ports()</v> @@ -94,7 +94,7 @@ megaco_version() = integer() >= 1 </func> <func> - <name>stop(PortOrPorts) -> stopped</name> + <name since="">stop(PortOrPorts) -> stopped</name> <fsummary></fsummary> <type> <v>PortOrPorts = megaco_ports()</v> @@ -108,7 +108,7 @@ megaco_version() = integer() >= 1 </func> <func> - <name>is_reentrant_enabled() -> Boolean</name> + <name since="">is_reentrant_enabled() -> Boolean</name> <fsummary></fsummary> <type> <v>Boolean = boolean()</v> @@ -121,7 +121,7 @@ megaco_version() = integer() >= 1 </func> <func> - <name>is_scanner_port(Port, PortOrPorts) -> Boolean</name> + <name since="">is_scanner_port(Port, PortOrPorts) -> Boolean</name> <fsummary></fsummary> <type> <v>Port = port()</v> @@ -137,7 +137,7 @@ megaco_version() = integer() >= 1 </func> <func> - <name>scan(Binary, PortOrPorts) -> {ok, Tokens, Version, LatestLine} | {error, Reason, LatestLine} </name> + <name since="">scan(Binary, PortOrPorts) -> {ok, Tokens, Version, LatestLine} | {error, Reason, LatestLine} </name> <fsummary></fsummary> <type> <v>Binary = binary()</v> diff --git a/lib/megaco/doc/src/megaco_tcp.xml b/lib/megaco/doc/src/megaco_tcp.xml index 77aee32f6c..63713b2c56 100644 --- a/lib/megaco/doc/src/megaco_tcp.xml +++ b/lib/megaco/doc/src/megaco_tcp.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_tcp.xml</file> </header> - <module>megaco_tcp</module> + <module since="">megaco_tcp</module> <modulesummary>Interface module to TPKT transport protocol for Megaco/H.248.</modulesummary> <description> <p>This module contains the public interface to the TPKT (TCP/IP) version @@ -40,7 +40,7 @@ </description> <funcs> <func> - <name>start_transport() -> {ok, TransportRef}</name> + <name since="">start_transport() -> {ok, TransportRef}</name> <fsummary></fsummary> <type> <v>TransportRef = pid()</v> @@ -51,7 +51,7 @@ </desc> </func> <func> - <name>listen(TransportRef, ListenPortSpecList) -> ok</name> + <name since="">listen(TransportRef, ListenPortSpecList) -> ok</name> <fsummary></fsummary> <type> <v>TransportRef = pid() | regname()</v> @@ -65,7 +65,7 @@ </desc> </func> <func> - <name>connect(TransportRef, OptionList) -> {ok, Handle, ControlPid} | {error, Reason}</name> + <name since="">connect(TransportRef, OptionList) -> {ok, Handle, ControlPid} | {error, Reason}</name> <fsummary></fsummary> <type> <v>TransportRef = pid() | regname()</v> @@ -86,7 +86,7 @@ </desc> </func> <func> - <name>close(Handle) -> ok</name> + <name since="">close(Handle) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -96,7 +96,7 @@ </desc> </func> <func> - <name>socket(Handle) -> Socket</name> + <name since="">socket(Handle) -> Socket</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -109,7 +109,7 @@ </desc> </func> <func> - <name>send_message(Handle, Message) -> ok</name> + <name since="">send_message(Handle, Message) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -120,7 +120,7 @@ </desc> </func> <func> - <name>block(Handle) -> ok</name> + <name since="">block(Handle) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -130,7 +130,7 @@ </desc> </func> <func> - <name>unblock(Handle) -> ok</name> + <name since="">unblock(Handle) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -141,7 +141,7 @@ </desc> </func> <func> - <name>upgrade_receive_handle(ControlPid) -> ok</name> + <name since="">upgrade_receive_handle(ControlPid) -> ok</name> <fsummary></fsummary> <type> <v>ControlPid = pid()</v> @@ -153,9 +153,9 @@ </desc> </func> <func> - <name>get_stats() -> {ok, TotalStats} | {error, Reason}</name> - <name>get_stats(SendHandle) -> {ok, SendHandleStats} | {error, Reason}</name> - <name>get_stats(SendHandle, Counter) -> {ok, CounterStats} | {error, Reason}</name> + <name since="">get_stats() -> {ok, TotalStats} | {error, Reason}</name> + <name since="">get_stats(SendHandle) -> {ok, SendHandleStats} | {error, Reason}</name> + <name since="">get_stats(SendHandle, Counter) -> {ok, CounterStats} | {error, Reason}</name> <fsummary></fsummary> <type> <v>TotalStats = [send_handle_stats()]</v> @@ -173,8 +173,8 @@ </desc> </func> <func> - <name>reset_stats() -> void()</name> - <name>reset_stats(SendHandle) -> void()</name> + <name since="">reset_stats() -> void()</name> + <name since="">reset_stats(SendHandle) -> void()</name> <fsummary></fsummary> <type> <v>SendHandle = send_handle()</v> diff --git a/lib/megaco/doc/src/megaco_transport.xml b/lib/megaco/doc/src/megaco_transport.xml index 3002e9b74e..ba8c794750 100644 --- a/lib/megaco/doc/src/megaco_transport.xml +++ b/lib/megaco/doc/src/megaco_transport.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_transport.xml</file> </header> - <module>megaco_transport</module> + <module since="">megaco_transport</module> <modulesummary>Megaco transport behaviour.</modulesummary> <description> <p>The following functions should be exported from a @@ -54,8 +54,8 @@ </description> <funcs> <func> - <name>Module:send_message(Handle, Msg) -> ok | {cancel, Reason} | Error</name> - <name>Module:send_message(Handle, Msg, Resend) -> ok | {cancel, Reason} | Error</name> + <name since="">Module:send_message(Handle, Msg) -> ok | {cancel, Reason} | Error</name> + <name since="">Module:send_message(Handle, Msg, Resend) -> ok | {cancel, Reason} | Error</name> <fsummary>Send a megaco message.</fsummary> <type> <v>Handle = term()</v> @@ -99,7 +99,7 @@ </func> <func> - <name>Module:resend_message(Handle, Msg) -> ok | {cancel, Reason} | Error</name> + <name since="">Module:resend_message(Handle, Msg) -> ok | {cancel, Reason} | Error</name> <fsummary>Re-send a megaco message.</fsummary> <type> <v>Handle = term()</v> diff --git a/lib/megaco/doc/src/megaco_udp.xml b/lib/megaco/doc/src/megaco_udp.xml index b2559c77d5..3d776c19b6 100644 --- a/lib/megaco/doc/src/megaco_udp.xml +++ b/lib/megaco/doc/src/megaco_udp.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_udp.xml</file> </header> - <module>megaco_udp</module> + <module since="">megaco_udp</module> <modulesummary>Interface module to UDP transport protocol for Megaco/H.248.</modulesummary> <description> <p>This module contains the public interface to the UDP/IP version @@ -40,7 +40,7 @@ </description> <funcs> <func> - <name>start_transport() -> {ok, TransportRef}</name> + <name since="">start_transport() -> {ok, TransportRef}</name> <fsummary></fsummary> <type> <v>TransportRef = pid()</v> @@ -51,7 +51,7 @@ </desc> </func> <func> - <name>open(TransportRef, OptionList) -> {ok, Handle, ControlPid} | {error, Reason}</name> + <name since="">open(TransportRef, OptionList) -> {ok, Handle, ControlPid} | {error, Reason}</name> <fsummary></fsummary> <type> <v>TransportRef = pid() | regname()</v> @@ -73,7 +73,7 @@ </desc> </func> <func> - <name>close(Handle, Msg) -> ok</name> + <name since="">close(Handle, Msg) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -84,7 +84,7 @@ </desc> </func> <func> - <name>socket(Handle) -> Socket</name> + <name since="">socket(Handle) -> Socket</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -97,7 +97,7 @@ </desc> </func> <func> - <name>create_send_handle(Handle, Host, Port) -> send_handle()</name> + <name since="">create_send_handle(Handle, Host, Port) -> send_handle()</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -110,7 +110,7 @@ </desc> </func> <func> - <name>send_message(SendHandle, Msg) -> ok</name> + <name since="">send_message(SendHandle, Msg) -> ok</name> <fsummary></fsummary> <type> <v>SendHandle = send_handle()</v> @@ -125,7 +125,7 @@ </desc> </func> <func> - <name>block(Handle) -> ok</name> + <name since="">block(Handle) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -135,7 +135,7 @@ </desc> </func> <func> - <name>unblock(Handle) -> ok</name> + <name since="">unblock(Handle) -> ok</name> <fsummary></fsummary> <type> <v>Handle = socket_handle()</v> @@ -146,7 +146,7 @@ </desc> </func> <func> - <name>upgrade_receive_handle(ControlPid, NewHandle) -> ok</name> + <name since="">upgrade_receive_handle(ControlPid, NewHandle) -> ok</name> <fsummary></fsummary> <type> <v>ControlPid = pid()</v> @@ -160,9 +160,9 @@ </desc> </func> <func> - <name>get_stats() -> {ok, TotalStats} | {error, Reason}</name> - <name>get_stats(SendHandle) -> {ok, SendHandleStats} | {error, Reason}</name> - <name>get_stats(SendHandle, Counter) -> {ok, CounterStats} | {error, Reason}</name> + <name since="">get_stats() -> {ok, TotalStats} | {error, Reason}</name> + <name since="">get_stats(SendHandle) -> {ok, SendHandleStats} | {error, Reason}</name> + <name since="">get_stats(SendHandle, Counter) -> {ok, CounterStats} | {error, Reason}</name> <fsummary></fsummary> <type> <v>TotalStats = [total_stats()]</v> @@ -180,8 +180,8 @@ </desc> </func> <func> - <name>reset_stats() -> void()</name> - <name>reset_stats(SendHandle) -> void()</name> + <name since="">reset_stats() -> void()</name> + <name since="">reset_stats(SendHandle) -> void()</name> <fsummary></fsummary> <type> <v>SendHandle = send_handle()</v> diff --git a/lib/megaco/doc/src/megaco_user.xml b/lib/megaco/doc/src/megaco_user.xml index 067be15fe0..198f2aa24c 100644 --- a/lib/megaco/doc/src/megaco_user.xml +++ b/lib/megaco/doc/src/megaco_user.xml @@ -32,7 +32,7 @@ <rev>%VSN%</rev> <file>megaco_user.xml</file> </header> - <module>megaco_user</module> + <module since="">megaco_user</module> <modulesummary>Callback module for users of the Megaco application</modulesummary> <description> <p>This module defines the callback behaviour of Megaco users. A @@ -164,8 +164,8 @@ protocol_version() = integer() ]]></code> <funcs> <func> - <name>handle_connect(ConnHandle, ProtocolVersion) -> ok | error | {error,ErrorDescr}</name> - <name>handle_connect(ConnHandle, ProtocolVersion, Extra]) -> ok | error | {error,ErrorDescr}</name> + <name since="">handle_connect(ConnHandle, ProtocolVersion) -> ok | error | {error,ErrorDescr}</name> + <name since="">handle_connect(ConnHandle, ProtocolVersion, Extra]) -> ok | error | {error,ErrorDescr}</name> <fsummary>Invoked when a new connection is established</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -202,7 +202,7 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> ok</name> + <name since="">handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> ok</name> <fsummary>Invoked when a connection is teared down</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -220,8 +220,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED) -> reply | {reply, ED} | no_reply | {no_reply, ED} </name> - <name>handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED, Extra) -> reply | {reply, ED} | no_reply | {no_reply, ED} </name> + <name since="">handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED) -> reply | {reply, ED} | no_reply | {no_reply, ED} </name> + <name since="">handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED, Extra) -> reply | {reply, ED} | no_reply | {no_reply, ED} </name> <fsummary>Invoked when a received message had syntax errors</fsummary> <type> <v>ReceiveHandle = receive_handle()</v> @@ -258,8 +258,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr) -> ok</name> - <name>handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr, Extra) -> ok</name> + <name since="">handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr) -> ok</name> + <name since="">handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr, Extra) -> ok</name> <fsummary>Invoked when a received message just contains an error</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -291,8 +291,8 @@ protocol_version() = integer() ]]></code> <!-- <func> - <name>handle_segment_error(ConnHandle, ProtocolVersion, TransId, SegmentError) -> ok</name> - <name>handle_segment_error(ConnHandle, ProtocolVersion, TransId, SegmentError, Extra) -> ok</name> + <name since="">handle_segment_error(ConnHandle, ProtocolVersion, TransId, SegmentError) -> ok</name> + <name since="">handle_segment_error(ConnHandle, ProtocolVersion, TransId, SegmentError, Extra) -> ok</name> <fsummary>Invoked when a segment error has been detected</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -316,8 +316,8 @@ protocol_version() = integer() ]]></code> --> <func> - <name>handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests) -> pending() | reply() | ignore_trans_request</name> - <name>handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests, Extra) -> pending() | reply() | ignore_trans_request</name> + <name since="">handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests) -> pending() | reply() | ignore_trans_request</name> + <name since="">handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests, Extra) -> pending() | reply() | ignore_trans_request</name> <fsummary>Invoked for each transaction request</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -420,8 +420,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData) -> reply()</name> - <name>handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Extra) -> reply()</name> + <name since="">handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData) -> reply()</name> + <name since="">handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Extra) -> reply()</name> <fsummary>Optionally invoked for a time consuming transaction request</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -460,8 +460,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_trans_reply(ConnHandle, ProtocolVersion, UserReply, ReplyData) -> ok</name> - <name>handle_trans_reply(ConnHandle, ProtocolVersion, UserReply, ReplyData, Extra) -> ok</name> + <name since="">handle_trans_reply(ConnHandle, ProtocolVersion, UserReply, ReplyData) -> ok</name> + <name since="">handle_trans_reply(ConnHandle, ProtocolVersion, UserReply, ReplyData, Extra) -> ok</name> <fsummary>Optionally invoked for a transaction reply</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -593,8 +593,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData) -> ok</name> - <name>handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Extra) -> ok</name> + <name since="">handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData) -> ok</name> + <name since="">handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Extra) -> ok</name> <fsummary>Optionally invoked for a transaction acknowledgement</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -658,8 +658,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans) -> ok</name> - <name>handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans, Extra) -> ok</name> + <name since="">handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans) -> ok</name> + <name since="">handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans, Extra) -> ok</name> <fsummary>Invoked when an unexpected message is received</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -685,8 +685,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_trans_request_abort(ConnHandle, ProtocolVersion, TransNo, Pid) -> ok</name> - <name>handle_trans_request_abort(ConnHandle, ProtocolVersion, TransNo, Pid, Extra) -> ok</name> + <name since="">handle_trans_request_abort(ConnHandle, ProtocolVersion, TransNo, Pid) -> ok</name> + <name since="">handle_trans_request_abort(ConnHandle, ProtocolVersion, TransNo, Pid, Extra) -> ok</name> <fsummary>Invoked when an transaction request has been aborted</fsummary> <type> <v>ConnHandle = conn_handle()</v> @@ -710,8 +710,8 @@ protocol_version() = integer() ]]></code> </func> <func> - <name>handle_segment_reply(ConnHandle, ProtocolVersion, TransNo, SegNo, SegCompl) -> ok</name> - <name>handle_segment_reply(ConnHandle, ProtocolVersion, TransNo, SegNo, SegCompl, Extra) -> ok</name> + <name since="">handle_segment_reply(ConnHandle, ProtocolVersion, TransNo, SegNo, SegCompl) -> ok</name> + <name since="">handle_segment_reply(ConnHandle, ProtocolVersion, TransNo, SegNo, SegCompl, Extra) -> ok</name> <fsummary>Segment Reply Indication</fsummary> <type> <v>ConnHandle = conn_handle()</v> diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index 621b6047ee..11b0b8e987 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -32,7 +32,7 @@ <rev></rev> <file></file> </header> - <module>mnesia</module> + <module since="">mnesia</module> <modulesummary>A distributed telecommunications DBMS</modulesummary> <description> @@ -183,7 +183,7 @@ <funcs> <func> - <name>abort(Reason) -> transaction abort</name> + <name since="">abort(Reason) -> transaction abort</name> <fsummary>Terminates the current transaction.</fsummary> <desc> <p>Makes the transaction silently @@ -195,7 +195,7 @@ </desc> </func> <func> - <name>activate_checkpoint(Args) -> {ok,Name,Nodes} | {error,Reason}</name> + <name since="">activate_checkpoint(Args) -> {ok,Name,Nodes} | {error,Reason}</name> <fsummary>Activates a checkpoint.</fsummary> <desc> <marker id="activate_checkpoint"></marker> @@ -259,7 +259,7 @@ </desc> </func> <func> - <name>activity(AccessContext, Fun [, Args]) -> ResultOfFun | exit(Reason)</name> + <name since="">activity(AccessContext, Fun [, Args]) -> ResultOfFun | exit(Reason)</name> <fsummary>Executes <c>Fun</c> in <c>AccessContext</c>.</fsummary> <desc> <marker id="activity_2_3"></marker> @@ -271,7 +271,7 @@ </desc> </func> <func> - <name>activity(AccessContext, Fun, Args, AccessMod) -> ResultOfFun | exit(Reason)</name> + <name since="">activity(AccessContext, Fun, Args, AccessMod) -> ResultOfFun | exit(Reason)</name> <fsummary>Executes <c>Fun</c> in <c>AccessContext</c>.</fsummary> <desc> <marker id="activity_4"></marker> @@ -403,7 +403,7 @@ </desc> </func> <func> - <name>add_table_copy(Tab, Node, Type) -> {aborted, R} | {atomic, ok}</name> + <name since="">add_table_copy(Tab, Node, Type) -> {aborted, R} | {atomic, ok}</name> <fsummary>Copies a table to a remote node.</fsummary> <desc> <marker id="add_table_copy"></marker> @@ -420,7 +420,7 @@ mnesia:add_table_copy(person, Node, disc_copies)</code> </desc> </func> <func> - <name>add_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name> + <name since="">add_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name> <fsummary>Creates an index for a table.</fsummary> <desc> <marker id="add_table_index"></marker> @@ -441,7 +441,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>all_keys(Tab) -> KeyList | transaction abort</name> + <name since="">all_keys(Tab) -> KeyList | transaction abort</name> <fsummary>Returns all keys in a table.</fsummary> <desc> <marker id="all_keys"></marker> @@ -453,7 +453,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>async_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> + <name since="">async_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> <fsummary>Calls the <c>Fun</c> in a context that is not protected by a transaction.</fsummary> <desc> <marker id="async_dirty"></marker> @@ -493,7 +493,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>backup(Opaque [, BackupMod]) -> ok | {error,Reason}</name> + <name since="">backup(Opaque [, BackupMod]) -> ok | {error,Reason}</name> <fsummary>Backs up all tables in the database.</fsummary> <desc> <marker id="backup"></marker> @@ -505,7 +505,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>backup_checkpoint(Name, Opaque [, BackupMod]) -> ok | {error,Reason}</name> + <name since="">backup_checkpoint(Name, Opaque [, BackupMod]) -> ok | {error,Reason}</name> <fsummary>Backs up all tables in a checkpoint.</fsummary> <desc> <marker id="backup_checkpoint"></marker> @@ -520,7 +520,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>change_config(Config, Value) -> {error, Reason} | {ok, ReturnValue}</name> + <name since="">change_config(Config, Value) -> {error, Reason} | {ok, ReturnValue}</name> <fsummary>Changes a configuration parameter.</fsummary> <desc> <marker id="change_config"></marker> @@ -554,7 +554,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>change_table_access_mode(Tab, AccessMode) -> {aborted, R} | {atomic, ok}</name> + <name since="">change_table_access_mode(Tab, AccessMode) -> {aborted, R} | {atomic, ok}</name> <fsummary>Changes the access mode for the table.</fsummary> <desc> <marker id="change_table_access_mode"></marker> @@ -568,7 +568,7 @@ mnesia:add_table_index(person, age)</code> </desc> </func> <func> - <name>change_table_copy_type(Tab, Node, To) -> {aborted, R} | {atomic, ok}</name> + <name since="">change_table_copy_type(Tab, Node, To) -> {aborted, R} | {atomic, ok}</name> <fsummary>Changes the storage type of a table.</fsummary> <desc> <marker id="change_table_copy_type"></marker> @@ -585,7 +585,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </desc> </func> <func> - <name>change_table_load_order(Tab, LoadOrder) -> {aborted, R} | {atomic, ok}</name> + <name since="">change_table_load_order(Tab, LoadOrder) -> {aborted, R} | {atomic, ok}</name> <fsummary>Changes the load order priority for the table.</fsummary> <desc> <marker id="change_table_load_order"></marker> @@ -595,7 +595,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </desc> </func> <func> - <name>change_table_majority(Tab, Majority) -> {aborted, R} | {atomic, ok}</name> + <name since="OTP R14B03">change_table_majority(Tab, Majority) -> {aborted, R} | {atomic, ok}</name> <fsummary>Changes the majority check setting for the table.</fsummary> <desc> <p><c>Majority</c> must be a boolean. Default is <c>false</c>. @@ -607,7 +607,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </desc> </func> <func> - <name>clear_table(Tab) -> {aborted, R} | {atomic, ok}</name> + <name since="">clear_table(Tab) -> {aborted, R} | {atomic, ok}</name> <fsummary>Deletes all entries in a table.</fsummary> <desc> <marker id="clear_table"></marker> @@ -615,7 +615,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </desc> </func> <func> - <name>create_schema(DiscNodes) -> ok | {error,Reason}</name> + <name since="">create_schema(DiscNodes) -> ok | {error,Reason}</name> <fsummary>Creates a new schema on the specified nodes.</fsummary> <desc> <marker id="create_schema"></marker> @@ -637,7 +637,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </desc> </func> <func> - <name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name> + <name since="">create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name> <fsummary>Creates a Mnesia table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary> <desc> <marker id="create_table"></marker> @@ -799,7 +799,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>deactivate_checkpoint(Name) -> ok | {error, Reason}</name> + <name since="">deactivate_checkpoint(Name) -> ok | {error, Reason}</name> <fsummary>Deactivates a checkpoint.</fsummary> <desc> <marker id="deactivate_checkpoint"></marker> @@ -811,7 +811,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>del_table_copy(Tab, Node) -> {aborted, R} | {atomic, ok}</name> + <name since="">del_table_copy(Tab, Node) -> {aborted, R} | {atomic, ok}</name> <fsummary>Deletes the replica of table <c>Tab</c> at node <c>Node</c>.</fsummary> <desc> <marker id="del_table_copy"></marker> @@ -825,7 +825,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>del_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name> + <name since="">del_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name> <fsummary>Deletes an index in a table.</fsummary> <desc> <marker id="del_table_index"></marker> @@ -834,7 +834,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>delete({Tab, Key}) -> transaction abort | ok</name> + <name since="">delete({Tab, Key}) -> transaction abort | ok</name> <fsummary>Deletes all records in table <c>Tab</c> with the key <c>Key</c>.</fsummary> <desc> <marker id="delete_2"></marker> @@ -842,7 +842,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>delete(Tab, Key, LockKind) -> transaction abort | ok</name> + <name since="">delete(Tab, Key, LockKind) -> transaction abort | ok</name> <fsummary>Deletes all records in table <c>Tab</c>with the key <c>Key</c>.</fsummary> <desc> <marker id="delete_3"></marker> @@ -857,7 +857,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>delete_object(Record) -> transaction abort | ok</name> + <name since="">delete_object(Record) -> transaction abort | ok</name> <fsummary>Delete a record.</fsummary> <desc> <marker id="delete_object_1"></marker> @@ -866,7 +866,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>delete_object(Tab, Record, LockKind) -> transaction abort | ok</name> + <name since="">delete_object(Tab, Record, LockKind) -> transaction abort | ok</name> <fsummary>Deletes a record.</fsummary> <desc> <marker id="delete_object_3"></marker> @@ -883,7 +883,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>delete_schema(DiscNodes) -> ok | {error,Reason}</name> + <name since="">delete_schema(DiscNodes) -> ok | {error,Reason}</name> <fsummary>Deletes the schema on the given nodes.</fsummary> <desc> <marker id="delete_schema"></marker> @@ -904,7 +904,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>delete_table(Tab) -> {aborted, Reason} | {atomic, ok}</name> + <name since="">delete_table(Tab) -> {aborted, Reason} | {atomic, ok}</name> <fsummary>Deletes permanently all replicas of table <c>Tab</c>.</fsummary> <desc> <marker id="delete_table"></marker> @@ -912,7 +912,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_all_keys(Tab) -> KeyList | exit({aborted, Reason})</name> + <name since="">dirty_all_keys(Tab) -> KeyList | exit({aborted, Reason})</name> <fsummary>Dirty search for all record keys in table.</fsummary> <desc> <marker id="delete_all_keys"></marker> @@ -920,7 +920,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_delete({Tab, Key}) -> ok | exit({aborted, Reason})</name> + <name since="">dirty_delete({Tab, Key}) -> ok | exit({aborted, Reason})</name> <fsummary>Dirty delete of a record.</fsummary> <desc> <marker id="dirty_delete"></marker> @@ -928,14 +928,14 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_delete(Tab, Key) -> ok | exit({aborted, Reason})</name> + <name since="">dirty_delete(Tab, Key) -> ok | exit({aborted, Reason})</name> <fsummary>Dirty delete of a record.</fsummary> <desc> <p>Dirty equivalent of the function <c>mnesia:delete/3</c>.</p> </desc> </func> <func> - <name>dirty_delete_object(Record)</name> + <name since="">dirty_delete_object(Record)</name> <fsummary>Dirty delete of a record.</fsummary> <desc> <marker id="dirty_delete_object_1"></marker> @@ -944,14 +944,14 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_delete_object(Tab, Record)</name> + <name since="">dirty_delete_object(Tab, Record)</name> <fsummary>Dirty delete of a record.</fsummary> <desc> <p>Dirty equivalent of the function <c>mnesia:delete_object/3</c>.</p> </desc> </func> <func> - <name>dirty_first(Tab) -> Key | exit({aborted, Reason})</name> + <name since="">dirty_first(Tab) -> Key | exit({aborted, Reason})</name> <fsummary>Returns the key for the first record in a table.</fsummary> <desc> <marker id="dirty_first"></marker> @@ -967,7 +967,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_index_match_object(Pattern, Pos)</name> + <name since="">dirty_index_match_object(Pattern, Pos)</name> <fsummary>Dirty pattern match using index.</fsummary> <desc> <marker id="dirty_index_match_object_2"></marker> @@ -977,7 +977,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_index_match_object(Tab, Pattern, Pos)</name> + <name since="">dirty_index_match_object(Tab, Pattern, Pos)</name> <fsummary>Dirty pattern match using index.</fsummary> <desc> <p>Dirty equivalent of the function @@ -985,7 +985,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_index_read(Tab, SecondaryKey, Pos)</name> + <name since="">dirty_index_read(Tab, SecondaryKey, Pos)</name> <fsummary>Dirty read using index.</fsummary> <desc> <marker id="dirty_index_read"></marker> @@ -994,7 +994,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_last(Tab) -> Key | exit({aborted, Reason})</name> + <name since="">dirty_last(Tab) -> Key | exit({aborted, Reason})</name> <fsummary>Returns the key for the last record in a table.</fsummary> <desc> <marker id="dirty_last"></marker> @@ -1006,7 +1006,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_match_object(Pattern) -> RecordList | exit({aborted, Reason})</name> + <name since="">dirty_match_object(Pattern) -> RecordList | exit({aborted, Reason})</name> <fsummary>Dirty pattern match pattern.</fsummary> <desc> <marker id="dirty_match_object_1"></marker> @@ -1015,7 +1015,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_match_object(Tab, Pattern) -> RecordList | exit({aborted, Reason})</name> + <name since="">dirty_match_object(Tab, Pattern) -> RecordList | exit({aborted, Reason})</name> <fsummary>Dirty pattern match pattern.</fsummary> <desc> <p>Dirty equivalent of the function @@ -1023,7 +1023,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_next(Tab, Key) -> Key | exit({aborted, Reason})</name> + <name since="">dirty_next(Tab, Key) -> Key | exit({aborted, Reason})</name> <fsummary>Return the next key in a table.</fsummary> <desc> <marker id="dirty_next"></marker> @@ -1038,7 +1038,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_prev(Tab, Key) -> Key | exit({aborted, Reason})</name> + <name since="">dirty_prev(Tab, Key) -> Key | exit({aborted, Reason})</name> <fsummary>Returns the previous key in a table.</fsummary> <desc> <marker id="dirty_prev"></marker> @@ -1050,7 +1050,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_read({Tab, Key}) -> ValueList | exit({aborted, Reason}</name> + <name since="">dirty_read({Tab, Key}) -> ValueList | exit({aborted, Reason}</name> <fsummary>Dirty read of records.</fsummary> <desc> <marker id="dirty_read"></marker> @@ -1058,14 +1058,14 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_read(Tab, Key) -> ValueList | exit({aborted, Reason}</name> + <name since="">dirty_read(Tab, Key) -> ValueList | exit({aborted, Reason}</name> <fsummary>Dirty read of records.</fsummary> <desc> <p>Dirty equivalent of the function <c>mnesia:read/3</c>.</p> </desc> </func> <func> - <name>dirty_select(Tab, MatchSpec) -> ValueList | exit({aborted, Reason}</name> + <name since="">dirty_select(Tab, MatchSpec) -> ValueList | exit({aborted, Reason}</name> <fsummary>Dirty matches the objects in <c>Tab</c> against <c>MatchSpec</c>.</fsummary> <desc> <marker id="dirty_select"></marker> @@ -1073,7 +1073,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_slot(Tab, Slot) -> RecordList | exit({aborted, Reason})</name> + <name since="">dirty_slot(Tab, Slot) -> RecordList | exit({aborted, Reason})</name> <fsummary>Returns the list of records that are associated with <c>Slot</c> in a table.</fsummary> <desc> <marker id="dirty_slot"></marker> @@ -1089,7 +1089,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_update_counter({Tab, Key}, Incr) -> NewVal | exit({aborted, Reason})</name> + <name since="">dirty_update_counter({Tab, Key}, Incr) -> NewVal | exit({aborted, Reason})</name> <fsummary>Dirty update of a counter record.</fsummary> <desc> <marker id="dirty_update_counter"></marker> @@ -1097,7 +1097,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name> + <name since="">dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name> <fsummary>Dirty update of a counter record.</fsummary> <desc> <p>Mnesia has no special counter records. However, @@ -1126,7 +1126,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_write(Record) -> ok | exit({aborted, Reason})</name> + <name since="">dirty_write(Record) -> ok | exit({aborted, Reason})</name> <fsummary>Dirty write of a record.</fsummary> <desc> <marker id="dirty_write_1"></marker> @@ -1135,14 +1135,14 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dirty_write(Tab, Record) -> ok | exit({aborted, Reason})</name> + <name since="">dirty_write(Tab, Record) -> ok | exit({aborted, Reason})</name> <fsummary>Dirty write of a record.</fsummary> <desc> <p>Dirty equivalent of the function <c>mnesia:write/3</c>.</p> </desc> </func> <func> - <name>dump_log() -> dumped</name> + <name since="">dump_log() -> dumped</name> <fsummary>Performs a user-initiated dump of the local log file.</fsummary> <desc> <marker id="dump_log"></marker> @@ -1156,7 +1156,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dump_tables(TabList) -> {atomic, ok} | {aborted, Reason}</name> + <name since="">dump_tables(TabList) -> {atomic, ok} | {aborted, Reason}</name> <fsummary>Dumps all RAM tables to disc.</fsummary> <desc> <marker id="dump_tables"></marker> @@ -1168,7 +1168,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>dump_to_textfile(Filename)</name> + <name since="">dump_to_textfile(Filename)</name> <fsummary>Dumps local tables into a text file.</fsummary> <desc> <marker id="dump_to_textfile"></marker> @@ -1181,7 +1181,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>error_description(Error) -> String</name> + <name since="">error_description(Error) -> String</name> <fsummary>Returns a string describing a particular Mnesia error.</fsummary> <desc> <marker id="error_description"></marker> @@ -1259,7 +1259,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>ets(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> + <name since="">ets(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> <fsummary>Calls the <c>Fun</c> in a raw context that is not protected by a transaction.</fsummary> <desc> <marker id="ets"></marker> @@ -1278,7 +1278,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>first(Tab) -> Key | transaction abort</name> + <name since="">first(Tab) -> Key | transaction abort</name> <fsummary>Returns the key for the first record in a table.</fsummary> <desc> <marker id="first"></marker> @@ -1293,7 +1293,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>foldl(Function, Acc, Table) -> NewAcc | transaction abort</name> + <name since="">foldl(Function, Acc, Table) -> NewAcc | transaction abort</name> <fsummary>Calls <c>Function</c> for each record in <c>Table</c>.</fsummary> <desc> <marker id="foldl"></marker> @@ -1306,7 +1306,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>foldr(Function, Acc, Table) -> NewAcc | transaction abort</name> + <name since="">foldr(Function, Acc, Table) -> NewAcc | transaction abort</name> <fsummary>Calls <c>Function</c> for each record in <c>Table</c>.</fsummary> <desc> <marker id="foldr"></marker> @@ -1317,7 +1317,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>force_load_table(Tab) -> yes | ErrorDescription</name> + <name since="">force_load_table(Tab) -> yes | ErrorDescription</name> <fsummary>Forces a table to be loaded into the system.</fsummary> <desc> <marker id="force_load_table"></marker> @@ -1335,7 +1335,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>index_match_object(Pattern, Pos) -> transaction abort | ObjList</name> + <name since="">index_match_object(Pattern, Pos) -> transaction abort | ObjList</name> <fsummary>Matches records and uses index information.</fsummary> <desc> <marker id="index_match_object_2"></marker> @@ -1345,7 +1345,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>index_match_object(Tab, Pattern, Pos, LockKind) -> transaction abort | ObjList</name> + <name since="">index_match_object(Tab, Pattern, Pos, LockKind) -> transaction abort | ObjList</name> <fsummary>Matches records and uses index information.</fsummary> <desc> <marker id="index_match_object_4"></marker> @@ -1377,7 +1377,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>index_read(Tab, SecondaryKey, Pos) -> transaction abort | RecordList</name> + <name since="">index_read(Tab, SecondaryKey, Pos) -> transaction abort | RecordList</name> <fsummary>Reads records through index table.</fsummary> <desc> <marker id="index_read"></marker> @@ -1397,7 +1397,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>info() -> ok</name> + <name since="">info() -> ok</name> <fsummary>Prints system information on the terminal.</fsummary> <desc> <marker id="info"></marker> @@ -1408,7 +1408,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>install_fallback(Opaque) -> ok | {error,Reason}</name> + <name since="">install_fallback(Opaque) -> ok | {error,Reason}</name> <fsummary>Installs a backup as fallback.</fsummary> <desc> <marker id="install_fallback_1"></marker> @@ -1417,7 +1417,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>install_fallback(Opaque), BackupMod) -> ok | {error,Reason}</name> + <name since="">install_fallback(Opaque), BackupMod) -> ok | {error,Reason}</name> <fsummary>Installs a backup as fallback.</fsummary> <desc> <p>Calls <c>mnesia:install_fallback(Opaque, Args)</c>, where @@ -1425,7 +1425,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>install_fallback(Opaque, Args) -> ok | {error,Reason}</name> + <name since="">install_fallback(Opaque, Args) -> ok | {error,Reason}</name> <fsummary>Installs a backup as fallback.</fsummary> <desc> <p>Installs a backup as fallback. The fallback is used to @@ -1483,7 +1483,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>is_transaction() -> boolean</name> + <name since="">is_transaction() -> boolean</name> <fsummary>Checks if code is running in a transaction.</fsummary> <desc> <marker id="is_transaction"></marker> @@ -1492,7 +1492,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>last(Tab) -> Key | transaction abort</name> + <name since="">last(Tab) -> Key | transaction abort</name> <fsummary>Returns the key for the last record in a table.</fsummary> <desc> <p>Works exactly like @@ -1503,7 +1503,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>load_textfile(Filename)</name> + <name since="">load_textfile(Filename)</name> <fsummary>Loads tables from a text file.</fsummary> <desc> <marker id="load_textfile"></marker> @@ -1516,7 +1516,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>lock(LockItem, LockKind) -> Nodes | ok | transaction abort</name> + <name since="">lock(LockItem, LockKind) -> Nodes | ok | transaction abort</name> <fsummary>Explicit grab lock.</fsummary> <desc> <marker id="lock"></marker> @@ -1605,7 +1605,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>match_object(Pattern) -> transaction abort | RecList</name> + <name since="">match_object(Pattern) -> transaction abort | RecList</name> <fsummary>Matches <c>Pattern</c> for records.</fsummary> <desc> <marker id="match_object_1"></marker> @@ -1614,7 +1614,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>match_object(Tab, Pattern, LockKind) -> transaction abort | RecList</name> + <name since="">match_object(Tab, Pattern, LockKind) -> transaction abort | RecList</name> <fsummary>Matches <c>Pattern</c> for records.</fsummary> <desc> <marker id="match_object_3"></marker> @@ -1639,7 +1639,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>move_table_copy(Tab, From, To) -> {aborted, Reason} | {atomic, ok}</name> + <name since="">move_table_copy(Tab, From, To) -> {aborted, Reason} | {atomic, ok}</name> <fsummary>Moves the copy of table <c>Tab</c> from node <c>From</c> to node <c>To</c>.</fsummary> <desc> <marker id="move_table_copy"></marker> @@ -1653,7 +1653,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>next(Tab, Key) -> Key | transaction abort</name> + <name since="">next(Tab, Key) -> Key | transaction abort</name> <fsummary>Returns the next key in a table.</fsummary> <desc> <marker id="next"></marker> @@ -1665,7 +1665,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>prev(Tab, Key) -> Key | transaction abort</name> + <name since="">prev(Tab, Key) -> Key | transaction abort</name> <fsummary>Returns the previous key in a table.</fsummary> <desc> <p>Works exactly like @@ -1676,7 +1676,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>read({Tab, Key}) -> transaction abort | RecordList</name> + <name since="">read({Tab, Key}) -> transaction abort | RecordList</name> <fsummary>Reads records(s) with a given key.</fsummary> <desc> <marker id="read_2"></marker> @@ -1684,14 +1684,14 @@ mnesia:create_table(person, </desc> </func> <func> - <name>read(Tab, Key) -> transaction abort | RecordList</name> + <name since="">read(Tab, Key) -> transaction abort | RecordList</name> <fsummary>Reads records(s) with a given key.</fsummary> <desc> <p>Calls function <c>mnesia:read(Tab, Key, read)</c>.</p> </desc> </func> <func> - <name>read(Tab, Key, LockKind) -> transaction abort | RecordList</name> + <name since="">read(Tab, Key, LockKind) -> transaction abort | RecordList</name> <fsummary>Reads records(s) with a given key.</fsummary> <desc> <marker id="read_3"></marker> @@ -1716,7 +1716,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>read_lock_table(Tab) -> ok | transaction abort</name> + <name since="">read_lock_table(Tab) -> ok | transaction abort</name> <fsummary>Sets a read lock on an entire table.</fsummary> <desc> <marker id="read_lock_table"></marker> @@ -1725,7 +1725,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>report_event(Event) -> ok</name> + <name since="">report_event(Event) -> ok</name> <fsummary>Reports a user event to the Mnesia event handler.</fsummary> <desc> <marker id="report_event"></marker> @@ -1743,7 +1743,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>restore(Opaque, Args) -> {atomic, RestoredTabs} |{aborted, Reason}</name> + <name since="">restore(Opaque, Args) -> {atomic, RestoredTabs} |{aborted, Reason}</name> <fsummary>Online restore of backup.</fsummary> <desc> <marker id="restore"></marker> @@ -1803,7 +1803,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>s_delete({Tab, Key}) -> ok | transaction abort</name> + <name since="">s_delete({Tab, Key}) -> ok | transaction abort</name> <fsummary>Sets sticky lock and delete records.</fsummary> <desc> <marker id="s_delete"></marker> @@ -1812,7 +1812,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>s_delete_object(Record) -> ok | transaction abort</name> + <name since="">s_delete_object(Record) -> ok | transaction abort</name> <fsummary>Sets sticky lock and delete record.</fsummary> <desc> <marker id="s_delete_object"></marker> @@ -1822,7 +1822,7 @@ mnesia:create_table(person, </desc> </func> <func> - <name>s_write(Record) -> ok | transaction abort</name> + <name since="">s_write(Record) -> ok | transaction abort</name> <fsummary>Writes <c>Record</c> and sets sticky lock.</fsummary> <desc> <marker id="s_write"></marker> @@ -1832,21 +1832,21 @@ mnesia:create_table(person, </desc> </func> <func> - <name>schema() -> ok</name> + <name since="">schema() -> ok</name> <fsummary>Prints information about all table definitions on the terminal.</fsummary> <desc> <p>Prints information about all table definitions on the terminal.</p> </desc> </func> <func> - <name>schema(Tab) -> ok</name> + <name since="">schema(Tab) -> ok</name> <fsummary>Prints information about one table definition on the terminal.</fsummary> <desc> <p>Prints information about one table definition on the terminal.</p> </desc> </func> <func> - <name>select(Tab, MatchSpec [, Lock]) -> transaction abort | [Object]</name> + <name since="">select(Tab, MatchSpec [, Lock]) -> transaction abort | [Object]</name> <fsummary>Matches the objects in <c>Tab</c> against <c>MatchSpec</c>.</fsummary> <desc> <marker id="select_2_3"></marker> @@ -1884,7 +1884,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>select(Tab, MatchSpec, NObjects, Lock) -> transaction abort | {[Object],Cont} | '$end_of_table'</name> + <name since="">select(Tab, MatchSpec, NObjects, Lock) -> transaction abort | {[Object],Cont} | '$end_of_table'</name> <fsummary>Matches the objects in <c>Tab</c> against <c>MatchSpec</c>.</fsummary> <desc> <marker id="select_4"></marker> @@ -1907,7 +1907,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>select(Cont) -> transaction abort | {[Object],Cont} | '$end_of_table'</name> + <name since="">select(Cont) -> transaction abort | {[Object],Cont} | '$end_of_table'</name> <fsummary>Continues selecting objects.</fsummary> <desc> <p>Selects more objects with the match specification initiated @@ -1919,7 +1919,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>set_debug_level(Level) -> OldLevel</name> + <name since="">set_debug_level(Level) -> OldLevel</name> <fsummary>Changes the internal debug level of Mnesia.</fsummary> <desc> <marker id="set_debug_level"></marker> @@ -1930,7 +1930,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>set_master_nodes(MasterNodes) -> ok | {error, Reason}</name> + <name since="">set_master_nodes(MasterNodes) -> ok | {error, Reason}</name> <fsummary>Sets the master nodes for all tables.</fsummary> <desc> <marker id="set_master_nodes_1"></marker> @@ -1943,7 +1943,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>set_master_nodes(Tab, MasterNodes) -> ok | {error, Reason}</name> + <name since="">set_master_nodes(Tab, MasterNodes) -> ok | {error, Reason}</name> <fsummary>Sets the master nodes for a table.</fsummary> <desc> <marker id="set_master_nodes_2"></marker> @@ -1968,14 +1968,14 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>snmp_close_table(Tab) -> {aborted, R} | {atomic, ok}</name> + <name since="">snmp_close_table(Tab) -> {aborted, R} | {atomic, ok}</name> <fsummary>Removes the possibility for SNMP to manipulate the table.</fsummary> <desc> <p>Removes the possibility for SNMP to manipulate the table.</p> </desc> </func> <func> - <name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name> + <name since="">snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name> <fsummary>Gets the corresponding Mnesia key from an SNMP index.</fsummary> <type> <v>Tab ::= atom()</v> @@ -1990,7 +1990,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>snmp_get_next_index(Tab, RowIndex) -> {ok, NextIndex} | endOfTable</name> + <name since="">snmp_get_next_index(Tab, RowIndex) -> {ok, NextIndex} | endOfTable</name> <fsummary>Gets the index of the next lexicographical row.</fsummary> <type> <v>Tab ::= atom()</v> @@ -2006,7 +2006,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>snmp_get_row(Tab, RowIndex) -> {ok, Row} | undefined</name> + <name since="">snmp_get_row(Tab, RowIndex) -> {ok, Row} | undefined</name> <fsummary>Retrieves a row indexed by an SNMP index.</fsummary> <type> <v>Tab ::= atom()</v> @@ -2019,7 +2019,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </desc> </func> <func> - <name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name> + <name since="">snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name> <fsummary>Organizes a Mnesia table as an SNMP table.</fsummary> <type> <v>Tab ::= atom()</v> @@ -2073,10 +2073,17 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>start() -> ok | {error, Reason}</name> + <name since="">start() -> ok | {error, Reason}</name> <fsummary>Starts a local Mnesia system.</fsummary> <desc> <marker id="start"></marker> + <p>Mnesia startup is asynchronous. The function call + <c>mnesia:start()</c> returns the atom <c>ok</c> and then + starts to initialize the different tables. Depending on the + size of the database, this can take some time, and the + application programmer must wait for the tables that the + application needs before they can be used. This is achieved + by using the function <c>mnesia:wait_for_tables/2</c>.</p> <p>The startup procedure for a set of Mnesia nodes is a fairly complicated operation. A Mnesia system consists of a set of nodes, with Mnesia started locally on all @@ -2108,7 +2115,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>stop() -> stopped</name> + <name since="">stop() -> stopped</name> <fsummary>Stops Mnesia locally.</fsummary> <desc> <marker id="stop"></marker> @@ -2117,7 +2124,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>subscribe(EventCategory) -> {ok, Node} | {error, Reason}</name> + <name since="">subscribe(EventCategory) -> {ok, Node} | {error, Reason}</name> <fsummary>Subscribes to events of type <c>EventCategory</c>.</fsummary> <desc> <marker id="subscribe"></marker> @@ -2127,7 +2134,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>sync_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> + <name since="">sync_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> <fsummary>Calls the <c>Fun</c> in a context that is not protected by a transaction.</fsummary> <desc> <marker id="sync_dirty"></marker> @@ -2143,7 +2150,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>sync_log() -> ok | {error, Reason}</name> + <name since="OTP 17.0">sync_log() -> ok | {error, Reason}</name> <fsummary>Performs a file sync of the local log file.</fsummary> <desc> <p>Ensures that the local transaction log file is synced to disk. @@ -2153,7 +2160,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>sync_transaction(Fun, [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun}</name> + <name since="">sync_transaction(Fun, [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun}</name> <fsummary>Synchronously executes a transaction.</fsummary> <desc> <marker id="sync_transaction"></marker> @@ -2166,7 +2173,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name> + <name since="">system_info(InfoKey) -> Info | exit({aborted, Reason})</name> <fsummary>Returns information about the Mnesia system.</fsummary> <desc> <marker id="system_info"></marker> @@ -2353,7 +2360,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>table(Tab [,[Option]]) -> QueryHandle</name> + <name since="">table(Tab [,[Option]]) -> QueryHandle</name> <fsummary>Return a QLC query handle.</fsummary> <desc> <marker id="table"></marker> @@ -2408,7 +2415,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>table_info(Tab, InfoKey) -> Info | exit({aborted, Reason})</name> + <name since="">table_info(Tab, InfoKey) -> Info | exit({aborted, Reason})</name> <fsummary>Returns local information about table.</fsummary> <desc> <marker id="table_info"></marker> @@ -2560,7 +2567,7 @@ mnesia:create_table(employee, </desc> </func> <func> - <name>transaction(Fun [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun}</name> + <name since="">transaction(Fun [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun}</name> <fsummary>Executes a transaction.</fsummary> <desc> <marker id="transaction"></marker> @@ -2628,7 +2635,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>transform_table(Tab, Fun, NewAttributeList, NewRecordName) -> {aborted, R} | {atomic, ok}</name> + <name since="">transform_table(Tab, Fun, NewAttributeList, NewRecordName) -> {aborted, R} | {atomic, ok}</name> <fsummary>Changes format on all records in table <c>Tab</c>.</fsummary> <desc> <marker id="transform_table_4"></marker> @@ -2649,7 +2656,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>transform_table(Tab, Fun, NewAttributeList) -> {aborted, R} | {atomic, ok}</name> + <name since="">transform_table(Tab, Fun, NewAttributeList) -> {aborted, R} | {atomic, ok}</name> <fsummary>Changes format on all records in table <c>Tab</c>.</fsummary> <desc> <p>Calls <c>mnesia:transform_table(Tab, Fun, @@ -2658,7 +2665,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>traverse_backup(Source, [SourceMod,] Target, [TargetMod,] Fun, Acc) -> {ok, LastAcc} | {error, Reason}</name> + <name since="">traverse_backup(Source, [SourceMod,] Target, [TargetMod,] Fun, Acc) -> {ok, LastAcc} | {error, Reason}</name> <fsummary>Traversal of a backup.</fsummary> <desc> <marker id="traverse_backup"></marker> @@ -2689,7 +2696,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>uninstall_fallback() -> ok | {error,Reason}</name> + <name since="">uninstall_fallback() -> ok | {error,Reason}</name> <fsummary>Uninstalls a fallback.</fsummary> <desc> <marker id="uninstall_fallback_0"></marker> @@ -2698,7 +2705,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>uninstall_fallback(Args) -> ok | {error,Reason}</name> + <name since="">uninstall_fallback(Args) -> ok | {error,Reason}</name> <fsummary>Uninstalls a fallback.</fsummary> <desc> <p>Deinstalls a fallback before it @@ -2725,7 +2732,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>unsubscribe(EventCategory) -> {ok, Node} | {error, Reason}</name> + <name since="">unsubscribe(EventCategory) -> {ok, Node} | {error, Reason}</name> <fsummary>Subscribes to events of type <c>EventCategory</c>.</fsummary> <desc> <marker id="unsubscribe"></marker> @@ -2735,7 +2742,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>wait_for_tables(TabList, Timeout) -> ok | {timeout, BadTabList} | {error, Reason}</name> + <name since="">wait_for_tables(TabList, Timeout) -> ok | {timeout, BadTabList} | {error, Reason}</name> <fsummary>Waits for tables to be accessible.</fsummary> <desc> <marker id="wait_for_tables"></marker> @@ -2746,7 +2753,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>wread({Tab, Key}) -> transaction abort | RecordList</name> + <name since="">wread({Tab, Key}) -> transaction abort | RecordList</name> <fsummary>Reads records with given key.</fsummary> <desc> <marker id="wread"></marker> @@ -2754,7 +2761,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>write(Record) -> transaction abort | ok</name> + <name since="">write(Record) -> transaction abort | ok</name> <fsummary>Writes a record into the database.</fsummary> <desc> <marker id="write_1"></marker> @@ -2763,7 +2770,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>write(Tab, Record, LockKind) -> transaction abort | ok</name> + <name since="">write(Tab, Record, LockKind) -> transaction abort | ok</name> <fsummary>Writes a record into the database.</fsummary> <desc> <marker id="write_3"></marker> @@ -2779,7 +2786,7 @@ raise(Name, Amount) -> </desc> </func> <func> - <name>write_lock_table(Tab) -> ok | transaction abort</name> + <name since="">write_lock_table(Tab) -> ok | transaction abort</name> <fsummary>Sets write lock on an entire table.</fsummary> <desc> <marker id="write_lock_table"></marker> diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml index c233acdb05..ccaba412d0 100644 --- a/lib/mnesia/doc/src/mnesia_frag_hash.xml +++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>mnesia_frag_hash.sgml</file> </header> - <module>mnesia_frag_hash</module> + <module since="">mnesia_frag_hash</module> <modulesummary>Defines mnesia_frag_hash callback behavior</modulesummary> <description> <p>This module defines a callback behavior for user-defined hash @@ -50,7 +50,7 @@ <funcs> <func> - <name>init_state(Tab, State) -> NewState | abort(Reason)</name> + <name since="">init_state(Tab, State) -> NewState | abort(Reason)</name> <fsummary>Initiates the hash state for a new table.</fsummary> <type> <v>Tab = atom()</v> @@ -72,7 +72,7 @@ </desc> </func> <func> - <name>add_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name> + <name since="">add_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name> <fsummary>Starts when a new fragment is added to a fragmented table.</fsummary> <type> <v>State = term()</v> @@ -100,7 +100,7 @@ </desc> </func> <func> - <name>del_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name> + <name since="">del_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name> <fsummary>Starts when a fragment is deleted from a fragmented table.</fsummary> <type> <v>State = term()</v> @@ -127,7 +127,7 @@ </desc> </func> <func> - <name>key_to_frag_number(State, Key) -> FragNum | abort(Reason)</name> + <name since="">key_to_frag_number(State, Key) -> FragNum | abort(Reason)</name> <fsummary>Resolves the key of a record into a fragment number.</fsummary> <type> <v>FragNum = integer()</v> @@ -140,7 +140,7 @@ </desc> </func> <func> - <name>match_spec_to_frag_numbers(State, MatchSpec) -> FragNums | abort(Reason)</name> + <name since="">match_spec_to_frag_numbers(State, MatchSpec) -> FragNums | abort(Reason)</name> <fsummary>Resolves a <c>MatchSpec</c> into a list of fragment numbers.</fsummary> <type> <v>MatcSpec = ets_select_match_spec()</v> diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml index a76f716981..18ddc4ab9e 100644 --- a/lib/mnesia/doc/src/mnesia_registry.xml +++ b/lib/mnesia/doc/src/mnesia_registry.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>mnesia_registry.sgml</file> </header> - <module>mnesia_registry</module> + <module since="">mnesia_registry</module> <modulesummary>Dump support for registries in erl_interface.</modulesummary> <description> <p>This module is usually part of the <c>erl_interface</c> @@ -57,7 +57,7 @@ <funcs> <func> - <name>create_table(Tab) -> ok | exit(Reason)</name> + <name since="">create_table(Tab) -> ok | exit(Reason)</name> <fsummary>Creates a registry table in Mnesia.</fsummary> <desc> <p>A wrapper function for <c>mnesia:create_table/2</c>, @@ -73,7 +73,7 @@ </desc> </func> <func> - <name>create_table(Tab, TabDef) -> ok | exit(Reason)</name> + <name since="">create_table(Tab, TabDef) -> ok | exit(Reason)</name> <fsummary>Creates a customized registry table in Mnesia.</fsummary> <desc> <p>A wrapper function for <c>mnesia:create_table/2</c>, diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index a2880d6cf4..cb2ee504f9 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -67,10 +67,10 @@ get_log_writes() -> incr_log_writes() -> Left = mnesia_lib:incr_counter(trans_log_writes_left, -1), if - Left > 0 -> - ignore; + Left =:= 0 -> + adjust_log_writes(true); true -> - adjust_log_writes(true) + ignore end. adjust_log_writes(DoCast) -> diff --git a/lib/observer/doc/src/crashdump.xml b/lib/observer/doc/src/crashdump.xml index 48f944cbce..62c6ff1f25 100644 --- a/lib/observer/doc/src/crashdump.xml +++ b/lib/observer/doc/src/crashdump.xml @@ -34,7 +34,7 @@ <rev>PA1</rev> <file>crashdump.xml</file> </header> - <module>crashdump_viewer</module> + <module since="">crashdump_viewer</module> <modulesummary>A WxWidgets based tool for browsing Erlang crashdumps.</modulesummary> <description> @@ -46,8 +46,8 @@ </description> <funcs> <func> - <name>start() -> ok</name> - <name>start(File) -> ok</name> + <name since="">start() -> ok</name> + <name since="OTP 17.0">start(File) -> ok</name> <fsummary>Start the Crashdump Viewer.</fsummary> <type> <v>File = string()</v> @@ -62,7 +62,7 @@ </desc> </func> <func> - <name>stop() -> ok</name> + <name since="">stop() -> ok</name> <fsummary>Terminate the Crashdump Viewer.</fsummary> <desc> <p>Terminates the Crashdump Viewer and closes diff --git a/lib/observer/doc/src/etop.xml b/lib/observer/doc/src/etop.xml index e7a83d0514..f0acc7b5d8 100644 --- a/lib/observer/doc/src/etop.xml +++ b/lib/observer/doc/src/etop.xml @@ -34,7 +34,7 @@ <rev></rev> <file></file> </header> - <module>etop</module> + <module since="">etop</module> <modulesummary>Erlang Top is a tool for presenting information about Erlang processes similar to the information presented by "top" in UNIX.</modulesummary> <description> @@ -98,7 +98,7 @@ </description> <funcs> <func> - <name>start() -> ok</name> + <name since="OTP R15B01">start() -> ok</name> <fsummary>Start etop.</fsummary> <desc> <p>Starts <c>etop</c>. @@ -106,7 +106,7 @@ </desc> </func> <func> - <name>start(Options) -> ok</name> + <name since="OTP R15B01">start(Options) -> ok</name> <fsummary>Start etop.</fsummary> <type> <v>Options = [Option]</v> @@ -120,7 +120,7 @@ </desc> </func> <func> - <name>help() -> ok</name> + <name since="OTP R15B01">help() -> ok</name> <fsummary>Display the etop help.</fsummary> <desc> <p>Displays the help of <c>etop</c> and @@ -128,7 +128,7 @@ </desc> </func> <func> - <name>config(Key,Value) -> Result</name> + <name since="">config(Key,Value) -> Result</name> <fsummary>Change the configuration of the tool.</fsummary> <type> <v>Result = ok | {error,Reason}</v> @@ -142,7 +142,7 @@ </desc> </func> <func> - <name>dump(File) -> Result</name> + <name since="">dump(File) -> Result</name> <fsummary>Dump the current display to a file.</fsummary> <type> <v>Result = ok | {error,Reason}</v> @@ -153,7 +153,7 @@ </desc> </func> <func> - <name>stop() -> stop</name> + <name since="">stop() -> stop</name> <fsummary>Terminate etop.</fsummary> <desc> <p>Terminates <c>etop</c>.</p> diff --git a/lib/observer/doc/src/observer.xml b/lib/observer/doc/src/observer.xml index 843be26ee1..7fb1dd044e 100644 --- a/lib/observer/doc/src/observer.xml +++ b/lib/observer/doc/src/observer.xml @@ -33,7 +33,7 @@ <rev>PA1</rev> <file>observer.xml</file> </header> - <module>observer</module> + <module since="OTP R15B">observer</module> <modulesummary>A GUI tool for observing an Erlang system.</modulesummary> <description> <p>Observer is a graphical tool for observing the characteristics of @@ -48,7 +48,7 @@ </description> <funcs> <func> - <name>start() -> ok</name> + <name since="OTP R15B">start() -> ok</name> <fsummary>Start the Observer GUI.</fsummary> <desc> <p>Starts the Observer GUI. diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index 7cd15e15d3..fee95e0b21 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -33,7 +33,7 @@ <rev>PA1</rev> <file>ttb.xml</file> </header> - <module>ttb</module> + <module since="">ttb</module> <modulesummary>A base for building trace tools for distributed systems.</modulesummary> <description> <p>The Trace Tool Builder, <c>ttb</c>, is a base for building trace @@ -44,7 +44,7 @@ </description> <funcs> <func> - <name>start_trace(Nodes, Patterns, FlagSpec, Opts) -> Result</name> + <name since="OTP R15B">start_trace(Nodes, Patterns, FlagSpec, Opts) -> Result</name> <fsummary>Start a trace port on each specified node.</fsummary> <type> <v>Result = see p/2</v> @@ -76,7 +76,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>tracer() -> Result</name> + <name since="">tracer() -> Result</name> <fsummary>Equivalent to tracer(node()).</fsummary> <desc> <p>Equivalent to <c>tracer(node())</c>.</p> @@ -84,7 +84,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>tracer(Shortcut) -> Result</name> + <name since="">tracer(Shortcut) -> Result</name> <fsummary>Handy shortcuts for common tracing settings.</fsummary> <type> <v>Shortcut = shell | dbg</v> @@ -97,7 +97,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>tracer(Nodes) -> Result</name> + <name since="">tracer(Nodes) -> Result</name> <fsummary>Equivalent to tracer(Nodes,[]).</fsummary> <desc> <p>Equivalent to <c>tracer(Nodes,[])</c>.</p> @@ -105,7 +105,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>tracer(Nodes,Opts) -> Result</name> + <name since="">tracer(Nodes,Opts) -> Result</name> <fsummary>Start a trace port on each specified node.</fsummary> <type> <v>Result = {ok, ActivatedNodes} | {error,Reason}</v> @@ -243,7 +243,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>p(Item,Flags) -> Return</name> + <name since="">p(Item,Flags) -> Return</name> <fsummary>Set the specified trace flags on the specified processes or ports.</fsummary> <type> <v>Return = {ok,[{Item,MatchDesc}]}</v> @@ -277,7 +277,8 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>tp, tpl, tpe, ctp, ctpl, ctpg, ctpe</name> + <name since="">tp, tpl, ctp, ctpl, ctpg</name> + <name since="OTP 19.0">tpe, ctpe</name> <fsummary>Set and clear trace patterns.</fsummary> <desc> <p>These functions are to be used with trace @@ -338,7 +339,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>list_history() -> History</name> + <name since="">list_history() -> History</name> <fsummary>Return all calls stored in history.</fsummary> <type> <v>History = [{N,Func,Args}]</v> @@ -352,7 +353,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>run_history(N) -> ok | {error, Reason}</name> + <name since="">run_history(N) -> ok | {error, Reason}</name> <fsummary>Execute one entry of the history.</fsummary> <type> <v>N = integer() | [integer()]</v> @@ -364,7 +365,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>write_config(ConfigFile,Config)</name> + <name since="">write_config(ConfigFile,Config)</name> <fsummary>Equivalent to write_config(ConfigFile,Config,[]).</fsummary> <desc> <p>Equivalent to <c>write_config(ConfigFile,Config,[])</c>.</p> @@ -372,7 +373,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>write_config(ConfigFile,Config,Opts) -> ok | {error,Reason}</name> + <name since="">write_config(ConfigFile,Config,Opts) -> ok | {error,Reason}</name> <fsummary>Create a configuration file.</fsummary> <type> <v>ConfigFile = string()</v> @@ -405,7 +406,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>run_config(ConfigFile) -> ok | {error,Reason}</name> + <name since="">run_config(ConfigFile) -> ok | {error,Reason}</name> <fsummary>Execute all entries in a configuration file.</fsummary> <type> <v>ConfigFile = string()</v> @@ -418,7 +419,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>run_config(ConfigFile,NumList) -> ok | {error,Reason}</name> + <name since="">run_config(ConfigFile,NumList) -> ok | {error,Reason}</name> <fsummary>Execute selected entries from a configuration file.</fsummary> <type> <v>ConfigFile = string()</v> @@ -437,7 +438,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>list_config(ConfigFile) -> Config | {error,Reason}</name> + <name since="">list_config(ConfigFile) -> Config | {error,Reason}</name> <fsummary>List all entries in a configuration file.</fsummary> <type> <v>ConfigFile = string()</v> @@ -449,7 +450,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>write_trace_info(Key,Info) -> ok</name> + <name since="">write_trace_info(Key,Info) -> ok</name> <fsummary>Write any information to file <c>.ti</c>.</fsummary> <type> <v>Key = term()</v> @@ -465,7 +466,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>seq_trigger_ms() -> MatchSpec</name> + <name since="">seq_trigger_ms() -> MatchSpec</name> <fsummary>Equivalent to seq_trigger_ms(all).</fsummary> <desc> <p>Equivalent to <c>seq_trigger_ms(all)</c>.</p> @@ -473,7 +474,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>seq_trigger_ms(Flags) -> MatchSpec</name> + <name since="">seq_trigger_ms(Flags) -> MatchSpec</name> <fsummary>Return a match_spec() which starts sequential tracing.</fsummary> <type> <v>MatchSpec = match_spec()</v> @@ -521,7 +522,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>stop()</name> + <name since="">stop()</name> <fsummary>Equivalent to stop([]).</fsummary> <desc> <p>Equivalent to <c>stop([])</c>.</p> @@ -529,7 +530,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>stop(Opts) -> stopped | {stopped, Dir}</name> + <name since="">stop(Opts) -> stopped | {stopped, Dir}</name> <fsummary>Stop tracing and fetch/format logs from all nodes.</fsummary> <type> <v>Opts = Opt | [Opt]</v> @@ -573,7 +574,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>get_et_handler()</name> + <name since="OTP R15B">get_et_handler()</name> <fsummary>Return the <c>et</c> handler.</fsummary> <desc> <p>Returns the <c>et</c> handler, which can be used with <c>format/2</c> @@ -583,7 +584,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>format(File)</name> + <name since="">format(File)</name> <fsummary>Equivalent to <c>format(File,[])</c>.</fsummary> <desc> <p>Equivalent to <c>format(File,[])</c>.</p> @@ -591,7 +592,7 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>format(File,Options) -> ok | {error, Reason}</name> + <name since="">format(File,Options) -> ok | {error, Reason}</name> <fsummary>Format a binary trace log.</fsummary> <type> <v>File = string() | [string()]</v> diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl index 4b1984c394..5e1137511a 100644 --- a/lib/observer/src/cdv_detail_wx.erl +++ b/lib/observer/src/cdv_detail_wx.erl @@ -84,8 +84,9 @@ destroy_progress(_) -> ok. init(Id,ParentFrame,Callback,App,Parent,{Title,Info,TW}) -> + Scale = observer_wx:get_scale(), Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [Title], - [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), + [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {Scale*850,Scale*600}}]), MenuBar = wxMenuBar:new(), create_menus(MenuBar), wxFrame:setMenuBar(Frame, MenuBar), diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl index ffef83227c..8956173c93 100644 --- a/lib/observer/src/cdv_html_wx.erl +++ b/lib/observer/src/cdv_html_wx.erl @@ -79,14 +79,14 @@ handle_info(active, #state{panel=HtmlWin,delayed_fetch=Callback}=State) observer_lib:sync_destroy_progress_dialog(), wx_misc:beginBusyCursor(), wxHtmlWindow:setPage(HtmlWin,HtmlText), - cdv_wx:set_status(TW), + cdv_wx_set_status(State, TW), wx_misc:endBusyCursor(), {noreply, State#state{expand_table=Tab, delayed_fetch=undefined, trunc_warn=TW}}; handle_info(active, State) -> - cdv_wx:set_status(State#state.trunc_warn), + cdv_wx_set_status(State, State#state.trunc_warn), {noreply, State}; handle_info(Info, State) -> @@ -164,3 +164,10 @@ expand(Id,Callback,#state{expand_wins=Opened0, app=App}=State) -> Opened0 end, State#state{expand_wins=Opened}. + +cdv_wx_set_status(#state{app = cdv}, Status) -> + %% this module is used by the observer when cdw_wx isn't started + %% only try to set status when used by cdv + cdv_wx:set_status(Status); +cdv_wx_set_status(_, _) -> + ok. diff --git a/lib/observer/src/cdv_table_wx.erl b/lib/observer/src/cdv_table_wx.erl index 0f28a51017..0cad272262 100644 --- a/lib/observer/src/cdv_table_wx.erl +++ b/lib/observer/src/cdv_table_wx.erl @@ -50,11 +50,12 @@ init([ParentWin, {ColumnSpec,Info,TW}]) -> end, Grid = wxListCtrl:new(ParentWin, [{style, Style}]), Li = wxListItem:new(), + Scale = observer_wx:get_scale(), AddListEntry = fun({Name, Align, DefSize}, Col) -> wxListItem:setText(Li, Name), wxListItem:setAlign(Li, Align), wxListCtrl:insertColumn(Grid, Col, Li), - wxListCtrl:setColumnWidth(Grid, Col, DefSize), + wxListCtrl:setColumnWidth(Grid, Col, DefSize*Scale), Col + 1 end, lists:foldl(AddListEntry, 0, ColumnSpec), diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl index 2702301021..14877b7eab 100644 --- a/lib/observer/src/cdv_virtual_list_wx.erl +++ b/lib/observer/src/cdv_virtual_list_wx.erl @@ -132,11 +132,12 @@ create_list_box(Panel, Holder, Callback, Owner) -> end} ]), Li = wxListItem:new(), + Scale = observer_wx:get_scale(), AddListEntry = fun({Name, Align, DefSize}, Col) -> wxListItem:setText(Li, Name), wxListItem:setAlign(Li, Align), wxListCtrl:insertColumn(ListCtrl, Col, Li), - wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize), + wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize*Scale), Col + 1 end, ListItems = Callback:col_spec(), diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl index 1e9cef8952..811c767e66 100644 --- a/lib/observer/src/cdv_wx.erl +++ b/lib/observer/src/cdv_wx.erl @@ -101,8 +101,9 @@ init(File0) -> {ok,CdvServer} = crashdump_viewer:start_link(), catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1), + Scale = observer_wx:get_scale(), Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Crashdump Viewer", - [{size, {850, 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]), + [{size, {Scale*850, Scale*600}}, {style, ?wxDEFAULT_FRAME_STYLE}]), IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"), Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]), wxFrame:setIcon(Frame, Icon), diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl index 54e246f247..da47a30fb1 100644 --- a/lib/observer/src/observer_alloc_wx.erl +++ b/lib/observer/src/observer_alloc_wx.erl @@ -282,11 +282,12 @@ create_mem_info(Parent) -> Grid = wxListCtrl:new(Parent, [{style, Style}]), Li = wxListItem:new(), + Scale = observer_wx:get_scale(), AddListEntry = fun({Name, Align, DefSize}, Col) -> wxListItem:setText(Li, Name), wxListItem:setAlign(Li, Align), wxListCtrl:insertColumn(Grid, Col, Li), - wxListCtrl:setColumnWidth(Grid, Col, DefSize), + wxListCtrl:setColumnWidth(Grid, Col, DefSize*Scale), Col + 1 end, ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200}, diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 2a481966da..8c3eef5411 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -117,16 +117,19 @@ init([Notebook, Parent, _Config]) -> UseGC = haveGC(), Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8, + Scale = observer_wx:get_scale(), Font = case os:type() of {unix,_} when UseGC, Version28 -> - wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL); + wxFont:new(Scale * 12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL); _ -> - wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT) + Font0 = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + wxFont:setPointSize(Font0, Scale * wxFont:getPointSize(Font0)), + Font0 end, SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), GreyBrush = wxBrush:new({230,230,240}), SelBrush = wxBrush:new(SelCol), - LinkPen = wxPen:new(SelCol, [{width, 2}]), + LinkPen = wxPen:new(SelCol, [{width, Scale * 2}]), process_flag(trap_exit, true), {Panel, #state{parent=Parent, panel =Panel, @@ -134,7 +137,7 @@ init([Notebook, Parent, _Config]) -> app_w =DrawingArea, usegc = UseGC, paint=#paint{font = Font, - pen = wxPen:new({80,80,80}, [{width, 2}]), + pen = wxPen:new({80,80,80}, [{width, Scale * 2}]), brush= GreyBrush, sel = SelBrush, links= LinkPen diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 21c6d26f49..79271addf2 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -110,25 +110,26 @@ setup_graph_drawing(Panels) -> _ = [Do(Panel) || Panel <- Panels], UseGC = haveGC(), Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8, + Scale = observer_wx:get_scale(), {Font, SmallFont} = if UseGC, Version28 -> %% Def font is really small when using Graphics contexts in 2.8 %% Hardcode it - F = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), - SF = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + F = wxFont:new(Scale * 12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), + SF = wxFont:new(Scale * 10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), {F, SF}; true -> DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), DefSize = wxFont:getPointSize(DefFont), DefFamily = wxFont:getFamily(DefFont), - F = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), - SF = wxFont:new(DefSize-2, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + F = wxFont:new(Scale * (DefSize-1), DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), + SF = wxFont:new(Scale * (DefSize-2), DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), {F, SF} end, - BlackPen = wxPen:new({0,0,0}, [{width, 1}]), - Pens = [wxPen:new(Col, [{width, 1}, {style, ?wxSOLID}]) + BlackPen = wxPen:new({0,0,0}, [{width, Scale}]), + Pens = [wxPen:new(Col, [{width, Scale}, {style, ?wxSOLID}]) || Col <- tuple_to_list(colors())], - DotPens = [wxPen:new(Col, [{width, 1}, {style, ?wxDOT}]) + DotPens = [wxPen:new(Col, [{width, Scale}, {style, ?wxDOT}]) || Col <- tuple_to_list(colors())], #paint{usegc = UseGC, font = Font, diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl index 445f3dd6b1..00cf1b5fba 100644 --- a/lib/observer/src/observer_port_wx.erl +++ b/lib/observer/src/observer_port_wx.erl @@ -96,11 +96,12 @@ init([Notebook, Parent, Config]) -> wxListCtrl:setColumnWidth(Grid, Col, DefSize), Col + 1 end, - ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, 150}, - {"Connected", ?wxLIST_FORMAT_LEFT, 150}, - {"Name", ?wxLIST_FORMAT_LEFT, 150}, - {"Controls", ?wxLIST_FORMAT_LEFT, 200}, - {"Slot", ?wxLIST_FORMAT_RIGHT, 50}], + Scale = observer_wx:get_scale(), + ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, Scale*150}, + {"Connected", ?wxLIST_FORMAT_LEFT, Scale*150}, + {"Name", ?wxLIST_FORMAT_LEFT, Scale*150}, + {"Controls", ?wxLIST_FORMAT_LEFT, Scale*200}, + {"Slot", ?wxLIST_FORMAT_RIGHT, Scale*50}], lists:foldl(AddListEntry, 0, ListItems), wxListItem:destroy(Li), @@ -461,10 +462,11 @@ display_port_info(Parent, PortRec, Opened) -> do_display_port_info(Parent0, PortRec) -> Parent = observer_lib:get_wx_parent(Parent0), Title = "Port Info: " ++ PortRec#port.id_str, + Scale = observer_wx:get_scale(), Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title, [{style, ?wxSYSTEM_MENU bor ?wxCAPTION bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}, - {size,{600,400}}]), + {size,{Scale * 600, Scale * 400}}]), ScrolledWin = wxScrolledWindow:new(Frame,[{style,?wxHSCROLL bor ?wxVSCROLL}]), wxScrolledWindow:enableScrolling(ScrolledWin,true,true), wxScrolledWindow:setScrollbars(ScrolledWin,20,20,0,0), diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 04e654a37e..4ab4a78462 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -163,13 +163,14 @@ create_list_box(Panel, Holder) -> wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize), Col + 1 end, - ListItems = [{"Pid", ?wxLIST_FORMAT_CENTRE, 120}, - {"Name or Initial Func", ?wxLIST_FORMAT_LEFT, 200}, -%% {"Time", ?wxLIST_FORMAT_CENTRE, 50}, - {"Reds", ?wxLIST_FORMAT_RIGHT, 100}, - {"Memory", ?wxLIST_FORMAT_RIGHT, 100}, - {"MsgQ", ?wxLIST_FORMAT_RIGHT, 50}, - {"Current Function", ?wxLIST_FORMAT_LEFT, 200}], + Scale = observer_wx:get_scale(), + ListItems = [{"Pid", ?wxLIST_FORMAT_CENTRE, Scale*120}, + {"Name or Initial Func", ?wxLIST_FORMAT_LEFT, Scale*200}, +%% {"Time", ?wxLIST_FORMAT_CENTRE, Scale*50}, + {"Reds", ?wxLIST_FORMAT_RIGHT, Scale*100}, + {"Memory", ?wxLIST_FORMAT_RIGHT, Scale*100}, + {"MsgQ", ?wxLIST_FORMAT_RIGHT, Scale*50}, + {"Current Function", ?wxLIST_FORMAT_LEFT, Scale*200}], lists:foldl(AddListEntry, 0, ListItems), wxListItem:destroy(Li), diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index f436886735..bd5fed0951 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -59,8 +59,9 @@ init([Pid, ParentFrame, Parent]) -> {registered_name, Registered} -> io_lib:format("~tp (~p)",[Registered, Pid]); undefined -> throw(process_undefined) end, + Scale = observer_wx:get_scale(), Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title], - [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), + [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {Scale * 850, Scale * 600}}]), MenuBar = wxMenuBar:new(), create_menus(MenuBar), wxFrame:setMenuBar(Frame, MenuBar), @@ -245,12 +246,13 @@ init_dict_page(Parent, Pid, Table) -> init_stack_page(Parent, Pid) -> LCtrl = wxListCtrl:new(Parent, [{style, ?wxLC_REPORT bor ?wxLC_HRULES}]), Li = wxListItem:new(), + Scale = observer_wx:get_scale(), wxListItem:setText(Li, "Module:Function/Arg"), wxListCtrl:insertColumn(LCtrl, 0, Li), - wxListCtrl:setColumnWidth(LCtrl, 0, 300), + wxListCtrl:setColumnWidth(LCtrl, 0, Scale * 300), wxListItem:setText(Li, "File:LineNumber"), wxListCtrl:insertColumn(LCtrl, 1, Li), - wxListCtrl:setColumnWidth(LCtrl, 1, 300), + wxListCtrl:setColumnWidth(LCtrl, 1, Scale * 300), wxListItem:destroy(Li), Update = fun() -> case observer_wx:try_rpc(node(Pid), erlang, process_info, diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 2c3b46a3a1..f458c8c34a 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -188,8 +188,9 @@ create_proc_port_view(Parent) -> wxListCtrl:setColumnWidth(Procs, Col, DefSize), Col + 1 end, - ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120}, - {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + Scale = observer_wx:get_scale(), + ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, Scale*120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, Scale*300}], lists:foldl(AddProc, 0, ProcListItems), AddPort = fun({Name, Align, DefSize}, Col) -> @@ -199,8 +200,8 @@ create_proc_port_view(Parent) -> wxListCtrl:setColumnWidth(Ports, Col, DefSize), Col + 1 end, - PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, 120}, - {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, Scale*120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, Scale*300}], lists:foldl(AddPort, 0, PortListItems), wxListItem:destroy(Li), @@ -242,14 +243,15 @@ create_matchspec_view(Parent) -> Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCS_WIN}, {style, Style}]), Li = wxListItem:new(), + Scale = observer_wx:get_scale(), wxListItem:setText(Li, "Modules"), wxListCtrl:insertColumn(Modules, 0, Li), wxListItem:setText(Li, "Functions"), wxListCtrl:insertColumn(Funcs, 0, Li), - wxListCtrl:setColumnWidth(Funcs, 0, 150), + wxListCtrl:setColumnWidth(Funcs, 0, Scale*150), wxListItem:setText(Li, "Match Spec"), wxListCtrl:insertColumn(Funcs, 1, Li), - wxListCtrl:setColumnWidth(Funcs, 1, 300), + wxListCtrl:setColumnWidth(Funcs, 1, Scale*300), wxListItem:destroy(Li), wxSplitterWindow:setSashGravity(Splitter, 0.0), @@ -969,7 +971,8 @@ output_file(true, true, Opts) -> create_logwindow(_Parent, false) -> {false, false}; create_logwindow(Parent, true) -> - LogWin = wxFrame:new(Parent, ?LOG_WIN, "Trace Log", [{size, {750, 800}}]), + Scale = observer_wx:get_scale(), + LogWin = wxFrame:new(Parent, ?LOG_WIN, "Trace Log", [{size, {750*Scale, 800*Scale}}]), MB = wxMenuBar:new(), File = wxMenu:new(), wxMenu:append(File, ?LOG_CLEAR, "Clear Log\tCtrl-C"), diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index ea292b92af..514d55ff24 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -167,9 +167,10 @@ select_nodes(Parent, Nodes) -> check_selector(Parent, Choices). module_selector(Parent, Node) -> + Scale = observer_wx:get_scale(), Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, - {size, {400, 400}}]), + {size, {400*Scale, 400*Scale}}]), Panel = wxPanel:new(Dialog), PanelSz = wxBoxSizer:new(?wxVERTICAL), MainSz = wxBoxSizer:new(?wxVERTICAL), @@ -237,9 +238,10 @@ function_selector(Parent, Node, Module) -> end. check_selector(Parent, ParsedChoices) -> + Scale = observer_wx:get_scale(), Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Functions", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, - {size, {400, 400}}]), + {size, {400*Scale, 400*Scale}}]), Panel = wxPanel:new(Dialog), PanelSz = wxBoxSizer:new(?wxVERTICAL), @@ -331,9 +333,10 @@ select_matchspec(Pid, Parent, AllMatchSpecs, Key) -> {value,{Key,MSs0},Rest} -> {MSs0,Rest}; false -> {[],AllMatchSpecs} end, + Scale = observer_wx:get_scale(), Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, - {size, {400, 400}}]), + {size, {400*Scale, 400*Scale}}]), Panel = wxPanel:new(Dialog), PanelSz = wxBoxSizer:new(?wxVERTICAL), diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl index d6dcee2cda..7bd67a0f0b 100644 --- a/lib/observer/src/observer_tv_table.erl +++ b/lib/observer/src/observer_tv_table.erl @@ -99,7 +99,8 @@ init([Parent, Opts]) -> ets -> "TV Ets: " ++ Title0; mnesia -> "TV Mnesia: " ++ Title0 end, - Frame = wxFrame:new(Parent, ?wxID_ANY, Title, [{size, {800, 600}}]), + Scale = observer_wx:get_scale(), + Frame = wxFrame:new(Parent, ?wxID_ANY, Title, [{size, {Scale * 800, Scale * 600}}]), IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"), Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]), wxFrame:setIcon(Frame, Icon), diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl index 814f3a1260..247b3e869f 100644 --- a/lib/observer/src/observer_tv_wx.erl +++ b/lib/observer/src/observer_tv_wx.erl @@ -87,12 +87,13 @@ init([Notebook, Parent, Config]) -> wxListCtrl:setColumnWidth(Grid, Col, DefSize), Col + 1 end, - ListItems = [{"Table Name", ?wxLIST_FORMAT_LEFT, 200}, - {"Objects", ?wxLIST_FORMAT_RIGHT, 100}, - {"Size (kB)", ?wxLIST_FORMAT_RIGHT, 100}, - {"Owner Pid", ?wxLIST_FORMAT_CENTER, 150}, - {"Owner Name", ?wxLIST_FORMAT_LEFT, 200}, - {"Table Id", ?wxLIST_FORMAT_LEFT, 250} + Scale = observer_wx:get_scale(), + ListItems = [{"Table Name", ?wxLIST_FORMAT_LEFT, Scale*200}, + {"Objects", ?wxLIST_FORMAT_RIGHT, Scale*100}, + {"Size (kB)", ?wxLIST_FORMAT_RIGHT, Scale*100}, + {"Owner Pid", ?wxLIST_FORMAT_CENTER, Scale*150}, + {"Owner Name", ?wxLIST_FORMAT_LEFT, Scale*200}, + {"Table Id", ?wxLIST_FORMAT_LEFT, Scale*250} ], lists:foldl(AddListEntry, 0, ListItems), wxListItem:destroy(Li), diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 453e3bdc2d..71db586845 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -22,7 +22,7 @@ -export([start/0, stop/0]). -export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, get_menubar/0, - set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). + get_scale/0, set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, handle_call/3, handle_info/2, check_page_title/1]). @@ -91,14 +91,24 @@ get_active_node() -> get_menubar() -> wx_object:call(observer, get_menubar). +get_scale() -> + ScaleStr = os:getenv("OBSERVER_SCALE", "1"), + try list_to_integer(ScaleStr) of + Scale when Scale < 1 -> 1; + Scale -> Scale + catch _:_ -> + 1 + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(_Args) -> register(observer, self()), wx:new(), catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1), + Scale = get_scale(), Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Observer", - [{size, {850, 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]), + [{size, {Scale * 850, Scale * 600}}, {style, ?wxDEFAULT_FRAME_STYLE}]), IconFile = filename:join(code:priv_dir(observer), "erlang_observer.png"), Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]), wxFrame:setIcon(Frame, Icon), @@ -771,7 +781,11 @@ ensure_sasl_started(Node) -> ensure_mf_h_handler_used(Node) -> %% is log_mf_h used ? - Handlers = rpc:block_call(Node, gen_event, which_handlers, [error_logger]), + Handlers = + case rpc:block_call(Node, gen_event, which_handlers, [error_logger]) of + {badrpc,{'EXIT',noproc}} -> []; % OTP-21+ and no event handler exists + Hs -> Hs + end, case lists:any(fun(L)-> L == log_mf_h end, Handlers) of false -> throw("Error: log_mf_h handler not used in sasl."), error; diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl index d5d3649525..d6b5eff9b5 100644 --- a/lib/observer/test/crashdump_helper.erl +++ b/lib/observer/test/crashdump_helper.erl @@ -204,5 +204,4 @@ dump_persistent_terms() -> create_persistent_terms() -> persistent_term:put({?MODULE,first}, {pid,42.0}), persistent_term:put({?MODULE,second}, [1,2,3]), - persistent_term:get(). - + {persistent_term:get({?MODULE,first}),persistent_term:get({?MODULE,second})}. diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 8c5e618f4a..31cf7011d4 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -615,9 +615,8 @@ special(File,Procs) -> #proc{dict=Dict} = ProcDetails, %% io:format("~p\n", [Dict]), - Pts1 = crashdump_helper:create_persistent_terms(), - Pts2 = proplists:get_value(pts,Dict), - true = lists:sort(Pts1) =:= lists:sort(Pts2), + Pts = crashdump_helper:create_persistent_terms(), + Pts = proplists:get_value(pts,Dict), io:format(" persistent terms ok",[]), ok; _ -> diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index 8c799f6ff1..fb4f61417e 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -2749,6 +2749,11 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext errmsg_buffer_size = errmsg_buffer_size - errmsg_size; acc_errmsg_size = acc_errmsg_size + errmsg_size; current_errmsg_pos = current_errmsg_pos + errmsg_size; + } else if(result == SQL_SUCCESS_WITH_INFO && errmsg_size >= errmsg_buffer_size) { + memcpy(diagnos.sqlState, current_sql_state, SQL_STATE_SIZE); + diagnos.nativeError = nativeError; + acc_errmsg_size = errmsg_buffer_size; + break; } else { break; } diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml index 4bb1f035f9..4d941b3b36 100644 --- a/lib/odbc/doc/src/odbc.xml +++ b/lib/odbc/doc/src/odbc.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>odbc</module> + <module since="">odbc</module> <modulesummary>Erlang ODBC application</modulesummary> <description> <p>This application provides an Erlang interface to communicate @@ -130,8 +130,8 @@ </section> <funcs> <func> - <name>commit(Ref, CommitMode) -></name> - <name>commit(Ref, CommitMode, TimeOut) -> ok | {error, Reason} </name> + <name since="">commit(Ref, CommitMode) -></name> + <name since="">commit(Ref, CommitMode, TimeOut) -> ok | {error, Reason} </name> <fsummary>Commits or rollbacks a transaction. </fsummary> <type> <v>Ref = connection_reference() </v> @@ -145,7 +145,7 @@ </desc> </func> <func> - <name>connect(ConnectStr, Options) -> {ok, Ref} | {error, Reason} </name> + <name since="">connect(ConnectStr, Options) -> {ok, Ref} | {error, Reason} </name> <fsummary>Opens a connection to the database. </fsummary> <type> <v>ConnectStr = string()</v> @@ -236,7 +236,7 @@ </desc> </func> <func> - <name>disconnect(Ref) -> ok | {error, Reason} </name> + <name since="">disconnect(Ref) -> ok | {error, Reason} </name> <fsummary>Closes a connection to a database. </fsummary> <type> <v>Ref = connection_reference()</v> @@ -255,8 +255,8 @@ </desc> </func> <func> - <name>describe_table(Ref, Table) -> </name> - <name>describe_table(Ref, Table, Timeout) -> {ok, Description} | {error, Reason} </name> + <name since="">describe_table(Ref, Table) -> </name> + <name since="">describe_table(Ref, Table, Timeout) -> {ok, Description} | {error, Reason} </name> <fsummary>Queries the database to find out the data types of the columns of the table <c>Table</c>. </fsummary> <type> <v>Ref = connection_reference()</v> @@ -271,8 +271,8 @@ </desc> </func> <func> - <name>first(Ref) -></name> - <name>first(Ref, Timeout) -> {selected, ColNames, Rows} | {error, Reason} </name> + <name since="">first(Ref) -></name> + <name since="">first(Ref, Timeout) -> {selected, ColNames, Rows} | {error, Reason} </name> <fsummary>Returns the first row of the result set and positions a cursor at this row.</fsummary> <type> <v>Ref = connection_reference()</v> @@ -287,8 +287,8 @@ </desc> </func> <func> - <name>last(Ref) -></name> - <name>last(Ref, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> + <name since="">last(Ref) -></name> + <name since="">last(Ref, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> <fsummary>Returns the last row of the result set and positions a cursor at this row. </fsummary> <type> <v>Ref = connection_reference()</v> @@ -303,8 +303,8 @@ </desc> </func> <func> - <name>next(Ref) -> </name> - <name>next(Ref, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> + <name since="">next(Ref) -> </name> + <name since="">next(Ref, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> <fsummary>Returns the next row of the result set relative the current cursor position and positions the cursor at this row. </fsummary> <type> <v>Ref = connection_reference()</v> @@ -325,8 +325,8 @@ </desc> </func> <func> - <name>param_query(Ref, SQLQuery, Params) -> </name> - <name>param_query(Ref, SQLQuery, Params, TimeOut) -> ResultTuple | {error, Reason} </name> + <name since="">param_query(Ref, SQLQuery, Params) -> </name> + <name since="">param_query(Ref, SQLQuery, Params, TimeOut) -> ResultTuple | {error, Reason} </name> <fsummary>Executes a parameterized SQL query.</fsummary> <type> <v>Ref = connection_reference()</v> @@ -353,8 +353,8 @@ </desc> </func> <func> - <name>prev(Ref) -> </name> - <name>prev(ConnectionReference, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> + <name since="">prev(Ref) -> </name> + <name since="">prev(ConnectionReference, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> <fsummary>Returns the previous row of the result set relative the current cursor position and positions the cursor at this row. </fsummary> <type> <v>Ref = connection_reference()</v> @@ -371,8 +371,8 @@ </func> <func> - <name>start() -> </name> - <name>start(Type) -> ok | {error, Reason}</name> + <name since="">start() -> </name> + <name since="">start(Type) -> ok | {error, Reason}</name> <fsummary>Starts the odb application. </fsummary> <type> @@ -389,7 +389,7 @@ </func> <func> - <name>stop() -> ok </name> + <name since="">stop() -> ok </name> <fsummary> Stops the odbc application.</fsummary> <desc> @@ -400,8 +400,8 @@ </func> <func> - <name>sql_query(Ref, SQLQuery) -> </name> - <name>sql_query(Ref, SQLQuery, TimeOut) -> ResultTuple | [ResultTuple] |{error, Reason}</name> + <name since="">sql_query(Ref, SQLQuery) -> </name> + <name since="">sql_query(Ref, SQLQuery, TimeOut) -> ResultTuple | [ResultTuple] |{error, Reason}</name> <fsummary>Executes a SQL query or a batch of SQL queries. If it is a SELECT query the result set is returned, on the format<c>{selected, ColNames, Rows}</c>. For other query types the tuple <c>{updated, NRows}</c>is returned, and for batched queries, if the driver supports them, this function can also return a list of result tuples.</fsummary> <type> <v>Ref = connection_reference()</v> @@ -429,8 +429,8 @@ </desc> </func> <func> - <name>select_count(Ref, SelectQuery) -> </name> - <name>select_count(Ref, SelectQuery, TimeOut) -> {ok, NrRows} | {error, Reason} </name> + <name since="">select_count(Ref, SelectQuery) -> </name> + <name since="">select_count(Ref, SelectQuery, TimeOut) -> {ok, NrRows} | {error, Reason} </name> <fsummary>Executes a SQL SELECT query and associates the result set with the connection. A cursor is positioned before the first row in the result set and the tuple <c>{ok, NrRows}</c>is returned. </fsummary> <type> <v>Ref = connection_reference()</v> @@ -453,8 +453,8 @@ </desc> </func> <func> - <name>select(Ref, Position, N) -></name> - <name>select(Ref, Position, N, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> + <name since="">select(Ref, Position, N) -></name> + <name since="">select(Ref, Position, N, TimeOut) -> {selected, ColNames, Rows} | {error, Reason} </name> <fsummary>Selects <c>N</c>consecutive rows of the result set.</fsummary> <type> <v>Ref = connection_reference()</v> diff --git a/lib/os_mon/doc/src/cpu_sup.xml b/lib/os_mon/doc/src/cpu_sup.xml index bada165a06..b7adb2bcd2 100644 --- a/lib/os_mon/doc/src/cpu_sup.xml +++ b/lib/os_mon/doc/src/cpu_sup.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>cpu_sup</module> + <module since="">cpu_sup</module> <modulesummary>A CPU Load and CPU Utilization Supervisor Process</modulesummary> <description> <p><c>cpu_sup</c> is a process which supervises the CPU load @@ -76,7 +76,7 @@ </description> <funcs> <func> - <name>nprocs() -> UnixProcesses | {error, Reason}</name> + <name since="">nprocs() -> UnixProcesses | {error, Reason}</name> <fsummary>Get the number of UNIX processes running on this host</fsummary> <type> <v>UnixProcesses = int()</v> @@ -90,7 +90,7 @@ </desc> </func> <func> - <name>avg1() -> SystemLoad | {error, Reason}</name> + <name since="">avg1() -> SystemLoad | {error, Reason}</name> <fsummary>Get the system load average for the last minute</fsummary> <type> <v>SystemLoad = int()</v> @@ -104,7 +104,7 @@ </desc> </func> <func> - <name>avg5() -> SystemLoad | {error, Reason}</name> + <name since="">avg5() -> SystemLoad | {error, Reason}</name> <fsummary>Get the system load average for the last five minutes</fsummary> <type> <v>SystemLoad = int()</v> @@ -118,7 +118,7 @@ </desc> </func> <func> - <name>avg15() -> SystemLoad | {error, Reason}</name> + <name since="">avg15() -> SystemLoad | {error, Reason}</name> <fsummary>Get the system load average for the last fifteen minutes</fsummary> <type> <v>SystemLoad = int()</v> @@ -132,7 +132,7 @@ </desc> </func> <func> - <name>util() -> CpuUtil | {error, Reason}</name> + <name since="">util() -> CpuUtil | {error, Reason}</name> <fsummary>Get the CPU utilization</fsummary> <type> <v>CpuUtil = float()</v> @@ -156,7 +156,7 @@ </desc> </func> <func> - <name>util(Opts) -> UtilSpec | {error, Reason}</name> + <name since="">util(Opts) -> UtilSpec | {error, Reason}</name> <fsummary>Get the CPU utilization</fsummary> <type> <v>Opts = [detailed | per_cpu]</v> diff --git a/lib/os_mon/doc/src/disksup.xml b/lib/os_mon/doc/src/disksup.xml index 610ef2c907..116a6dfd19 100644 --- a/lib/os_mon/doc/src/disksup.xml +++ b/lib/os_mon/doc/src/disksup.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>disksup</module> + <module since="">disksup</module> <modulesummary>A Disk Supervisor Process</modulesummary> <description> <p><c>disksup</c> is a process which supervises the available disk @@ -92,7 +92,7 @@ </section> <funcs> <func> - <name>get_disk_data() -> [DiskData]</name> + <name since="">get_disk_data() -> [DiskData]</name> <fsummary>Get data for the disks in the system</fsummary> <type> <v>DiskData = {Id, KByte, Capacity}</v> @@ -112,7 +112,7 @@ </desc> </func> <func> - <name>get_check_interval() -> MS</name> + <name since="">get_check_interval() -> MS</name> <fsummary>Get time interval, in milliseconds, for the periodic disk space check</fsummary> <type> <v>MS = int()</v> @@ -123,7 +123,7 @@ </desc> </func> <func> - <name>set_check_interval(Minutes) -> ok</name> + <name since="">set_check_interval(Minutes) -> ok</name> <fsummary>Set time interval, in minutes, for the periodic disk space check</fsummary> <type> <v>Minutes = int()>=1</v> @@ -138,7 +138,7 @@ </desc> </func> <func> - <name>get_almost_full_threshold() -> Percent</name> + <name since="">get_almost_full_threshold() -> Percent</name> <fsummary>Get threshold, in percent, for disk space utilization</fsummary> <type> <v>Percent = int()</v> @@ -148,7 +148,7 @@ </desc> </func> <func> - <name>set_almost_full_threshold(Float) -> ok</name> + <name since="">set_almost_full_threshold(Float) -> ok</name> <fsummary>Set threshold, as percentage represented by a float, for disk space utilization</fsummary> <type> <v>Float = float(), 0=<Float=<1</v> diff --git a/lib/os_mon/doc/src/memsup.xml b/lib/os_mon/doc/src/memsup.xml index c669e4670a..51c78b07c2 100644 --- a/lib/os_mon/doc/src/memsup.xml +++ b/lib/os_mon/doc/src/memsup.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>memsup</module> + <module since="">memsup</module> <modulesummary>A Memory Supervisor Process</modulesummary> <description> <p><c>memsup</c> is a process which supervises the memory usage for @@ -127,7 +127,7 @@ </section> <funcs> <func> - <name>get_memory_data() -> {Total,Allocated,Worst}</name> + <name since="">get_memory_data() -> {Total,Allocated,Worst}</name> <fsummary>Get data for the memory in the system</fsummary> <type> <v>Total = Allocated = int()</v> @@ -155,7 +155,7 @@ </desc> </func> <func> - <name>get_system_memory_data() -> MemDataList</name> + <name since="">get_system_memory_data() -> MemDataList</name> <fsummary>Get system dependent memory data</fsummary> <type> <v>MemDataList = [{Tag, Size}]</v> @@ -216,7 +216,7 @@ </desc> </func> <func> - <name>get_os_wordsize() -> Wordsize</name> + <name since="">get_os_wordsize() -> Wordsize</name> <fsummary>Get the wordsize of running os.</fsummary> <type> <v>Wordsize = 32 | 64 | unsupported_os</v> @@ -226,7 +226,7 @@ </desc> </func> <func> - <name>get_check_interval() -> MS</name> + <name since="">get_check_interval() -> MS</name> <fsummary>Get time interval, in milliseconds, for the periodic memory check</fsummary> <type> <v>MS = int()</v> @@ -237,7 +237,7 @@ </desc> </func> <func> - <name>set_check_interval(Minutes) -> ok</name> + <name since="">set_check_interval(Minutes) -> ok</name> <fsummary>Set time interval, in minutes, for the periodic memory check</fsummary> <type> <v>Minutes = int()>0</v> @@ -252,7 +252,7 @@ </desc> </func> <func> - <name>get_procmem_high_watermark() -> int()</name> + <name since="">get_procmem_high_watermark() -> int()</name> <fsummary>Get threshold, in percent, for process memory allocation</fsummary> <desc> <p>Returns the threshold, in percent, for process memory @@ -260,7 +260,7 @@ </desc> </func> <func> - <name>set_procmem_high_watermark(Float) -> ok</name> + <name since="">set_procmem_high_watermark(Float) -> ok</name> <fsummary>Set threshold, as percentage represented by a float, for process memory allocation</fsummary> <desc> <p>Changes the threshold, given as a float, for process memory @@ -273,7 +273,7 @@ </desc> </func> <func> - <name>get_sysmem_high_watermark() -> int()</name> + <name since="">get_sysmem_high_watermark() -> int()</name> <fsummary>Get threshold, in percent, for system memory allocation</fsummary> <desc> <p>Returns the threshold, in percent, for system memory @@ -281,7 +281,7 @@ </desc> </func> <func> - <name>set_sysmem_high_watermark(Float) -> ok</name> + <name since="">set_sysmem_high_watermark(Float) -> ok</name> <fsummary>Set threshold, given as a float, for system memory allocation</fsummary> <desc> <p>Changes the threshold, given as a float, for system memory @@ -294,7 +294,7 @@ </desc> </func> <func> - <name>get_helper_timeout() -> Seconds</name> + <name since="">get_helper_timeout() -> Seconds</name> <fsummary>Get the timeout value, in seconds, for memory checks</fsummary> <type> <v>Seconds = int()</v> @@ -304,7 +304,7 @@ </desc> </func> <func> - <name>set_helper_timeout(Seconds) -> ok</name> + <name since="">set_helper_timeout(Seconds) -> ok</name> <fsummary>Set the timeout value, in seconds, for memory checks</fsummary> <type> <v>Seconds = int() (>= 1)</v> diff --git a/lib/os_mon/doc/src/nteventlog.xml b/lib/os_mon/doc/src/nteventlog.xml index d32427227c..08cf165a24 100644 --- a/lib/os_mon/doc/src/nteventlog.xml +++ b/lib/os_mon/doc/src/nteventlog.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>nteventlog</module> + <module since="">nteventlog</module> <modulesummary>Interface to Windows Event Log</modulesummary> <description> <p><c>nteventlog</c> provides a generic interface to the Windows @@ -61,8 +61,8 @@ </description> <funcs> <func> - <name>start(Identifier, MFA) -> Result</name> - <name>start_link(Identifier, MFA) -> Result</name> + <name since="">start(Identifier, MFA) -> Result</name> + <name since="">start_link(Identifier, MFA) -> Result</name> <fsummary>Start the NT eventlog server</fsummary> <type> <v>Identifier = string() | atom()</v> @@ -82,7 +82,7 @@ </desc> </func> <func> - <name>stop() -> stopped</name> + <name since="">stop() -> stopped</name> <fsummary>Stop the NT eventlog server</fsummary> <type> <v>Result = stopped</v> diff --git a/lib/os_mon/doc/src/os_mon_mib.xml b/lib/os_mon/doc/src/os_mon_mib.xml index e995bf3de1..f6d0b20094 100644 --- a/lib/os_mon/doc/src/os_mon_mib.xml +++ b/lib/os_mon/doc/src/os_mon_mib.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>os_mon_mib</module> + <module since="">os_mon_mib</module> <modulesummary>Loading and Unloading of OTP-OS-MON-MIB</modulesummary> <description> <p>Functions for loading and unloading the OTP-OS-MON-MIB into/from @@ -42,7 +42,7 @@ </description> <funcs> <func> - <name>load(Agent) -> ok | {error, Reason}</name> + <name since="">load(Agent) -> ok | {error, Reason}</name> <fsummary>Load the OTP-OS-MON-MIB</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -53,7 +53,7 @@ </desc> </func> <func> - <name>unload(Agent) -> ok | {error, Reason}</name> + <name since="">unload(Agent) -> ok | {error, Reason}</name> <fsummary>Unload the OTP-OS-MON-MIB</fsummary> <type> <v>Agent = pid() | atom() </v> diff --git a/lib/os_mon/doc/src/os_sup.xml b/lib/os_mon/doc/src/os_sup.xml index d517f387b4..4a84165a6c 100644 --- a/lib/os_mon/doc/src/os_sup.xml +++ b/lib/os_mon/doc/src/os_sup.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>os_sup</module> + <module since="">os_sup</module> <modulesummary>Interface to OS System Messages</modulesummary> <description> <p><c>os_sup</c> is a process providing a message passing service @@ -159,8 +159,8 @@ </section> <funcs> <func> - <name>enable() -> ok | {error, Res}</name> - <name>enable(Dir, Conf) -> ok | {error, Error}</name> + <name since="">enable() -> ok | {error, Res}</name> + <name since="">enable(Dir, Conf) -> ok | {error, Error}</name> <fsummary>Enable the service (Solaris only)</fsummary> <type> <v>Dir = Conf = Res = string()</v> @@ -194,8 +194,8 @@ </desc> </func> <func> - <name>disable() -> ok | {error, Res}</name> - <name>disable(Dir, Conf) -> ok | {error, Error}</name> + <name since="">disable() -> ok | {error, Res}</name> + <name since="">disable(Dir, Conf) -> ok | {error, Error}</name> <fsummary>Disable the service (Solaris only)</fsummary> <type> <v>Dir = Conf = Res = string()</v> diff --git a/lib/otp_mibs/doc/src/otp_mib.xml b/lib/otp_mibs/doc/src/otp_mib.xml index 530c529c69..e7d338c165 100644 --- a/lib/otp_mibs/doc/src/otp_mib.xml +++ b/lib/otp_mibs/doc/src/otp_mib.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>otp_mib</module> + <module since="">otp_mib</module> <modulesummary>Handles the OTP-MIB</modulesummary> <description> <p>The SNMP application should be used to start an SNMP agent. Then @@ -41,7 +41,7 @@ </description> <funcs> <func> - <name>load(Agent) -> ok | {error, Reason}</name> + <name since="">load(Agent) -> ok | {error, Reason}</name> <fsummary>Load the OTP-MIB</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -52,7 +52,7 @@ </desc> </func> <func> - <name>unload(Agent) -> ok | {error, Reason}</name> + <name since="">unload(Agent) -> ok | {error, Reason}</name> <fsummary>Unload the OTP-MIB</fsummary> <type> <v>Agent = pid() | atom()</v> diff --git a/lib/parsetools/doc/src/leex.xml b/lib/parsetools/doc/src/leex.xml index 1227625287..3b82f60201 100644 --- a/lib/parsetools/doc/src/leex.xml +++ b/lib/parsetools/doc/src/leex.xml @@ -21,7 +21,7 @@ <rev>A</rev> <file>leex.xml</file> </header> - <module>leex</module> + <module since="">leex</module> <modulesummary>Lexical analyzer generator for Erlang</modulesummary> <description> <p>A regular expression based lexical analyzer generator for @@ -38,7 +38,8 @@ Token = tuple()</code> </section> <funcs> <func> - <name>file(FileName, [, Options]) -> LeexRet</name> + <name since="">file(FileName) -> LeexRet</name> + <name since="OTP R16B02">file(FileName, Options) -> LeexRet</name> <fsummary>Generate a lexical analyzer</fsummary> <type> <v>FileName = filename()</v> @@ -124,7 +125,7 @@ Token = tuple()</code> </desc> </func> <func> - <name>format_error(ErrorInfo) -> Chars</name> + <name since="">format_error(ErrorInfo) -> Chars</name> <fsummary>Return an English description of a an error tuple.</fsummary> <type> <v>Chars = [char() | Chars]</v> @@ -145,8 +146,8 @@ Token = tuple()</code> <funcs> <func> - <name>string(String) -> StringRet</name> - <name>string(String, StartLine) -> StringRet</name> + <name since="">string(String) -> StringRet</name> + <name since="">string(String, StartLine) -> StringRet</name> <fsummary>Generated by Leex</fsummary> <type> <v>String = string()</v> @@ -163,9 +164,9 @@ Token = tuple()</code> </func> <func> - <name>token(Cont, Chars) -> {more,Cont1} | {done,TokenRet,RestChars} + <name since="">token(Cont, Chars) -> {more,Cont1} | {done,TokenRet,RestChars} </name> - <name>token(Cont, Chars, StartLine) -> {more,Cont1} + <name since="">token(Cont, Chars, StartLine) -> {more,Cont1} | {done,TokenRet,RestChars} </name> <fsummary>Generated by Leex</fsummary> @@ -198,9 +199,9 @@ io:request(InFile, {get_until,Prompt,Module,token,[Line]}) </func> <func> - <name>tokens(Cont, Chars) -> {more,Cont1} | {done,TokensRet,RestChars} + <name since="">tokens(Cont, Chars) -> {more,Cont1} | {done,TokensRet,RestChars} </name> - <name>tokens(Cont, Chars, StartLine) -> + <name since="">tokens(Cont, Chars, StartLine) -> {more,Cont1} | {done,TokensRet,RestChars} </name> <fsummary>Generated by Leex</fsummary> diff --git a/lib/parsetools/doc/src/yecc.xml b/lib/parsetools/doc/src/yecc.xml index 5f95b5c150..67a2c95c25 100644 --- a/lib/parsetools/doc/src/yecc.xml +++ b/lib/parsetools/doc/src/yecc.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>yecc.sgml</file> </header> - <module>yecc</module> + <module since="">yecc</module> <modulesummary>LALR-1 Parser Generator</modulesummary> <description> <p>An LALR-1 parser generator for Erlang, similar to <c>yacc</c>. @@ -46,7 +46,7 @@ </description> <funcs> <func> - <name>file(Grammarfile [, Options]) -> YeccRet</name> + <name since="">file(Grammarfile [, Options]) -> YeccRet</name> <fsummary>Give information about resolved and unresolved parse action conflicts.</fsummary> <type> <v>Grammarfile = filename()</v> @@ -137,7 +137,7 @@ </desc> </func> <func> - <name>format_error(Reason) -> Chars</name> + <name since="">format_error(Reason) -> Chars</name> <fsummary>Return an English description of a an error tuple.</fsummary> <type> <v>Reason = - as returned by yecc:file/1,2 -</v> diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index ee3877ddd0..b7589f6653 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -31,7 +31,7 @@ <date></date> <rev></rev> </header> - <module>public_key</module> + <module since="">public_key</module> <modulesummary>API module for public-key infrastructure.</modulesummary> <description> <p>Provides functions to handle public-key infrastructure, @@ -176,7 +176,7 @@ <funcs> <func> - <name name="compute_key" arity="2"/> + <name name="compute_key" arity="2" since="OTP R16B01"/> <fsummary>Computes shared secret.</fsummary> <desc> <p>Computes shared secret.</p> @@ -184,7 +184,7 @@ </func> <func> - <name name="compute_key" arity="3"/> + <name name="compute_key" arity="3" since="OTP R16B01"/> <fsummary>Computes shared secret.</fsummary> <desc> <p>Computes shared secret.</p> @@ -192,8 +192,8 @@ </func> <func> - <name name="decrypt_private" arity="2"/> - <name name="decrypt_private" arity="3"/> + <name name="decrypt_private" arity="2" since="OTP R14B"/> + <name name="decrypt_private" arity="3" since="OTP R14B"/> <fsummary>Public-key decryption.</fsummary> <desc> <p>Public-key decryption using the private key. See also <seealso @@ -202,8 +202,8 @@ </func> <func> - <name name="decrypt_public" arity="2"/> - <name name="decrypt_public" arity="3"/> + <name name="decrypt_public" arity="2" since="OTP R14B"/> + <name name="decrypt_public" arity="3" since="OTP R14B"/> <fsummary>Public-key decryption.</fsummary> <desc> <p>Public-key decryption using the public key. See also <seealso @@ -212,7 +212,7 @@ </func> <func> - <name name="der_decode" arity="2"/> + <name name="der_decode" arity="2" since="OTP R14B"/> <fsummary>Decodes a public-key ASN.1 DER encoded entity.</fsummary> <desc> <p>Decodes a public-key ASN.1 DER encoded entity.</p> @@ -220,7 +220,7 @@ </func> <func> - <name name="der_encode" arity="2"/> + <name name="der_encode" arity="2" since="OTP R14B"/> <fsummary>Encodes a public-key entity with ASN.1 DER encoding.</fsummary> <desc> <p>Encodes a public-key entity with ASN.1 DER encoding.</p> @@ -228,7 +228,7 @@ </func> <func> - <name name="dh_gex_group" arity="4"/> + <name name="dh_gex_group" arity="4" since="OTP 18.2"/> <fsummary>Selects a group for Diffie-Hellman key exchange</fsummary> <desc> <p>Selects a group for Diffie-Hellman key exchange with the key size in the range <c>MinSize...MaxSize</c> @@ -249,8 +249,8 @@ </func> <func> - <name name="encrypt_private" arity="2"/> - <name name="encrypt_private" arity="3"/> + <name name="encrypt_private" arity="2" since="OTP R14B"/> + <name name="encrypt_private" arity="3" since="OTP 21.1"/> <fsummary>Public-key encryption using the private key.</fsummary> <desc> <p>Public-key encryption using the private key. @@ -260,8 +260,8 @@ </func> <func> - <name name="encrypt_public" arity="2"/> - <name name="encrypt_public" arity="3"/> + <name name="encrypt_public" arity="2" since="OTP R14B"/> + <name name="encrypt_public" arity="3" since="OTP 21.1"/> <fsummary>Public-key encryption using the public key.</fsummary> <desc> <p>Public-key encryption using the public key. See also <seealso @@ -270,7 +270,7 @@ </func> <func> - <name name="generate_key" arity="1"/> + <name name="generate_key" arity="1" since="OTP R16B01"/> <fsummary>Generates a new keypair.</fsummary> <desc> <p>Generates a new keypair. Note that except for Diffie-Hellman @@ -281,7 +281,7 @@ </func> <func> - <name name="pem_decode" arity="1"/> + <name name="pem_decode" arity="1" since="OTP R14B"/> <fsummary>Decodes PEM binary data and returns entries as ASN.1 DER encoded entities.</fsummary> <desc> @@ -291,7 +291,7 @@ </func> <func> - <name name="pem_encode" arity="1"/> + <name name="pem_encode" arity="1" since="OTP R14B"/> <fsummary>Creates a PEM binary.</fsummary> <desc> <p>Creates a PEM binary.</p> @@ -299,8 +299,8 @@ </func> <func> - <name name="pem_entry_decode" arity="1"/> - <name name="pem_entry_decode" arity="2"/> + <name name="pem_entry_decode" arity="1" since="OTP R14B"/> + <name name="pem_entry_decode" arity="2" since="OTP R14B"/> <fsummary>Decodes a PEM entry.</fsummary> <desc> <p>Decodes a PEM entry. <c>pem_decode/1</c> returns a list of PEM @@ -311,8 +311,8 @@ </func> <func> - <name name="pem_entry_encode" arity="2"/> - <name name="pem_entry_encode" arity="3"/> + <name name="pem_entry_encode" arity="2" since="OTP R14B"/> + <name name="pem_entry_encode" arity="3" since="OTP R14B"/> <fsummary>Creates a PEM entry that can be fed to <c>pem_encode/1</c>.</fsummary> <desc> <p>Creates a PEM entry that can be feed to <c>pem_encode/1</c>.</p> @@ -326,7 +326,7 @@ </func> <func> - <name name="pkix_decode_cert" arity="2"/> + <name name="pkix_decode_cert" arity="2" since=""/> <fsummary>Decodes an ASN.1 DER-encoded PKIX x509 certificate.</fsummary> <desc> <p>Decodes an ASN.1 DER-encoded PKIX certificate. Option <c>otp</c> @@ -337,7 +337,7 @@ </func> <func> - <name name="pkix_encode" arity="3"/> + <name name="pkix_encode" arity="3" since="OTP R14B"/> <fsummary>DER encodes a PKIX x509 certificate or part of such a certificate.</fsummary> <desc> @@ -349,7 +349,7 @@ </func> <func> - <name name="pkix_is_issuer" arity="2"/> + <name name="pkix_is_issuer" arity="2" since="OTP R14B"/> <fsummary>Checks if <c>IssuerCert</c> issued <c>Cert</c>.</fsummary> <desc> <p>Checks if <c>IssuerCert</c> issued <c>Cert</c>.</p> @@ -357,7 +357,7 @@ </func> <func> - <name name="pkix_is_fixed_dh_cert" arity="1"/> + <name name="pkix_is_fixed_dh_cert" arity="1" since="OTP R14B"/> <fsummary>Checks if a certificate is a fixed Diffie-Hellman certificate.</fsummary> <desc> <p>Checks if a certificate is a fixed Diffie-Hellman certificate.</p> @@ -365,7 +365,7 @@ </func> <func> - <name name="pkix_is_self_signed" arity="1"/> + <name name="pkix_is_self_signed" arity="1" since="OTP R14B"/> <fsummary>Checks if a certificate is self-signed.</fsummary> <desc> <p>Checks if a certificate is self-signed.</p> @@ -373,7 +373,7 @@ </func> <func> - <name name="pkix_issuer_id" arity="2"/> + <name name="pkix_issuer_id" arity="2" since="OTP R14B"/> <fsummary>Returns the issuer id.</fsummary> <desc> <p>Returns the issuer id.</p> @@ -381,7 +381,7 @@ </func> <func> - <name name="pkix_normalize_name" arity="1"/> + <name name="pkix_normalize_name" arity="1" since="OTP R14B"/> <fsummary>Normalizes an issuer name so that it can be easily compared to another issuer name.</fsummary> <desc> @@ -391,7 +391,7 @@ </func> <func> - <name>pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, {PublicKeyInfo, PolicyTree}} | {error, {bad_cert, Reason}} </name> + <name since="OTP R16B">pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, {PublicKeyInfo, PolicyTree}} | {error, {bad_cert, Reason}} </name> <fsummary>Performs a basic path validation according to RFC 5280.</fsummary> <type> <v>TrustedCert = #'OTPCertificate'{} | der_encoded() | atom()</v> @@ -491,7 +491,7 @@ fun(OtpCert :: #'OTPCertificate'{}, </func> <func> - <name name="pkix_crl_issuer" arity="1"/> + <name name="pkix_crl_issuer" arity="1" since="OTP 17.5"/> <fsummary>Returns the issuer of the <c>CRL</c>.</fsummary> <desc> <p>Returns the issuer of the <c>CRL</c>.</p> @@ -499,7 +499,7 @@ fun(OtpCert :: #'OTPCertificate'{}, </func> <func> - <name name="pkix_crls_validate" arity="3"/> + <name name="pkix_crls_validate" arity="3" since="OTP R16B"/> <fsummary>Performs CRL validation.</fsummary> <desc> <p>Performs CRL validation. It is intended to be called from @@ -551,7 +551,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_crl_verify" arity="2"/> + <name name="pkix_crl_verify" arity="2" since="OTP 17.5"/> <fsummary> Verify that <c>Cert</c> is the <c> CRL</c> signer. </fsummary> <desc> <p>Verify that <c>Cert</c> is the <c>CRL</c> signer.</p> @@ -559,7 +559,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_dist_point" arity="1"/> + <name name="pkix_dist_point" arity="1" since="OTP 17.5"/> <fsummary>Creates a distribution point for CRLs issued by the same issuer as <c>Cert</c>.</fsummary> <desc> <p>Creates a distribution point for CRLs issued by the same issuer as <c>Cert</c>. @@ -570,7 +570,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_dist_points" arity="1"/> + <name name="pkix_dist_points" arity="1" since="OTP 17.5"/> <fsummary> Extracts distribution points from the certificates extensions.</fsummary> <desc> <p> Extracts distribution points from the certificates extensions.</p> @@ -578,7 +578,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_match_dist_point" arity="2"/> + <name name="pkix_match_dist_point" arity="2" since="OTP 19.0"/> <fsummary>Checks whether the given distribution point matches the Issuing Distribution Point of the CRL.</fsummary> <desc> @@ -590,7 +590,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_sign" arity="2"/> + <name name="pkix_sign" arity="2" since="OTP R14B"/> <fsummary>Signs certificate.</fsummary> <desc> <p>Signs an 'OTPTBSCertificate'. Returns the corresponding @@ -599,7 +599,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_sign_types" arity="1"/> + <name name="pkix_sign_types" arity="1" since="OTP R16B01"/> <fsummary>Translates signature algorithm OID to Erlang digest and signature algorithm types.</fsummary> <desc> <p>Translates signature algorithm OID to Erlang digest and signature types. @@ -609,8 +609,8 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name>pkix_test_data(Options) -> Config </name> - <name>pkix_test_data([chain_opts()]) -> [conf_opt()]</name> + <name since="OTP 20.1">pkix_test_data(Options) -> Config </name> + <name since="OTP 20.1">pkix_test_data([chain_opts()]) -> [conf_opt()]</name> <fsummary>Creates certificate test data.</fsummary> <type> <v>Options = #{chain_type() := chain_opts()} </v> @@ -644,7 +644,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, <v>conf_opt() = {cert, der_encoded()} | {key, PrivateKey} |{cacerts, [der_encoded()]}</v> <d> This is a subset of the type - <seealso marker="ssl:ssl#type-ssloption"> ssl:ssl_option()</seealso>. + <seealso marker="ssl:ssl#type-tls_option"> ssl:tls_option()</seealso>. <c>PrivateKey</c> is what <seealso marker="#generate_key-1">generate_key/1</seealso> returns. @@ -742,7 +742,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name>pkix_test_root_cert(Name, Options) -> RootCert</name> + <name since="OTP 20.2">pkix_test_root_cert(Name, Options) -> RootCert</name> <fsummary>Generates a test data root cert.</fsummary> <type> <v>Name = string()</v> @@ -772,7 +772,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name name="pkix_verify" arity="2"/> + <name name="pkix_verify" arity="2" since="OTP R14B"/> <fsummary>Verifies PKIX x.509 certificate signature.</fsummary> <desc> <p>Verifies PKIX x.509 certificate signature.</p> @@ -780,8 +780,8 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </func> <func> - <name>pkix_verify_hostname(Cert, ReferenceIDs) -> boolean()</name> - <name>pkix_verify_hostname(Cert, ReferenceIDs, Opts) -> boolean()</name> + <name since="OTP 19.3">pkix_verify_hostname(Cert, ReferenceIDs) -> boolean()</name> + <name since="OTP 19.3">pkix_verify_hostname(Cert, ReferenceIDs, Opts) -> boolean()</name> <fsummary>Verifies that a PKIX x.509 certificate <i>presented identifier</i> (e.g hostname) is an expected one.</fsummary> <type> @@ -864,7 +864,7 @@ end </func> <func> - <name>pkix_verify_hostname_match_fun(Protcol) -> fun(RefId | FQDN::string(), PresentedID) -> boolean() | default</name> + <name since="OTP 21.0">pkix_verify_hostname_match_fun(Protcol) -> fun(RefId | FQDN::string(), PresentedID) -> boolean() | default</name> <fsummary>Returns a fun that is intendended as argument to the match_fun option in pkix_verify_hostname/3. </fsummary> <type> @@ -889,8 +889,8 @@ end <func> - <name name="sign" arity="3"/> - <name name="sign" arity="4"/> + <name name="sign" arity="3" since=""/> + <name name="sign" arity="4" since="OTP 20.1"/> <fsummary>Creates a digital signature.</fsummary> <desc> <p>Creates a digital signature.</p> @@ -901,7 +901,7 @@ end </func> <func> - <name name="ssh_decode" arity="2"/> + <name name="ssh_decode" arity="2" since="OTP R14B03"/> <fsummary>Decodes an SSH file-binary.</fsummary> <desc> <p>Decodes an SSH file-binary. In the case of <c>known_hosts</c> or @@ -933,7 +933,7 @@ end </func> <func> - <name name="ssh_encode" arity="2"/> + <name name="ssh_encode" arity="2" since="OTP R14B03"/> <fsummary>Encodes a list of SSH file entries to a binary.</fsummary> <desc> <p>Encodes a list of SSH file entries (public keys and attributes) to a binary. Possible @@ -947,9 +947,9 @@ end </func> <func> - <name>ssh_hostkey_fingerprint(HostKey) -> string()</name> - <name>ssh_hostkey_fingerprint(DigestType, HostKey) -> string()</name> - <name>ssh_hostkey_fingerprint([DigestType], HostKey) -> [string()]</name> + <name since="OTP 19.2">ssh_hostkey_fingerprint(HostKey) -> string()</name> + <name since="OTP 19.2">ssh_hostkey_fingerprint(DigestType, HostKey) -> string()</name> + <name since="OTP 19.2">ssh_hostkey_fingerprint([DigestType], HostKey) -> [string()]</name> <fsummary>Calculates a ssh fingerprint for a hostkey.</fsummary> <type> <v>HostKey = <seealso marker="#type-public_key">public_key()</seealso></v> @@ -982,8 +982,8 @@ end </func> <func> - <name name="verify" arity="4"/> - <name name="verify" arity="5"/> + <name name="verify" arity="4" since="OTP R14B"/> + <name name="verify" arity="5" since="OTP 20.1"/> <fsummary>Verifies a digital signature.</fsummary> <desc> <p>Verifies a digital signature.</p> @@ -993,7 +993,7 @@ end </func> <func> - <name name="short_name_hash" arity="1"/> + <name name="short_name_hash" arity="1" since="OTP 19.0"/> <fsummary>Generates a short hash of an issuer name.</fsummary> <desc> <p>Generates a short hash of an issuer name. The hash is diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 75d40d2e8a..47c5dbb95a 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -66,7 +66,7 @@ -export_type([public_key/0, private_key/0, pem_entry/0, pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0, - key_params/0, digest_type/0]). + key_params/0, digest_type/0, issuer_name/0, oid/0]). -type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key() | ed_public_key() . -type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key() | ed_private_key() . diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 874cda8369..5afbad0ba8 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -33,7 +33,7 @@ <date></date> <rev>%VSN%</rev> </header> - <module>reltool</module> + <module since="">reltool</module> <modulesummary>Main API of the Reltool application</modulesummary> <description> <p>This is an interface module for the Reltool application.</p> @@ -591,7 +591,7 @@ target_spec() = [target_spec()] <funcs> <func> - <name>create_target(Server, TargetDir) -> ok | {error, Reason}</name> + <name since="">create_target(Server, TargetDir) -> ok | {error, Reason}</name> <fsummary>Create a target system</fsummary> <type> <v>Server = server()</v> @@ -604,7 +604,7 @@ target_spec() = [target_spec()] </func> <func> - <name>eval_target_spec(TargetSpec, RootDir, TargetDir) -> ok | {error, Reason}</name> + <name since="">eval_target_spec(TargetSpec, RootDir, TargetDir) -> ok | {error, Reason}</name> <fsummary>Create a target system</fsummary> <type> <v>TargetSpec = target_spec()</v> @@ -655,7 +655,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_config(Server) -> {ok, Config} | {error, Reason}</name> + <name since="">get_config(Server) -> {ok, Config} | {error, Reason}</name> <fsummary>Get reltool configuration</fsummary> <type> <v>Server = server()</v> @@ -667,7 +667,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_config(Server, InclDefaults, InclDerived) -> {ok, Config} | {error, Reason}</name> + <name since="">get_config(Server, InclDefaults, InclDerived) -> {ok, Config} | {error, Reason}</name> <fsummary>Get reltool configuration</fsummary> <type> <v>Server = server()</v> @@ -685,7 +685,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_rel(Server, Relname) -> {ok, RelFile} | {error, Reason}</name> + <name since="">get_rel(Server, Relname) -> {ok, RelFile} | {error, Reason}</name> <fsummary>Get contents of a release file</fsummary> <type> <v>Server = server()</v> @@ -698,7 +698,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_script(Server, Relname) -> {ok, ScriptFile | {error, Reason}</name> + <name since="">get_script(Server, Relname) -> {ok, ScriptFile | {error, Reason}</name> <fsummary>Get contents of a boot script file</fsummary> <type> <v>Server = server()</v> @@ -711,7 +711,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_status(Server) -> {ok, [Warning]} | {error, Reason}</name> + <name since="OTP R14B">get_status(Server) -> {ok, [Warning]} | {error, Reason}</name> <fsummary>Get contents of a release file</fsummary> <type> <v>Server = server()</v> @@ -722,7 +722,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_server(WindowPid) -> {ok, ServerPid} | {error, Reason}</name> + <name since="">get_server(WindowPid) -> {ok, ServerPid} | {error, Reason}</name> <fsummary>Start server process with options</fsummary> <type> <v>WindowPid = window_pid()</v> @@ -733,7 +733,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_target_spec(Server) -> {ok, TargetSpec} | {error, Reason}</name> + <name since="">get_target_spec(Server) -> {ok, TargetSpec} | {error, Reason}</name> <fsummary>Return a specification of the target system</fsummary> <type> <v>Server = server()</v> @@ -747,7 +747,7 @@ target_spec() = [target_spec()] </func> <func> - <name>install(RelName, TargetDir) -> ok | {error, Reason}</name> + <name since="">install(RelName, TargetDir) -> ok | {error, Reason}</name> <fsummary>Install a target system</fsummary> <type> <v>RelName = rel_name()</v> @@ -758,7 +758,7 @@ target_spec() = [target_spec()] </func> <func> - <name>start() -> {ok, WindowPid} | {error, Reason}</name> + <name since="">start() -> {ok, WindowPid} | {error, Reason}</name> <fsummary>Start main window process</fsummary> <type> <v>WindowPid = window_pid()</v> @@ -768,7 +768,7 @@ target_spec() = [target_spec()] </func> <func> - <name>start(Options) -> {ok, WindowPid} | {error, Reason}</name> + <name since="">start(Options) -> {ok, WindowPid} | {error, Reason}</name> <fsummary>Start main window process with options</fsummary> <type> <v>Options = options()</v> @@ -779,7 +779,7 @@ target_spec() = [target_spec()] </func> <func> - <name>start_link(Options) -> {ok, WindowPid} | {error, Reason}</name> + <name since="">start_link(Options) -> {ok, WindowPid} | {error, Reason}</name> <fsummary>Start main window process with options</fsummary> <type> <v>Options = options()</v> @@ -790,7 +790,7 @@ target_spec() = [target_spec()] </func> <func> - <name>start_server(Options) -> {ok, ServerPid} | {error, Reason}</name> + <name since="">start_server(Options) -> {ok, ServerPid} | {error, Reason}</name> <fsummary>Start server process with options</fsummary> <type> <v>Options = options()</v> @@ -803,7 +803,7 @@ target_spec() = [target_spec()] </func> <func> - <name>stop(Pid) -> ok | {error, Reason}</name> + <name since="">stop(Pid) -> ok | {error, Reason}</name> <fsummary>Stop a server or window process</fsummary> <type> <v>Pid = server_pid() | window_pid()</v> diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index 3262cafefc..941a880778 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>dbg.sgml</file> </header> - <module>dbg</module> + <module since="">dbg</module> <modulesummary>The Text Based Trace Facility</modulesummary> <description> <p>This module implements a text based interface to the @@ -68,7 +68,7 @@ </description> <funcs> <func> - <name>fun2ms(LiteralFun) -> MatchSpec</name> + <name since="">fun2ms(LiteralFun) -> MatchSpec</name> <fsummary>Pseudo function that transforms fun syntax to match_spec.</fsummary> <type> <v>LiteralFun = fun() literal</v> @@ -145,14 +145,14 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>h() -> ok </name> + <name since="">h() -> ok </name> <fsummary>Give a list of available help items on standard output.</fsummary> <desc> <p>Gives a list of items for brief online help.</p> </desc> </func> <func> - <name>h(Item) -> ok </name> + <name since="">h(Item) -> ok </name> <fsummary>Give brief help for an item.</fsummary> <type> <v>Item = atom()</v> @@ -163,14 +163,14 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>p(Item) -> {ok, MatchDesc} | {error, term()} </name> + <name since="">p(Item) -> {ok, MatchDesc} | {error, term()} </name> <fsummary>Trace messages to and from Item.</fsummary> <desc> <p>Equivalent to <c>p(Item, [m])</c>.</p> </desc> </func> <func> - <name>p(Item, Flags) -> {ok, MatchDesc} | {error, term()}</name> + <name since="">p(Item, Flags) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Trace Item according to Flags.</fsummary> <type> <v>MatchDesc = [MatchNum]</v> @@ -303,14 +303,14 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>c(Mod, Fun, Args)</name> + <name since="">c(Mod, Fun, Args)</name> <fsummary>Evaluate <c>apply(M,F,Args)</c>with <c>all</c>trace flags set.</fsummary> <desc> <p>Equivalent to <c>c(Mod, Fun, Args, all)</c>.</p> </desc> </func> <func> - <name>c(Mod, Fun, Args, Flags)</name> + <name since="">c(Mod, Fun, Args, Flags)</name> <fsummary>Evaluate <c>apply(M,F,Args)</c>with <c>Flags</c>trace flags set.</fsummary> <desc> <p>Evaluates the expression <c>apply(Mod, Fun, Args)</c> with the trace @@ -319,35 +319,35 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>i() -> ok</name> + <name since="">i() -> ok</name> <fsummary>Display information about all traced processes and ports.</fsummary> <desc> <p>Displays information about all traced processes and ports.</p> </desc> </func> <func> - <name>tp(Module,MatchSpec)</name> + <name since="">tp(Module,MatchSpec)</name> <fsummary>Set pattern for traced global function calls</fsummary> <desc> <p>Same as tp({Module, '_', '_'}, MatchSpec)</p> </desc> </func> <func> - <name>tp(Module,Function,MatchSpec)</name> + <name since="">tp(Module,Function,MatchSpec)</name> <fsummary>Set pattern for traced global function calls</fsummary> <desc> <p>Same as tp({Module, Function, '_'}, MatchSpec)</p> </desc> </func> <func> - <name>tp(Module, Function, Arity, MatchSpec)</name> + <name since="">tp(Module, Function, Arity, MatchSpec)</name> <fsummary>Set pattern for traced global function calls</fsummary> <desc> <p>Same as tp({Module, Function, Arity}, MatchSpec)</p> </desc> </func> <func> - <name>tp({Module, Function, Arity}, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name> + <name since="">tp({Module, Function, Arity}, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Set pattern for traced global function calls</fsummary> <type> <v>Module = atom() | '_'</v> @@ -410,28 +410,28 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>tpl(Module,MatchSpec)</name> + <name since="">tpl(Module,MatchSpec)</name> <fsummary>Set pattern for traced local (as well as global) function calls</fsummary> <desc> <p>Same as tpl({Module, '_', '_'}, MatchSpec)</p> </desc> </func> <func> - <name>tpl(Module,Function,MatchSpec)</name> + <name since="">tpl(Module,Function,MatchSpec)</name> <fsummary>Set pattern for traced local (as well as global) function calls</fsummary> <desc> <p>Same as tpl({Module, Function, '_'}, MatchSpec)</p> </desc> </func> <func> - <name>tpl(Module, Function, Arity, MatchSpec)</name> + <name since="">tpl(Module, Function, Arity, MatchSpec)</name> <fsummary>Set pattern for traced local (as well as global) function calls</fsummary> <desc> <p>Same as tpl({Module, Function, Arity}, MatchSpec)</p> </desc> </func> <func> - <name>tpl({Module, Function, Arity}, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name> + <name since="">tpl({Module, Function, Arity}, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Set pattern for traced local (as well as global) function calls</fsummary> <desc> <p>This function works as <seealso marker="#tp-2"><c>tp/2</c></seealso>, but enables @@ -442,7 +442,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ <func> - <name>tpe(Event, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name> + <name since="OTP 19.0">tpe(Event, MatchSpec) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Set pattern for traced event</fsummary> <type> <v>Event = send | 'receive'</v> @@ -484,35 +484,35 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>ctp()</name> + <name since="">ctp()</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctp({'_', '_', '_'})</p> </desc> </func> <func> - <name>ctp(Module)</name> + <name since="">ctp(Module)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctp({Module, '_', '_'})</p> </desc> </func> <func> - <name>ctp(Module, Function)</name> + <name since="">ctp(Module, Function)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctp({Module, Function, '_'})</p> </desc> </func> <func> - <name>ctp(Module, Function, Arity)</name> + <name since="">ctp(Module, Function, Arity)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctp({Module, Function, Arity})</p> </desc> </func> <func> - <name>ctp({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name> + <name since="">ctp({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <type> <v>Module = atom() | '_'</v> @@ -533,35 +533,35 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>ctpl()</name> + <name since="">ctpl()</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpl({'_', '_', '_'})</p> </desc> </func> <func> - <name>ctpl(Module)</name> + <name since="">ctpl(Module)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpl({Module, '_', '_'})</p> </desc> </func> <func> - <name>ctpl(Module, Function)</name> + <name since="">ctpl(Module, Function)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpl({Module, Function, '_'})</p> </desc> </func> <func> - <name>ctpl(Module, Function, Arity)</name> + <name since="">ctpl(Module, Function, Arity)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpl({Module, Function, Arity})</p> </desc> </func> <func> - <name>ctpl({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name> + <name since="">ctpl({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>This function works as <seealso marker="#ctp-1"><c>ctp/1</c></seealso>, but only disables @@ -570,35 +570,35 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>ctpg()</name> + <name since="">ctpg()</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpg({'_', '_', '_'})</p> </desc> </func> <func> - <name>ctpg(Module)</name> + <name since="">ctpg(Module)</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpg({Module, '_', '_'})</p> </desc> </func> <func> - <name>ctpg(Module, Function)</name> + <name since="">ctpg(Module, Function)</name> <fsummary>>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpg({Module, Function, '_'})</p> </desc> </func> <func> - <name>ctpg(Module, Function, Arity)</name> + <name since="">ctpg(Module, Function, Arity)</name> <fsummary>>Clear call trace pattern for the specified functions</fsummary> <desc> <p>Same as ctpg({Module, Function, Arity})</p> </desc> </func> <func> - <name>ctpg({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name> + <name since="">ctpg({Module, Function, Arity}) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Clear call trace pattern for the specified functions</fsummary> <desc> <p>This function works as <seealso marker="#ctp-1"><c>ctp/1</c></seealso>, but only disables @@ -607,7 +607,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>ctpe(Event) -> {ok, MatchDesc} | {error, term()}</name> + <name since="OTP 19.0">ctpe(Event) -> {ok, MatchDesc} | {error, term()}</name> <fsummary>Clear trace pattern for the specified event</fsummary> <type> <v>Event = send | 'receive'</v> @@ -623,7 +623,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>ltp() -> ok</name> + <name since="">ltp() -> ok</name> <fsummary>List saved and built-in match specifications on the console.</fsummary> <desc> <p>Use this function to recall all match specifications previously @@ -654,7 +654,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>dtp() -> ok</name> + <name since="">dtp() -> ok</name> <fsummary>Delete all saved match specifications.</fsummary> <desc> <p>Use this function to "forget" all match specifications @@ -665,7 +665,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>dtp(N) -> ok</name> + <name since="">dtp(N) -> ok</name> <fsummary>Delete a specific saved match_spec.</fsummary> <type> <v>N = integer()</v> @@ -676,7 +676,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>wtp(Name) -> ok | {error, IOError}</name> + <name since="">wtp(Name) -> ok | {error, IOError}</name> <fsummary>Write all saved and built-in match specifications to a file</fsummary> <type> <v>Name = string()</v> @@ -699,7 +699,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>rtp(Name) -> ok | {error, Error}</name> + <name since="">rtp(Name) -> ok | {error, Error}</name> <fsummary>Read saved match specifications from file.</fsummary> <type> <v>Name = string()</v> @@ -728,7 +728,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>n(Nodename) -> {ok, Nodename} | {error, Reason}</name> + <name since="">n(Nodename) -> {ok, Nodename} | {error, Reason}</name> <fsummary>Add a remote node to the list of traced nodes</fsummary> <type> <v>Nodename = atom()</v> @@ -767,7 +767,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>cn(Nodename) -> ok</name> + <name since="">cn(Nodename) -> ok</name> <fsummary>Clear a node from the list of traced nodes.</fsummary> <type> <v>Nodename = atom()</v> @@ -782,14 +782,14 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>ln() -> ok</name> + <name since="">ln() -> ok</name> <fsummary>Show the list of traced nodes on the console.</fsummary> <desc> <p>Shows the list of traced nodes on the console.</p> </desc> </func> <func> - <name>tracer() -> {ok, pid()} | {error, already_started}</name> + <name since="">tracer() -> {ok, pid()} | {error, already_started}</name> <fsummary>Start a tracer server that handles trace messages.</fsummary> <desc> <p>This function starts a server on the local node that will @@ -805,7 +805,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>tracer(Type, Data) -> {ok, pid()} | {error, Error}</name> + <name since="">tracer(Type, Data) -> {ok, pid()} | {error, Error}</name> <fsummary>Start a tracer server with additional parameters</fsummary> <type> <v>Type = port | process | module</v> @@ -859,7 +859,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>tracer(Nodename, Type, Data) -> {ok, Nodename} | {error, Reason}</name> + <name since="">tracer(Nodename, Type, Data) -> {ok, Nodename} | {error, Reason}</name> <fsummary>Start a tracer server on given node with additional parameters</fsummary> <type> <v>Nodename = atom()</v> @@ -881,7 +881,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>trace_port(Type, Parameters) -> fun()</name> + <name since="">trace_port(Type, Parameters) -> fun()</name> <fsummary>Create and returns a trace port generating<em>fun</em></fsummary> <type> <v>Type = ip | file</v> @@ -958,28 +958,28 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>flush_trace_port()</name> + <name since="">flush_trace_port()</name> <fsummary>Equivalent to flush_trace_port(node()).</fsummary> <desc> <p>Equivalent to <c>flush_trace_port(node())</c>.</p> </desc> </func> <func> - <name>flush_trace_port(Nodename) -> ok | {error, Reason}</name> + <name since="">flush_trace_port(Nodename) -> ok | {error, Reason}</name> <fsummary>Flush internal data buffers in a trace driver on the given node.</fsummary> <desc> <p>Equivalent to <c>trace_port_control(Nodename,flush)</c>.</p> </desc> </func> <func> - <name>trace_port_control(Operation)</name> + <name since="">trace_port_control(Operation)</name> <fsummary>Equivalent to trace_port_control(node(),Operation).</fsummary> <desc> <p>Equivalent to <c>trace_port_control(node(),Operation)</c>.</p> </desc> </func> <func> - <name>trace_port_control(Nodename,Operation) -> ok | {ok, Result} | {error, Reason}</name> + <name since="">trace_port_control(Nodename,Operation) -> ok | {ok, Result} | {error, Reason}</name> <fsummary>Perform a control operation on the active trace port driver on the given node.</fsummary> <type> <v>Nodename = atom()</v> @@ -1013,7 +1013,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </desc> </func> <func> - <name>trace_client(Type, Parameters) -> pid()</name> + <name since="">trace_client(Type, Parameters) -> pid()</name> <fsummary>Start a trace client that reads messages created by a trace port driver</fsummary> <type> <v>Type = ip | file | follow_file</v> @@ -1080,7 +1080,7 @@ hello</pre> </desc> </func> <func> - <name>trace_client(Type, Parameters, HandlerSpec) -> pid()</name> + <name since="">trace_client(Type, Parameters, HandlerSpec) -> pid()</name> <fsummary>Start a trace client that reads messages created by a trace port driver, with a user defined handler</fsummary> <type> <v>Type = ip | file | follow_file</v> @@ -1110,7 +1110,7 @@ hello</pre> </desc> </func> <func> - <name>stop_trace_client(Pid) -> ok</name> + <name since="">stop_trace_client(Pid) -> ok</name> <fsummary>Stop a trace client gracefully.</fsummary> <type> <v>Pid = pid()</v> @@ -1123,14 +1123,14 @@ hello</pre> </desc> </func> <func> - <name>get_tracer()</name> + <name since="">get_tracer()</name> <fsummary>Equivalent to get_tracer(node()).</fsummary> <desc> <p>Equivalent to <c>get_tracer(node())</c>.</p> </desc> </func> <func> - <name>get_tracer(Nodename) -> {ok, Tracer}</name> + <name since="">get_tracer(Nodename) -> {ok, Tracer}</name> <fsummary>Return the process or port to which all trace messages are sent.</fsummary> <type> <v>Nodename = atom()</v> @@ -1142,7 +1142,7 @@ hello</pre> </desc> </func> <func> - <name>stop() -> ok</name> + <name since="">stop() -> ok</name> <fsummary>Stop the <c>dbg</c>server and the tracing of all processes.</fsummary> <desc> <p>Stops the <c>dbg</c> server and clears all trace flags for @@ -1153,7 +1153,7 @@ hello</pre> </desc> </func> <func> - <name>stop_clear() -> ok</name> + <name since="">stop_clear() -> ok</name> <fsummary>Stop the <c>dbg</c>server and the tracing of all processes, and clears trace patterns.</fsummary> <desc> <p>Same as stop/0, but also clears all trace patterns on global functions calls.</p> diff --git a/lib/runtime_tools/doc/src/dyntrace.xml b/lib/runtime_tools/doc/src/dyntrace.xml index 0cdcecab68..4935dfcd71 100644 --- a/lib/runtime_tools/doc/src/dyntrace.xml +++ b/lib/runtime_tools/doc/src/dyntrace.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>dyntrace.xml</file> </header> - <module>dyntrace</module> + <module since="OTP R15B01">dyntrace</module> <modulesummary>Interface to dynamic tracing</modulesummary> <description> <p>This module implements interfaces to dynamic tracing, should such be compiled into the virtual machine. For a standard and/or commercial build, no dynamic tracing is available, in which case none of the functions in this module is usable or give any effect.</p> @@ -47,7 +47,7 @@ </description> <funcs> <func> - <name>available() -> boolean()</name> + <name since="OTP R15B01">available() -> boolean()</name> <fsummary>Check if dynamic tracing is available</fsummary> <desc> <p>This function uses the NIF library to determine if dynamic @@ -59,42 +59,42 @@ </desc> </func> <func> - <name>p() -> true | false | error | badarg</name> + <name since="OTP R15B01">p() -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message only containing the user tag and zeroes/empty strings in all other fields.</p> </desc> </func> <func> - <name>p(integer() | string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer() | string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer or string parameter in the first integer/string field.</p> </desc> </func> <func> - <name>p(integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer() | string(), integer() | string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters. I.e. <c>p(1,"Hello")</c> is ok, as is <c>p(1,1)</c> and <c>p("Hello","Again")</c>, but not <c>p("Hello",1)</c>.</p> </desc> </func> <func> - <name>p(integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> </desc> </func> <func> - <name>p(integer() | string(), integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer() | string(), integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> </desc> </func> <func> - <name>p(integer(), integer() | string(), integer() | string(), integer() | string(), string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer(), integer() | string(), integer() | string(), integer() | string(), string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> @@ -102,7 +102,7 @@ </desc> </func> <func> - <name>p(integer(), integer(), integer() | string(), integer() | string(), string(), string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer(), integer(), integer() | string(), integer() | string(), string(), string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> @@ -110,7 +110,7 @@ </desc> </func> <func> - <name>p(integer(), integer(), integer(), integer() | string(), string(), string(), string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer(), integer(), integer(), integer() | string(), string(), string(), string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> @@ -118,14 +118,14 @@ </desc> </func> <func> - <name>p(integer(), integer(), integer(), integer(), string(), string(), string(), string()) -> true | false | error | badarg</name> + <name since="OTP R15B01">p(integer(), integer(), integer(), integer(), string(), string(), string(), string()) -> true | false | error | badarg</name> <fsummary>Trigger the user trace probe.</fsummary> <desc> <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing all the integer()'s and string()'s provided, as well as any user tag set in the current process.</p> </desc> </func> <func> - <name>get_tag() -> binary() | undefined</name> + <name since="OTP R15B01">get_tag() -> binary() | undefined</name> <fsummary>Get the user tag set in the process.</fsummary> <desc> <p>This function returns the user tag set in the current @@ -134,7 +134,7 @@ </desc> </func> <func> - <name>get_tag() -> binary() | undefined</name> + <name since="OTP R15B01">get_tag() -> binary() | undefined</name> <fsummary>Get the user tag set in the process or sent to the process.</fsummary> <desc> <p>This function returns the user tag set in the current @@ -151,7 +151,7 @@ </func> <func> - <name>put_tag(Item) -> binary() | undefined </name> + <name since="OTP R15B01">put_tag(Item) -> binary() | undefined </name> <fsummary>Set the user tag of the current process.</fsummary> <type> <v>Item = iodata()</v> @@ -163,7 +163,7 @@ </desc> </func> <func> - <name>spread_tag(boolean()) -> TagData</name> + <name since="OTP R15B01">spread_tag(boolean()) -> TagData</name> <fsummary>Start or stop spreading dynamic trace user tags with the next message.</fsummary> <type> <v>TagData = opaque data that can be used as parameter to <seealso marker="#restore_tag/1">restore_tag/1</seealso></v> @@ -185,7 +185,7 @@ f() -> </desc> </func> <func> - <name>restore_tag(TagData) -> true</name> + <name since="OTP R15B01">restore_tag(TagData) -> true</name> <fsummary>Restore to a previous state of user tag spreading.</fsummary> <type> <v>TagData = opaque data returned by <seealso marker="#spread_tag/1">spread_tag/1</seealso></v> diff --git a/lib/runtime_tools/doc/src/erts_alloc_config.xml b/lib/runtime_tools/doc/src/erts_alloc_config.xml index ffc4ec5285..5bcce1b5e3 100644 --- a/lib/runtime_tools/doc/src/erts_alloc_config.xml +++ b/lib/runtime_tools/doc/src/erts_alloc_config.xml @@ -29,7 +29,7 @@ <rev>1</rev> <file>erts_alloc_config.sgml</file> </header> - <module>erts_alloc_config</module> + <module since="">erts_alloc_config</module> <modulesummary>Configuration tool for erts_alloc</modulesummary> <description> <note> @@ -136,7 +136,7 @@ </description> <funcs> <func> - <name>save_scenario() -> ok | {error, Error}</name> + <name since="">save_scenario() -> ok | {error, Error}</name> <fsummary>Saves information about current runtime scenario</fsummary> <type> <v>Error = term()</v> @@ -154,7 +154,7 @@ </desc> </func> <func> - <name>make_config() -> ok | {error, Error}</name> + <name since="">make_config() -> ok | {error, Error}</name> <fsummary>Creates an erts_alloc configuration</fsummary> <type> <v>Error = term()</v> @@ -165,7 +165,7 @@ </desc> </func> <func> - <name>make_config(FileNameOrIODev) -> ok | {error, Error}</name> + <name since="">make_config(FileNameOrIODev) -> ok | {error, Error}</name> <fsummary>Creates an erts_alloc configuration</fsummary> <type> <v>FileNameOrIODev = string() | io_device()</v> @@ -190,7 +190,7 @@ </desc> </func> <func> - <name>stop() -> ok | {error, Error}</name> + <name since="">stop() -> ok | {error, Error}</name> <fsummary></fsummary> <type> <v>Error = term()</v> diff --git a/lib/runtime_tools/doc/src/msacc.xml b/lib/runtime_tools/doc/src/msacc.xml index 129da3d230..ae089de8d0 100644 --- a/lib/runtime_tools/doc/src/msacc.xml +++ b/lib/runtime_tools/doc/src/msacc.xml @@ -31,7 +31,7 @@ <rev>A</rev> <file>msacc.xml</file> </header> - <module>msacc</module> + <module since="OTP 19.0">msacc</module> <modulesummary>Convenience functions for microstate accounting</modulesummary> <description> <p>This module implements some convenience functions for analyzing @@ -146,7 +146,7 @@ ok </datatypes> <funcs> <func> - <name name="available" arity="0"/> + <name name="available" arity="0" since="OTP 19.0"/> <fsummary>Check if microstate accounting is available</fsummary> <desc> <p>This function checks whether microstate accounting @@ -154,7 +154,7 @@ ok </desc> </func> <func> - <name name="start" arity="0"/> + <name name="start" arity="0" since="OTP 19.0"/> <fsummary>Start microstate accounting.</fsummary> <desc> <p>Start microstate accounting. Returns whether it was @@ -162,7 +162,7 @@ ok </desc> </func> <func> - <name name="start" arity="1"/> + <name name="start" arity="1" since="OTP 19.0"/> <fsummary>Start microstate accounting for a time.</fsummary> <desc> <p>Resets all counters and then starts microstate accounting @@ -170,7 +170,7 @@ ok </desc> </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since="OTP 19.0"/> <fsummary>Stop microstate accounting.</fsummary> <desc> <p>Stop microstate accounting. @@ -178,7 +178,7 @@ ok </desc> </func> <func> - <name name="reset" arity="0"/> + <name name="reset" arity="0" since="OTP 19.0"/> <fsummary>Reset microstate accounting counters</fsummary> <desc> <p>Reset microstate accounting counters. @@ -186,7 +186,7 @@ ok </desc> </func> <func> - <name name="print" arity="0"/> + <name name="print" arity="0" since="OTP 19.0"/> <fsummary>Print microstate statistics</fsummary> <desc> <p> @@ -199,7 +199,7 @@ ok </desc> </func> <func> - <name name="print" arity="1"/> + <name name="print" arity="1" since="OTP 19.0"/> <fsummary>Print microstate statistics</fsummary> <desc> <p>Print the given microstate statistics values to stdout. @@ -211,7 +211,7 @@ ok </desc> </func> <func> - <name name="print" arity="2"/> + <name name="print" arity="2" since="OTP 19.0"/> <fsummary>Print microstate statistics</fsummary> <desc> <p>Print the given microstate statistics values to standard out. @@ -234,7 +234,7 @@ ok </desc> </func> <func> - <name name="print" arity="3"/> + <name name="print" arity="3" since="OTP 19.0"/> <fsummary>Print microstate statistics</fsummary> <desc> <p>Print the given microstate statistics values to the given file @@ -243,7 +243,7 @@ ok </desc> </func> <func> - <name name="stats" arity="0"/> + <name name="stats" arity="0" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Returns a runtime system independent version of the microstate @@ -254,7 +254,7 @@ ok </desc> </func> <func> - <name name="stats" arity="2" clause_i="1"/> + <name name="stats" arity="2" clause_i="1" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Returns the system time for the given microstate statistics values. @@ -269,7 +269,7 @@ ok </desc> </func> <func> - <name name="stats" arity="2" clause_i="2"/> + <name name="stats" arity="2" clause_i="2" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Returns fractions of real-time or run-time spent in the various @@ -277,7 +277,7 @@ ok </desc> </func> <func> - <name name="stats" arity="2" clause_i="3"/> + <name name="stats" arity="2" clause_i="3" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Returns a list of microstate statistics values where the values @@ -285,7 +285,7 @@ ok </desc> </func> <func> - <name name="to_file" arity="1"/> + <name name="to_file" arity="1" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Dumps the current microstate statistics counters to a file that can @@ -294,7 +294,7 @@ ok </desc> </func> <func> - <name name="from_file" arity="1"/> + <name name="from_file" arity="1" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Read a file dump produced by <seealso marker="#to_file/1"> diff --git a/lib/runtime_tools/doc/src/scheduler.xml b/lib/runtime_tools/doc/src/scheduler.xml index dd8bf73bae..b033430183 100644 --- a/lib/runtime_tools/doc/src/scheduler.xml +++ b/lib/runtime_tools/doc/src/scheduler.xml @@ -32,7 +32,7 @@ <rev></rev> <file>scheduler.xml</file> </header> - <module>scheduler</module> + <module since="OTP 21.0">scheduler</module> <modulesummary>Measure scheduler utilization</modulesummary> <description> <p>This module contains utility functions for easier measurement and @@ -84,7 +84,7 @@ <funcs> <func> - <name name="sample" arity="0"/> + <name name="sample" arity="0" since="OTP 21.0"/> <fsummary>Get scheduler utilization sample.</fsummary> <desc> <p>Return a scheduler utilization sample for normal and dirty-cpu @@ -93,7 +93,7 @@ </func> <func> - <name name="sample_all" arity="0"/> + <name name="sample_all" arity="0" since="OTP 21.0"/> <fsummary>Get scheduler utilization sample.</fsummary> <desc> <p>Return a scheduler utilization sample for all schedulers, @@ -102,7 +102,7 @@ </func> <func> - <name name="utilization" arity="1" clause_i="1"/> + <name name="utilization" arity="1" clause_i="1" since="OTP 21.0"/> <fsummary>Measure scheduler utilizations during a period of time.</fsummary> <desc> <p>Measure utilization for normal and dirty-cpu schedulers during @@ -111,7 +111,7 @@ </func> <func> - <name name="utilization" arity="1" clause_i="2"/> + <name name="utilization" arity="1" clause_i="2" since="OTP 21.0"/> <fsummary>Measure scheduler utilizations since sample.</fsummary> <desc> <p>Calculate scheduler utilizations for the time interval from when @@ -121,7 +121,7 @@ </func> <func> - <name name="utilization" arity="2"/> + <name name="utilization" arity="2" since="OTP 21.0"/> <fsummary>Measure scheduler utilizations between two samples.</fsummary> <desc> <p>Calculates scheduler utilizations for the time interval between diff --git a/lib/runtime_tools/doc/src/system_information.xml b/lib/runtime_tools/doc/src/system_information.xml index 53dc595e64..a356b5c6f8 100644 --- a/lib/runtime_tools/doc/src/system_information.xml +++ b/lib/runtime_tools/doc/src/system_information.xml @@ -32,14 +32,14 @@ <rev></rev> <file>system_information.xml</file> </header> - <module>system_information</module> + <module since="OTP 17.0">system_information</module> <modulesummary>System Information</modulesummary> <description> <p></p> </description> <funcs> <func> - <name name="sanity_check" arity="0"/> + <name name="sanity_check" arity="0" since="OTP 17.0"/> <fsummary>Perform a sanity check</fsummary> <desc> <p>Performs a sanity check on the system. If no issues @@ -88,7 +88,7 @@ </desc> </func> <func> - <name name="to_file" arity="1"/> + <name name="to_file" arity="1" since="OTP 17.0"/> <fsummary>Write miscellaneous system information to file</fsummary> <desc><p>Writes miscellaneous system information to file. This information will typically be requested by the Erlang/OTP team diff --git a/lib/runtime_tools/examples/dist.systemtap b/lib/runtime_tools/examples/dist.systemtap index bb20d617e1..4102a5243c 100644 --- a/lib/runtime_tools/examples/dist.systemtap +++ b/lib/runtime_tools/examples/dist.systemtap @@ -19,18 +19,18 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("dist-monitor") +probe process("beam.smp").mark("dist-monitor") { printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", pid(), @@ -38,38 +38,38 @@ probe process("beam").mark("dist-monitor") user_string($arg5)); } -probe process("beam").mark("dist-port_busy") +probe process("beam.smp").mark("dist-port_busy") { printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); - blocked_procs[user_string($arg4)] = timestamp; + blocked_procs[user_string($arg4)] = local_clock_ns(); } -probe process("beam").mark("dist-port_busy") +probe process("beam.smp").mark("dist-port_busy") { printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); - blocked_procs[user_string($arg4)] = timestamp; + blocked_procs[user_string($arg4)] = local_clock_ns(); } -probe process("beam").mark("dist-output") +probe process("beam.smp").mark("dist-output") { printf("dist output: node %s, port %s, remote_node %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("dist-outputv") +probe process("beam.smp").mark("dist-outputv") { printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("process-scheduled") +probe process("beam.smp").mark("process-scheduled") { pidstr = user_string($arg1); if (pidstr in blocked_procs) { printf("blocked pid %s scheduled now, waited %d microseconds\n", - pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + pidstr, (local_clock_ns() - blocked_procs[pidstr]) / 1000); delete blocked_procs[pidstr]; } } diff --git a/lib/runtime_tools/examples/driver1.systemtap b/lib/runtime_tools/examples/driver1.systemtap index e1ee8ecffc..f5bc28b42d 100644 --- a/lib/runtime_tools/examples/driver1.systemtap +++ b/lib/runtime_tools/examples/driver1.systemtap @@ -19,108 +19,102 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("driver-init") +probe process("beam.smp").mark("driver__init") { printf("driver init name %s major %d minor %d flags %d\n", user_string($arg1), $arg2, $arg3, $arg4); } -probe process("beam").mark("driver-start") +probe process("beam.smp").mark("driver__start") { printf("driver start pid %s driver name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-stop") +probe process("beam.smp").mark("driver__stop") { printf("driver stop pid %s driver name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-finish") +probe process("beam.smp").mark("driver__finish") { printf("driver finish driver name %s\n", user_string($arg1)); } -probe process("beam").mark("driver-flush") +probe process("beam.smp").mark("driver__flush") { printf("driver flush pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-output") +probe process("beam.smp").mark("driver__output") { printf("driver output pid %s port %s port name %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("driver-outputv") +probe process("beam.smp").mark("driver__outputv") { printf("driver outputv pid %s port %s port name %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("driver-control") +probe process("beam.smp").mark("driver__control") { printf("driver control pid %s port %s port name %s command %d bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); } -probe process("beam").mark("driver-call") +probe process("beam.smp").mark("driver__call") { printf("driver call pid %s port %s port name %s command %d bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); } -probe process("beam").mark("driver-event") -{ - printf("driver event pid %s port %s port name %s\n", - user_string($arg1), user_string($arg2), user_string($arg3)); -} - -probe process("beam").mark("driver-ready_input") +probe process("beam.smp").mark("driver__ready_input") { printf("driver ready_input pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-ready_output") +probe process("beam.smp").mark("driver__ready_output") { printf("driver ready_output pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-timeout") +probe process("beam.smp").mark("driver__timeout") { printf("driver timeout pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-ready_async") +probe process("beam.smp").mark("driver__ready_async") { printf("driver ready_async pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-process_exit") +probe process("beam.smp").mark("driver__process_exit") { printf("driver process_exit pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-stop_select") +probe process("beam.smp").mark("driver__stop_select") { printf("driver stop_select driver name %s\n", user_string($arg1)); } diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap index 9c44b2d014..6bb173b3ec 100644 --- a/lib/runtime_tools/examples/function-calls.systemtap +++ b/lib/runtime_tools/examples/function-calls.systemtap @@ -18,51 +18,51 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("local-function-entry") +probe process("beam.smp").mark("local-function-entry") { printf("pid %s enter (local) %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("global-function-entry") +probe process("beam.smp").mark("global-function-entry") { printf("pid %s enter (global) %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("function-return") +probe process("beam.smp").mark("function-return") { printf("pid %s return %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("bif-entry") +probe process("beam.smp").mark("bif-entry") { printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("bif-return") +probe process("beam.smp").mark("bif-return") { printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("nif-entry") +probe process("beam.smp").mark("nif-entry") { printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("nif-return") +probe process("beam.smp").mark("nif-return") { printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2)); } diff --git a/lib/runtime_tools/examples/garbage-collection.systemtap b/lib/runtime_tools/examples/garbage-collection.systemtap index e414eea821..14f0d6851c 100644 --- a/lib/runtime_tools/examples/garbage-collection.systemtap +++ b/lib/runtime_tools/examples/garbage-collection.systemtap @@ -18,33 +18,33 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("gc_major-start") +probe process("beam.smp").mark("gc_major-start") { printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_minor-start") +probe process("beam.smp").mark("gc_minor-start") { printf("GC minor start pid %s need %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_major-end") +probe process("beam.smp").mark("gc_major-end") { printf("GC major end pid %s reclaimed %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_minor-start") +probe process("beam.smp").mark("gc_minor-start") { printf("GC minor end pid %s reclaimed %d words\n", user_string($arg1), $arg2); } diff --git a/lib/runtime_tools/examples/memory1.systemtap b/lib/runtime_tools/examples/memory1.systemtap index 04df4d64c4..2fdc5a796c 100644 --- a/lib/runtime_tools/examples/memory1.systemtap +++ b/lib/runtime_tools/examples/memory1.systemtap @@ -18,34 +18,34 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("copy-struct") +probe process("beam.smp").mark("copy-struct") { printf("copy_struct %d bytes\n", $arg1); } -probe process("beam").mark("copy-object") +probe process("beam.smp").mark("copy-object") { printf("copy_object pid %s %d bytes\n", user_string($arg1), $arg2); } -probe process("beam").mark("process-heap_grow") +probe process("beam.smp").mark("process-heap_grow") { printf("proc heap grow pid %s %d -> %d bytes\n", user_string($arg1), $arg2, $arg3); } -probe process("beam").mark("process-heap_shrink") +probe process("beam.smp").mark("process-heap_shrink") { printf("proc heap shrink pid %s %d -> %d bytes\n", user_string($arg1), $arg2, $arg3); diff --git a/lib/runtime_tools/examples/messages.systemtap b/lib/runtime_tools/examples/messages.systemtap index f2ef56a22b..49b7f46d69 100644 --- a/lib/runtime_tools/examples/messages.systemtap +++ b/lib/runtime_tools/examples/messages.systemtap @@ -18,15 +18,15 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ probe begin @@ -38,7 +38,7 @@ probe begin printf("\n"); } -probe process("beam").mark("message-send") +probe process("beam.smp").mark("message-send") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("send: %s -> %s: %d words\n", @@ -51,7 +51,7 @@ probe process("beam").mark("message-send") } } -probe process("beam").mark("message-send-remote") +probe process("beam.smp").mark("message-send-remote") { if ($arg5 == 0 && $arg6 == 0 && $arg7 == 0) { printf("send : %s -> %s %s: %d words\n", @@ -64,7 +64,7 @@ probe process("beam").mark("message-send-remote") } } -probe process("beam").mark("message-queued") +probe process("beam.smp").mark("message-queued") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("queued: %s: %d words, queue len %d\n", user_string($arg1), $arg2, $arg3); @@ -75,7 +75,7 @@ probe process("beam").mark("message-queued") } } -probe process("beam").mark("message-receive") +probe process("beam.smp").mark("message-receive") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("receive: %s: %d words, queue len %d\n", diff --git a/lib/runtime_tools/examples/port1.systemtap b/lib/runtime_tools/examples/port1.systemtap index f7ce03a65e..235581b0b1 100644 --- a/lib/runtime_tools/examples/port1.systemtap +++ b/lib/runtime_tools/examples/port1.systemtap @@ -18,15 +18,15 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ probe begin @@ -96,19 +96,19 @@ probe begin driver_map["udp_inet", 62] = "BINDX"; } -probe process("beam").mark("port-open") +probe process("beam.smp").mark("port-open") { printf("port open pid %s port name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("port-command") +probe process("beam.smp").mark("port-command") { printf("port command pid %s port %s port name %s command type %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-control") +probe process("beam.smp").mark("port-control") { cmd = driver_map[user_string($arg3), $arg4]; cmd_str = (cmd == "") ? "unknown" : cmd; @@ -118,36 +118,36 @@ probe process("beam").mark("port-control") /* port-exit is fired as a result of port_close() or exit signal */ -probe process("beam").mark("port-exit") +probe process("beam.smp").mark("port-exit") { printf("port exit pid %s port %s port name %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-connect") +probe process("beam.smp").mark("port-connect") { printf("port connect pid %s port %s port name %s new pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-busy") +probe process("beam.smp").mark("port-busy") { printf("port busy %s\n", user_string($arg1)); } -probe process("beam").mark("port-not_busy") +probe process("beam.smp").mark("port-not_busy") { printf("port not busy %s\n", user_string($arg1)); } -probe process("beam").mark("aio_pool-add") +probe process("beam.smp").mark("aio_pool-add") { printf("async I/O pool add thread %d queue len %d\n", $arg1, $arg2); } -probe process("beam").mark("aio_pool-get") +probe process("beam.smp").mark("aio_pool-get") { printf("async I/O pool get thread %d queue len %d\n", $arg1, $arg2); } -global driver_map;
\ No newline at end of file +global driver_map; diff --git a/lib/runtime_tools/examples/process-scheduling.systemtap b/lib/runtime_tools/examples/process-scheduling.systemtap index b0b74257b3..231c589f64 100644 --- a/lib/runtime_tools/examples/process-scheduling.systemtap +++ b/lib/runtime_tools/examples/process-scheduling.systemtap @@ -18,28 +18,28 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("process-scheduled") +probe process("beam.smp").mark("process-scheduled") { printf(" Schedule pid %s mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-unscheduled") +probe process("beam.smp").mark("process-unscheduled") { printf("Unschedule pid %s\n", user_string($arg1)); } -probe process("beam").mark("process-hibernate") +probe process("beam.smp").mark("process-hibernate") { printf(" Hibernate pid %s resume mfa %s\n", user_string($arg1), user_string($arg2)); diff --git a/lib/runtime_tools/examples/spawn-exit.systemtap b/lib/runtime_tools/examples/spawn-exit.systemtap index 89bca14496..a7b4a0a3ea 100644 --- a/lib/runtime_tools/examples/spawn-exit.systemtap +++ b/lib/runtime_tools/examples/spawn-exit.systemtap @@ -18,34 +18,34 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("process-spawn") +probe process("beam.smp").mark("process-spawn") { printf("pid %s mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-exit") +probe process("beam.smp").mark("process-exit") { printf("pid %s reason %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-exit_signal") +probe process("beam.smp").mark("process-exit_signal") { printf("sender %s -> pid %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("process-exit_signal-remote") +probe process("beam.smp").mark("process-exit_signal-remote") { printf("sender %s -> node %s pid %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap index 25f7503283..8a0a89c931 100644 --- a/lib/runtime_tools/examples/user-probe-n.systemtap +++ b/lib/runtime_tools/examples/user-probe-n.systemtap @@ -18,18 +18,19 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("user_trace-n0") + +probe process("beam.smp").mark("user_trace-n0") { printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), @@ -41,7 +42,7 @@ probe process("beam").mark("user_trace-n0") $arg9 == NULL ? "" : user_string($arg9)); } -probe process("beam").mark("user_trace-n1") +probe process("beam.smp").mark("user_trace-n1") { printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap index 1777476e54..ce9dde30f8 100644 --- a/lib/runtime_tools/examples/user-probe.systemtap +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -18,23 +18,23 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("user_trace-s1") +probe process("beam.smp").mark("user_trace-s1") { printf("%s\n", user_string($arg1)); } -probe process("beam").mark("user_trace-i4s4") +probe process("beam.smp").mark("user_trace-i4s4") { printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml index 4160757164..6e74f833cd 100644 --- a/lib/sasl/doc/src/alarm_handler.xml +++ b/lib/sasl/doc/src/alarm_handler.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>alarm_handler.sgml.t1</file> </header> - <module>alarm_handler</module> + <module since="">alarm_handler</module> <modulesummary>An Alarm Handling Process</modulesummary> <description> <p>The alarm handler process is a @@ -81,7 +81,7 @@ <funcs> <func> - <name>clear_alarm(AlarmId) -> void()</name> + <name since="">clear_alarm(AlarmId) -> void()</name> <fsummary>Clears the specified alarms.</fsummary> <type> <v>AlarmId = term()</v> @@ -94,7 +94,7 @@ </func> <func> - <name>get_alarms() -> [alarm()]</name> + <name since="">get_alarms() -> [alarm()]</name> <fsummary>Gets all active alarms.</fsummary> <desc> <p>Returns a list of all active alarms. This function can only @@ -103,7 +103,7 @@ </func> <func> - <name>set_alarm(alarm())</name> + <name since="">set_alarm(alarm())</name> <fsummary>Sets an alarm with an id.</fsummary> <type> <v>alarm() = {AlarmId, AlarmDescription}</v> diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml index d5df4fd345..0ed7e91c11 100644 --- a/lib/sasl/doc/src/rb.xml +++ b/lib/sasl/doc/src/rb.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>rb.sgml</file> </header> - <module>rb</module> + <module since="">rb</module> <modulesummary>The Report Browser Tool</modulesummary> <description> <p>The Report Browser (RB) tool is used to browse and @@ -43,8 +43,8 @@ <funcs> <func> - <name>filter(Filters)</name> - <name>filter(Filters, Dates)</name> + <name since="OTP R13B04">filter(Filters)</name> + <name since="OTP R13B04">filter(Filters, Dates)</name> <fsummary>Filters reports and displays them on the screen.</fsummary> <type> <v>Filters = [filter()]</v> @@ -86,7 +86,7 @@ </func> <func> - <name>grep(RegExp)</name> + <name since="">grep(RegExp)</name> <fsummary>Searches the reports for a regular expression.</fsummary> <type> <v>RegExp = string() | {string(), Options} | re:mp() | {re:mp(), Options}</v> @@ -109,8 +109,8 @@ </func> <func> - <name>h()</name> - <name>help()</name> + <name since="">h()</name> + <name since="">help()</name> <fsummary>Displays help information.</fsummary> <desc> <p>Displays online help information.</p> @@ -118,8 +118,8 @@ </func> <func> - <name>list()</name> - <name>list(Type)</name> + <name since="">list()</name> + <name since="">list(Type)</name> <fsummary>Lists all reports.</fsummary> <type> <v>Type = type()</v> @@ -137,8 +137,8 @@ </func> <func> - <name>log_list()</name> - <name>log_list(Type)</name> + <name since="OTP R16B02">log_list()</name> + <name since="OTP R16B02">log_list(Type)</name> <fsummary>Logs report lists.</fsummary> <type> <v>Type = type()</v> @@ -157,8 +157,8 @@ </func> <func> - <name>rescan()</name> - <name>rescan(Options)</name> + <name since="">rescan()</name> + <name since="">rescan(Options)</name> <fsummary>Rescans the report directory.</fsummary> <type> <v>Options = [opt()]</v> @@ -171,8 +171,8 @@ </func> <func> - <name>show()</name> - <name>show(Report)</name> + <name since="">show()</name> + <name since="">show(Report)</name> <fsummary>Displays reports.</fsummary> <type> <v>Report = integer() | type()</v> @@ -186,8 +186,8 @@ </func> <func> - <name>start()</name> - <name>start(Options)</name> + <name since="">start()</name> + <name since="">start(Options)</name> <fsummary>Starts the <c>rb_server</c>.</fsummary> <type> <v>Options = [opt()]</v> @@ -256,7 +256,7 @@ </func> <func> - <name>start_log(FileName)</name> + <name since="">start_log(FileName)</name> <fsummary>Redirects all output to <c>FileName</c>.</fsummary> <type> <v>FileName = string() | atom() | pid()</v> @@ -268,7 +268,7 @@ </func> <func> - <name>stop()</name> + <name since="">stop()</name> <fsummary>Stops the <c>rb_server</c>.</fsummary> <desc> <p>Stops <c>rb_server</c>.</p> @@ -276,7 +276,7 @@ </func> <func> - <name>stop_log()</name> + <name since="">stop_log()</name> <fsummary>Stops logging to file.</fsummary> <desc> <p>Closes the log file. The output from the RB tool is diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml index 9ba276aeac..f8ee0306d8 100644 --- a/lib/sasl/doc/src/release_handler.xml +++ b/lib/sasl/doc/src/release_handler.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>release_handler</module> + <module since="">release_handler</module> <modulesummary>Unpacking and Installation of Release Packages</modulesummary> <description> <p>The <em>release handler</em> process belongs to the SASL @@ -168,8 +168,8 @@ <funcs> <func> - <name>check_install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name> - <name>check_install_release(Vsn,Opts) -> {ok, OtherVsn, Descr} | {error, Reason}</name> + <name since="">check_install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name> + <name since="OTP R14B04">check_install_release(Vsn,Opts) -> {ok, OtherVsn, Descr} | {error, Reason}</name> <fsummary>Checks installation of a release in the system.</fsummary> <type> <v>Vsn = OtherVsn = string()</v> @@ -202,7 +202,7 @@ </func> <func> - <name>create_RELEASES(Root, RelDir, RelFile, AppDirs) -> ok | {error, Reason}</name> + <name since="">create_RELEASES(Root, RelDir, RelFile, AppDirs) -> ok | {error, Reason}</name> <fsummary>Creates an initial <c>RELEASES</c> file.</fsummary> <type> <v>Root = RelDir = RelFile = string()</v> @@ -233,7 +233,7 @@ </func> <func> - <name>install_file(Vsn, File) -> ok | {error, Reason}</name> + <name since="">install_file(Vsn, File) -> ok | {error, Reason}</name> <fsummary>Installs a release file in the release structure.</fsummary> <type> <v>Vsn = File = string()</v> @@ -252,8 +252,8 @@ </func> <func> - <name>install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name> - <name>install_release(Vsn, [Opt]) -> {ok, OtherVsn, Descr} | {continue_after_restart, OtherVsn, Descr} | {error, Reason}</name> + <name since="">install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name> + <name since="">install_release(Vsn, [Opt]) -> {ok, OtherVsn, Descr} | {continue_after_restart, OtherVsn, Descr} | {error, Reason}</name> <fsummary>Installs a release in the system.</fsummary> <type> <v>Vsn = OtherVsn = string()</v> @@ -383,7 +383,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>make_permanent(Vsn) -> ok | {error, Reason}</name> + <name since="">make_permanent(Vsn) -> ok | {error, Reason}</name> <fsummary>Makes the specified release version permanent.</fsummary> <type> <v>Vsn = string()</v> @@ -396,7 +396,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>remove_release(Vsn) -> ok | {error, Reason}</name> + <name since="">remove_release(Vsn) -> ok | {error, Reason}</name> <fsummary>Removes a release from the system.</fsummary> <type> <v>Vsn = string()</v> @@ -410,7 +410,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>reboot_old_release(Vsn) -> ok | {error, Reason}</name> + <name since="">reboot_old_release(Vsn) -> ok | {error, Reason}</name> <fsummary>Reboots the system from an old release.</fsummary> <type> <v>Vsn = string()</v> @@ -425,7 +425,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>set_removed(Vsn) -> ok | {error, Reason}</name> + <name since="">set_removed(Vsn) -> ok | {error, Reason}</name> <fsummary>Marks a release as removed.</fsummary> <type> <v>Vsn = string()</v> @@ -440,7 +440,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>set_unpacked(RelFile, AppDirs) -> {ok, Vsn} | {error, Reason}</name> + <name since="">set_unpacked(RelFile, AppDirs) -> {ok, Vsn} | {error, Reason}</name> <fsummary>Marks a release as unpacked.</fsummary> <type> <v>RelFile = string()</v> @@ -466,7 +466,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>unpack_release(Name) -> {ok, Vsn} | {error, Reason}</name> + <name since="">unpack_release(Name) -> {ok, Vsn} | {error, Reason}</name> <fsummary>Unpacks a release package.</fsummary> <type> <v>Name = Vsn = string()</v> @@ -482,7 +482,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>which_releases() -> [{Name, Vsn, Apps, Status}]</name> + <name since="">which_releases() -> [{Name, Vsn, Apps, Status}]</name> <fsummary>Returns all known releases.</fsummary> <type> <v>Name = Vsn = string()</v> @@ -495,7 +495,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>which_releases(Status) -> [{Name, Vsn, Apps, Status}]</name> + <name since="OTP R15B">which_releases(Status) -> [{Name, Vsn, Apps, Status}]</name> <fsummary>Returns all known releases of a specific status.</fsummary> <type> <v>Name = Vsn = string()</v> @@ -537,7 +537,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). <funcs> <func> - <name>upgrade_app(App, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason}</name> + <name since="">upgrade_app(App, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason}</name> <fsummary>Upgrades to a new application version.</fsummary> <type> <v>App = atom()</v> @@ -586,8 +586,8 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>downgrade_app(App, Dir) -></name> - <name>downgrade_app(App, OldVsn, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason}</name> + <name since="">downgrade_app(App, Dir) -></name> + <name since="">downgrade_app(App, OldVsn, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason}</name> <fsummary>Downgrades to a previous application version.</fsummary> <type> <v>App = atom()</v> @@ -633,7 +633,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>upgrade_script(App, Dir) -> {ok, NewVsn, Script}</name> + <name since="">upgrade_script(App, Dir) -> {ok, NewVsn, Script}</name> <fsummary>Finds an application upgrade script.</fsummary> <type> <v>App = atom()</v> @@ -671,7 +671,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>downgrade_script(App, OldVsn, Dir) -> {ok, Script}</name> + <name since="">downgrade_script(App, OldVsn, Dir) -> {ok, Script}</name> <fsummary>Finds an application downgrade script.</fsummary> <type> <v>App = atom()</v> @@ -710,7 +710,7 @@ release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). </func> <func> - <name>eval_appup_script(App, ToVsn, ToDir, Script) -> {ok, Unpurged} | restart_emulator | {error, Reason}</name> + <name since="">eval_appup_script(App, ToVsn, ToDir, Script) -> {ok, Unpurged} | restart_emulator | {error, Reason}</name> <fsummary>Evaluates an application upgrade or downgrade script.</fsummary> <type> <v>App = atom()</v> diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml index 4842c732b1..6facb8ddae 100644 --- a/lib/sasl/doc/src/systools.xml +++ b/lib/sasl/doc/src/systools.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>systools</module> + <module since="">systools</module> <modulesummary>A Set of Release Handling Tools</modulesummary> <description> <p>This module contains functions to generate boot scripts @@ -40,8 +40,8 @@ <funcs> <func> - <name>make_relup(Name, UpFrom, DownTo) -> Result</name> - <name>make_relup(Name, UpFrom, DownTo, [Opt]) -> Result</name> + <name since="">make_relup(Name, UpFrom, DownTo) -> Result</name> + <name since="">make_relup(Name, UpFrom, DownTo, [Opt]) -> Result</name> <fsummary>Generates a release upgrade file <c>relup</c>.</fsummary> <type> <v>Name = string()</v> @@ -136,8 +136,8 @@ </func> <func> - <name>make_script(Name) -> Result</name> - <name>make_script(Name, [Opt]) -> Result</name> + <name since="">make_script(Name) -> Result</name> + <name since="">make_script(Name, [Opt]) -> Result</name> <fsummary>Generates a boot script <c>.script/.boot</c>.</fsummary> <type> <v>Name = string()</v> @@ -263,8 +263,8 @@ </func> <func> - <name>make_tar(Name) -> Result</name> - <name>make_tar(Name, [Opt]) -> Result</name> + <name since="">make_tar(Name) -> Result</name> + <name since="">make_tar(Name, [Opt]) -> Result</name> <fsummary>Creates a release package.</fsummary> <type> <v>Name = string()</v> @@ -369,7 +369,7 @@ myapp-1/ebin/myapp.app </func> <func> - <name>script2boot(File) -> ok | error</name> + <name since="">script2boot(File) -> ok | error</name> <fsummary>Generates a binary version of a boot script.</fsummary> <type> <v>File = string()</v> diff --git a/lib/snmp/doc/src/snmp.xml b/lib/snmp/doc/src/snmp.xml index 480ed2e825..d20f1a8d06 100644 --- a/lib/snmp/doc/src/snmp.xml +++ b/lib/snmp/doc/src/snmp.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp.xml</file> </header> - <module>snmp</module> + <module since="">snmp</module> <modulesummary>Interface functions to the SNMP toolkit</modulesummary> <description> <p>The module <c>snmp</c> contains interface functions to the @@ -56,7 +56,7 @@ <funcs> <func> - <name>config() -> ok | {error, Reason}</name> + <name since="">config() -> ok | {error, Reason}</name> <fsummary>Configure with a simple interactive tool</fsummary> <desc> <p>A simple interactive configuration tool. Simple @@ -78,8 +78,8 @@ </func> <func> - <name>start() -> ok | {error, Reason}</name> - <name>start(Type) -> ok | {error, Reason}</name> + <name since="">start() -> ok | {error, Reason}</name> + <name since="">start(Type) -> ok | {error, Reason}</name> <fsummary>Start the SNMP application</fsummary> <type> <v>Type = start_type()</v> @@ -93,8 +93,8 @@ </func> <func> - <name>start_agent() -> ok | {error, Reason}</name> - <name>start_agent(Type) -> ok | {error, Reason}</name> + <name since="">start_agent() -> ok | {error, Reason}</name> + <name since="">start_agent(Type) -> ok | {error, Reason}</name> <fsummary>Start the agent part of the SNMP application</fsummary> <type> <v>Type = start_type()</v> @@ -117,8 +117,8 @@ </func> <func> - <name>start_manager() -> ok | {error, Reason}</name> - <name>start_manager(Type) -> ok | {error, Reason}</name> + <name since="">start_manager() -> ok | {error, Reason}</name> + <name since="">start_manager(Type) -> ok | {error, Reason}</name> <fsummary>Start the manager part of the SNMP application</fsummary> <type> <v>Type = start_type()</v> @@ -141,7 +141,7 @@ </func> <func> - <name>date_and_time() -> DateAndTime</name> + <name since="">date_and_time() -> DateAndTime</name> <fsummary>Return the current date and time as an OCTET STRING</fsummary> <type> <v>DateAndTime = [int()]</v> @@ -155,7 +155,7 @@ </func> <func> - <name>date_and_time_to_universal_time_dst(DateAndTime) -> [utc()]</name> + <name since="">date_and_time_to_universal_time_dst(DateAndTime) -> [utc()]</name> <fsummary>Convert a DateAndTime value to a list of possible utc()</fsummary> <type> <v>DateAndTime = [int()]</v> @@ -171,8 +171,8 @@ </func> <func> - <name>date_and_time_to_string(DateAndTime) -> string()</name> - <name>date_and_time_to_string(DateAndTime, Validate) -> string()</name> + <name since="">date_and_time_to_string(DateAndTime) -> string()</name> + <name since="">date_and_time_to_string(DateAndTime, Validate) -> string()</name> <fsummary>Convert a DateAndTime value to a string</fsummary> <type> <v>DateAndTime = [int()]</v> @@ -194,7 +194,7 @@ </func> <func> - <name>date_and_time_to_string2(DateAndTime) -> string()</name> + <name since="">date_and_time_to_string2(DateAndTime) -> string()</name> <fsummary>Convert a DateAndTime value to a string</fsummary> <type> <v>DateAndTime = [int()]</v> @@ -210,7 +210,7 @@ </func> <func> - <name>local_time_to_date_and_time_dst(Local) -> [DateAndTime]</name> + <name since="">local_time_to_date_and_time_dst(Local) -> [DateAndTime]</name> <fsummary>Convert a Local time value to a list of possible DateAndTime(s)</fsummary> <type> <v>Local = {{Y,Mo,D},{H,M,S}}</v> @@ -226,7 +226,7 @@ </func> <func> - <name>universal_time_to_date_and_time(UTC) -> DateAndTime</name> + <name since="">universal_time_to_date_and_time(UTC) -> DateAndTime</name> <fsummary>Convert a UTC value to DateAndTime</fsummary> <type> <v>UTC = {{Y,Mo,D},{H,M,S}}</v> @@ -241,8 +241,8 @@ </func> <func> - <name>validate_date_and_time(DateAndTime) -> bool()</name> - <name>validate_date_and_time(DateAndTime, Validate) -> bool()</name> + <name since="">validate_date_and_time(DateAndTime) -> bool()</name> + <name since="">validate_date_and_time(DateAndTime, Validate) -> bool()</name> <fsummary>Check if a DateAndTime value is correct</fsummary> <type> <v>DateAndTime = term()</v> @@ -279,7 +279,7 @@ </func> <func> - <name>passwd2localized_key(Alg, Passwd, EngineID) -> Key</name> + <name since="">passwd2localized_key(Alg, Passwd, EngineID) -> Key</name> <fsummary>Generates an localized key</fsummary> <type> <v>Alg = algorithm()</v> @@ -298,7 +298,7 @@ </func> <func> - <name>octet_string_to_bits(S) -> Val</name> + <name since="">octet_string_to_bits(S) -> Val</name> <fsummary>Convert an OCTET-STRING to BITS</fsummary> <type> <v>Val = bits()</v> @@ -312,7 +312,7 @@ </func> <func> - <name>bits_to_octet_string(B) -> Val</name> + <name since="">bits_to_octet_string(B) -> Val</name> <fsummary>Convert an OCTET-STRING to BITS</fsummary> <type> <v>Val = octet_string()</v> @@ -326,7 +326,7 @@ </func> <func> - <name>read_mib(FileName) -> {ok, mib()} | {error, Reason}</name> + <name since="">read_mib(FileName) -> {ok, mib()} | {error, Reason}</name> <fsummary></fsummary> <type> <v>FileName = string()</v> @@ -341,10 +341,10 @@ </func> <func> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Block | Stop) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop, Block) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Block | Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop, Block) -> ok | {ok, Cnt} | {error, Reason}</name> <fsummary>Convert an Audit Trail Log to text format</fsummary> <type> <v>LogDir = string()</v> @@ -412,10 +412,10 @@ </func> <func> - <name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Block | Stop) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop, Block) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Start, Block | Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop, Block) -> ok | {ok, Cnt} | {error, Reason}</name> <fsummary>Convert an Audit Trail Log to text format</fsummary> <type> <v>LogDir = string()</v> @@ -440,7 +440,7 @@ </func> <func> - <name>change_log_size(LogName, NewSize) -> ok | {error, Reason}</name> + <name since="">change_log_size(LogName, NewSize) -> ok | {error, Reason}</name> <fsummary>Change the size of the Audit Trail Log</fsummary> <type> <v>LogName = string()</v> @@ -463,8 +463,8 @@ </func> <func> - <name>print_version_info() -> void()</name> - <name>print_version_info(Prefix) -> void()</name> + <name since="">print_version_info() -> void()</name> + <name since="">print_version_info(Prefix) -> void()</name> <fsummary>Formatted print of result of the versions functions</fsummary> <type> <v>Prefix = string() | integer()</v> @@ -484,8 +484,8 @@ </func> <func> - <name>versions1() -> {ok, Info} | {error, Reason}</name> - <name>versions2() -> {ok, Info} | {error, Reason}</name> + <name since="">versions1() -> {ok, Info} | {error, Reason}</name> + <name since="">versions2() -> {ok, Info} | {error, Reason}</name> <fsummary>Retrieve various system and application info</fsummary> <type> <v>Info = [info()]</v> @@ -504,8 +504,8 @@ </func> <func> - <name>print_versions(VersionInfo) -> void()</name> - <name>print_versions(Prefix, VersionInfo) -> void()</name> + <name since="">print_versions(VersionInfo) -> void()</name> + <name since="">print_versions(Prefix, VersionInfo) -> void()</name> <fsummary>Formatted print of result of the versions functions</fsummary> <type> <v>VersionInfo = [version_info()]</v> @@ -527,7 +527,7 @@ </func> <func> - <name>enable_trace() -> void()</name> + <name since="">enable_trace() -> void()</name> <fsummary>Starts a tracer</fsummary> <!-- <type> @@ -543,7 +543,7 @@ </func> <func> - <name>disable_trace() -> void()</name> + <name since="">disable_trace() -> void()</name> <fsummary>Stop the tracer</fsummary> <!-- <type> @@ -558,7 +558,7 @@ </func> <func> - <name>set_trace(Targets) -> void()</name> + <name since="">set_trace(Targets) -> void()</name> <fsummary>Set trace target</fsummary> <type> <v>Targets = target() | targets()</v> @@ -582,7 +582,7 @@ </func> <func> - <name>reset_trace(Targets) -> void()</name> + <name since="">reset_trace(Targets) -> void()</name> <fsummary>Reset trace target</fsummary> <type> <v>Targets = module() | modules()</v> @@ -598,7 +598,7 @@ </func> <func> - <name>set_trace(Targets, Opts) -> void()</name> + <name since="">set_trace(Targets, Opts) -> void()</name> <fsummary>Set trace target</fsummary> <type> <v>Targets = target() | targets()</v> diff --git a/lib/snmp/doc/src/snmp_community_mib.xml b/lib/snmp/doc/src/snmp_community_mib.xml index 61dea05950..9800fb6c00 100644 --- a/lib/snmp/doc/src/snmp_community_mib.xml +++ b/lib/snmp/doc/src/snmp_community_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_community_mib.xml</file> </header> - <module>snmp_community_mib</module> + <module since="">snmp_community_mib</module> <modulesummary>Instrumentation Functions for SNMP-COMMUNITY-MIB</modulesummary> <description> <p>The module <c>snmp_community_mib</c> implements the instrumentation @@ -45,7 +45,7 @@ <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-COMMUNITY-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -77,7 +77,7 @@ </func> <func> - <name>reconfigure(ConfDir) -> void()</name> + <name since="">reconfigure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-COMMUNITY-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -108,8 +108,8 @@ </func> <func> - <name>add_community(Idx, CommName, SecName, CtxName, TransportTag) -> Ret</name> - <name>add_community(Idx, CommName, SecName, EngineId, CtxName, TransportTag) -> Ret</name> + <name since="">add_community(Idx, CommName, SecName, CtxName, TransportTag) -> Ret</name> + <name since="OTP R14B03">add_community(Idx, CommName, SecName, EngineId, CtxName, TransportTag) -> Ret</name> <fsummary>Added one community</fsummary> <type> <v>Idx = string()</v> @@ -132,7 +132,7 @@ </func> <func> - <name>delete_community(Key) -> Ret</name> + <name since="">delete_community(Key) -> Ret</name> <fsummary>Delete one community</fsummary> <type> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmp_framework_mib.xml b/lib/snmp/doc/src/snmp_framework_mib.xml index 64e5df6ff5..d84327d4d5 100644 --- a/lib/snmp/doc/src/snmp_framework_mib.xml +++ b/lib/snmp/doc/src/snmp_framework_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_framework_mib.xml</file> </header> - <module>snmp_framework_mib</module> + <module since="">snmp_framework_mib</module> <modulesummary>Instrumentation Functions for SNMP-FRAMEWORK-MIB</modulesummary> <description> <p>The module <c>snmp_framework_mib</c> implements instrumentation @@ -44,7 +44,7 @@ </description> <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-FRAMEWORK-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -75,7 +75,7 @@ </desc> </func> <func> - <name>init() -> void()</name> + <name since="">init() -> void()</name> <fsummary>Initialize the SNMP-FRAMEWORK-MIB</fsummary> <desc> <p>This function is called from the supervisor at system @@ -88,7 +88,7 @@ </desc> </func> <func> - <name>add_context(Ctx) -> Ret</name> + <name since="">add_context(Ctx) -> Ret</name> <fsummary>Added one context</fsummary> <type> <v>Ctx = string()</v> @@ -103,7 +103,7 @@ </desc> </func> <func> - <name>delete_context(Key) -> Ret</name> + <name since="">delete_context(Key) -> Ret</name> <fsummary>Delete one context</fsummary> <type> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmp_generic.xml b/lib/snmp/doc/src/snmp_generic.xml index 44762dec59..6fb714907c 100644 --- a/lib/snmp/doc/src/snmp_generic.xml +++ b/lib/snmp/doc/src/snmp_generic.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_generic.xml</file> </header> - <module>snmp_generic</module> + <module since="">snmp_generic</module> <modulesummary>Generic Functions for Implementing SNMP Objects in a Database</modulesummary> <description> <marker id="description"></marker> @@ -127,8 +127,8 @@ value() = term() <funcs> <func> - <name>get_status_col(Name, Cols)</name> - <name>get_status_col(NameDb, Cols) -> {ok, StatusVal} | false</name> + <name since="">get_status_col(Name, Cols)</name> + <name since="">get_status_col(NameDb, Cols) -> {ok, StatusVal} | false</name> <fsummary>Get the value of the status column from <c>Cols</c></fsummary> <type> <v>Name = name()</v> @@ -148,7 +148,7 @@ value() = term() </func> <func> - <name>get_index_types(Name)</name> + <name since="">get_index_types(Name)</name> <fsummary>Get the index types of <c>Name</c></fsummary> <type> <v>Name = name()</v> @@ -163,7 +163,7 @@ value() = term() </func> <func> - <name>get_table_info(Name, Item) -> table_info_result()</name> + <name since="OTP R15B01">get_table_info(Name, Item) -> table_info_result()</name> <fsummary>Get table info item of MIB table <c>Name</c></fsummary> <type> <v>Name = name()</v> @@ -187,8 +187,8 @@ value() = term() </func> <func> - <name>table_func(Op1, NameDb)</name> - <name>table_func(Op2, RowIndex, Cols, NameDb) -> Ret</name> + <name since="">table_func(Op1, NameDb)</name> + <name since="">table_func(Op2, RowIndex, Cols, NameDb) -> Ret</name> <fsummary>Default instrumentation function for tables</fsummary> <type> <v>Op1 = new | delete </v> @@ -232,7 +232,7 @@ value() = term() </func> <func> - <name>table_get_elements(NameDb, RowIndex, Cols) -> Values</name> + <name since="">table_get_elements(NameDb, RowIndex, Cols) -> Values</name> <fsummary>Get elements in a table row</fsummary> <type> <v>NameDb = name_db()</v> @@ -249,7 +249,7 @@ value() = term() </func> <func> - <name>table_next(NameDb, RestOid) -> RowIndex | endOfTable</name> + <name since="">table_next(NameDb, RestOid) -> RowIndex | endOfTable</name> <fsummary>Find the next row in the table</fsummary> <type> <v>NameDb = name_db()</v> @@ -265,7 +265,7 @@ value() = term() </func> <func> - <name>table_row_exists(NameDb, RowIndex) -> bool()</name> + <name since="">table_row_exists(NameDb, RowIndex) -> bool()</name> <fsummary>Check if a row in a table exists</fsummary> <type> <v>NameDb = name_db()</v> @@ -279,7 +279,7 @@ value() = term() </func> <func> - <name>table_set_elements(NameDb, RowIndex, Cols) -> bool()</name> + <name since="">table_set_elements(NameDb, RowIndex, Cols) -> bool()</name> <fsummary>Set elements in a table row</fsummary> <type> <v>NameDb = name_db()</v> @@ -300,8 +300,8 @@ value() = term() </func> <func> - <name>variable_func(Op1, NameDb)</name> - <name>variable_func(Op2, Val, NameDb) -> Ret</name> + <name since="">variable_func(Op1, NameDb)</name> + <name since="">variable_func(Op2, Val, NameDb) -> Ret</name> <fsummary>Default instrumentation function for tables</fsummary> <type> <v>Op1 = new | delete | get</v> @@ -325,7 +325,7 @@ value() = term() </func> <func> - <name>variable_get(NameDb) -> {value, Value} | undefined</name> + <name since="">variable_get(NameDb) -> {value, Value} | undefined</name> <fsummary>Get the value of a variable</fsummary> <type> <v>NameDb = name_db()</v> @@ -339,7 +339,7 @@ value() = term() </func> <func> - <name>variable_set(NameDb, NewVal) -> true | false</name> + <name since="">variable_set(NameDb, NewVal) -> true | false</name> <fsummary>Set a value for a variable</fsummary> <type> <v>NameDb = name_db()</v> diff --git a/lib/snmp/doc/src/snmp_index.xml b/lib/snmp/doc/src/snmp_index.xml index 646e9661a3..1497f4cf67 100644 --- a/lib/snmp/doc/src/snmp_index.xml +++ b/lib/snmp/doc/src/snmp_index.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_index.xml</file> </header> - <module>snmp_index</module> + <module since="">snmp_index</module> <modulesummary>Abstract Data Type for SNMP Indexing</modulesummary> <description> <p>The module <c>snmp_index</c> implements an Abstract @@ -159,7 +159,7 @@ get_next_pid(Oid, SnmpIndex) -> </section> <funcs> <func> - <name>delete(Index) -> true</name> + <name since="">delete(Index) -> true</name> <fsummary>Delete an index table</fsummary> <type> <v>Index = NewIndex = index()</v> @@ -173,7 +173,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>delete(Index, Key) -> NewIndex</name> + <name since="">delete(Index, Key) -> NewIndex</name> <fsummary>Delete an item from the index</fsummary> <type> <v>Index = NewIndex = index()</v> @@ -185,7 +185,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>get(Index, KeyOid) -> {ok, {KeyOid, Value}} | undefined</name> + <name since="">get(Index, KeyOid) -> {ok, {KeyOid, Value}} | undefined</name> <fsummary>Get the item with <c>KeyOid</c></fsummary> <type> <v>Index = index()</v> @@ -198,7 +198,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>get_last(Index) -> {ok, {KeyOid, Value}} | undefined</name> + <name since="">get_last(Index) -> {ok, {KeyOid, Value}} | undefined</name> <fsummary>Get the last item in the index structure</fsummary> <type> <v>Index = index()</v> @@ -210,7 +210,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>get_next(Index, KeyOid) -> {ok, {NextKeyOid, Value}} | undefined</name> + <name since="">get_next(Index, KeyOid) -> {ok, {NextKeyOid, Value}} | undefined</name> <fsummary>Get the next item</fsummary> <type> <v>Index = index()</v> @@ -224,7 +224,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>insert(Index, Key, Value) -> NewIndex</name> + <name since="">insert(Index, Key, Value) -> NewIndex</name> <fsummary>Insert an item into the index</fsummary> <type> <v>Index = NewIndex = index()</v> @@ -238,7 +238,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>key_to_oid(Index, Key) -> KeyOid</name> + <name since="">key_to_oid(Index, Key) -> KeyOid</name> <fsummary>Convert a key to an OBJECT IDENTIFIER</fsummary> <type> <v>Index = index()</v> @@ -250,7 +250,7 @@ get_next_pid(Oid, SnmpIndex) -> </desc> </func> <func> - <name>new(KeyTypes) -> Index</name> + <name since="">new(KeyTypes) -> Index</name> <fsummary>Create a new snmp index structure</fsummary> <type> <v>KeyTypes = key_types()</v> diff --git a/lib/snmp/doc/src/snmp_notification_mib.xml b/lib/snmp/doc/src/snmp_notification_mib.xml index d2e288ec15..9395edf155 100644 --- a/lib/snmp/doc/src/snmp_notification_mib.xml +++ b/lib/snmp/doc/src/snmp_notification_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_notification_mib.xml</file> </header> - <module>snmp_notification_mib</module> + <module since="">snmp_notification_mib</module> <modulesummary>Instrumentation Functions for SNMP-NOTIFICATION-MIB</modulesummary> <description> <p>The module <c>snmp_notification_mib</c> implements the @@ -43,7 +43,7 @@ </description> <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-NOTIFICATION-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -70,7 +70,7 @@ </desc> </func> <func> - <name>reconfigure(ConfDir) -> void()</name> + <name since="">reconfigure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-NOTIFICATION-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -98,7 +98,7 @@ </desc> </func> <func> - <name>add_notify(Name, Tag, Type) -> Ret</name> + <name since="">add_notify(Name, Tag, Type) -> Ret</name> <fsummary>Added one notify definition</fsummary> <type> <v>Name = string()</v> @@ -115,7 +115,7 @@ </desc> </func> <func> - <name>delete_notify(Key) -> Ret</name> + <name since="">delete_notify(Key) -> Ret</name> <fsummary>Delete one notify definition</fsummary> <type> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmp_pdus.xml b/lib/snmp/doc/src/snmp_pdus.xml index 1d086e6f48..f403b6edf4 100644 --- a/lib/snmp/doc/src/snmp_pdus.xml +++ b/lib/snmp/doc/src/snmp_pdus.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_pdus.xml</file> </header> - <module>snmp_pdus</module> + <module since="">snmp_pdus</module> <modulesummary>Encode and Decode Functions for SNMP PDUs</modulesummary> <description> <p>RFC1157, RFC1905 and/or RFC2272 should be studied carefully @@ -55,7 +55,7 @@ </description> <funcs> <func> - <name>dec_message([byte()]) -> Message</name> + <name since="">dec_message([byte()]) -> Message</name> <fsummary>Decode an SNMP Message</fsummary> <type> <v>Message = #message</v> @@ -71,7 +71,7 @@ </desc> </func> <func> - <name>dec_message_only([byte()]) -> Message</name> + <name since="">dec_message_only([byte()]) -> Message</name> <fsummary>Decode an SNMP Message, but not the data part</fsummary> <type> <v>Message = #message</v> @@ -84,7 +84,7 @@ </desc> </func> <func> - <name>dec_pdu([byte()]) -> Pdu</name> + <name since="">dec_pdu([byte()]) -> Pdu</name> <fsummary>Decode an SNMP Pdu</fsummary> <type> <v>Pdu = #pdu</v> @@ -94,7 +94,7 @@ </desc> </func> <func> - <name>dec_scoped_pdu([byte()]) -> ScopedPdu</name> + <name since="">dec_scoped_pdu([byte()]) -> ScopedPdu</name> <fsummary>Decode an SNMP ScopedPdu</fsummary> <type> <v>ScopedPdu = #scoped_pdu</v> @@ -104,7 +104,7 @@ </desc> </func> <func> - <name>dec_scoped_pdu_data([byte()]) -> ScopedPduData</name> + <name since="">dec_scoped_pdu_data([byte()]) -> ScopedPduData</name> <fsummary>Decode an SNMP ScopedPduData</fsummary> <type> <v>ScopedPduData = #scoped_pdu | EncryptedPDU</v> @@ -116,7 +116,7 @@ </desc> </func> <func> - <name>dec_usm_security_parameters([byte()]) -> UsmSecParams</name> + <name since="">dec_usm_security_parameters([byte()]) -> UsmSecParams</name> <fsummary>Decode SNMP UsmSecurityParameters</fsummary> <type> <v>UsmSecParams = #usmSecurityParameters</v> @@ -126,7 +126,7 @@ </desc> </func> <func> - <name>enc_encrypted_scoped_pdu(EncryptedScopedPdu) -> [byte()]</name> + <name since="">enc_encrypted_scoped_pdu(EncryptedScopedPdu) -> [byte()]</name> <fsummary>Encode an encrypted SNMP scopedPDU</fsummary> <type> <v>EncryptedScopedPdu = [byte()]</v> @@ -142,7 +142,7 @@ </desc> </func> <func> - <name>enc_message(Message) -> [byte()]</name> + <name since="">enc_message(Message) -> [byte()]</name> <fsummary>Encode an SNMP Message</fsummary> <type> <v>Message = #message</v> @@ -152,7 +152,7 @@ </desc> </func> <func> - <name>enc_message_only(Message) -> [byte()]</name> + <name since="">enc_message_only(Message) -> [byte()]</name> <fsummary>Encode an SNMP Message, but not the data part</fsummary> <type> <v>Message = #message</v> @@ -166,7 +166,7 @@ </desc> </func> <func> - <name>enc_pdu(Pd) -> [byte()]</name> + <name since="">enc_pdu(Pd) -> [byte()]</name> <fsummary>Encode an SNMP Pdu</fsummary> <type> <v>Pdu = #pdu</v> @@ -176,7 +176,7 @@ </desc> </func> <func> - <name>enc_scoped_pdu(ScopedPdu) -> [byte()]</name> + <name since="">enc_scoped_pdu(ScopedPdu) -> [byte()]</name> <fsummary>Encode an SNMP scopedPDU</fsummary> <type> <v>ScopedPdu = #scoped_pdu</v> @@ -190,7 +190,7 @@ </desc> </func> <func> - <name>enc_usm_security_parameters(UsmSecParams) -> [byte()]</name> + <name since="">enc_usm_security_parameters(UsmSecParams) -> [byte()]</name> <fsummary>Encode SNMP UsmSecurityParameters</fsummary> <type> <v>UsmSecParams = #usmSecurityParameters</v> diff --git a/lib/snmp/doc/src/snmp_standard_mib.xml b/lib/snmp/doc/src/snmp_standard_mib.xml index 35efbba483..eb4e2fd097 100644 --- a/lib/snmp/doc/src/snmp_standard_mib.xml +++ b/lib/snmp/doc/src/snmp_standard_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_standard_mib.xml</file> </header> - <module>snmp_standard_mib</module> + <module since="">snmp_standard_mib</module> <modulesummary>Instrumentation Functions for STANDARD-MIB and SNMPv2-MIB</modulesummary> <description> <p>The module <c>snmp_standard_mib</c> implements the instrumentation functions for the @@ -42,7 +42,7 @@ </description> <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the STANDARD-MIB and SNMPv2-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -71,8 +71,8 @@ </desc> </func> <func> - <name>inc(Name) -> void()</name> - <name>inc(Name, N) -> void()</name> + <name since="">inc(Name) -> void()</name> + <name since="">inc(Name, N) -> void()</name> <fsummary>Increment a variable in the MIB</fsummary> <type> <v>Name = atom()</v> @@ -84,7 +84,7 @@ </desc> </func> <func> - <name>reconfigure(ConfDir) -> void()</name> + <name since="">reconfigure(ConfDir) -> void()</name> <fsummary>Configure the STANDARD-MIB and SNMPv2-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -114,14 +114,14 @@ </desc> </func> <func> - <name>reset() -> void()</name> + <name since="">reset() -> void()</name> <fsummary>Reset all <c>snmp</c>counters to 0</fsummary> <desc> <p>Resets all <c>snmp</c> counters to 0.</p> </desc> </func> <func> - <name>sys_up_time() -> Time</name> + <name since="">sys_up_time() -> Time</name> <fsummary>Get the system up time</fsummary> <type> <v>Time = int()</v> diff --git a/lib/snmp/doc/src/snmp_target_mib.xml b/lib/snmp/doc/src/snmp_target_mib.xml index c3bcd3b4e3..c46edb810d 100644 --- a/lib/snmp/doc/src/snmp_target_mib.xml +++ b/lib/snmp/doc/src/snmp_target_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_target_mib.xml</file> </header> - <module>snmp_target_mib</module> + <module since="">snmp_target_mib</module> <modulesummary>Instrumentation Functions for SNMP-TARGET-MIB</modulesummary> <description> <p>The module <c>snmp_target_mib</c> implements the instrumentation @@ -57,7 +57,7 @@ <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-TARGET-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -85,7 +85,7 @@ </func> <func> - <name>reconfigure(ConfDir) -> void()</name> + <name since="">reconfigure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-TARGET-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -113,7 +113,7 @@ </func> <func> - <name>set_target_engine_id(TargetAddrName, EngineId) -> boolean()</name> + <name since="">set_target_engine_id(TargetAddrName, EngineId) -> boolean()</name> <fsummary>Set the engine id for a targetAddr row.</fsummary> <type> <v>TargetAddrName = string()</v> @@ -130,7 +130,7 @@ </func> <func> - <name>add_addr(Name, Domain, Addr, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name> + <name since="">add_addr(Name, Domain, Addr, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name> <fsummary>Add one target address definition</fsummary> <type> <v>Name = string()</v> @@ -156,7 +156,7 @@ </func> <func> - <name>delete_addr(Key) -> Ret</name> + <name since="">delete_addr(Key) -> Ret</name> <fsummary>Delete one target address definition</fsummary> <type> <v>Key = term()</v> @@ -171,7 +171,7 @@ </func> <func> - <name>add_params(Name, MPModel, SecModel, SecName, SecLevel) -> Ret</name> + <name since="">add_params(Name, MPModel, SecModel, SecName, SecLevel) -> Ret</name> <fsummary>Add one target parameter definition</fsummary> <type> <v>Name = string()</v> @@ -191,7 +191,7 @@ </desc> </func> <func> - <name>delete_params(Key) -> Ret</name> + <name since="">delete_params(Key) -> Ret</name> <fsummary>Delete one target parameter definition</fsummary> <type> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmp_user_based_sm_mib.xml b/lib/snmp/doc/src/snmp_user_based_sm_mib.xml index cc376ac118..6c2203ed22 100644 --- a/lib/snmp/doc/src/snmp_user_based_sm_mib.xml +++ b/lib/snmp/doc/src/snmp_user_based_sm_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_user_based_sm_mib.xml</file> </header> - <module>snmp_user_based_sm_mib</module> + <module since="">snmp_user_based_sm_mib</module> <modulesummary>Instrumentation Functions for SNMP-USER-BASED-SM-MIB</modulesummary> <description> <p>The module <c>snmp_user_based_sm_mib</c> implements the instrumentation @@ -43,7 +43,7 @@ </description> <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-USER-BASED-SM-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -72,7 +72,7 @@ </desc> </func> <func> - <name>reconfigure(ConfDir) -> void()</name> + <name since="">reconfigure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-USER-BASED-SM-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -103,7 +103,7 @@ </desc> </func> <func> - <name>add_user(EngineID, Name, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey) -> Ret</name> + <name since="">add_user(EngineID, Name, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey) -> Ret</name> <fsummary>Add one user</fsummary> <type> <v>EngineID = string()</v> @@ -130,7 +130,7 @@ </desc> </func> <func> - <name>delete_user(Key) -> Ret</name> + <name since="">delete_user(Key) -> Ret</name> <fsummary>Delete one user</fsummary> <type> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmp_view_based_acm_mib.xml b/lib/snmp/doc/src/snmp_view_based_acm_mib.xml index fdad735e71..c5e98a3eb5 100644 --- a/lib/snmp/doc/src/snmp_view_based_acm_mib.xml +++ b/lib/snmp/doc/src/snmp_view_based_acm_mib.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmp_view_based_acm_mib.xml</file> </header> - <module>snmp_view_based_acm_mib</module> + <module since="">snmp_view_based_acm_mib</module> <modulesummary>Instrumentation Functions for SNMP-VIEW-BASED-ACM-MIB</modulesummary> <description> <p>The module <c>snmp_view_based_acm_mib</c> implements the instrumentation functions for the @@ -45,7 +45,7 @@ <funcs> <func> - <name>configure(ConfDir) -> void()</name> + <name since="">configure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-VIEW-BASED-ACM-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -71,7 +71,7 @@ </func> <func> - <name>reconfigure(ConfDir) -> void()</name> + <name since="">reconfigure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-VIEW-BASED-ACM-MIB</fsummary> <type> <v>ConfDir = string()</v> @@ -104,7 +104,7 @@ </func> <func> - <name>add_sec2group(SecModel, SecName, GroupName) -> Ret</name> + <name since="">add_sec2group(SecModel, SecName, GroupName) -> Ret</name> <fsummary>Add one security to group definition</fsummary> <type> <v>SecModel = v1 | v2c | usm</v> @@ -124,7 +124,7 @@ </func> <func> - <name>delete_sec2group(Key) -> Ret</name> + <name since="">delete_sec2group(Key) -> Ret</name> <fsummary>Delete one security to group definition</fsummary> <type> <v>Key = term()</v> @@ -139,7 +139,7 @@ </func> <func> - <name>add_access(GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV) -> Ret</name> + <name since="">add_access(GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV) -> Ret</name> <fsummary>Add one access definition</fsummary> <type> <v>GroupName = string()</v> @@ -163,7 +163,7 @@ </func> <func> - <name>delete_access(Key) -> Ret</name> + <name since="">delete_access(Key) -> Ret</name> <fsummary>Delete one access definition</fsummary> <type> <v>Key = term()</v> @@ -178,7 +178,7 @@ </func> <func> - <name>add_view_tree_fam(ViewIndex, SubTree, Status, Mask) -> Ret</name> + <name since="">add_view_tree_fam(ViewIndex, SubTree, Status, Mask) -> Ret</name> <fsummary>Add one view tree family definition</fsummary> <type> <v>ViewIndex = integer()</v> @@ -199,7 +199,7 @@ </func> <func> - <name>delete_view_tree_fam(Key) -> Ret</name> + <name since="">delete_view_tree_fam(Key) -> Ret</name> <fsummary>Delete one view tree family definition</fsummary> <type> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml index b78f14da01..dc2f4e6d66 100644 --- a/lib/snmp/doc/src/snmpa.xml +++ b/lib/snmp/doc/src/snmpa.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa.xml</file> </header> - <module>snmpa</module> + <module since="">snmpa</module> <modulesummary>Interface Functions to the SNMP toolkit agent</modulesummary> <description> <p>The module <c>snmpa</c> contains interface functions to the @@ -77,7 +77,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} <funcs> <func> - <name>add_agent_caps(SysORID, SysORDescr) -> SysORIndex</name> + <name since="">add_agent_caps(SysORID, SysORDescr) -> SysORIndex</name> <fsummary>Add an AGENT-CAPABILITY definition to the agent</fsummary> <type> <v>SysORID = oid()</v> @@ -93,7 +93,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>del_agent_caps(SysORIndex) -> void()</name> + <name since="">del_agent_caps(SysORIndex) -> void()</name> <fsummary>Delete an AGENT-CAPABILITY definition from the agent</fsummary> <type> <v>SysORIndex = integer()</v> @@ -108,7 +108,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>get_agent_caps() -> [[SysORIndex, SysORID, SysORDescr, SysORUpTime]]</name> + <name since="">get_agent_caps() -> [[SysORIndex, SysORID, SysORDescr, SysORUpTime]]</name> <fsummary>Return all AGENT-CAPABILITY definitions in the agent</fsummary> <type> <v>SysORIndex = integer()</v> @@ -125,8 +125,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>get(Agent, Vars) -> Values | {error, Reason}</name> - <name>get(Agent, Vars, Context) -> Values | {error, Reason}</name> + <name since="">get(Agent, Vars) -> Values | {error, Reason}</name> + <name since="">get(Agent, Vars, Context) -> Values | {error, Reason}</name> <fsummary>Perform a get operation on the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -150,8 +150,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>get_next(Agent, Vars) -> Values | {error, Reason}</name> - <name>get_next(Agent, Vars, Context) -> Values | {error, Reason}</name> + <name since="">get_next(Agent, Vars) -> Values | {error, Reason}</name> + <name since="">get_next(Agent, Vars, Context) -> Values | {error, Reason}</name> <fsummary>Perform a get-next operation on the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -176,7 +176,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} <!-- <func> - <name>get_symbolic_store_db() -> Db</name> + <name since="">get_symbolic_store_db() -> Db</name> <fsummary>Retrieve the symbolic store database reference</fsummary> <type> <v>Db = term()</v> @@ -193,8 +193,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} --> <func> - <name>backup(BackupDir) -> ok | {error, Reason}</name> - <name>backup(Agent, BackupDir) -> ok | {error, Reason}</name> + <name since="">backup(BackupDir) -> ok | {error, Reason}</name> + <name since="">backup(Agent, BackupDir) -> ok | {error, Reason}</name> <fsummary>Backup agent data</fsummary> <type> <v>BackupDir = string()</v> @@ -216,8 +216,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </desc> </func> <func> - <name>info() -> [{Key, Value}]</name> - <name>info(Agent) -> [{Key, Value}]</name> + <name since="">info() -> [{Key, Value}]</name> + <name since="">info(Agent) -> [{Key, Value}]</name> <fsummary>Return information about the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -236,7 +236,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>old_info_format(NewInfo) -> OldInfo</name> + <name since="">old_info_format(NewInfo) -> OldInfo</name> <fsummary>Return information about the agent</fsummary> <type> <v>OldInfo = NewInfo = [{Key, Value}]</v> @@ -251,8 +251,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>load_mib(Mib) -> ok | {error, Reason}</name> - <name>load_mib(Agent, Mib) -> ok | {error, Reason}</name> + <name since="OTP R16B02">load_mib(Mib) -> ok | {error, Reason}</name> + <name since="OTP R16B02">load_mib(Agent, Mib) -> ok | {error, Reason}</name> <fsummary>Load single MIB into the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -273,10 +273,10 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>load_mibs(Mibs) -> ok | {error, Reason}</name> - <name>load_mibs(Mibs, Force) -> ok | {error, Reason}</name> - <name>load_mibs(Agent, Mibs) -> ok | {error, Reason}</name> - <name>load_mibs(Agent, Mibs, Force) -> ok | {error, Reason}</name> + <name since="">load_mibs(Mibs) -> ok | {error, Reason}</name> + <name since="">load_mibs(Mibs, Force) -> ok | {error, Reason}</name> + <name since="">load_mibs(Agent, Mibs) -> ok | {error, Reason}</name> + <name since="OTP R16B02">load_mibs(Agent, Mibs, Force) -> ok | {error, Reason}</name> <fsummary>Load MIBs into the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -305,8 +305,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>unload_mib(Mib) -> ok | {error, Reason}</name> - <name>unload_mib(Agent, Mib) -> ok | {error, Reason}</name> + <name since="OTP R16B02">unload_mib(Mib) -> ok | {error, Reason}</name> + <name since="OTP R16B02">unload_mib(Agent, Mib) -> ok | {error, Reason}</name> <fsummary>Unload single MIB from the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -321,10 +321,10 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>unload_mibs(Mibs) -> ok | {error, Reason}</name> - <name>unload_mibs(Mibs, Force) -> ok | {error, Reason}</name> - <name>unload_mibs(Agent, Mibs) -> ok | {error, Reason}</name> - <name>unload_mibs(Agent, Mibs, Force) -> ok | {error, Reason}</name> + <name since="">unload_mibs(Mibs) -> ok | {error, Reason}</name> + <name since="">unload_mibs(Mibs, Force) -> ok | {error, Reason}</name> + <name since="">unload_mibs(Agent, Mibs) -> ok | {error, Reason}</name> + <name since="OTP R16B02">unload_mibs(Agent, Mibs, Force) -> ok | {error, Reason}</name> <fsummary>Unload MIBs from the agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -347,8 +347,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_mibs() -> Mibs</name> - <name>which_mibs(Agent) -> Mibs</name> + <name since="">which_mibs() -> Mibs</name> + <name since="">which_mibs(Agent) -> Mibs</name> <fsummary>Get a list of all the loaded mibs</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -365,8 +365,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>whereis_mib(MibName) -> {ok, MibFile} | {error, Reason}</name> - <name>whereis_mib(Agent, MibName) -> {ok, MibFile} | {error, Reason}</name> + <name since="">whereis_mib(MibName) -> {ok, MibFile} | {error, Reason}</name> + <name since="">whereis_mib(Agent, MibName) -> {ok, MibFile} | {error, Reason}</name> <fsummary>Get the path to the mib file</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -385,10 +385,10 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>current_request_id() -> {value, RequestId} | false</name> - <name>current_context() -> {value, Context} | false</name> - <name>current_community() -> {value, Community} | false</name> - <name>current_address() -> {value, Address} | false</name> + <name since="">current_request_id() -> {value, RequestId} | false</name> + <name since="">current_context() -> {value, Context} | false</name> + <name since="">current_community() -> {value, Community} | false</name> + <name since="">current_address() -> {value, Address} | false</name> <fsummary>Get the request-id, context, community and address of the current request</fsummary> <type> <v>RequestId = integer()</v> @@ -409,8 +409,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>enum_to_int(Name, Enum) -> {value, Int} | false</name> - <name>enum_to_int(Db, Name, Enum) -> {value, Int} | false</name> + <name since="">enum_to_int(Name, Enum) -> {value, Int} | false</name> + <name since="">enum_to_int(Db, Name, Enum) -> {value, Int} | false</name> <fsummary>Convert an enum value to an integer</fsummary> <type> <v>Db = term()</v> @@ -435,8 +435,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>int_to_enum(Name, Int) -> {value, Enum} | false</name> - <name>int_to_enum(Db, Name, Int) -> {value, Enum} | false</name> + <name since="">int_to_enum(Name, Int) -> {value, Enum} | false</name> + <name since="">int_to_enum(Db, Name, Int) -> {value, Enum} | false</name> <fsummary>Convert an integer to an enum value</fsummary> <type> <v>Db = term()</v> @@ -461,8 +461,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>name_to_oid(Name) -> {value, oid()} | false</name> - <name>name_to_oid(Db, Name) -> {value, oid()} | false</name> + <name since="">name_to_oid(Name) -> {value, oid()} | false</name> + <name since="">name_to_oid(Db, Name) -> {value, oid()} | false</name> <fsummary>Convert a symbolic name to an OID</fsummary> <type> <v>Db = term()</v> @@ -482,8 +482,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>oid_to_name(OID) -> {value, Name} | false</name> - <name>oid_to_name(Db, OID) -> {value, Name} | false</name> + <name since="">oid_to_name(OID) -> {value, Name} | false</name> + <name since="">oid_to_name(Db, OID) -> {value, Name} | false</name> <fsummary>Convert an OID to a symbolic name</fsummary> <type> <v>Db = term()</v> @@ -503,7 +503,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_aliasnames() -> Result</name> + <name since="">which_aliasnames() -> Result</name> <fsummary>Get all alias-names known to the agent</fsummary> <type> <v>Result = [atom()]</v> @@ -515,7 +515,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_tables() -> Result</name> + <name since="">which_tables() -> Result</name> <fsummary>Get all tables known to the agent</fsummary> <type> <v>Result = [atom()]</v> @@ -528,7 +528,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_variables() -> Result</name> + <name since="">which_variables() -> Result</name> <fsummary>Get all variables known to the agent</fsummary> <type> <v>Result = [atom()]</v> @@ -541,7 +541,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_notifications() -> Result</name> + <name since="">which_notifications() -> Result</name> <fsummary>Get all notifications known to the agent</fsummary> <type> <v>Result = [{Name, MibName, Info}]</v> @@ -557,15 +557,15 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>log_to_txt(LogDir)</name> - <name>log_to_txt(LogDir, Block | Mibs)</name> - <name>log_to_txt(LogDir, Mibs, Block | OutFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_txt(LogDir)</name> + <name since="">log_to_txt(LogDir, Block | Mibs)</name> + <name since="">log_to_txt(LogDir, Mibs, Block | OutFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> <fsummary>Convert an Audit Trail Log to text format</fsummary> <type> <v>LogDir = string()</v> @@ -600,14 +600,14 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>log_to_io(LogDir) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Block | Mibs) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Block | Mibs) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> <fsummary>Convert an Audit Trail Log to text format</fsummary> <type> <v>LogDir = string()</v> @@ -641,7 +641,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>change_log_size(NewSize) -> ok | {error, Reason}</name> + <name since="">change_log_size(NewSize) -> ok | {error, Reason}</name> <fsummary>Change the size of the Audit Trail Log</fsummary> <type> <v>NewSize = {MaxBytes, MaxFiles}</v> @@ -662,8 +662,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>set_log_type(NewType) -> {ok, OldType} | {error, Reason}</name> - <name>set_log_type(Agent, NewType) -> {ok, OldType} | {error, Reason}</name> + <name since="">set_log_type(NewType) -> {ok, OldType} | {error, Reason}</name> + <name since="">set_log_type(Agent, NewType) -> {ok, OldType} | {error, Reason}</name> <fsummary>Change the type of the Audit Trail Log</fsummary> <type> <v>NewType = OldType = atl_type()</v> @@ -684,8 +684,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>mib_of(Oid) -> {ok, MibName} | {error, Reason}</name> - <name>mib_of(Agent, Oid) -> {ok, MibName} | {error, Reason}</name> + <name since="">mib_of(Oid) -> {ok, MibName} | {error, Reason}</name> + <name since="">mib_of(Agent, Oid) -> {ok, MibName} | {error, Reason}</name> <fsummary>Which mib an Oid belongs to</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -704,8 +704,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>me_of(Oid) -> {ok, Me} | {error, Reason}</name> - <name>me_of(Agent, Oid) -> {ok, Me} | {error, Reason}</name> + <name since="">me_of(Oid) -> {ok, Me} | {error, Reason}</name> + <name since="">me_of(Agent, Oid) -> {ok, Me} | {error, Reason}</name> <fsummary>Retrieve the mib-entry of an Oid</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -724,8 +724,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>invalidate_mibs_cache() -> void()</name> - <name>invalidate_mibs_cache(Agent) -> void()</name> + <name since="">invalidate_mibs_cache() -> void()</name> + <name since="">invalidate_mibs_cache(Agent) -> void()</name> <fsummary>Invalidate the mib server cache</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -739,8 +739,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>enable_mibs_cache() -> void()</name> - <name>enable_mibs_cache(Agent) -> void()</name> + <name since="">enable_mibs_cache() -> void()</name> + <name since="">enable_mibs_cache(Agent) -> void()</name> <fsummary>Enable the mib server cache</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -753,8 +753,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>disable_mibs_cache() -> void()</name> - <name>disable_mibs_cache(Agent) -> void()</name> + <name since="">disable_mibs_cache() -> void()</name> + <name since="">disable_mibs_cache(Agent) -> void()</name> <fsummary>Disable the mib server cache</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -767,8 +767,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_mibs_cache_size() -> void()</name> - <name>which_mibs_cache_size(Agent) -> void()</name> + <name since="OTP R14B">which_mibs_cache_size() -> void()</name> + <name since="OTP R14B">which_mibs_cache_size(Agent) -> void()</name> <fsummary>The size of the mib server cache</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -781,12 +781,12 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>gc_mibs_cache() -> {ok, NumElementsGCed} | {error, Reason}</name> - <name>gc_mibs_cache(Agent) -> {ok, NumElementsGCed} | {error, Reason}</name> - <name>gc_mibs_cache(Age) -> {ok, NumElementsGCed} | {error, Reason}</name> - <name>gc_mibs_cache(Agent, Age) -> {ok, NumElementsGCed} | {error, Reason}</name> - <name>gc_mibs_cache(Age, GcLimit) -> {ok, NumElementsGCed} | {error, Reason}</name> - <name>gc_mibs_cache(Agent, Age, GcLimit) -> {ok, NumElementsGCed} | {error, Reason}</name> + <name since="">gc_mibs_cache() -> {ok, NumElementsGCed} | {error, Reason}</name> + <name since="">gc_mibs_cache(Agent) -> {ok, NumElementsGCed} | {error, Reason}</name> + <name since="">gc_mibs_cache(Age) -> {ok, NumElementsGCed} | {error, Reason}</name> + <name since="">gc_mibs_cache(Agent, Age) -> {ok, NumElementsGCed} | {error, Reason}</name> + <name since="">gc_mibs_cache(Age, GcLimit) -> {ok, NumElementsGCed} | {error, Reason}</name> + <name since="">gc_mibs_cache(Agent, Age, GcLimit) -> {ok, NumElementsGCed} | {error, Reason}</name> <fsummary>Perform mib server cache gc</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -808,8 +808,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>enable_mibs_cache_autogc() -> void()</name> - <name>enable_mibs_cache_autogc(Agent) -> void()</name> + <name since="">enable_mibs_cache_autogc() -> void()</name> + <name since="">enable_mibs_cache_autogc(Agent) -> void()</name> <fsummary>Enable automatic gc of the mib server cache</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -822,8 +822,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>disable_mibs_cache_autogc() -> void()</name> - <name>disable_mibs_cache_autogc(Agent) -> void()</name> + <name since="">disable_mibs_cache_autogc() -> void()</name> + <name since="">disable_mibs_cache_autogc(Agent) -> void()</name> <fsummary>Disable automatic gc of the mib server cache</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -836,8 +836,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>update_mibs_cache_age(NewAge) -> ok | {error, Reason}</name> - <name>update_mibs_cache_age(Agent, NewAge) -> ok | {error, Reason}</name> + <name since="">update_mibs_cache_age(NewAge) -> ok | {error, Reason}</name> + <name since="">update_mibs_cache_age(Agent, NewAge) -> ok | {error, Reason}</name> <fsummary>Change the mib server cache age property</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -852,8 +852,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>update_mibs_cache_gclimit(NewGcLimit) -> ok | {error, Reason}</name> - <name>update_mibs_cache_gclimit(Agent, NewGCLimit) -> ok | {error, Reason}</name> + <name since="">update_mibs_cache_gclimit(NewGcLimit) -> ok | {error, Reason}</name> + <name since="">update_mibs_cache_gclimit(Agent, NewGCLimit) -> ok | {error, Reason}</name> <fsummary>Change the mib server cache gclimit property</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -869,10 +869,10 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} <func> - <name>register_notification_filter(Id, Mod, Data) -> ok | {error, Reason}</name> - <name>register_notification_filter(Agent, Id, Mod, Data) -> ok | {error, Reason}</name> - <name>register_notification_filter(Id, Mod, Data, Where) -> ok | {error, Reason}</name> - <name>register_notification_filter(Agent, Id, Mod, Data, Where) -> ok | {error, Reason}</name> + <name since="">register_notification_filter(Id, Mod, Data) -> ok | {error, Reason}</name> + <name since="">register_notification_filter(Agent, Id, Mod, Data) -> ok | {error, Reason}</name> + <name since="">register_notification_filter(Id, Mod, Data, Where) -> ok | {error, Reason}</name> + <name since="">register_notification_filter(Agent, Id, Mod, Data, Where) -> ok | {error, Reason}</name> <fsummary>Register a notification filter</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -897,8 +897,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>unregister_notification_filter(Id) -> ok | {error, Reason}</name> - <name>unregister_notification_filter(Agent, Id) -> ok | {error, Reason}</name> + <name since="">unregister_notification_filter(Id) -> ok | {error, Reason}</name> + <name since="">unregister_notification_filter(Agent, Id) -> ok | {error, Reason}</name> <fsummary>Unregister a notification filter</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -913,8 +913,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>which_notification_filter() -> Filters</name> - <name>which_notification_filter(Agent) -> Filters</name> + <name since="">which_notification_filter() -> Filters</name> + <name since="">which_notification_filter(Agent) -> Filters</name> <fsummary>Which notification filter</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -929,8 +929,8 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>set_request_limit(NewLimit) -> {ok, OldLimit} | {error, Reason}</name> - <name>set_request_limit(Agent, NewLimit) -> {ok, OldLimit} | {error, Reason}</name> + <name since="">set_request_limit(NewLimit) -> {ok, OldLimit} | {error, Reason}</name> + <name since="">set_request_limit(Agent, NewLimit) -> {ok, OldLimit} | {error, Reason}</name> <fsummary>Change the request limit</fsummary> <type> <v>NewLimit = OldLimit = infinity | integer() >= 0</v> @@ -950,7 +950,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> - <name>register_subagent(Agent, SubTreeOid, Subagent) -> ok | {error, Reason}</name> + <name since="">register_subagent(Agent, SubTreeOid, Subagent) -> ok | {error, Reason}</name> <fsummary>Register a sub-agent under a sub-tree</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -975,7 +975,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>unregister_subagent(Agent, SubagentOidOrPid) -> ok | {ok, SubAgentPid} | {error, Reason}</name> + <name since="">unregister_subagent(Agent, SubagentOidOrPid) -> ok | {ok, SubAgentPid} | {error, Reason}</name> <fsummary>Unregister a sub-agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -992,7 +992,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). <func> - <name>send_notification2(Agent, Notification, SendOpts) -> void()</name> + <name since="OTP R14B03">send_notification2(Agent, Notification, SendOpts) -> void()</name> <fsummary>Send notification</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -1119,11 +1119,11 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). <func> - <name>send_notification(Agent, Notification, Receiver)</name> - <name>send_notification(Agent, Notification, Receiver, Varbinds)</name> - <name>send_notification(Agent, Notification, Receiver, NotifyName, Varbinds)</name> - <name>send_notification(Agent, Notification, Receiver, NotifyName, ContextName, Varbinds) -> void() </name> - <name>send_notification(Agent, Notification, Receiver, NotifyName, ContextName, Varbinds, LocalEngineID) -> void() </name> + <name since="">send_notification(Agent, Notification, Receiver)</name> + <name since="">send_notification(Agent, Notification, Receiver, Varbinds)</name> + <name since="">send_notification(Agent, Notification, Receiver, NotifyName, Varbinds)</name> + <name since="">send_notification(Agent, Notification, Receiver, NotifyName, ContextName, Varbinds) -> void() </name> + <name since="OTP R14B">send_notification(Agent, Notification, Receiver, NotifyName, ContextName, Varbinds, LocalEngineID) -> void() </name> <fsummary>Send a notification</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -1324,13 +1324,13 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). <func> - <name>discovery(TargetName, Notification) -> {ok, ManagerEngineID} | {error, Reason}</name> - <name>discovery(TargetName, Notification, Varbinds) -> {ok, ManagerEngineID} | {error, Reason}</name> - <name>discovery(TargetName, Notification, DiscoHandler) -> {ok, ManagerEngineID} | {error, Reason}</name> - <name>discovery(TargetName, Notification, ContextName, Varbinds) -> {ok, ManagerEngineID} | {error, Reason}</name> - <name>discovery(TargetName, Notification, Varbinds, DiscoHandler) -> {ok, ManagerEngineID} | {error, Reason}</name> - <name>discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler) -> {ok, ManagerEngineID} | {error, Reason}</name> - <name>discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler, ExtraInfo) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification, Varbinds) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification, DiscoHandler) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification, ContextName, Varbinds) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification, Varbinds, DiscoHandler) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler) -> {ok, ManagerEngineID} | {error, Reason}</name> + <name since="">discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler, ExtraInfo) -> {ok, ManagerEngineID} | {error, Reason}</name> <fsummary>Initiate the discovery process with a manager</fsummary> <type> <v>TargetName = string()</v> @@ -1379,7 +1379,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>convert_config(OldConfig) -> AgentConfig</name> + <name since="">convert_config(OldConfig) -> AgentConfig</name> <fsummary>Convert old snmp config to new agent config</fsummary> <type> <v>OldConfig = list()</v> @@ -1403,8 +1403,8 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>restart_worker() -> void()</name> - <name>restart_worker(Agent) -> void()</name> + <name since="">restart_worker() -> void()</name> + <name since="">restart_worker(Agent) -> void()</name> <fsummary>Restart the worker process of a multi-threaded agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -1419,8 +1419,8 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>restart_set_worker() -> void()</name> - <name>restart_set_worker(Agent) -> void()</name> + <name since="">restart_set_worker() -> void()</name> + <name since="">restart_set_worker(Agent) -> void()</name> <fsummary>Restart the set worker process of a multi-threaded agent</fsummary> <type> <v>Agent = pid() | atom()</v> @@ -1435,7 +1435,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>print_mib_info() -> void()</name> + <name since="OTP R14B02">print_mib_info() -> void()</name> <fsummary>Print mib info</fsummary> <desc> <p>Prints the content of all the (snmp) tables and variables @@ -1446,7 +1446,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>print_mib_tables() -> void()</name> + <name since="OTP R14B02">print_mib_tables() -> void()</name> <fsummary>Print mib tables</fsummary> <desc> <p>Prints the content of all the (snmp) tables @@ -1457,7 +1457,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>print_mib_variables() -> void()</name> + <name since="OTP R14B02">print_mib_variables() -> void()</name> <fsummary>Print mib variables</fsummary> <desc> <p>Prints the content of all the (snmp) variables @@ -1468,7 +1468,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </func> <func> - <name>verbosity(Ref,Verbosity) -> void()</name> + <name since="">verbosity(Ref,Verbosity) -> void()</name> <fsummary>Assign a new verbosity for the process</fsummary> <type> <v>Ref = pid() | sub_agents | master_agent | net_if | mib_server | symbolic_store | note_store | local_db</v> diff --git a/lib/snmp/doc/src/snmpa_conf.xml b/lib/snmp/doc/src/snmpa_conf.xml index 503e44a6a2..4134a81c0c 100644 --- a/lib/snmp/doc/src/snmpa_conf.xml +++ b/lib/snmp/doc/src/snmpa_conf.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_conf.xml</file> </header> - <module>snmpa_conf</module> + <module since="">snmpa_conf</module> <modulesummary>Utility functions for handling the agent config files.</modulesummary> <description> <p>The module <c>snmpa_conf</c> contains various utility functions to @@ -92,7 +92,7 @@ word() = 0..65535 <funcs> <func> - <name>agent_entry(Tag, Val) -> agent_entry()</name> + <name since="">agent_entry(Tag, Val) -> agent_entry()</name> <fsummary>Create an agent entry</fsummary> <type> <v>Tag = intAgentTransports | intAgentUDPPort | intAgentMaxPacketSize | snmpEngineMaxMessageSize | snmpEngineID</v> @@ -111,8 +111,8 @@ word() = 0..65535 </func> <func> - <name>write_agent_config(Dir, Conf) -> ok</name> - <name>write_agent_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_agent_config(Dir, Conf) -> ok</name> + <name since="">write_agent_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -134,7 +134,7 @@ word() = 0..65535 </func> <func> - <name>append_agent_config(Dir, Conf) -> ok</name> + <name since="">append_agent_config(Dir, Conf) -> ok</name> <fsummary>Append the agent config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -153,7 +153,7 @@ word() = 0..65535 </func> <func> - <name>read_agent_config(Dir) -> Conf</name> + <name since="">read_agent_config(Dir) -> Conf</name> <fsummary>Read the agent config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -172,7 +172,7 @@ word() = 0..65535 </func> <func> - <name>standard_entry(Tag, Val) -> standard_entry()</name> + <name since="">standard_entry(Tag, Val) -> standard_entry()</name> <fsummary>Create an standard entry</fsummary> <type> <v>Tag = sysDescr | sysObjectID | sysContact | sysName | sysLocation | sysServices | snmpEnableAuthenTraps</v> @@ -192,8 +192,8 @@ word() = 0..65535 </func> <func> - <name>write_standard_config(Dir, Conf) -> ok</name> - <name>write_standard_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_standard_config(Dir, Conf) -> ok</name> + <name since="">write_standard_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent standard config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -216,7 +216,7 @@ word() = 0..65535 </func> <func> - <name>append_standard_config(Dir, Conf) -> ok</name> + <name since="">append_standard_config(Dir, Conf) -> ok</name> <fsummary>Append the agent standard config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -236,7 +236,7 @@ word() = 0..65535 </func> <func> - <name>read_standard_config(Dir) -> Conf</name> + <name since="">read_standard_config(Dir) -> Conf</name> <fsummary>Read the agent standard config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -255,7 +255,7 @@ word() = 0..65535 </func> <func> - <name>context_entry(Context) -> context_entry()</name> + <name since="">context_entry(Context) -> context_entry()</name> <fsummary>Create an context entry</fsummary> <type> <v>Context = string()</v> @@ -273,8 +273,8 @@ word() = 0..65535 </func> <func> - <name>write_context_config(Dir, Conf) -> ok</name> - <name>write_context_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_context_config(Dir, Conf) -> ok</name> + <name since="">write_context_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent context(s) to the config file</fsummary> <type> <v>Dir = string()</v> @@ -297,7 +297,7 @@ word() = 0..65535 </func> <func> - <name>append_context_config(Dir, Conf) -> ok</name> + <name since="">append_context_config(Dir, Conf) -> ok</name> <fsummary>Append the agent context(s) to the config file</fsummary> <type> <v>Dir = string()</v> @@ -317,7 +317,7 @@ word() = 0..65535 </func> <func> - <name>read_context_config(Dir) -> Conf</name> + <name since="">read_context_config(Dir) -> Conf</name> <fsummary>Read the agent context config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -336,8 +336,8 @@ word() = 0..65535 </func> <func> - <name>community_entry(CommunityIndex) -> community_entry()</name> - <name>community_entry(CommunityIndex, CommunityName, SecName, ContextName, TransportTag) -> community_entry()</name> + <name since="">community_entry(CommunityIndex) -> community_entry()</name> + <name since="">community_entry(CommunityIndex, CommunityName, SecName, ContextName, TransportTag) -> community_entry()</name> <fsummary>Create an community entry</fsummary> <type> <v>CommunityIndex = string()</v> @@ -364,8 +364,8 @@ word() = 0..65535 </func> <func> - <name>write_community_config(Dir, Conf) -> ok</name> - <name>write_community_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_community_config(Dir, Conf) -> ok</name> + <name since="">write_community_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent community config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -388,7 +388,7 @@ word() = 0..65535 </func> <func> - <name>append_community_config(Dir, Conf) -> ok</name> + <name since="">append_community_config(Dir, Conf) -> ok</name> <fsummary>Append the agent community config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -408,7 +408,7 @@ word() = 0..65535 </func> <func> - <name>read_community_config(Dir) -> Conf</name> + <name since="">read_community_config(Dir) -> Conf</name> <fsummary>Read the agent community config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -427,10 +427,10 @@ word() = 0..65535 </func> <func> - <name>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId) -> target_addr_entry()</name> - <name>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask) -> target_addr_entry()</name> - <name>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> - <name>target_addr_entry(Name, Domain, Addr, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> + <name since="">target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId) -> target_addr_entry()</name> + <name since="OTP 17.3">target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask) -> target_addr_entry()</name> + <name since="">target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> + <name since="">target_addr_entry(Name, Domain, Addr, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> <fsummary>Create an target_addr entry</fsummary> <type> <v>Name = string()</v> @@ -464,8 +464,8 @@ word() = 0..65535 </func> <func> - <name>write_target_addr_config(Dir, Conf) -> ok</name> - <name>write_target_addr_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_target_addr_config(Dir, Conf) -> ok</name> + <name since="">write_target_addr_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent target_addr config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -488,7 +488,7 @@ word() = 0..65535 </func> <func> - <name>append_target_addr_config(Dir, Conf) -> ok</name> + <name since="">append_target_addr_config(Dir, Conf) -> ok</name> <fsummary>Append the agent target_addr config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -508,7 +508,7 @@ word() = 0..65535 </func> <func> - <name>read_target_addr_config(Dir) -> Conf</name> + <name since="">read_target_addr_config(Dir) -> Conf</name> <fsummary>Read the agent target_addr config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -527,9 +527,9 @@ word() = 0..65535 </func> <func> - <name>target_params_entry(Name, Vsn) -> target_params_entry()</name> - <name>target_params_entry(Name, Vsn, SecName, SecLevel) -> target_params_entry()</name> - <name>target_params_entry(Name, MPModel, SecModel, SecName, SecLevel) -> target_params_entry()</name> + <name since="">target_params_entry(Name, Vsn) -> target_params_entry()</name> + <name since="">target_params_entry(Name, Vsn, SecName, SecLevel) -> target_params_entry()</name> + <name since="">target_params_entry(Name, MPModel, SecModel, SecName, SecLevel) -> target_params_entry()</name> <fsummary>Create an target_params entry</fsummary> <type> <v>Name = string()</v> @@ -564,8 +564,8 @@ word() = 0..65535 </func> <func> - <name>write_target_params_config(Dir, Conf) -> ok</name> - <name>write_target_params_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_target_params_config(Dir, Conf) -> ok</name> + <name since="">write_target_params_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent target_params config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -588,7 +588,7 @@ word() = 0..65535 </func> <func> - <name>append_target_params_config(Dir, Conf) -> ok</name> + <name since="">append_target_params_config(Dir, Conf) -> ok</name> <fsummary>Append the agent target_params config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -608,7 +608,7 @@ word() = 0..65535 </func> <func> - <name>read_target_params_config(Dir) -> Conf</name> + <name since="">read_target_params_config(Dir) -> Conf</name> <fsummary>Read the agent target_params config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -627,10 +627,10 @@ word() = 0..65535 </func> <func> - <name>vacm_s2g_entry(SecModel, SecName, GroupName) -> vacm_s2g_entry()</name> - <name>vacm_acc_entry(GroupName, Prefix, SecModel, SecLevel, Match, ReadView, WriteView, NotifyView) -> vacm_acc_entry()</name> - <name>vacm_vtf_entry(ViewIndex, ViewSubtree) -> vacm_vtf_entry()</name> - <name>vacm_vtf_entry(ViewIndex, ViewSubtree, ViewStatus, ViewMask) -> vacm_vtf_entry()</name> + <name since="">vacm_s2g_entry(SecModel, SecName, GroupName) -> vacm_s2g_entry()</name> + <name since="">vacm_acc_entry(GroupName, Prefix, SecModel, SecLevel, Match, ReadView, WriteView, NotifyView) -> vacm_acc_entry()</name> + <name since="">vacm_vtf_entry(ViewIndex, ViewSubtree) -> vacm_vtf_entry()</name> + <name since="">vacm_vtf_entry(ViewIndex, ViewSubtree, ViewStatus, ViewMask) -> vacm_vtf_entry()</name> <fsummary>Create an vacm entry</fsummary> <type> <v>SecModel = v1 | v2c | usm</v> @@ -665,8 +665,8 @@ word() = 0..65535 </func> <func> - <name>write_vacm_config(Dir, Conf) -> ok</name> - <name>write_vacm_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_vacm_config(Dir, Conf) -> ok</name> + <name since="">write_vacm_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent vacm config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -690,7 +690,7 @@ word() = 0..65535 </func> <func> - <name>append_vacm_config(Dir, Conf) -> ok</name> + <name since="">append_vacm_config(Dir, Conf) -> ok</name> <fsummary>Append the agent vacm config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -710,7 +710,7 @@ word() = 0..65535 </func> <func> - <name>read_vacm_config(Dir) -> Conf</name> + <name since="">read_vacm_config(Dir) -> Conf</name> <fsummary>Read the agent vacm config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -729,8 +729,8 @@ word() = 0..65535 </func> <func> - <name>usm_entry(EngineId) -> usm_entry()</name> - <name>usm_entry(EngineID, UserName, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey) -> usm_entry()</name> + <name since="">usm_entry(EngineId) -> usm_entry()</name> + <name since="">usm_entry(EngineID, UserName, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey) -> usm_entry()</name> <fsummary>Create an usm entry</fsummary> <type> <v>EngineId = string()</v> @@ -762,8 +762,8 @@ word() = 0..65535 </func> <func> - <name>write_usm_config(Dir, Conf) -> ok</name> - <name>write_usm_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_usm_config(Dir, Conf) -> ok</name> + <name since="">write_usm_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent usm config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -786,7 +786,7 @@ word() = 0..65535 </func> <func> - <name>append_usm_config(Dir, Conf) -> ok</name> + <name since="">append_usm_config(Dir, Conf) -> ok</name> <fsummary>Append the agent usm config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -805,7 +805,7 @@ word() = 0..65535 </func> <func> - <name>read_usm_config(Dir) -> Conf</name> + <name since="">read_usm_config(Dir) -> Conf</name> <fsummary>Read the agent usm config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -824,7 +824,7 @@ word() = 0..65535 </func> <func> - <name>notify_entry(Name, Tag, Type) -> notify_entry()</name> + <name since="">notify_entry(Name, Tag, Type) -> notify_entry()</name> <fsummary>Create an notify entry</fsummary> <type> <v>Name = string()</v> @@ -845,8 +845,8 @@ word() = 0..65535 </func> <func> - <name>write_notify_config(Dir, Conf) -> ok</name> - <name>write_notify_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_notify_config(Dir, Conf) -> ok</name> + <name since="">write_notify_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the agent notify config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -869,7 +869,7 @@ word() = 0..65535 </func> <func> - <name>append_notify_config(Dir, Conf) -> ok</name> + <name since="">append_notify_config(Dir, Conf) -> ok</name> <fsummary>Append the agent notify config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -889,7 +889,7 @@ word() = 0..65535 </func> <func> - <name>read_notify_config(Dir) -> Conf</name> + <name since="">read_notify_config(Dir) -> Conf</name> <fsummary>Read the agent notify config from the config file</fsummary> <type> <v>Dir = string()</v> diff --git a/lib/snmp/doc/src/snmpa_discovery_handler.xml b/lib/snmp/doc/src/snmpa_discovery_handler.xml index 0ea72a880c..21b8746c11 100644 --- a/lib/snmp/doc/src/snmpa_discovery_handler.xml +++ b/lib/snmp/doc/src/snmpa_discovery_handler.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_discovery_handler.xml</file> </header> - <module>snmpa_discovery_handler</module> + <module since="">snmpa_discovery_handler</module> <modulesummary>Behaviour module for the SNMP agent discovery handler.</modulesummary> <description> <p>This module defines the behaviour of the agent discovery @@ -51,7 +51,7 @@ <funcs> <func> - <name>stage1_finish(TargetName, ManagerEngineID, ExtraInfo) -> ignore | {ok, usm_entry() | [usm_entry()]} | {ok, usm_entry() | [usm_entry()], NewExtraInfo}</name> + <name since="">stage1_finish(TargetName, ManagerEngineID, ExtraInfo) -> ignore | {ok, usm_entry() | [usm_entry()]} | {ok, usm_entry() | [usm_entry()], NewExtraInfo}</name> <fsummary>Discovery stage 1 finish</fsummary> <type> <v>TargetName = string()</v> diff --git a/lib/snmp/doc/src/snmpa_error.xml b/lib/snmp/doc/src/snmpa_error.xml index 7cc4a3513d..6e6761b7a5 100644 --- a/lib/snmp/doc/src/snmpa_error.xml +++ b/lib/snmp/doc/src/snmpa_error.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_error.xml</file> </header> - <module>snmpa_error</module> + <module since="">snmpa_error</module> <modulesummary>Functions for Reporting SNMP Errors</modulesummary> <description> <marker id="desc"></marker> @@ -57,7 +57,7 @@ </description> <funcs> <func> - <name>config_err(Format, Args) -> void()</name> + <name since="">config_err(Format, Args) -> void()</name> <fsummary>Called if a configuration error occurs</fsummary> <type> <v>Format = string()</v> @@ -76,7 +76,7 @@ </func> <func> - <name>user_err(Format, Args) -> void()</name> + <name since="">user_err(Format, Args) -> void()</name> <fsummary>Called if a user related error occurs</fsummary> <type> <v>Format = string()</v> diff --git a/lib/snmp/doc/src/snmpa_error_io.xml b/lib/snmp/doc/src/snmpa_error_io.xml index bcb2688646..d78e09ff13 100644 --- a/lib/snmp/doc/src/snmpa_error_io.xml +++ b/lib/snmp/doc/src/snmpa_error_io.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_error_io.xml</file> </header> - <module>snmpa_error_io</module> + <module since="">snmpa_error_io</module> <modulesummary>Functions for Reporting SNMP Errors on stdio</modulesummary> <description> <p>The module <c>snmpa_error_io</c> implements the @@ -52,7 +52,7 @@ </description> <funcs> <func> - <name>config_err(Format, Args) -> void()</name> + <name since="">config_err(Format, Args) -> void()</name> <fsummary>Called if a configuration error occurs</fsummary> <type> <v>Format = string()</v> @@ -68,7 +68,7 @@ </desc> </func> <func> - <name>user_err(Format, Args) -> void()</name> + <name since="">user_err(Format, Args) -> void()</name> <fsummary>Called if a user related error occurs</fsummary> <type> <v>Format = string()</v> diff --git a/lib/snmp/doc/src/snmpa_error_logger.xml b/lib/snmp/doc/src/snmpa_error_logger.xml index 4feb2e7f32..b0565a6839 100644 --- a/lib/snmp/doc/src/snmpa_error_logger.xml +++ b/lib/snmp/doc/src/snmpa_error_logger.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_error_logger.xml</file> </header> - <module>snmpa_error_logger</module> + <module since="">snmpa_error_logger</module> <modulesummary>Functions for Reporting SNMP Errors through the error_logger</modulesummary> <description> <p>The module <c>snmpa_error_logger</c> implements the @@ -54,7 +54,7 @@ </description> <funcs> <func> - <name>config_err(Format, Args) -> void()</name> + <name since="">config_err(Format, Args) -> void()</name> <fsummary>Called if a configuration error occurs</fsummary> <type> <v>Format = string()</v> @@ -70,7 +70,7 @@ </desc> </func> <func> - <name>user_err(Format, Args) -> void()</name> + <name since="">user_err(Format, Args) -> void()</name> <fsummary>Called if a user related error occurs</fsummary> <type> <v>Format = string()</v> diff --git a/lib/snmp/doc/src/snmpa_error_report.xml b/lib/snmp/doc/src/snmpa_error_report.xml index 282d9b4e59..f08dc1df23 100644 --- a/lib/snmp/doc/src/snmpa_error_report.xml +++ b/lib/snmp/doc/src/snmpa_error_report.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_error_report.xml</file> </header> - <module>snmpa_error_report</module> + <module since="">snmpa_error_report</module> <modulesummary>Behaviour module for reporting SNMP agent errors</modulesummary> <description> <marker id="desc"></marker> @@ -52,7 +52,7 @@ </description> <funcs> <func> - <name>config_err(Format, Args) -> void()</name> + <name since="">config_err(Format, Args) -> void()</name> <fsummary>Called if a configuration error occurs</fsummary> <type> <v>Format = string()</v> @@ -68,7 +68,7 @@ </desc> </func> <func> - <name>user_err(Format, Args) -> void()</name> + <name since="">user_err(Format, Args) -> void()</name> <fsummary>Called if a user related error occurs</fsummary> <type> <v>Format = string()</v> diff --git a/lib/snmp/doc/src/snmpa_local_db.xml b/lib/snmp/doc/src/snmpa_local_db.xml index ac8d466ab3..229f22ab70 100644 --- a/lib/snmp/doc/src/snmpa_local_db.xml +++ b/lib/snmp/doc/src/snmpa_local_db.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_local_db.xml</file> </header> - <module>snmpa_local_db</module> + <module since="">snmpa_local_db</module> <modulesummary>The SNMP built-in database</modulesummary> <description> <p>The module <c>snmpa_local_db</c> contains functions for @@ -86,7 +86,7 @@ </section> <funcs> <func> - <name>dump() -> ok | {error, Reason}</name> + <name since="">dump() -> ok | {error, Reason}</name> <fsummary>Dump the database to disk</fsummary> <type> <v>Reason = term()</v> @@ -97,7 +97,7 @@ </desc> </func> <func> - <name>match(NameDb, Pattern)</name> + <name since="">match(NameDb, Pattern)</name> <fsummary>Perform a match on the table</fsummary> <desc> <p>Performs an ets/dets matching on the table. @@ -106,9 +106,9 @@ </desc> </func> <func> - <name>print()</name> - <name>print(TableName)</name> - <name>print(TableName, Db)</name> + <name since="">print()</name> + <name since="">print(TableName)</name> + <name since="">print(TableName, Db)</name> <fsummary>Print the database to screen</fsummary> <type> <v>TableName = atom()</v> @@ -124,7 +124,7 @@ </desc> </func> <func> - <name>table_create(NameDb) -> bool()</name> + <name since="">table_create(NameDb) -> bool()</name> <fsummary>Create a table</fsummary> <desc> <p>Creates a table. If the table already exist, the old copy @@ -135,7 +135,7 @@ </desc> </func> <func> - <name>table_create_row(NameDb, RowIndex, Row) -> bool()</name> + <name since="">table_create_row(NameDb, RowIndex, Row) -> bool()</name> <fsummary>Create a row in a table</fsummary> <type> <v>Row = {Val1, Val2, ..., ValN}</v> @@ -147,28 +147,28 @@ </desc> </func> <func> - <name>table_delete(NameDb) -> void()</name> + <name since="">table_delete(NameDb) -> void()</name> <fsummary>Delete a table</fsummary> <desc> <p>Deletes a table.</p> </desc> </func> <func> - <name>table_delete_row(NameDb, RowIndex) -> bool()</name> + <name since="">table_delete_row(NameDb, RowIndex) -> bool()</name> <fsummary>Delete the row in the table</fsummary> <desc> <p>Deletes the row in the table.</p> </desc> </func> <func> - <name>table_exists(NameDb) -> bool()</name> + <name since="">table_exists(NameDb) -> bool()</name> <fsummary>Check if a table exists</fsummary> <desc> <p>Checks if a table exists.</p> </desc> </func> <func> - <name>table_get_row(NameDb, RowIndex) -> Row | undefined</name> + <name since="">table_get_row(NameDb, RowIndex) -> Row | undefined</name> <fsummary>Get a row from the table</fsummary> <type> <v>Row = {Val1, Val2, ..., ValN}</v> diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml index 1a73c9b634..b849a2826a 100644 --- a/lib/snmp/doc/src/snmpa_mib_data.xml +++ b/lib/snmp/doc/src/snmpa_mib_data.xml @@ -30,7 +30,7 @@ <file>snmpa_mib_data.xml</file> </header> - <module>snmpa_mib_data</module> + <module since="OTP R16B01">snmpa_mib_data</module> <modulesummary>Behaviour module for the SNMP agent mib-server data module.</modulesummary> <description> @@ -108,7 +108,7 @@ <funcs> <func> - <name>Module:new(Storage) -> State</name> + <name since="OTP R16B01">Module:new(Storage) -> State</name> <fsummary>Create new (mib-server) data instance</fsummary> <type> <v>Storage = mib_storage()</v> @@ -122,7 +122,7 @@ </func> <func> - <name>Module:close(State) -> void()</name> + <name since="OTP R16B01">Module:close(State) -> void()</name> <fsummary>Close the mib-server data instance</fsummary> <type> <v>State = term()</v> @@ -135,7 +135,7 @@ </func> <func> - <name>Module:sync(State) -> void()</name> + <name since="OTP R16B01">Module:sync(State) -> void()</name> <fsummary>Synchronize to disc</fsummary> <type> <v>State = term()</v> @@ -151,7 +151,7 @@ </func> <func> - <name>Module:load_mib(State, Filename, MeOverride, TeOverride) -> {ok, NewState} | {error, Reason}</name> + <name since="OTP R16B01">Module:load_mib(State, Filename, MeOverride, TeOverride) -> {ok, NewState} | {error, Reason}</name> <fsummary>Load a mib into the mib-server</fsummary> <type> <v>State = NewState = term()</v> @@ -172,7 +172,7 @@ </func> <func> - <name>Module:unload_mib(State, Filename) -> {ok, NewState} | {error, Reason}</name> + <name since="OTP R16B01">Module:unload_mib(State, Filename) -> {ok, NewState} | {error, Reason}</name> <fsummary>Unload mib from the mib-server</fsummary> <type> <v>State = NewState = term()</v> @@ -188,7 +188,7 @@ </func> <func> - <name>Module:lookup(State, Oid) -> Reply</name> + <name since="OTP R16B01">Module:lookup(State, Oid) -> Reply</name> <fsummary>Find the mib-entry corresponding to the Oid</fsummary> <type> <v>State = term()</v> @@ -210,7 +210,7 @@ </func> <func> - <name>Module:next(State, Oid, MibView) -> Reply</name> + <name since="OTP R16B01">Module:next(State, Oid, MibView) -> Reply</name> <fsummary>Finds the lexicographically next oid</fsummary> <type> <v>State = term()</v> @@ -227,7 +227,7 @@ </func> <func> - <name>Module:register_subagent(State, Oid, Pid) -> Reply</name> + <name since="OTP R16B01">Module:register_subagent(State, Oid, Pid) -> Reply</name> <fsummary>Register the subagent</fsummary> <type> <v>State = NewState = term()</v> @@ -245,7 +245,7 @@ </func> <func> - <name>Module:unregister_subagent(State, PidOrOid) -> Reply</name> + <name since="OTP R16B01">Module:unregister_subagent(State, PidOrOid) -> Reply</name> <fsummary>Unregister the subagent</fsummary> <type> <v>State = NewState = term()</v> @@ -266,7 +266,7 @@ </func> <func> - <name>Module:dump(State, Destination) -> Reply</name> + <name since="OTP R16B01">Module:dump(State, Destination) -> Reply</name> <fsummary>Unregister the subagent</fsummary> <type> <v>State = term()</v> @@ -284,7 +284,7 @@ </func> <func> - <name>Module:which_mib(State, Oid) -> Reply</name> + <name since="OTP R16B01">Module:which_mib(State, Oid) -> Reply</name> <fsummary>Retrieve the mib file for an oid()</fsummary> <type> <v>State = term()</v> @@ -301,7 +301,7 @@ </func> <func> - <name>Module:which_mibs(State) -> Reply</name> + <name since="OTP R16B01">Module:which_mibs(State) -> Reply</name> <fsummary>Retrieve all loaded mib files</fsummary> <type> <v>State = term()</v> @@ -317,7 +317,7 @@ </func> <func> - <name>Module:whereis_mib(State, MibName) -> Reply</name> + <name since="OTP R16B01">Module:whereis_mib(State, MibName) -> Reply</name> <fsummary>Retrieve the mib file for the mib</fsummary> <type> <v>State = term()</v> @@ -334,7 +334,7 @@ </func> <func> - <name>Module:info(State) -> Reply</name> + <name since="OTP R16B01">Module:info(State) -> Reply</name> <fsummary>Retrieve misc info for the mib data</fsummary> <type> <v>State = term()</v> @@ -352,7 +352,7 @@ </func> <func> - <name>Module:backup(State, BackupDir) -> Reply</name> + <name since="OTP R16B01">Module:backup(State, BackupDir) -> Reply</name> <fsummary>Perform a backup of the mib-server data</fsummary> <type> <v>State = term()</v> @@ -370,7 +370,7 @@ </func> <func> - <name>Module:code_change(Destination, Vsn, Extra, State) -> NewState</name> + <name since="OTP R16B01">Module:code_change(Destination, Vsn, Extra, State) -> NewState</name> <fsummary>Perform a code-change</fsummary> <type> <v>Destination = up | down</v> diff --git a/lib/snmp/doc/src/snmpa_mib_storage.xml b/lib/snmp/doc/src/snmpa_mib_storage.xml index 58ce2167ec..ee2b009e77 100644 --- a/lib/snmp/doc/src/snmpa_mib_storage.xml +++ b/lib/snmp/doc/src/snmpa_mib_storage.xml @@ -30,7 +30,7 @@ <file>snmpa_mib_storage.xml</file> </header> - <module>snmpa_mib_storage</module> + <module since="OTP R16B01">snmpa_mib_storage</module> <modulesummary> Behaviour module for the SNMP agent mib storage. </modulesummary> @@ -96,7 +96,7 @@ <funcs> <func> - <name>Module:open(Name, RecordName, Fields, Type, Options) -> {ok, TabId} | {error, Reason}</name> + <name since="OTP R16B01">Module:open(Name, RecordName, Fields, Type, Options) -> {ok, TabId} | {error, Reason}</name> <fsummary>Create new (mib-server) data instance</fsummary> <type> <v>Name = atom()</v> @@ -122,7 +122,7 @@ </func> <func> - <name>Module:close(TabId) -> void()</name> + <name since="OTP R16B01">Module:close(TabId) -> void()</name> <fsummary>Close the mib-storage table</fsummary> <type> <v>State = term()</v> @@ -135,7 +135,7 @@ </func> <func> - <name>Module:read(TabId, Key) -> false | {value, Record}</name> + <name since="OTP R16B01">Module:read(TabId, Key) -> false | {value, Record}</name> <fsummary>Read a record from the mib-storage table</fsummary> <type> <v>TabId = term()</v> @@ -150,7 +150,7 @@ </func> <func> - <name>Module:write(TabId, Record) -> ok | {error, Reason}</name> + <name since="OTP R16B01">Module:write(TabId, Record) -> ok | {error, Reason}</name> <fsummary>Write a record to the mib-storage table</fsummary> <type> <v>TabId = term()</v> @@ -165,7 +165,7 @@ </func> <func> - <name>Module:delete(TabId) -> void()</name> + <name since="OTP R16B01">Module:delete(TabId) -> void()</name> <fsummary>Delete an entire mib-storage table</fsummary> <type> <v>TabId = term()</v> @@ -178,7 +178,7 @@ </func> <func> - <name>Module:delete(TabId, Key) -> ok | {error, Reason}</name> + <name since="OTP R16B01">Module:delete(TabId, Key) -> ok | {error, Reason}</name> <fsummary>Delete a record from the mib-storage table</fsummary> <type> <v>TabId = term()</v> @@ -193,7 +193,7 @@ </func> <func> - <name>Module:match_object(TabId, Pattern) -> {ok, Recs} | {error, Reason}</name> + <name since="OTP R16B01">Module:match_object(TabId, Pattern) -> {ok, Recs} | {error, Reason}</name> <fsummary>Search the mib-storage table for record matching pattern</fsummary> <type> <v>TabId = term()</v> @@ -210,7 +210,7 @@ </func> <func> - <name>Module:match_delete(TabId, Pattern) -> {ok, Recs} | {error, Reason}</name> + <name since="OTP R16B01">Module:match_delete(TabId, Pattern) -> {ok, Recs} | {error, Reason}</name> <fsummary>Delete records in the mib-storage table matching pattern</fsummary> <type> <v>TabId = term()</v> @@ -228,7 +228,7 @@ </func> <func> - <name>Module:tab2list(TabId) -> Recs</name> + <name since="OTP R16B01">Module:tab2list(TabId) -> Recs</name> <fsummary>Return all records of the mib-storage table</fsummary> <type> <v>TabId = term()</v> @@ -243,7 +243,7 @@ </func> <func> - <name>Module:info(TabId) -> {ok, Info} | {error, Reason}</name> + <name since="OTP R16B01">Module:info(TabId) -> {ok, Info} | {error, Reason}</name> <fsummary>Returns information about the mib-storage table. </fsummary> <type> <v>TabId = term()</v> @@ -259,7 +259,7 @@ </func> <func> - <name>Module:sync(TabId) -> void()</name> + <name since="OTP R16B01">Module:sync(TabId) -> void()</name> <fsummary>Synchronize mib-storage table</fsummary> <type> <v>TabId = term()</v> @@ -273,7 +273,7 @@ </func> <func> - <name>Module:backup(TabId, BackupDir) -> ok | {error, Reason}</name> + <name since="OTP R16B01">Module:backup(TabId, BackupDir) -> ok | {error, Reason}</name> <fsummary>Perform a backup of the mib-storage table</fsummary> <type> <v>TabId = term()</v> diff --git a/lib/snmp/doc/src/snmpa_mpd.xml b/lib/snmp/doc/src/snmpa_mpd.xml index a39c087c20..d76a9c4a94 100644 --- a/lib/snmp/doc/src/snmpa_mpd.xml +++ b/lib/snmp/doc/src/snmpa_mpd.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_mpd.xml</file> </header> - <module>snmpa_mpd</module> + <module since="">snmpa_mpd</module> <modulesummary>Message Processing and Dispatch module for the SNMP agent</modulesummary> <description> <p>The module <c>snmpa_mpd</c> implements the version independent @@ -52,7 +52,7 @@ <funcs> <func> - <name>init(Vsns) -> mpd_state()</name> + <name since="">init(Vsns) -> mpd_state()</name> <fsummary>Initialize the MPD module</fsummary> <type> <v>Vsns = [Vsn]</v> @@ -70,8 +70,8 @@ </func> <func> - <name>process_packet(Packet, From, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name> - <name>process_packet(Packet, From, LocalEngineID, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name> + <name since="OTP 17.3">process_packet(Packet, From, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name> + <name since="OTP R14B">process_packet(Packet, From, LocalEngineID, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name> <fsummary>Process a packet received from the network</fsummary> <type> <v>Packet = binary()</v> @@ -108,8 +108,8 @@ </func> <func> - <name>generate_response_msg(Vsn, RePdu, Type, ACMData, Log) -> {ok, Packet} | {discarded, Reason}</name> - <name>generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log) -> {ok, Packet} | {discarded, Reason}</name> + <name since="OTP R14B">generate_response_msg(Vsn, RePdu, Type, ACMData, Log) -> {ok, Packet} | {discarded, Reason}</name> + <name since="OTP R14B">generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log) -> {ok, Packet} | {discarded, Reason}</name> <fsummary>Generate a response packet to be sent to the network</fsummary> <type> <v>Vsn = 'version-1' | 'version-2' | 'version-3'</v> @@ -136,8 +136,8 @@ </func> <func> - <name>generate_msg(Vsn, NoteStore, Pdu, MsgData, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name> - <name>generate_msg(Vsn, NoteStore, Pdu, MsgData, LocalEngineID, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name> + <name since="OTP R14B">generate_msg(Vsn, NoteStore, Pdu, MsgData, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name> + <name since="OTP R14B">generate_msg(Vsn, NoteStore, Pdu, MsgData, LocalEngineID, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name> <fsummary>Generate a request message to be sent to the network</fsummary> <type> <v>Vsn = 'version-1' | 'version-2' | 'version-3'</v> @@ -185,7 +185,7 @@ </func> <func> - <name>process_taddrs(TDests) -> Dests</name> + <name since="OTP 17.3">process_taddrs(TDests) -> Dests</name> <fsummary>Transform addresses from internal MIB format to a less internal </fsummary> <type> @@ -211,7 +211,7 @@ </func> <func> - <name>discarded_pdu(Variable) -> void()</name> + <name since="">discarded_pdu(Variable) -> void()</name> <fsummary>Increment the variable associated with a discarded pdu</fsummary> <type> <v>Variable = atom()</v> diff --git a/lib/snmp/doc/src/snmpa_network_interface.xml b/lib/snmp/doc/src/snmpa_network_interface.xml index d4d4989e90..3e79df11b1 100644 --- a/lib/snmp/doc/src/snmpa_network_interface.xml +++ b/lib/snmp/doc/src/snmpa_network_interface.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_network_interface.xml</file> </header> - <module>snmpa_network_interface</module> + <module since="">snmpa_network_interface</module> <modulesummary>Behaviour module for the SNMP agent network interface.</modulesummary> <description> <p>This module defines the behaviour of the agent network @@ -68,7 +68,7 @@ <funcs> <func> - <name>start_link(Prio, NoteStore, MasterAgent, Opts) -> {ok, Pid} | {error, Reason}</name> + <name since="">start_link(Prio, NoteStore, MasterAgent, Opts) -> {ok, Pid} | {error, Reason}</name> <fsummary>Start-link the network interface process</fsummary> <type> <v>Prio = priority()</v> @@ -93,7 +93,7 @@ </func> <func> - <name>info(Pid) -> [{Key, Value}]</name> + <name since="">info(Pid) -> [{Key, Value}]</name> <fsummary>Return information about the running network interface process</fsummary> <type> <v>Pid = pid()</v> @@ -112,7 +112,7 @@ </func> <func> - <name>verbosity(Pid, Verbosity) -> void()</name> + <name since="">verbosity(Pid, Verbosity) -> void()</name> <fsummary>Change the verbosity of a running network interface process</fsummary> <type> <v>Pid = pid()</v> @@ -126,7 +126,7 @@ </func> <func> - <name>get_log_type(Pid) -> {ok, LogType} | {error, Reason}</name> + <name since="">get_log_type(Pid) -> {ok, LogType} | {error, Reason}</name> <fsummary>Get the Audit Trail Log type</fsummary> <type> <v>Pid = pid()</v> @@ -147,7 +147,7 @@ </func> <func> - <name>set_log_type(Pid, NewType) -> {ok, OldType} | {error, Reason}</name> + <name since="">set_log_type(Pid, NewType) -> {ok, OldType} | {error, Reason}</name> <fsummary>Change the Audit Trail Log type</fsummary> <type> <v>Pid = pid()</v> diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml index 7cd08f8935..02c7d291dd 100644 --- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpa_network_interface_filter.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_network_interface_filter.xml</file> </header> - <module>snmpa_network_interface_filter</module> + <module since="">snmpa_network_interface_filter</module> <modulesummary>Behaviour module for the SNMP agent network-interface filter.</modulesummary> <description> <p>This module defines the behaviour of the agent network interface @@ -101,7 +101,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </section> <funcs> <func> - <name>accept_recv(Domain, Addr) -> boolean()</name> + <name since="">accept_recv(Domain, Addr) -> boolean()</name> <fsummary>Shall the received message be accepted</fsummary> <type> <v>Domain = transportDomain()</v> @@ -116,7 +116,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </desc> </func> <func> - <name>accept_send(Domain, Addr) -> boolean()</name> + <name since="">accept_send(Domain, Addr) -> boolean()</name> <fsummary>Shall the message be sent</fsummary> <type> <v>Domain = transportDomain()</v> @@ -131,7 +131,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </desc> </func> <func> - <name>accept_recv_pdu(Domain, Addr, PduType) -> boolean()</name> + <name since="">accept_recv_pdu(Domain, Addr, PduType) -> boolean()</name> <fsummary>Shall the received pdu be accepted</fsummary> <type> <v>Domain = transportDomain()</v> @@ -148,7 +148,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </desc> </func> <func> - <name>accept_send_pdu(Targets, PduType) -> Reply</name> + <name since="">accept_send_pdu(Targets, PduType) -> Reply</name> <fsummary>Shall the pdu be sent</fsummary> <type> <v>Targets = targets()</v> diff --git a/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml b/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml index cbae158544..5dad372710 100644 --- a/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml +++ b/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml @@ -34,7 +34,7 @@ <rev></rev> <file>snmpa_notification_delivery_info_receiver.xml</file> </header> - <module>snmpa_notification_delivery_info_receiver</module> + <module since="">snmpa_notification_delivery_info_receiver</module> <modulesummary> Behaviour module for the SNMP agent notification delivery information receiver. @@ -76,7 +76,7 @@ <funcs> <func> - <name>delivery_targets(Tag, Targets, Extra) -> void()</name> + <name since="">delivery_targets(Tag, Targets, Extra) -> void()</name> <fsummary>Inform about target addresses</fsummary> <type> <v>Tag = term()</v> @@ -97,7 +97,7 @@ </func> <func> - <name>delivery_info(Tag, Target, DeliveryResult, Extra) -> void()</name> + <name since="">delivery_info(Tag, Target, DeliveryResult, Extra) -> void()</name> <fsummary>Inform about delivery result</fsummary> <type> <v>Tag = term()</v> diff --git a/lib/snmp/doc/src/snmpa_notification_filter.xml b/lib/snmp/doc/src/snmpa_notification_filter.xml index 0f16ba4440..902412ccc5 100644 --- a/lib/snmp/doc/src/snmpa_notification_filter.xml +++ b/lib/snmp/doc/src/snmpa_notification_filter.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_notification_filter.xml</file> </header> - <module>snmpa_notification_filter</module> + <module since="">snmpa_notification_filter</module> <modulesummary>Behaviour module for the SNMP agent notification filters.</modulesummary> <description> <p>This module defines the behaviour of the agent notification @@ -51,7 +51,7 @@ </description> <funcs> <func> - <name>handle_notification(Notif, Data) -> Reply</name> + <name since="">handle_notification(Notif, Data) -> Reply</name> <fsummary>Handle a notification</fsummary> <type> <v>Reply = send | {send, NewNotif} | dont_send</v> diff --git a/lib/snmp/doc/src/snmpa_supervisor.xml b/lib/snmp/doc/src/snmpa_supervisor.xml index 86c6fbc350..e11cde390f 100644 --- a/lib/snmp/doc/src/snmpa_supervisor.xml +++ b/lib/snmp/doc/src/snmpa_supervisor.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpa_supervisor.xml</file> </header> - <module>snmpa_supervisor</module> + <module since="">snmpa_supervisor</module> <modulesummary>A supervisor for the SNMP agent Processes</modulesummary> <description> <p>This is the top supervisor for the agent part of the SNMP @@ -42,7 +42,7 @@ </description> <funcs> <func> - <name>start_sub_sup(Opts) -> {ok, pid()} | {error, {already_started, pid()}} | {error, Reason}</name> + <name since="">start_sub_sup(Opts) -> {ok, pid()} | {error, {already_started, pid()}} | {error, Reason}</name> <fsummary>Start the SNMP supervisor for sub-agents only</fsummary> <type> <v>Opts = [opt()]</v> @@ -60,7 +60,7 @@ </desc> </func> <func> - <name>start_master_sup(Opts) -> {ok, pid()} | {error, {already_started, pid()}} | {error, Reason}</name> + <name since="">start_master_sup(Opts) -> {ok, pid()} | {error, {already_started, pid()}} | {error, Reason}</name> <fsummary>Start the SNMP supervisor for all agents</fsummary> <type> <v>Opts = [opt()]</v> @@ -82,7 +82,7 @@ </desc> </func> <func> - <name>start_sub_agent(ParentAgent,Subtree,Mibs) -> {ok, pid()} | {error, Reason}</name> + <name since="">start_sub_agent(ParentAgent,Subtree,Mibs) -> {ok, pid()} | {error, Reason}</name> <fsummary>Start a sub-agent</fsummary> <type> <v>ParentAgent = pid()</v> @@ -99,7 +99,7 @@ </desc> </func> <func> - <name>stop_sub_agent(SubAgent) -> ok | no_such_child</name> + <name since="">stop_sub_agent(SubAgent) -> ok | no_such_child</name> <fsummary>Stop a sub-agent</fsummary> <type> <v>SubAgent = pid()</v> diff --git a/lib/snmp/doc/src/snmpc.xml b/lib/snmp/doc/src/snmpc.xml index aba51bb500..b22b32a133 100644 --- a/lib/snmp/doc/src/snmpc.xml +++ b/lib/snmp/doc/src/snmpc.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpc.xml</file> </header> - <module>snmpc</module> + <module since="">snmpc</module> <modulesummary>Interface Functions to the SNMP toolkit MIB compiler</modulesummary> <description> <p>The module <c>snmpc</c> contains interface functions to the @@ -43,8 +43,8 @@ <funcs> <func> - <name>compile(File)</name> - <name>compile(File, Options) -> {ok, BinFileName} | {error, Reason}</name> + <name since="">compile(File)</name> + <name since="">compile(File, Options) -> {ok, BinFileName} | {error, Reason}</name> <fsummary>Compile the specified MIB</fsummary> <type> <v>File = string()</v> @@ -236,7 +236,7 @@ </func> <func> - <name>is_consistent(Mibs) -> ok | {error, Reason}</name> + <name since="">is_consistent(Mibs) -> ok | {error, Reason}</name> <fsummary>Check for OID conflicts between MIBs</fsummary> <type> <v>Mibs = [MibName]</v> @@ -252,7 +252,7 @@ </func> <func> - <name>mib_to_hrl(MibName) -> ok | {error, Reason}</name> + <name since="">mib_to_hrl(MibName) -> ok | {error, Reason}</name> <fsummary>Generate constants for the objects in the MIB</fsummary> <type> <v>MibName = string()</v> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index be4cd58a1a..c45df98ee0 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpm.xml</file> </header> - <module>snmpm</module> + <module since="">snmpm</module> <modulesummary>Interface functions to the SNMP toolkit manager</modulesummary> <description> <p>The module <c>snmpm</c> contains interface functions to the @@ -77,7 +77,7 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </section> <funcs> <func> - <name>monitor() -> Ref</name> + <name since="">monitor() -> Ref</name> <fsummary>Monitor the snmp manager</fsummary> <type> <v>Ref = reference()</v> @@ -92,7 +92,7 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>demonitor(Ref) -> void()</name> + <name since="">demonitor(Ref) -> void()</name> <fsummary>Turn off monitoring of the snmp manager</fsummary> <type> <v>Ref = reference()</v> @@ -105,7 +105,7 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>notify_started(Timeout) -> Pid</name> + <name since="">notify_started(Timeout) -> Pid</name> <fsummary>Request to be notified when manager started</fsummary> <type> <v>Timeout = integer()</v> @@ -148,7 +148,7 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>cancel_notify_started(Pid) -> void()</name> + <name since="">cancel_notify_started(Pid) -> void()</name> <fsummary>Cancel request to be notified when manager started</fsummary> <type> <v>Pid = pid()</v> @@ -161,8 +161,8 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>register_user(Id, Module, Data) -> ok | {error, Reason}</name> - <name>register_user(Id, Module, Data, DefaultAgentConfig) -> ok | {error, Reason}</name> + <name since="">register_user(Id, Module, Data) -> ok | {error, Reason}</name> + <name since="">register_user(Id, Module, Data, DefaultAgentConfig) -> ok | {error, Reason}</name> <fsummary>Register a user of the manager</fsummary> <type> <v>Id = term()</v> @@ -204,8 +204,8 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>register_user_monitor(Id, Module, Data) -> ok | {error, Reason}</name> - <name>register_user_monitor(Id, Module, Data, DefaultAgentConfig) -> ok | {error, Reason}</name> + <name since="">register_user_monitor(Id, Module, Data) -> ok | {error, Reason}</name> + <name since="">register_user_monitor(Id, Module, Data, DefaultAgentConfig) -> ok | {error, Reason}</name> <fsummary>Register a monitored user of the manager</fsummary> <type> <v>Id = term()</v> @@ -252,7 +252,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>unregister_user(Id) -> ok | {error, Reason}</name> + <name since="">unregister_user(Id) -> ok | {error, Reason}</name> <fsummary>Unregister the user</fsummary> <type> <v>Id = term()</v> @@ -265,7 +265,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>which_users() -> Users</name> + <name since="">which_users() -> Users</name> <fsummary>Get a list of all users</fsummary> <type> <v>Users = [UserId]</v> @@ -279,7 +279,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>register_agent(UserId, TargetName, Config) -> ok | {error, Reason}</name> + <name since="">register_agent(UserId, TargetName, Config) -> ok | {error, Reason}</name> <fsummary>Register this agent</fsummary> <type> <v>UserId = term()</v> @@ -325,7 +325,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>unregister_agent(UserId, TargetName) -> ok | {error, Reason}</name> + <name since="">unregister_agent(UserId, TargetName) -> ok | {error, Reason}</name> <fsummary>Unregister the user</fsummary> <type> <v>UserId = term()</v> @@ -339,7 +339,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>agent_info(TargetName, Item) -> {ok, Val} | {error, Reason}</name> + <name since="">agent_info(TargetName, Item) -> {ok, Val} | {error, Reason}</name> <fsummary>Retrieve agent config</fsummary> <type> <v>TargetName = target_name()</v> @@ -354,8 +354,8 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>update_agent_info(UserId, TargetName, Info) -> ok | {error, Reason}</name> - <name>update_agent_info(UserId, TargetName, Item, Val) -> ok | {error, Reason}</name> + <name since="OTP R14B04">update_agent_info(UserId, TargetName, Info) -> ok | {error, Reason}</name> + <name since="">update_agent_info(UserId, TargetName, Item, Val) -> ok | {error, Reason}</name> <fsummary>Update agent config</fsummary> <type> <v>UserId = term()</v> @@ -379,8 +379,8 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>which_agents() -> Agents</name> - <name>which_agents(UserId) -> Agents</name> + <name since="">which_agents() -> Agents</name> + <name since="">which_agents(UserId) -> Agents</name> <fsummary>List the registered agents</fsummary> <type> <v>UserId = term()</v> @@ -396,7 +396,7 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> - <name>register_usm_user(EngineID, UserName, Conf) -> ok | {error, Reason}</name> + <name since="">register_usm_user(EngineID, UserName, Conf) -> ok | {error, Reason}</name> <fsummary>Register this USM user</fsummary> <type> <v>EngineID = string()</v> @@ -427,7 +427,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>unregister_usm_user(EngineID, UserName) -> ok | {error, Reason}</name> + <name since="">unregister_usm_user(EngineID, UserName) -> ok | {error, Reason}</name> <fsummary>Unregister this USM user</fsummary> <type> <v>EngineID = string()</v> @@ -442,7 +442,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>usm_user_info(EngineID, UserName, Item) -> {ok, Val} | {error, Reason}</name> + <name since="">usm_user_info(EngineID, UserName, Item) -> {ok, Val} | {error, Reason}</name> <fsummary>Retrieve usm user config</fsummary> <type> <v>EngineID = string()</v> @@ -458,7 +458,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>update_usm_user_info(EngineID, UserName, Item, Val) -> ok | {error, Reason}</name> + <name since="">update_usm_user_info(EngineID, UserName, Item, Val) -> ok | {error, Reason}</name> <fsummary>Update agent config</fsummary> <type> <v>EngineID = string()</v> @@ -475,7 +475,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>which_usm_users() -> UsmUsers</name> + <name since="">which_usm_users() -> UsmUsers</name> <fsummary>List all the registered usm users</fsummary> <type> <v>UsmUsers = [{EngineID,UserName}]</v> @@ -490,7 +490,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>which_usm_users(EngineID) -> UsmUsers</name> + <name since="">which_usm_users(EngineID) -> UsmUsers</name> <fsummary>List the registered usm users</fsummary> <type> <v>UsmUsers = [UserName]</v> @@ -505,8 +505,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_get2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_get2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_get2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>get-request</c></fsummary> <type> <v>UserId = term()</v> @@ -559,11 +559,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_get(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>get-request</c></fsummary> <type> <v>UserId = term()</v> @@ -605,8 +605,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_get2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_get2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_get2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>get-request</c></fsummary> <type> <v>UserId = term()</v> @@ -647,11 +647,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_get(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>get-request</c></fsummary> <type> <v>UserId = term()</v> @@ -683,8 +683,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_get_next2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_get_next2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>get-next-request</c></fsummary> <type> <v>UserId = term()</v> @@ -737,11 +737,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_get_next(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_next(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_next(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_next(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_next(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_next(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_next(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_next(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_next(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_next(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>get-next-request</c></fsummary> <type> <v>UserId = term()</v> @@ -775,8 +775,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_get_next2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_get_next2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>get-next-request</c></fsummary> <type> <v>UserId = term()</v> @@ -814,11 +814,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_get_next(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_next(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_next(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_next(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_next(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_next(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_next(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_next(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_next(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_next(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>get-next-request</c></fsummary> <type> <v>UserId = term()</v> @@ -849,8 +849,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_set2(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_set2(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>set-request</c></fsummary> <type> <v>UserId = term()</v> @@ -906,11 +906,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_set(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_set(UserId, TargetName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_set(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_set(UserId, TargetName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>set-request</c></fsummary> <type> <v>UserId = term()</v> @@ -946,8 +946,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_set2(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> - <name>async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_set2(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>set-request</c></fsummary> <type> <v>UserId = term()</v> @@ -990,11 +990,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_set(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> - <name>async_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> - <name>async_set(UserId, TargetName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_set(UserId, TargetName, ContextName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_set(UserId, TargetName, ContextName, VarsAndVals, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_set(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_set(UserId, TargetName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_set(UserId, TargetName, ContextName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_set(UserId, TargetName, ContextName, VarsAndVals, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>set-request</c></fsummary> <type> <v>UserId = term()</v> @@ -1026,8 +1026,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="OTP R14B03">sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>get-bulk-request</c></fsummary> <type> <v>UserId = term()</v> @@ -1082,11 +1082,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> - <name>sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name since="">sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <fsummary>Synchronous <c>get-bulk-request</c></fsummary> <type> <v>UserId = term()</v> @@ -1121,8 +1121,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="OTP R14B03">async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>get-bulk-request</c></fsummary> <type> <v>UserId = term()</v> @@ -1162,11 +1162,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> - <name>async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason}</name> + <name since="">async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason}</name> <fsummary>Asynchronous <c>get-bulk-request</c></fsummary> <type> <v>UserId = term()</v> @@ -1199,7 +1199,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>cancel_async_request(UserId, ReqId) -> ok | {error, Reason}</name> + <name since="">cancel_async_request(UserId, ReqId) -> ok | {error, Reason}</name> <fsummary>Cancel a asynchronous request</fsummary> <type> <v>UserId = term()</v> @@ -1214,15 +1214,15 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>log_to_txt(LogDir)</name> - <name>log_to_txt(LogDir, Block | Mibs)</name> - <name>log_to_txt(LogDir, Mibs, Block | OutFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_txt(LogDir)</name> + <name since="">log_to_txt(LogDir, Block | Mibs)</name> + <name since="">log_to_txt(LogDir, Mibs, Block | OutFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> <fsummary>Convert an Audit Trail Log to text format</fsummary> <type> <v>LogDir = string()</v> @@ -1257,15 +1257,15 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>log_to_io(LogDir) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Block | Mibs) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> - <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Block | Mibs) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, Block | LogName) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, Block | LogFile) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R15B01">log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> + <name since="OTP R16B03">log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) -> ok | {ok, Cnt} | {error, Reason}</name> <fsummary>Convert an Audit Trail Log to text format</fsummary> <type> <v>LogDir = string()</v> @@ -1299,7 +1299,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>change_log_size(NewSize) -> ok | {error, Reason}</name> + <name since="">change_log_size(NewSize) -> ok | {error, Reason}</name> <fsummary>Change the size of the Audit Trail Log</fsummary> <type> <v>NewSize = {MaxBytes, MaxFiles}</v> @@ -1321,7 +1321,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>set_log_type(NewType) -> {ok, OldType} | {error, Reason}</name> + <name since="">set_log_type(NewType) -> {ok, OldType} | {error, Reason}</name> <fsummary>Change the Audit Trail Log type</fsummary> <type> <v>NewType = OldType = atl_type()</v> @@ -1340,7 +1340,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>load_mib(Mib) -> ok | {error, Reason}</name> + <name since="">load_mib(Mib) -> ok | {error, Reason}</name> <fsummary>Load a MIB into the manager</fsummary> <type> <v>Mib = MibName</v> @@ -1361,7 +1361,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>unload_mib(Mib) -> ok | {error, Reason}</name> + <name since="">unload_mib(Mib) -> ok | {error, Reason}</name> <fsummary>Unload a MIB from the manager</fsummary> <type> <v>Mib = MibName</v> @@ -1382,7 +1382,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>which_mibs() -> Mibs</name> + <name since="">which_mibs() -> Mibs</name> <fsummary>Which mibs are loaded into the manager</fsummary> <type> <v>Mibs = [{MibName, MibFile}]</v> @@ -1397,7 +1397,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>name_to_oid(Name) -> {ok, Oids} | {error, Reason}</name> + <name since="">name_to_oid(Name) -> {ok, Oids} | {error, Reason}</name> <fsummary>Get all the possible oid's for an alias-name</fsummary> <type> <v>Name = atom()</v> @@ -1414,7 +1414,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>oid_to_name(Oid) -> {ok, Name} | {error, Reason}</name> + <name since="">oid_to_name(Oid) -> {ok, Name} | {error, Reason}</name> <fsummary>Get the alias-name of the oid </fsummary> <type> <v>Oid = oid()</v> @@ -1429,7 +1429,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>oid_to_type(Oid) -> {ok, Type} | {error, Reason}</name> + <name since="">oid_to_type(Oid) -> {ok, Type} | {error, Reason}</name> <fsummary>Get the type of the the oid</fsummary> <type> <v>Oid = oid()</v> @@ -1444,7 +1444,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>backup(BackupDir) -> ok | {error, Reason}</name> + <name since="">backup(BackupDir) -> ok | {error, Reason}</name> <fsummary>Backup manager data</fsummary> <type> <v>BackupDir = string()</v> @@ -1458,7 +1458,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>info() -> [{Key, Value}]</name> + <name since="">info() -> [{Key, Value}]</name> <fsummary>Return information about the manager</fsummary> <type> <v>Key = atom()</v> @@ -1475,7 +1475,7 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>verbosity(Ref, Verbosity) -> void()</name> + <name since="">verbosity(Ref, Verbosity) -> void()</name> <fsummary>Assign a new verbosity for the process</fsummary> <type> <v>Ref = server | config | net_if | note_store | all</v> @@ -1492,8 +1492,8 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 </func> <func> - <name>format_reason(Reason) -> string()</name> - <name>format_reason(Prefix, Reason) -> string()</name> + <name since="">format_reason(Reason) -> string()</name> + <name since="">format_reason(Prefix, Reason) -> string()</name> <fsummary>Assign a new verbosity for the process</fsummary> <type> <v>Reason = term()</v> diff --git a/lib/snmp/doc/src/snmpm_conf.xml b/lib/snmp/doc/src/snmpm_conf.xml index a3097e5f7e..17ecf2df7c 100644 --- a/lib/snmp/doc/src/snmpm_conf.xml +++ b/lib/snmp/doc/src/snmpm_conf.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpm_conf.xml</file> </header> - <module>snmpm_conf</module> + <module since="">snmpm_conf</module> <modulesummary>Utility functions for handling the manager config files.</modulesummary> <description> <p>The module <c>snmpm_conf</c> contains various utility functions to @@ -42,7 +42,7 @@ </description> <funcs> <func> - <name>manager_entry(Tag, Val) -> manager_entry()</name> + <name since="">manager_entry(Tag, Val) -> manager_entry()</name> <fsummary>Create an manager entry</fsummary> <type> <v>Tag = address | port | engine_id | max_message_size</v> @@ -60,8 +60,8 @@ </desc> </func> <func> - <name>write_manager_config(Dir, Conf) -> ok</name> - <name>write_manager_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_manager_config(Dir, Conf) -> ok</name> + <name since="">write_manager_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the manager config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -81,7 +81,7 @@ </desc> </func> <func> - <name>append_manager_config(Dir, Conf) -> ok</name> + <name since="">append_manager_config(Dir, Conf) -> ok</name> <fsummary>Append the manager config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -98,7 +98,7 @@ </desc> </func> <func> - <name>read_manager_config(Dir) -> Conf</name> + <name since="">read_manager_config(Dir) -> Conf</name> <fsummary>Read the manager config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -115,9 +115,9 @@ </desc> </func> <func> - <name>users_entry(UserId) -> users_entry()</name> - <name>users_entry(UserId, UserMod) -> users_entry()</name> - <name>users_entry(UserId, UserMod, UserData) -> users_entry()</name> + <name since="">users_entry(UserId) -> users_entry()</name> + <name since="">users_entry(UserId, UserMod) -> users_entry()</name> + <name since="">users_entry(UserId, UserMod, UserData) -> users_entry()</name> <fsummary>Create an users entry</fsummary> <type> <v>UserId = term()</v> @@ -139,8 +139,8 @@ </desc> </func> <func> - <name>write_users_config(Dir, Conf) -> ok</name> - <name>write_users_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_users_config(Dir, Conf) -> ok</name> + <name since="">write_users_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the manager users config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -161,7 +161,7 @@ </desc> </func> <func> - <name>append_users_config(Dir, Conf) -> ok</name> + <name since="">append_users_config(Dir, Conf) -> ok</name> <fsummary>Append the manager users config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -179,7 +179,7 @@ </desc> </func> <func> - <name>read_users_config(Dir) -> Conf</name> + <name since="">read_users_config(Dir) -> Conf</name> <fsummary>Read the manager users config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -196,7 +196,7 @@ </desc> </func> <func> - <name>agents_entry(UserId, TargetName, Comm, Domain, Addr, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel) -> agents_entry()</name> + <name since="">agents_entry(UserId, TargetName, Comm, Domain, Addr, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel) -> agents_entry()</name> <fsummary>Create an agents entry</fsummary> <type> <v>UserId = term()</v> @@ -223,8 +223,8 @@ </desc> </func> <func> - <name>write_agents_config(Dir, Conf) -> ok</name> - <name>write_agents_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_agents_config(Dir, Conf) -> ok</name> + <name since="">write_agents_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the manager agents to the config file</fsummary> <type> <v>Dir = string()</v> @@ -245,7 +245,7 @@ </desc> </func> <func> - <name>append_agents_config(Dir, Conf) -> ok</name> + <name since="">append_agents_config(Dir, Conf) -> ok</name> <fsummary>Append the manager agents to the config file</fsummary> <type> <v>Dir = string()</v> @@ -263,7 +263,7 @@ </desc> </func> <func> - <name>read_agents_config(Dir) -> Conf</name> + <name since="">read_agents_config(Dir) -> Conf</name> <fsummary>Read the manager agents config from the config file</fsummary> <type> <v>Dir = string()</v> @@ -280,8 +280,8 @@ </desc> </func> <func> - <name>usm_entry(EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey) -> usm_entry()</name> - <name>usm_entry(EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey) -> usm_entry()</name> + <name since="">usm_entry(EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey) -> usm_entry()</name> + <name since="">usm_entry(EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey) -> usm_entry()</name> <fsummary>Create an usm entry</fsummary> <type> <v>EngineID = string()</v> @@ -303,8 +303,8 @@ </desc> </func> <func> - <name>write_usm_config(Dir, Conf) -> ok</name> - <name>write_usm_config(Dir, Hdr, Conf) -> ok</name> + <name since="">write_usm_config(Dir, Conf) -> ok</name> + <name since="">write_usm_config(Dir, Hdr, Conf) -> ok</name> <fsummary>Write the manager usm config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -325,7 +325,7 @@ </desc> </func> <func> - <name>append_usm_config(Dir, Conf) -> ok</name> + <name since="">append_usm_config(Dir, Conf) -> ok</name> <fsummary>Append the manager usm config to the config file</fsummary> <type> <v>Dir = string()</v> @@ -343,7 +343,7 @@ </desc> </func> <func> - <name>read_usm_config(Dir) -> Conf</name> + <name since="">read_usm_config(Dir) -> Conf</name> <fsummary>Read the manager usm config from the config file</fsummary> <type> <v>Dir = string()</v> diff --git a/lib/snmp/doc/src/snmpm_mpd.xml b/lib/snmp/doc/src/snmpm_mpd.xml index 08276e4b30..b024f8d294 100644 --- a/lib/snmp/doc/src/snmpm_mpd.xml +++ b/lib/snmp/doc/src/snmpm_mpd.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpm_mpd.xml</file> </header> - <module>snmpm_mpd</module> + <module since="">snmpm_mpd</module> <modulesummary>Message Processing and Dispatch module for the SNMP manager</modulesummary> <description> <p>The module <c>snmpm_mpd</c> implements the version independent @@ -48,7 +48,7 @@ <funcs> <func> - <name>init_mpd(Vsns) -> mpd_state()</name> + <name since="">init_mpd(Vsns) -> mpd_state()</name> <fsummary>Initialize the MPD module</fsummary> <type> <v>Vsns = [Vsn]</v> @@ -64,7 +64,7 @@ </desc> </func> <func> - <name>process_msg(Msg, Domain, Addr, State, NoteStore, Logger) -> {ok, Vsn, Pdu, PduMS, MsgData} | {discarded, Reason}</name> + <name since="OTP 17.3">process_msg(Msg, Domain, Addr, State, NoteStore, Logger) -> {ok, Vsn, Pdu, PduMS, MsgData} | {discarded, Reason}</name> <fsummary>Process a message received from the network</fsummary> <type> <v>Msg = binary()</v> @@ -92,7 +92,7 @@ </desc> </func> <func> - <name>generate_msg(Vsn, NoteStore, Pdu, MsgData, Logger) -> {ok, Packet} | {discarded, Reason}</name> + <name since="">generate_msg(Vsn, NoteStore, Pdu, MsgData, Logger) -> {ok, Packet} | {discarded, Reason}</name> <fsummary>Generate a request message to be sent to the network</fsummary> <type> <v>Vsn = 'version-1' | 'version-2' | 'version-3'</v> @@ -117,7 +117,7 @@ </desc> </func> <func> - <name>generate_response_msg(Vsn, Pdu, MsgData, Logger) -> {ok, Packet} | {discarded, Reason}</name> + <name since="">generate_response_msg(Vsn, Pdu, MsgData, Logger) -> {ok, Packet} | {discarded, Reason}</name> <fsummary>Generate a response packet to be sent to the network</fsummary> <type> <v>Vsn = 'version-1' | 'version-2' | 'version-3'</v> diff --git a/lib/snmp/doc/src/snmpm_network_interface.xml b/lib/snmp/doc/src/snmpm_network_interface.xml index 73892aa2e8..e4a66ceef2 100644 --- a/lib/snmp/doc/src/snmpm_network_interface.xml +++ b/lib/snmp/doc/src/snmpm_network_interface.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpm_network_interface.xml</file> </header> - <module>snmpm_network_interface</module> + <module since="">snmpm_network_interface</module> <modulesummary>Behaviour module for the SNMP manager network interface.</modulesummary> <description> <p>This module defines the behaviour of the manager network @@ -79,7 +79,7 @@ <funcs> <func> - <name>start_link(Server, NoteStore) -> {ok, Pid} | {error, Reason}</name> + <name since="">start_link(Server, NoteStore) -> {ok, Pid} | {error, Reason}</name> <fsummary>Start-link the network interface process</fsummary> <type> <v>Server = pid()</v> @@ -95,7 +95,7 @@ </func> <func> - <name>stop(Pid) -> void()</name> + <name since="">stop(Pid) -> void()</name> <fsummary>Stop the network interface process</fsummary> <type> <v>Pid = pid()</v> @@ -108,7 +108,7 @@ </func> <func> - <name>send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo) -> void()</name> + <name since="">send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo) -> void()</name> <fsummary>Request the network interface process to send this pdu</fsummary> <type> <v>Pid = pid()</v> @@ -142,7 +142,7 @@ </func> <func> - <name>inform_response(Pid, Ref, Addr, Port) -> void()</name> + <name since="">inform_response(Pid, Ref, Addr, Port) -> void()</name> <fsummary>Send the inform-request ack</fsummary> <type> <v>Pid = pid()</v> @@ -163,7 +163,7 @@ </func> <func> - <name>note_store(Pid, NoteStore) -> void()</name> + <name since="">note_store(Pid, NoteStore) -> void()</name> <fsummary>Change the verbosity of the network interface process</fsummary> <type> <v>Pid = pid()</v> @@ -179,7 +179,7 @@ </func> <func> - <name>info(Pid) -> [{Key, Value}]</name> + <name since="">info(Pid) -> [{Key, Value}]</name> <fsummary>Return information about the running network interface process</fsummary> <type> <v>Pid = pid()</v> @@ -198,7 +198,7 @@ </func> <func> - <name>verbosity(Pid, Verbosity) -> void()</name> + <name since="">verbosity(Pid, Verbosity) -> void()</name> <fsummary>Change the verbosity of the network interface process</fsummary> <type> <v>Pid = pid()</v> @@ -212,7 +212,7 @@ </func> <func> - <name>get_log_type(Pid) -> {ok, LogType} | {error, Reason}</name> + <name since="">get_log_type(Pid) -> {ok, LogType} | {error, Reason}</name> <fsummary>Get the Audit Trail Log type</fsummary> <type> <v>Pid = pid()</v> @@ -233,7 +233,7 @@ </func> <func> - <name>set_log_type(Pid, NewType) -> {ok, OldType} | {error, Reason}</name> + <name since="">set_log_type(Pid, NewType) -> {ok, OldType} | {error, Reason}</name> <fsummary>Change the Audit Trail Log type</fsummary> <type> <v>Pid = pid()</v> diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml index 742cd53fc6..a50572da51 100644 --- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpm_network_interface_filter.xml</file> </header> - <module>snmpm_network_interface_filter</module> + <module since="">snmpm_network_interface_filter</module> <modulesummary>Behaviour module for the SNMP manager network-interface filter.</modulesummary> <description> <p>This module defines the behaviour of the manager network interface @@ -100,7 +100,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | <funcs> <func> - <name>accept_recv(Domain, Addr) -> boolean()</name> + <name since="">accept_recv(Domain, Addr) -> boolean()</name> <fsummary>Shall the received message be accepted</fsummary> <type> <v>Domain = transportDomain()</v> @@ -116,7 +116,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </func> <func> - <name>accept_send(Domain, Addr) -> boolean()</name> + <name since="">accept_send(Domain, Addr) -> boolean()</name> <fsummary>Shall the message be sent</fsummary> <type> <v>Domain = transportDomain()</v> @@ -132,7 +132,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </func> <func> - <name>accept_recv_pdu(Domain, Addr, PduType) -> boolean()</name> + <name since="">accept_recv_pdu(Domain, Addr, PduType) -> boolean()</name> <fsummary>Shall the received pdu be accepted</fsummary> <type> <v>Domain = transportDomain()</v> @@ -150,7 +150,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | </func> <func> - <name>accept_send_pdu(Domain, Addr, PduType) -> boolean()</name> + <name since="">accept_send_pdu(Domain, Addr, PduType) -> boolean()</name> <fsummary>Shall the pdu be sent</fsummary> <type> <v>Domain = transportDomain()</v> diff --git a/lib/snmp/doc/src/snmpm_user.xml b/lib/snmp/doc/src/snmpm_user.xml index 87ae1d224a..9abf596c83 100644 --- a/lib/snmp/doc/src/snmpm_user.xml +++ b/lib/snmp/doc/src/snmpm_user.xml @@ -32,7 +32,7 @@ <rev></rev> <file>snmpm_user.xml</file> </header> - <module>snmpm_user</module> + <module since="">snmpm_user</module> <modulesummary>Behaviour module for the SNMP manager user.</modulesummary> <description> <p>This module defines the behaviour of the manager user. @@ -93,7 +93,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), <funcs> <func> - <name>handle_error(ReqId, Reason, UserData) -> void()</name> + <name since="">handle_error(ReqId, Reason, UserData) -> void()</name> <fsummary>Handle error</fsummary> <type> <v>ReqId = integer()</v> @@ -122,7 +122,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), </func> <func> - <name>handle_agent(Domain, Addr, Type, SnmpInfo, UserData) -> Reply</name> + <name since="">handle_agent(Domain, Addr, Type, SnmpInfo, UserData) -> Reply</name> <fsummary>Handle agent</fsummary> <type> <v>Domain = transportDomainUdpIpv4 | transportDomainUdpIpv6</v> @@ -181,7 +181,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), </func> <func> - <name>handle_pdu(TargetName, ReqId, SnmpPduInfo, UserData) -> void()</name> + <name since="">handle_pdu(TargetName, ReqId, SnmpPduInfo, UserData) -> void()</name> <fsummary>Handle the reply to an asynchronous request</fsummary> <type> <v>TargetName = target_name()</v> @@ -202,7 +202,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), </func> <func> - <name>handle_trap(TargetName, SnmpTrapInfo, UserData) -> Reply</name> + <name since="">handle_trap(TargetName, SnmpTrapInfo, UserData) -> Reply</name> <fsummary>Handle a trap/notification message</fsummary> <type> <v>TargetName = TargetName2 = target_name()</v> @@ -225,7 +225,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), </func> <func> - <name>handle_inform(TargetName, SnmpInformInfo, UserData) -> Reply</name> + <name since="">handle_inform(TargetName, SnmpInformInfo, UserData) -> Reply</name> <fsummary>Handle a inform message</fsummary> <type> <v>TargetName = TargetName2 = target_name()</v> @@ -253,7 +253,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), </func> <func> - <name>handle_report(TargetName, SnmpReportInfo, UserData) -> Reply</name> + <name since="">handle_report(TargetName, SnmpReportInfo, UserData) -> Reply</name> <fsummary>Handle a report message</fsummary> <type> <v>TargetName = TargetName2 = target_name()</v> @@ -278,7 +278,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), </func> <func> - <name>handle_invalid_result(IN, OUT) -> void()</name> + <name since="OTP R16B03">handle_invalid_result(IN, OUT) -> void()</name> <fsummary>Handle a report message</fsummary> <type> <v>IN = {Func, Args}</v> diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 177784384e..2e1b946ebb 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,21 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.7.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.7.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -293,6 +308,21 @@ </section> </section> +<section><title>Ssh 4.6.9.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.6.9.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 8435fced11..1a53a2ea98 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -28,7 +28,7 @@ <date>2007-10-06</date> <rev></rev> </header> - <module>ssh</module> + <module since="">ssh</module> <modulesummary>Main API of the ssh application</modulesummary> <description> <p>This is the interface module for the <c>SSH</c> application. @@ -1059,17 +1059,17 @@ <!-- CLOSE/1 --> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Closes an SSH connection.</fsummary> <desc><p>Closes an SSH connection.</p></desc> </func> <!-- CONNECT/2 etc --> <func> - <name>connect(Host, Port, Options) -> Result </name> - <name>connect(Host, Port, Options, NegotiationTimeout) -> Result </name> - <name>connect(TcpSocket, Options) -> Result</name> - <name>connect(TcpSocket, Options, NegotiationTimeout) -> Result</name> + <name since="">connect(Host, Port, Options) -> Result </name> + <name since="">connect(Host, Port, Options, NegotiationTimeout) -> Result </name> + <name since="OTP 19.0">connect(TcpSocket, Options) -> Result</name> + <name since="">connect(TcpSocket, Options, NegotiationTimeout) -> Result</name> <fsummary>Connects to an SSH server.</fsummary> <type> <v>Host = <seealso marker="#type-host">host()</seealso></v> @@ -1098,7 +1098,7 @@ <!-- CONNECTION_INFO/1, CONNECTION_INFO/2 --> <func> - <name name="connection_info" arity="2"/> + <name name="connection_info" arity="2" since=""/> <fsummary>Retrieves information about a connection.</fsummary> <desc> <p>Retrieves information about a connection. The list <c>Keys</c> defines which information that @@ -1108,9 +1108,9 @@ <!-- DEAMON/1,2,3 --> <func> - <name>daemon(Port | TcpSocket) -> Result</name> - <name>daemon(Port | TcpSocket, Options) -> Result</name> - <name>daemon(HostAddress, Port, Options) -> Result</name> + <name since="">daemon(Port | TcpSocket) -> Result</name> + <name since="">daemon(Port | TcpSocket, Options) -> Result</name> + <name since="">daemon(HostAddress, Port, Options) -> Result</name> <fsummary>Starts a server listening for SSH connections.</fsummary> <type> <v>Port = integer()</v> @@ -1154,7 +1154,7 @@ <!-- DAEMON_INFO/1 --> <func> - <name name="daemon_info" arity="1"/> + <name name="daemon_info" arity="1" since="OTP 19.0"/> <fsummary>Get info about a daemon</fsummary> <desc> <p>Returns a key-value list with information about the daemon.</p> @@ -1164,7 +1164,7 @@ <!-- DEFAULT_ALGORITHMS/0 --> <func> - <name name="default_algorithms" arity="0"/> + <name name="default_algorithms" arity="0" since="OTP 18.0"/> <fsummary>Get a list declaring the supported algorithms</fsummary> <desc> <p>Returns a key-value list, where the keys are the different types of algorithms and the values are the @@ -1176,9 +1176,9 @@ <!-- SHELL/1,2,3 --> <func> - <name>shell(Host | TcpSocket) -> Result </name> - <name>shell(Host | TcpSocket, Options) -> Result </name> - <name>shell(Host, Port, Options) -> Result </name> + <name since="">shell(Host | TcpSocket) -> Result </name> + <name since="">shell(Host | TcpSocket, Options) -> Result </name> + <name since="">shell(Host, Port, Options) -> Result </name> <fsummary>Starts an interactive shell on a remote SSH server.</fsummary> <type> <v>Host = <seealso marker="#type-host">host()</seealso></v> @@ -1203,8 +1203,8 @@ </func> <func> - <name name="start" arity="0"/> - <name name="start" arity="1"/> + <name name="start" arity="0" since=""/> + <name name="start" arity="1" since=""/> <fsummary>Starts the SSH application.</fsummary> <desc> <p>Utility function that starts the applications <c>crypto</c>, <c>public_key</c>, @@ -1215,7 +1215,7 @@ </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary>Stops the <c>ssh</c> application.</fsummary> <desc> <p>Stops the <c>ssh</c> application. @@ -1225,9 +1225,9 @@ </func> <func> - <name name="stop_daemon" arity="1"/> - <name name="stop_daemon" arity="2"/> - <name name="stop_daemon" arity="3"/> + <name name="stop_daemon" arity="1" since=""/> + <name name="stop_daemon" arity="2" since=""/> + <name name="stop_daemon" arity="3" since="OTP 21.0"/> <fsummary>Stops the listener and all connections started by the listener.</fsummary> <desc> <p>Stops the listener and all connections started by the listener.</p> @@ -1235,9 +1235,9 @@ </func> <func> - <name name="stop_listener" arity="1"/> - <name name="stop_listener" arity="2"/> - <name name="stop_listener" arity="3"/> + <name name="stop_listener" arity="1" since=""/> + <name name="stop_listener" arity="2" since=""/> + <name name="stop_listener" arity="3" since="OTP 21.0"/> <fsummary>Stops the listener, but leaves existing connections started by the listener operational.</fsummary> <desc> <p>Stops the listener, but leaves existing connections started by the listener operational.</p> diff --git a/lib/ssh/doc/src/ssh_client_channel.xml b/lib/ssh/doc/src/ssh_client_channel.xml index 9be4007c68..cd28b95fd3 100644 --- a/lib/ssh/doc/src/ssh_client_channel.xml +++ b/lib/ssh/doc/src/ssh_client_channel.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>ssh_client_channel</module> + <module since="OTP 21.0">ssh_client_channel</module> <modulesummary>-behaviour(ssh_client_channel). (Replaces ssh_channel) </modulesummary> <description> @@ -68,8 +68,8 @@ <funcs> <func> - <name>call(ChannelRef, Msg) -></name> - <name>call(ChannelRef, Msg, Timeout) -> Reply | {error, Reason}</name> + <name since="OTP 21.0">call(ChannelRef, Msg) -></name> + <name since="OTP 21.0">call(ChannelRef, Msg, Timeout) -> Reply | {error, Reason}</name> <fsummary>Makes a synchronous call to a channel.</fsummary> <type> <v>ChannelRef = pid() </v> @@ -92,7 +92,7 @@ </func> <func> - <name>cast(ChannelRef, Msg) -> ok </name> + <name since="OTP 21.0">cast(ChannelRef, Msg) -> ok </name> <fsummary>Sends an asynchronous message to the channel ChannelRef and returns ok.</fsummary> <type> @@ -111,7 +111,7 @@ </func> <func> - <name>enter_loop(State) -> _ </name> + <name since="OTP 21.0">enter_loop(State) -> _ </name> <fsummary>Makes an existing process an ssh_client_channel (replaces ssh_channel) process.</fsummary> <type> <v>State = term()</v> @@ -131,7 +131,7 @@ </func> <func> - <name>init(Options) -> {ok, State} | {ok, State, Timeout} | {stop, Reason} </name> + <name since="OTP 21.0">init(Options) -> {ok, State} | {ok, State, Timeout} | {stop, Reason} </name> <fsummary>Initiates an <c>ssh_client_channel</c> process.</fsummary> <type> <v>Options = [{Option, Value}]</v> @@ -173,7 +173,7 @@ </func> <func> - <name>reply(Client, Reply) -> _</name> + <name since="OTP 21.0">reply(Client, Reply) -> _</name> <fsummary>Sends a reply to a client.</fsummary> <type> <v>Client = opaque()</v> @@ -193,8 +193,8 @@ </func> <func> - <name>start(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> </name> - <name>start_link(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> + <name since="OTP 21.0">start(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> </name> + <name since="OTP 21.0">start_link(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> {ok, ChannelRef} | {error, Reason}</name> <fsummary>Starts a process that handles an SSH channel.</fsummary> <type> @@ -244,7 +244,7 @@ <funcs> <func> - <name>Module:code_change(OldVsn, State, Extra) -> {ok, + <name since="OTP 21.0">Module:code_change(OldVsn, State, Extra) -> {ok, NewState}</name> <fsummary>Converts process state when code is changed.</fsummary> <type> @@ -287,7 +287,7 @@ </func> <func> - <name>Module:init(Args) -> {ok, State} | {ok, State, timeout()} | + <name since="OTP 21.0">Module:init(Args) -> {ok, State} | {ok, State, timeout()} | {stop, Reason}</name> <fsummary>Makes necessary initializations and returns the initial channel state if the initializations succeed.</fsummary> @@ -307,7 +307,7 @@ </func> <func> - <name>Module:handle_call(Msg, From, State) -> Result</name> + <name since="OTP 21.0">Module:handle_call(Msg, From, State) -> Result</name> <fsummary>Handles messages sent by calling <c>call/[2,3]</c>.</fsummary> <type> @@ -334,7 +334,7 @@ </func> <func> - <name>Module:handle_cast(Msg, State) -> Result</name> + <name since="OTP 21.0">Module:handle_cast(Msg, State) -> Result</name> <fsummary>Handles messages sent by calling <c>cast/2</c>.</fsummary> <type> @@ -355,7 +355,7 @@ </func> <func> - <name>Module:handle_msg(Msg, State) -> {ok, State} | + <name since="OTP 21.0">Module:handle_msg(Msg, State) -> {ok, State} | {stop, ChannelId, State}</name> <fsummary>Handles other messages than SSH connection protocol, @@ -389,7 +389,7 @@ </func> <func> - <name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, + <name since="OTP 21.0">Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, ChannelId, State}</name> <fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary> <type> @@ -416,7 +416,7 @@ </func> <func> - <name>Module:terminate(Reason, State) -> _</name> + <name since="OTP 21.0">Module:terminate(Reason, State) -> _</name> <fsummary>Does cleaning up before channel process termination. </fsummary> <type> diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml index bc77756147..9f2f3013e5 100644 --- a/lib/ssh/doc/src/ssh_client_key_api.xml +++ b/lib/ssh/doc/src/ssh_client_key_api.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>ssh_client_key_api</module> + <module since="OTP R16B">ssh_client_key_api</module> <modulesummary> -behaviour(ssh_client_key_api). </modulesummary> @@ -86,7 +86,7 @@ <funcs> <func> - <name>Module:add_host_key(HostNames, PublicHostKey, ConnectOptions) -> ok | {error, Reason}</name> + <name since="OTP R16B">Module:add_host_key(HostNames, PublicHostKey, ConnectOptions) -> ok | {error, Reason}</name> <fsummary>Adds a host key to the set of trusted host keys.</fsummary> <type> <v>HostNames = string()</v> @@ -103,7 +103,7 @@ </func> <func> - <name>Module:is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name> + <name since="OTP R16B">Module:is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name> <fsummary>Checks if a host key is trusted.</fsummary> <type> <v>Key = <seealso marker="public_key:public_key#type-public_key">public_key:public_key()</seealso></v> @@ -125,7 +125,7 @@ </func> <func> - <name>Module:user_key(Algorithm, ConnectOptions) -> + <name since="OTP R16B">Module:user_key(Algorithm, ConnectOptions) -> {ok, PrivateKey} | {error, Reason}</name> <fsummary>Fetches the users <em>public key</em> matching the <c>Algorithm</c>.</fsummary> <type> diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml index 8e1cf156a8..2a701929f6 100644 --- a/lib/ssh/doc/src/ssh_connection.xml +++ b/lib/ssh/doc/src/ssh_connection.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>ssh_connection</module> + <module since="">ssh_connection</module> <modulesummary> This module provides API functions to send SSH Connection Protocol events to the other side of an SSH channel. @@ -201,7 +201,7 @@ <funcs> <func> - <name>adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name> + <name since="">adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name> <fsummary>Adjusts the SSH flow control window.</fsummary> <type> <v>ConnectionRef = connection_ref()</v> @@ -221,7 +221,7 @@ </func> <func> - <name>close(ConnectionRef, ChannelId) -> ok</name> + <name since="">close(ConnectionRef, ChannelId) -> ok</name> <fsummary>Sends a close message on the channel <c>ChannelId</c>.</fsummary> <type> <v>ConnectionRef = connection_ref()</v> @@ -240,7 +240,7 @@ </func> <func> - <name>exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() | + <name since="">exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() | {error, reason()}</name> <fsummary>Requests that the server starts the execution of the given command.</fsummary> <type> @@ -284,7 +284,7 @@ </func> <func> - <name>exit_status(ConnectionRef, ChannelId, Status) -> ok</name> + <name since="">exit_status(ConnectionRef, ChannelId, Status) -> ok</name> <fsummary>Sends the exit status of a command to the client.</fsummary> <type> <v>ConnectionRef = connection_ref() </v> @@ -298,8 +298,8 @@ </func> <func> - <name>ptty_alloc(ConnectionRef, ChannelId, Options) -></name> - <name>ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> > ssh_request_status() | + <name since="OTP 17.5">ptty_alloc(ConnectionRef, ChannelId, Options) -></name> + <name since="OTP 17.4">ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> > ssh_request_status() | {error, reason()}</name> <fsummary>Sends an SSH Connection Protocol <c>pty_req</c>, to allocate a pseudo-terminal.</fsummary> @@ -339,7 +339,7 @@ </func> <func> - <name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name> + <name since="">reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name> <fsummary>Sends status replies to requests that want such replies.</fsummary> <type> <v>ConnectionRef = connection_ref()</v> @@ -357,10 +357,10 @@ </func> <func> - <name>send(ConnectionRef, ChannelId, Data) -></name> - <name>send(ConnectionRef, ChannelId, Data, Timeout) -></name> - <name>send(ConnectionRef, ChannelId, Type, Data) -></name> - <name>send(ConnectionRef, ChannelId, Type, Data, TimeOut) -> + <name since="">send(ConnectionRef, ChannelId, Data) -></name> + <name since="">send(ConnectionRef, ChannelId, Data, Timeout) -></name> + <name since="">send(ConnectionRef, ChannelId, Type, Data) -></name> + <name since="">send(ConnectionRef, ChannelId, Type, Data, TimeOut) -> ok | {error, timeout} | {error, closed}</name> <fsummary>Sends channel data.</fsummary> <type> @@ -380,7 +380,7 @@ </func> <func> - <name>send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name> + <name since="">send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name> <fsummary>Sends EOF on channel <c>ChannelId</c>.</fsummary> <type> <v>ConnectionRef = connection_ref()</v> @@ -392,8 +392,8 @@ </func> <func> - <name>session_channel(ConnectionRef, Timeout) -></name> - <name>session_channel(ConnectionRef, InitialWindowSize, + <name since="">session_channel(ConnectionRef, Timeout) -></name> + <name since="">session_channel(ConnectionRef, InitialWindowSize, MaxPacketSize, Timeout) -> {ok, channel_id()} | {error, reason()}</name> <fsummary>Opens a channel for an SSH session.</fsummary> <type> @@ -410,7 +410,7 @@ </func> <func> - <name>setenv(ConnectionRef, ChannelId, Var, Value, TimeOut) -> ssh_request_status() | + <name since="">setenv(ConnectionRef, ChannelId, Var, Value, TimeOut) -> ssh_request_status() | {error, reason()}</name> <fsummary>Environment variables can be passed to the shell/command to be started later.</fsummary> @@ -428,7 +428,7 @@ </func> <func> - <name>shell(ConnectionRef, ChannelId) -> ok | failure | {error, closed} + <name since="">shell(ConnectionRef, ChannelId) -> ok | failure | {error, closed} </name> <fsummary>Requests that the user default shell (typically defined in /etc/passwd in Unix systems) is to be executed at the server end.</fsummary> @@ -448,7 +448,7 @@ </func> <func> - <name>subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> ssh_request_status() | + <name since="">subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> ssh_request_status() | {error, reason()}</name> <fsummary>Requests to execute a predefined subsystem on the server.</fsummary> <type> diff --git a/lib/ssh/doc/src/ssh_file.xml b/lib/ssh/doc/src/ssh_file.xml index 6681d9c306..f1fef09083 100644 --- a/lib/ssh/doc/src/ssh_file.xml +++ b/lib/ssh/doc/src/ssh_file.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>ssh_file</module> + <module since="OTP 21.2">ssh_file</module> <modulesummary>Default callback module for the client's and server's database operations in the ssh application</modulesummary> <description> <p>This module is the default callback handler for the client's and the server's user and host "database" operations. @@ -169,7 +169,7 @@ <funcs> <func> - <name>host_key(Algorithm, DaemonOptions) -> {ok, Key} | {error, Reason}</name> + <name since="OTP 21.2">host_key(Algorithm, DaemonOptions) -> {ok, Key} | {error, Reason}</name> <fsummary></fsummary> <desc> <p><strong>Types and description</strong></p> @@ -195,7 +195,7 @@ </func> <func> - <name>is_auth_key(PublicUserKey, User, DaemonOptions) -> Result</name> + <name since="OTP 21.2">is_auth_key(PublicUserKey, User, DaemonOptions) -> Result</name> <fsummary></fsummary> <desc> <p><strong>Types and description</strong></p> @@ -216,7 +216,7 @@ </func> <func> - <name>add_host_key(HostNames, PublicHostKey, ConnectOptions) -> ok | {error, Reason}</name> + <name since="OTP 21.2">add_host_key(HostNames, PublicHostKey, ConnectOptions) -> ok | {error, Reason}</name> <fsummary></fsummary> <desc> <p><strong>Types and description</strong></p> @@ -235,7 +235,7 @@ </func> <func> - <name>is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name> + <name since="OTP 21.2">is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name> <fsummary></fsummary> <desc> <p><strong>Types and description</strong></p> @@ -254,7 +254,7 @@ </func> <func> - <name>user_key(Algorithm, ConnectOptions) -> {ok, PrivateKey} | {error, Reason}</name> + <name since="OTP 21.2">user_key(Algorithm, ConnectOptions) -> {ok, PrivateKey} | {error, Reason}</name> <fsummary></fsummary> <desc> <p><strong>Types and description</strong></p> diff --git a/lib/ssh/doc/src/ssh_server_channel.xml b/lib/ssh/doc/src/ssh_server_channel.xml index 31ba9a3231..a4e18bbfbf 100644 --- a/lib/ssh/doc/src/ssh_server_channel.xml +++ b/lib/ssh/doc/src/ssh_server_channel.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>ssh_server_channel</module> + <module since="OTP 21.0">ssh_server_channel</module> <modulesummary>-behaviour(ssh_server_channel). (Replaces ssh_daemon_channel) </modulesummary> <description> @@ -70,7 +70,7 @@ <funcs> <func> - <name>Module:init(Args) -> {ok, State} | {ok, State, timeout()} | + <name since="OTP 21.0">Module:init(Args) -> {ok, State} | {ok, State, timeout()} | {stop, Reason}</name> <fsummary>Makes necessary initializations and returns the initial channel state if the initializations succeed.</fsummary> @@ -93,7 +93,7 @@ </func> <func> - <name>Module:handle_msg(Msg, State) -> {ok, State} | + <name since="OTP 21.0">Module:handle_msg(Msg, State) -> {ok, State} | {stop, ChannelId, State}</name> <fsummary>Handles other messages than SSH connection protocol, @@ -125,7 +125,7 @@ </func> <func> - <name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, + <name since="OTP 21.0">Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, ChannelId, State}</name> <fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary> <type> @@ -152,7 +152,7 @@ </func> <func> - <name>Module:terminate(Reason, State) -> _</name> + <name since="OTP 21.0">Module:terminate(Reason, State) -> _</name> <fsummary>Does cleaning up before channel process termination. </fsummary> <type> diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml index e2a31bd5f5..013a788a4a 100644 --- a/lib/ssh/doc/src/ssh_server_key_api.xml +++ b/lib/ssh/doc/src/ssh_server_key_api.xml @@ -29,7 +29,7 @@ <date></date> <rev></rev> </header> - <module>ssh_server_key_api</module> + <module since="OTP R16B">ssh_server_key_api</module> <modulesummary> -behaviour(ssh_server_key_api). </modulesummary> @@ -87,7 +87,7 @@ <funcs> <func> - <name>Module:host_key(Algorithm, DaemonOptions) -> + <name since="OTP R16B">Module:host_key(Algorithm, DaemonOptions) -> {ok, Key} | {error, Reason}</name> <fsummary>Fetches the host’s private key.</fsummary> <type> @@ -111,7 +111,7 @@ </func> <func> - <name>Module:is_auth_key(PublicUserKey, User, DaemonOptions) -> Result</name> + <name since="OTP R16B">Module:is_auth_key(PublicUserKey, User, DaemonOptions) -> Result</name> <fsummary>Checks if the user key is authorized.</fsummary> <type> <v>PublicUserKey = <seealso marker="public_key:public_key#type-public_key">public_key:public_key()</seealso></v> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index 8c105147d6..c89092798d 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -29,7 +29,7 @@ <rev></rev> <file>ssh_sftp.sgml</file> </header> - <module>ssh_sftp</module> + <module since="">ssh_sftp</module> <modulesummary>SFTP client.</modulesummary> <description> <p>This module implements an SSH FTP (SFTP) client. SFTP is a @@ -82,7 +82,7 @@ <funcs> <func> - <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, reason()}</name> + <name since="">apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, reason()}</name> <fsummary>Reads asynchronously from an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -98,7 +98,7 @@ </func> <func> - <name>apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | {error, reason()}</name> + <name since="">apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | {error, reason()}</name> <fsummary>Writes asynchronously to an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -116,7 +116,7 @@ </func> <func> - <name>aread(ChannelPid, Handle, Len) -> {async, N} | {error, reason()}</name> + <name since="">aread(ChannelPid, Handle, Len) -> {async, N} | {error, reason()}</name> <fsummary>Reads asynchronously from an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -137,7 +137,7 @@ </func> <func> - <name>awrite(ChannelPid, Handle, Data) -> {async, N} | {error, reason()}</name> + <name since="">awrite(ChannelPid, Handle, Data) -> {async, N} | {error, reason()}</name> <fsummary>Writes asynchronously to an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -159,8 +159,8 @@ </func> <func> - <name>close(ChannelPid, Handle) -></name> - <name>close(ChannelPid, Handle, Timeout) -> ok | {error, reason()}</name> + <name since="">close(ChannelPid, Handle) -></name> + <name since="">close(ChannelPid, Handle, Timeout) -> ok | {error, reason()}</name> <fsummary>Closes an open handle.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -173,8 +173,8 @@ </func> <func> - <name>delete(ChannelPid, Name) -></name> - <name>delete(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name> + <name since="">delete(ChannelPid, Name) -></name> + <name since="">delete(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name> <fsummary>Deletes a file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -188,8 +188,8 @@ </func> <func> - <name>del_dir(ChannelPid, Name) -></name> - <name>del_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name> + <name since="">del_dir(ChannelPid, Name) -></name> + <name since="">del_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name> <fsummary>Deletes an empty directory.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -204,8 +204,8 @@ </func> <func> - <name>list_dir(ChannelPid, Path) -></name> - <name>list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, reason()}</name> + <name since="">list_dir(ChannelPid, Path) -></name> + <name since="">list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, reason()}</name> <fsummary>Lists the directory.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -221,8 +221,8 @@ </func> <func> - <name>make_dir(ChannelPid, Name) -></name> - <name>make_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name> + <name since="">make_dir(ChannelPid, Name) -></name> + <name since="">make_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name> <fsummary>Creates a directory.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -237,8 +237,8 @@ </func> <func> - <name>make_symlink(ChannelPid, Name, Target) -></name> - <name>make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, reason()}</name> + <name since="">make_symlink(ChannelPid, Name, Target) -></name> + <name since="">make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, reason()}</name> <fsummary>Creates a symbolic link.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -253,8 +253,8 @@ </func> <func> - <name>open(ChannelPid, File, Mode) -></name> - <name>open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name> + <name since="">open(ChannelPid, File, Mode) -></name> + <name since="">open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name> <fsummary>Opens a file and returns a handle.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -270,8 +270,8 @@ </desc> </func> <func> - <name>opendir(ChannelPid, Path) -></name> - <name>opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, reason()}</name> + <name since="">opendir(ChannelPid, Path) -></name> + <name since="">opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, reason()}</name> <fsummary>Opens a directory and returns a handle.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -285,8 +285,8 @@ </func> <func> - <name>open_tar(ChannelPid, Path, Mode) -></name> - <name>open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name> + <name since="OTP 17.4">open_tar(ChannelPid, Path, Mode) -></name> + <name since="OTP 17.4">open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name> <fsummary>Opens a tar file on the server to which <c>ChannelPid</c> is connected and returns a handle.</fsummary> <type> @@ -339,8 +339,8 @@ </func> <func> - <name>position(ChannelPid, Handle, Location) -></name> - <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, reason()}</name> + <name since="">position(ChannelPid, Handle, Location) -></name> + <name since="">position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, reason()}</name> <fsummary>Sets the file position of a file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -384,8 +384,8 @@ </func> <func> - <name>pread(ChannelPid, Handle, Position, Len) -></name> - <name>pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name> + <name since="">pread(ChannelPid, Handle, Position, Len) -></name> + <name since="">pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name> <fsummary>Reads from an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -402,8 +402,8 @@ </func> <func> - <name>pwrite(ChannelPid, Handle, Position, Data) -> ok</name> - <name>pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, reason()}</name> + <name since="">pwrite(ChannelPid, Handle, Position, Data) -> ok</name> + <name since="">pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, reason()}</name> <fsummary>Writes to an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -419,8 +419,8 @@ </func> <func> - <name>read(ChannelPid, Handle, Len) -></name> - <name>read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name> + <name since="">read(ChannelPid, Handle, Len) -></name> + <name since="">read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name> <fsummary>Reads from an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -441,8 +441,8 @@ </func> <func> - <name>read_file(ChannelPid, File) -></name> - <name>read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, reason()}</name> + <name since="">read_file(ChannelPid, File) -></name> + <name since="">read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, reason()}</name> <fsummary>Reads a file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -456,8 +456,8 @@ </func> <func> - <name>read_file_info(ChannelPid, Name) -></name> - <name>read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name> + <name since="">read_file_info(ChannelPid, Name) -></name> + <name since="">read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name> <fsummary>Gets information about a file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -481,8 +481,8 @@ </func> <func> - <name>read_link(ChannelPid, Name) -></name> - <name>read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, reason()}</name> + <name since="">read_link(ChannelPid, Name) -></name> + <name since="">read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, reason()}</name> <fsummary>Reads symbolic link.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -496,8 +496,8 @@ </func> <func> - <name>read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, reason()}</name> - <name>read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name> + <name since="">read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, reason()}</name> + <name since="">read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name> <fsummary>Gets information about a symbolic link.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -517,8 +517,8 @@ </func> <func> - <name>rename(ChannelPid, OldName, NewName) -> </name> - <name>rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, reason()}</name> + <name since="">rename(ChannelPid, OldName, NewName) -> </name> + <name since="">rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, reason()}</name> <fsummary>Renames a file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -534,16 +534,16 @@ </func> <func> - <name>start_channel(ConnectionRef) -></name> - <name>start_channel(ConnectionRef, Options) -> + <name since="">start_channel(ConnectionRef) -></name> + <name since="">start_channel(ConnectionRef, Options) -> {ok, Pid} | {error, reason()|term()}</name> - <name>start_channel(Host, Options) -></name> - <name>start_channel(Host, Port, Options) -> + <name since="">start_channel(Host, Options) -></name> + <name since="">start_channel(Host, Port, Options) -> {ok, Pid, ConnectionRef} | {error, reason()|term()}</name> - <name>start_channel(TcpSocket) -></name> - <name>start_channel(TcpSocket, Options) -> + <name since="">start_channel(TcpSocket) -></name> + <name since="">start_channel(TcpSocket, Options) -> {ok, Pid, ConnectionRef} | {error, reason()|term()}</name> <fsummary>Starts an SFTP client.</fsummary> @@ -594,7 +594,7 @@ </func> <func> - <name>stop_channel(ChannelPid) -> ok</name> + <name since="">stop_channel(ChannelPid) -> ok</name> <fsummary>Stops the SFTP client channel.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -606,8 +606,8 @@ </func> <func> - <name>write(ChannelPid, Handle, Data) -></name> - <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, reason()}</name> + <name since="">write(ChannelPid, Handle, Data) -></name> + <name since="">write(ChannelPid, Handle, Data, Timeout) -> ok | {error, reason()}</name> <fsummary>Writes to an open file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -625,8 +625,8 @@ </func> <func> - <name>write_file(ChannelPid, File, Iolist) -></name> - <name>write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, reason()}</name> + <name since="">write_file(ChannelPid, File, Iolist) -></name> + <name since="">write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, reason()}</name> <fsummary>Writes a file.</fsummary> <type> <v>ChannelPid = pid()</v> @@ -641,8 +641,8 @@ </func> <func> - <name>write_file_info(ChannelPid, Name, Info) -></name> - <name>write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, reason()}</name> + <name since="">write_file_info(ChannelPid, Name, Info) -></name> + <name since="">write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, reason()}</name> <fsummary>Writes information for a file.</fsummary> <type> <v>ChannelPid = pid()</v> diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml index 3b34150e98..ee72784add 100644 --- a/lib/ssh/doc/src/ssh_sftpd.xml +++ b/lib/ssh/doc/src/ssh_sftpd.xml @@ -29,7 +29,7 @@ <rev></rev> <file>ssh_sftpd.sgml</file> </header> - <module>ssh_sftpd</module> + <module since="">ssh_sftpd</module> <modulesummary>Specifies the channel process to handle an SFTP subsystem.</modulesummary> <description> <p>Specifies a channel process to handle an SFTP subsystem.</p> @@ -51,7 +51,7 @@ </section> <funcs> <func> - <name>subsystem_spec(Options) -> subsystem_spec()</name> + <name since="">subsystem_spec(Options) -> subsystem_spec()</name> <fsummary>Returns the subsystem specification that allows an SSH daemon to handle the subsystem "sftp".</fsummary> <type> <v>Options = [{Option, Value}]</v> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 086fa6e5f8..9281bf84a7 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -270,25 +270,38 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535, try {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0), #{} = Options0 = ssh_options:handle_options(server, UserOptions), - - {{Host,Port}, ListenSocket} = - open_listen_socket(Host1, Port0, Options0), - - %% Now Host,Port is what to use for the supervisor to register its name, - %% and ListenSocket is for listening on connections. But it is still owned - %% by self()... - - finalize_start(Host, Port, ?GET_OPT(profile, Options0), - ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options0), - fun(Opts, Result) -> - {_, Callback, _} = ?GET_OPT(transport, Opts), - receive - {request_control, ListenSocket, ReqPid} -> - ok = Callback:controlling_process(ListenSocket, ReqPid), - ReqPid ! {its_yours,ListenSocket}, - Result - end - end) + {open_listen_socket(Host1, Port0, Options0), Options0} + of + {{{Host,Port}, ListenSocket}, Options1} -> + try + %% Now Host,Port is what to use for the supervisor to register its name, + %% and ListenSocket is for listening on connections. But it is still owned + %% by self()... + finalize_start(Host, Port, ?GET_OPT(profile, Options1), + ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options1), + fun(Opts, Result) -> + {_, Callback, _} = ?GET_OPT(transport, Opts), + receive + {request_control, ListenSocket, ReqPid} -> + ok = Callback:controlling_process(ListenSocket, ReqPid), + ReqPid ! {its_yours,ListenSocket}, + Result + end + end) + of + {error,Err} -> + close_listen_socket(ListenSocket, Options1), + {error,Err}; + OK -> + OK + catch + error:Error -> + close_listen_socket(ListenSocket, Options1), + error(Error); + exit:Exit -> + close_listen_socket(ListenSocket, Options1), + exit(Exit) + end catch throw:bad_fd -> {error,bad_fd}; @@ -524,6 +537,15 @@ open_listen_socket(_Host0, Port0, Options0) -> {{LHost,LPort}, LSock}. %%%---------------------------------------------------------------- +close_listen_socket(ListenSocket, Options) -> + try + {_, Callback, _} = ?GET_OPT(transport, Options), + Callback:close(ListenSocket) + catch + _C:_E -> ok + end. + +%%%---------------------------------------------------------------- finalize_start(Host, Port, Profile, Options0, F) -> try %% throws error:Error if no usable hostkey is found diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 278f6a9780..aa9ba0f9bb 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -508,11 +508,8 @@ close_our_file({_,Fd}, FileMod, FS0) -> FS1. %%% stat: do the stat -stat(Vsn, ReqId, Data, State, F) when Vsn =< 3-> - <<?UINT32(BLen), BPath:BLen/binary>> = Data, - stat(ReqId, unicode:characters_to_list(BPath), State, F); -stat(Vsn, ReqId, Data, State, F) when Vsn >= 4-> - <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(_Flags)>> = Data, +stat(Vsn, ReqId, Data, State, F) -> + <<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data, stat(ReqId, unicode:characters_to_list(BPath), State, F). fstat(Vsn, ReqId, Data, State) when Vsn =< 3-> diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index da94b5722f..5de6d52092 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -43,7 +43,9 @@ suite() -> {timetrap,{seconds,40}}]. all() -> - [{group, all_tests}]. + [{group, all_tests}, + daemon_already_started + ]. groups() -> [{all_tests, [parallel], [{group, ssh_renegotiate_SUITE}, @@ -801,6 +803,24 @@ daemon_already_started(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +%%% Test that a failed daemon start does not leave the port open +daemon_error_closes_port(Config) -> + GoodSystemDir = proplists:get_value(data_dir, Config), + Port = ssh_test_lib:inet_port(), + {error,_} = ssh_test_lib:daemon(Port, []), % No system dir + case ssh_test_lib:daemon(Port, [{system_dir, GoodSystemDir}]) of + {error,eaddrinuse} -> + {fail, "Port leakage"}; + {error,Error} -> + ct:log("Strange error: ~p",[Error]), + {fail, "Strange error"}; + {Pid, _Host, Port} -> + %% Ok + ssh:stop_daemon(Pid) + end. + + +%%-------------------------------------------------------------------- %%% check that known_hosts is updated correctly known_hosts(Config) when is_list(Config) -> SystemDir = proplists:get_value(data_dir, Config), diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl index 764c52b624..2ac4e5636a 100644 --- a/lib/ssh/test/ssh_bench_SUITE.erl +++ b/lib/ssh/test/ssh_bench_SUITE.erl @@ -109,11 +109,10 @@ connect(Config) -> lists:foreach( fun(KexAlg) -> PrefAlgs = preferred_algorithms(KexAlg), - report([{value, measure_connect(Config, - [{preferred_algorithms,PrefAlgs}])}, - {suite, ?MODULE}, - {name, mk_name(["Connect erlc erld ",KexAlg," [µs]"])} - ]) + TimeMicroSec = measure_connect(Config, + [{preferred_algorithms,PrefAlgs}]), + report(["Connect erlc erld ",KexAlg," [connects per sec]"], + 1000000 / TimeMicroSec) end, KexAlgs). @@ -130,7 +129,7 @@ measure_connect(Config, Opts) -> [begin {Time, {ok,Pid}} = timer:tc(ssh,connect,["localhost", Port, ConnectOptions]), ssh:close(Pid), - Time + Time % in µs end || _ <- lists:seq(1,?Nruns)]). %%%---------------------------------------------------------------- @@ -178,10 +177,6 @@ gen_data(DataSz) -> <<Data0/binary, Data1/binary>>. -%% connect_measure(Port, Cipher, Mac, Data, Options) -> -%% report([{value, 1}, -%% {suite, ?MODULE}, -%% {name, mk_name(["Transfer 1M bytes ",Cipher,"/",Mac," [µs]"])}]); connect_measure(Port, Cipher, Mac, Data, Options) -> AES_GCM = {cipher, []}, @@ -220,10 +215,8 @@ connect_measure(Port, Cipher, Mac, Data, Options) -> ssh:close(C), Time end || _ <- lists:seq(1,?Nruns)], - - report([{value, median(Times)}, - {suite, ?MODULE}, - {name, mk_name(["Transfer 1M bytes ",Cipher,"/",Mac," [µs]"])}]). + report(["Transfer ",Cipher,"/",Mac," [Mbyte per sec]"], + 1000000 / median(Times)). send_wait_acc(C, Ch, Data) -> ssh_connection:send(C, Ch, Data), @@ -238,12 +231,6 @@ send_wait_acc(C, Ch, Data) -> %%% %%%---------------------------------------------------------------- -mk_name(Name) -> [char(C) || C <- lists:concat(Name)]. - -char($-) -> $_; -char(C) -> C. - -%%%---------------------------------------------------------------- preferred_algorithms(KexAlg) -> [{kex, [KexAlg]}, {public_key, ['ssh-rsa']}, @@ -265,11 +252,22 @@ median(Data) when is_list(Data) -> 1 -> lists:nth(N div 2 + 1, SortedData) end, - ct:log("median(~p) = ~p",[SortedData,Median]), + ct:pal("median(~p) = ~p",[SortedData,Median]), Median. +%%%---------------------------------------------------------------- +report(LabelList, Value) -> + Label = report_chars(lists:concat(LabelList)), + ct:pal("ct_event:notify ~p: ~p", [Label, Value]), + ct_event:notify( + #event{name = benchmark_data, + data = [{suite, ?MODULE}, + {name, Label}, + {value, Value}]}). + +report_chars(Cs) -> + [case C of + $- -> $_; + _ -> C + end || C <- Cs]. -report(Data) -> - ct:log("EventData = ~p",[Data]), - ct_event:notify(#event{name = benchmark_data, - data = Data}). diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl index f4eef2dc77..8e82527c6e 100644 --- a/lib/ssh/test/ssh_compat_SUITE.erl +++ b/lib/ssh/test/ssh_compat_SUITE.erl @@ -1126,7 +1126,24 @@ prepare_local_directory(ServerRootDir) -> "chmod 222 unreadable_file", "exit"]. + check_local_directory(ServerRootDir) -> + TimesToTry = 3, % sleep 0.5, 1, 2 and then 4 secs (7.5s in total) + check_local_directory(ServerRootDir, 500, TimesToTry-1). + +check_local_directory(ServerRootDir, SleepTime, N) -> + case do_check_local_directory(ServerRootDir) of + {error,Error} when N>0 -> + %% Could be that the erlang side is faster and the docker's operations + %% are not yet finalized. + %% Sleep for a while and retry a few times: + timer:sleep(SleepTime), + check_local_directory(ServerRootDir, 2*SleepTime, N-1); + Other -> + Other + end. + +do_check_local_directory(ServerRootDir) -> case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of ["ex_tst1","mydir","tst2"] -> {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")), @@ -1161,6 +1178,7 @@ check_local_directory(ServerRootDir) -> {error,{bad_dir_contents,"/"}} end. + call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) -> {DockerIP,DockerPort} = ip_port(Config), {ok,C} = ssh:connect(DockerIP, DockerPort, diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl index 8de550af15..f2c9892f95 100644 --- a/lib/ssh/test/ssh_trpt_test_lib.erl +++ b/lib/ssh/test/ssh_trpt_test_lib.erl @@ -41,15 +41,20 @@ opts = [], timeout = 5000, % ms seen_hello = false, - enc = <<>>, ssh = #ssh{}, % #ssh{} alg_neg = {undefined,undefined}, % {own_kexinit, peer_kexinit} alg, % #alg{} vars = dict:new(), reply = [], % Some repy msgs are generated hidden in ssh_transport :[ prints = [], - return_value - }). + return_value, + + %% Packet retrival and decryption + decrypted_data_buffer = <<>>, + encrypted_data_buffer = <<>>, + aead_data = <<>>, + undecrypted_packet_length + }). -define(role(S), ((S#s.ssh)#ssh.role) ). @@ -475,11 +480,11 @@ recv(S0 = #s{}) -> %%%================================================================ try_find_crlf(Seen, S0) -> - case erlang:decode_packet(line,S0#s.enc,[]) of + case erlang:decode_packet(line,S0#s.encrypted_data_buffer,[]) of {more,_} -> - Line = <<Seen/binary,(S0#s.enc)/binary>>, + Line = <<Seen/binary,(S0#s.encrypted_data_buffer)/binary>>, S0#s{seen_hello = {more,Line}, - enc = <<>>, % didn't find a complete line + encrypted_data_buffer = <<>>, % didn't find a complete line % -> no more characters to test return_value = {more,Line} }; @@ -490,13 +495,13 @@ try_find_crlf(Seen, S0) -> S = opt(print_messages, S0, fun(X) when X==true;X==detail -> {"Recv info~n~p~n",[Line]} end), S#s{seen_hello = false, - enc = Rest, + encrypted_data_buffer = Rest, return_value = {info,Line}}; S1=#s{} -> S = opt(print_messages, S1, fun(X) when X==true;X==detail -> {"Recv hello~n~p~n",[Line]} end), S#s{seen_hello = true, - enc = Rest, + encrypted_data_buffer = Rest, return_value = {hello,Line}} end end. @@ -511,19 +516,73 @@ handle_hello(Bin, S=#s{ssh=C}) -> {{Vp,Vs}, server} -> S#s{ssh = C#ssh{c_vsn=Vp, c_version=Vs}} end. -receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize, +receive_binary_msg(S0=#s{}) -> + case ssh_transport:handle_packet_part( + S0#s.decrypted_data_buffer, + S0#s.encrypted_data_buffer, + S0#s.aead_data, + S0#s.undecrypted_packet_length, + S0#s.ssh) + of + {packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} -> + S1 = S0#s{ssh = Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)}, + decrypted_data_buffer = <<>>, + undecrypted_packet_length = undefined, + aead_data = <<>>, + encrypted_data_buffer = EncryptedDataRest}, + case + catch ssh_message:decode(set_prefix_if_trouble(DecryptedBytes,S1)) + of + {'EXIT',_} -> fail(decode_failed,S1); + + Msg -> + Ssh2 = case Msg of + #ssh_msg_kexinit{} -> + ssh_transport:key_init(opposite_role(Ssh1), Ssh1, DecryptedBytes); + _ -> + Ssh1 + end, + S2 = opt(print_messages, S1, + fun(X) when X==true;X==detail -> {"Recv~n~s~n",[format_msg(Msg)]} end), + S3 = opt(print_messages, S2, + fun(detail) -> {"decrypted bytes ~p~n",[DecryptedBytes]} end), + S3#s{ssh = inc_recv_seq_num(Ssh2), + return_value = Msg + } + end; + + {get_more, DecryptedBytes, EncryptedDataRest, AeadData, TotalNeeded, Ssh1} -> + %% Here we know that there are not enough bytes in + %% EncryptedDataRest to use. We must wait for more. + Remaining = case TotalNeeded of + undefined -> 8; + _ -> TotalNeeded - size(DecryptedBytes) - size(EncryptedDataRest) + end, + receive_binary_msg( + receive_wait(Remaining, + S0#s{encrypted_data_buffer = EncryptedDataRest, + decrypted_data_buffer = DecryptedBytes, + undecrypted_packet_length = TotalNeeded, + aead_data = AeadData, + ssh = Ssh1} + )) + end. + + + +old_receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize, recv_mac_size = MacSize } }) -> - case size(S0#s.enc) >= max(8,BlockSize) of + case size(S0#s.encrypted_data_buffer) >= max(8,BlockSize) of false -> %% Need more bytes to decode the packet_length field - Remaining = max(8,BlockSize) - size(S0#s.enc), + Remaining = max(8,BlockSize) - size(S0#s.encrypted_data_buffer), receive_binary_msg( receive_wait(Remaining, S0) ); true -> %% Has enough bytes to decode the packet_length field {_, <<?UINT32(PacketLen), _/binary>>, _} = - ssh_transport:decrypt_blocks(S0#s.enc, BlockSize, C0), % FIXME: BlockSize should be at least 4 + ssh_transport:decrypt_blocks(S0#s.encrypted_data_buffer, BlockSize, C0), % FIXME: BlockSize should be at least 4 %% FIXME: Check that ((4+PacketLen) rem BlockSize) == 0 ? @@ -534,19 +593,19 @@ receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize, ((4+PacketLen) rem BlockSize) =/= 0 -> fail(bad_packet_length_modulo, S0); % FIXME: disconnect - size(S0#s.enc) >= (4 + PacketLen + MacSize) -> + size(S0#s.encrypted_data_buffer) >= (4 + PacketLen + MacSize) -> %% has the whole packet S0; true -> %% need more bytes to get have the whole packet - Remaining = (4 + PacketLen + MacSize) - size(S0#s.enc), + Remaining = (4 + PacketLen + MacSize) - size(S0#s.encrypted_data_buffer), receive_wait(Remaining, S0) end, %% Decrypt all, including the packet_length part (re-use the initial #ssh{}) {C1, SshPacket = <<?UINT32(_),?BYTE(PadLen),Tail/binary>>, EncRest} = - ssh_transport:decrypt_blocks(S1#s.enc, PacketLen+4, C0), + ssh_transport:decrypt_blocks(S1#s.encrypted_data_buffer, PacketLen+4, C0), PayloadLen = PacketLen - 1 - PadLen, <<CompressedPayload:PayloadLen/binary, _Padding:PadLen/binary>> = Tail, @@ -573,7 +632,7 @@ receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize, S3 = opt(print_messages, S2, fun(detail) -> {"decrypted bytes ~p~n",[SshPacket]} end), S3#s{ssh = inc_recv_seq_num(C3), - enc = Rest, + encrypted_data_buffer = Rest, return_value = Msg } end @@ -602,7 +661,7 @@ receive_poll(S=#s{socket=Sock}) -> inet:setopts(Sock, [{active,once}]), receive {tcp,Sock,Data} -> - receive_poll( S#s{enc = <<(S#s.enc)/binary,Data/binary>>} ); + receive_poll( S#s{encrypted_data_buffer = <<(S#s.encrypted_data_buffer)/binary,Data/binary>>} ); {tcp_closed,Sock} -> throw({tcp,tcp_closed}); {tcp_error, Sock, Reason} -> @@ -616,7 +675,7 @@ receive_wait(S=#s{socket=Sock, inet:setopts(Sock, [{active,once}]), receive {tcp,Sock,Data} -> - S#s{enc = <<(S#s.enc)/binary,Data/binary>>}; + S#s{encrypted_data_buffer = <<(S#s.encrypted_data_buffer)/binary,Data/binary>>}; {tcp_closed,Sock} -> throw({tcp,tcp_closed}); {tcp_error, Sock, Reason} -> @@ -627,11 +686,11 @@ receive_wait(S=#s{socket=Sock, receive_wait(N, S=#s{socket=Sock, timeout=Timeout, - enc=Enc0}) when N>0 -> + encrypted_data_buffer=Enc0}) when N>0 -> inet:setopts(Sock, [{active,once}]), receive {tcp,Sock,Data} -> - receive_wait(N-size(Data), S#s{enc = <<Enc0/binary,Data/binary>>}); + receive_wait(N-size(Data), S#s{encrypted_data_buffer = <<Enc0/binary,Data/binary>>}); {tcp_closed,Sock} -> throw({tcp,tcp_closed}); {tcp_error, Sock, Reason} -> diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 3ba1e177be..2890d7fe5b 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.7.2 +SSH_VSN = 4.7.3 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/specs/.gitignore b/lib/ssl/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/ssl/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index c72b6d6cc4..7cf251d8f9 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -80,11 +80,16 @@ 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 += DVIPS_FLAGS += +SPECS_FLAGS = -I../../../public_key/include -I../../../public_key/src -I../../.. # ---------------------------------------------------- # Targets @@ -92,7 +97,7 @@ DVIPS_FLAGS += $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: html pdf man $(TOP_PDF_FILE): $(XML_FILES) @@ -105,6 +110,7 @@ clean clean_docs: rm -rf $(XMLDIR) rm -f $(MAN3DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECS_FILES) rm -f errs core *~ man: $(MAN3_FILES) $(MAN6_FILES) diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 48372babf8..674e38b054 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,58 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix encoding of the SRP extension length field in ssl. + The old encoding of the SRP extension length could cause + interoperability problems with third party SSL + implementations when SRP was used.</p> + <p> + Own Id: OTP-15477 Aux Id: ERL-790 </p> + </item> + <item> + <p> + Guarantee active once data delivery, handling TCP stream + properly.</p> + <p> + Own Id: OTP-15504 Aux Id: ERL-371 </p> + </item> + <item> + <p> + Correct gen_statem returns for some error cases</p> + <p> + Own Id: OTP-15505</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed renegotiation bug. Client did not handle server + initiated renegotiation correctly after rewrite to two + connection processes, due to ERL-622 commit + d87ac1c55188f5ba5cdf72384125d94d42118c18. This could + manifest it self as a " bad_record_mac" alert.</p> + <p> + Also included are some optimizations</p> + <p> + Own Id: OTP-15489 Aux Id: ERL-308 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/specs.xml b/lib/ssl/doc/src/specs.xml new file mode 100644 index 0000000000..50e9428fec --- /dev/null +++ b/lib/ssl/doc/src/specs.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_ssl_crl_cache_api.xml"/> + <xi:include href="../specs/specs_ssl_crl_cache.xml"/> + <xi:include href="../specs/specs_ssl_session_cache_api.xml"/> + <xi:include href="../specs/specs_ssl.xml"/> +</specs> + + diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 3029977745..90a9181ede 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -28,7 +28,7 @@ <rev></rev> <file>ssl.xml</file> </header> - <module>ssl</module> + <module since="">ssl</module> <modulesummary>Interface Functions for Secure Socket Layer</modulesummary> <description> <p> @@ -37,277 +37,337 @@ <seealso marker="ssl_app">ssl(6)</seealso>. </p> </description> - - <section> - <title>DATA TYPES</title> - <p>The following data types are used in the functions for SSL/TLS/DTLS:</p> - - <taglist> - - <tag><c>boolean() =</c></tag> - <item><p><c>true | false</c></p></item> - - <tag><c>option() =</c></tag> - <item><p><c>socketoption() | ssl_option() | transport_option()</c></p> - </item> - - <tag><c>socketoption() =</c></tag> - <item><p><c>proplists:property()</c></p> - <p>The default socket options are - <c>[{mode,list},{packet, 0},{header, 0},{active, true}]</c>.</p> - <p>For valid options, see the - <seealso marker="kernel:inet">inet(3)</seealso>, - <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> and - <seealso marker="kernel:gen_tcp">gen_udp(3)</seealso> - manual pages - in Kernel. Note that stream oriented options such as packet are only relevant for SSL/TLS and not DTLS</p></item> - - <tag><marker id="type-ssloption"/><c>ssl_option() =</c></tag> - <item> - <p><c>{verify, verify_type()}</c></p> - <p><c>| {verify_fun, {fun(), term()}}</c></p> - <p><c>| {fail_if_no_peer_cert, boolean()}</c></p> - <p><c>| {depth, integer()}</c></p> - <p><c>| {cert, public_key:der_encoded()}</c></p> - <p><c>| {certfile, path()}</c></p> - <p><c>| {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' - | 'PrivateKeyInfo', public_key:der_encoded()} | - #{algorithm := rsa | dss | ecdsa, - engine := crypto:engine_ref(), key_id := crypto:key_id(), password => crypto:password()}</c></p> - <p><c>| {keyfile, path()}</c></p> - <p><c>| {password, string()}</c></p> - <p><c>| {cacerts, [public_key:der_encoded()]}</c></p> - <p><c>| {cacertfile, path()}</c></p> - <p><c>| {dh, public_key:der_encoded()}</c></p> - <p><c>| {dhfile, path()}</c></p> - <p><c>| {ciphers, ciphers()}</c></p> - <p><c>| {user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, - {srp_identity, {string(), string()}}</c></p> - <p><c>| {reuse_sessions, boolean()}</c></p> - <p><c>| {reuse_session, fun()} {next_protocols_advertised, [binary()]}</c></p> - <p><c>| {client_preferred_next_protocols, {client | server, - [binary()]} | {client | server, [binary()], binary()}}</c></p> - <p><c>| {log_alert, boolean()}</c></p> - <p><c>| {server_name_indication, hostname() | disable}</c></p> - <p><c>| {customize_hostname_check, list()}</c></p> - <p><c>| {sni_hosts, [{hostname(), [ssl_option()]}]}</c></p> - <p><c>| {sni_fun, SNIfun::fun()}</c></p> - </item> - - <tag><c>transport_option() =</c></tag> - <item><p><c>{cb_info, {CallbackModule::atom(), DataTag::atom(), - - ClosedTag::atom(), ErrTag:atom()}}</c></p> - <p>Defaults to <c>{gen_tcp, tcp, tcp_closed, tcp_error}</c> for TLS - and <c>{gen_udp, udp, udp_closed, udp_error}</c> for DTLS. Can be used - to customize the transport layer. For TLS the callback module must implement a - reliable transport protocol, behave as <c>gen_tcp</c>, and have functions - corresponding to <c>inet:setopts/2</c>, <c>inet:getopts/2</c>, - <c>inet:peername/1</c>, <c>inet:sockname/1</c>, and <c>inet:port/1</c>. - The callback <c>gen_tcp</c> is treated specially and calls <c>inet</c> - directly. For DTLS this feature must be considered exprimental.</p> - <taglist> - <tag><c>CallbackModule =</c></tag> - <item><p><c>atom()</c></p></item> - <tag><c>DataTag =</c></tag> - <item><p><c>atom()</c></p> - <p>Used in socket data message.</p></item> - <tag><c>ClosedTag =</c></tag> - <item><p><c>atom()</c></p> - <p>Used in socket close message.</p></item> - </taglist> - </item> - - <tag><c>verify_type() =</c></tag> - <item><p><c>verify_none | verify_peer</c></p></item> - - <tag><c>path() =</c></tag> - <item><p><c>string()</c></p> - <p>Represents a file path.</p></item> - <tag><c>public_key:der_encoded() =</c></tag> - <item><p><c>binary()</c></p> - <p>ASN.1 DER-encoded entity as an Erlang binary.</p></item> + <!-- + ================================================================ + = Data types = + ================================================================ + --> - <tag><c>host() =</c></tag> - <item><p><c>hostname() | ipaddress()</c></p></item> + <datatypes> + <datatype_title>Types used in SSL/TLS/DTLS</datatype_title> - <tag><c>hostname() =</c></tag> - <item><p><c>string() - DNS hostname</c></p></item> - - <tag><c>ip_address() =</c></tag> - <item><p><c>{N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 - </c></p></item> + + <datatype> + <name name="socket"/> + </datatype> + + <datatype> + <name name="sslsocket"/> + <desc> + <p>An opaque reference to the TLS/DTLS connection, may be used for equality matching.</p> + </desc> + </datatype> + + <datatype> + <name name="tls_option"/> + </datatype> + + <datatype> + <name name="tls_client_option"/> + </datatype> + + <datatype> + <name name="tls_server_option"/> + </datatype> + + + <datatype> + <name name="socket_option"/> + <desc> + <p>The default socket options are + <c>[{mode,list},{packet, 0},{header, 0},{active, true}]</c>.</p> + <p>For valid options, see the + <seealso marker="kernel:inet">inet(3)</seealso>, + <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> and + <seealso marker="kernel:gen_tcp">gen_udp(3)</seealso> + manual pages in Kernel. Note that stream oriented options such as packet + are only relevant for SSL/TLS and not DTLS</p> + </desc> + </datatype> - <tag><c>sslsocket() =</c></tag> - <item><p>opaque()</p></item> - - <tag><marker id="type-protocol"/><c> protocol_version() =</c></tag> - <item><p><c> ssl_tls_protocol() | dtls_protocol() </c></p></item> - - <item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item> - - <tag><marker id="type-protocol"/><c> dtls_protocol() =</c></tag> - <item><p><c>'dtlsv1' | 'dtlsv1.2'</c></p></item> - - <tag><c>ciphers() =</c></tag> - <item><p><c>= [ciphersuite()]</c></p> - <p>Tuples and string formats accepted by versions - before ssl-8.2.4 will be converted for backwards compatibility</p></item> - - <tag><c>ciphersuite() =</c></tag> - <item><p><c> - #{key_exchange := key_exchange(), - cipher := cipher(), - mac := MAC::hash() | aead, - prf := PRF::hash() | default_prf} </c></p></item> - - <tag><c>key_exchange()=</c></tag> - <item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk - | rsa_psk | srp_anon | srp_dss | srp_rsa | ecdh_anon | ecdh_ecdsa - | ecdhe_ecdsa | ecdh_rsa | ecdhe_rsa</c></p></item> - - <tag><c>cipher() =</c></tag> - <item><p><c>rc4_128 | des_cbc | '3des_ede_cbc' - | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305</c></p></item> - - <tag><c>hash() =</c></tag> - <item><p><c>md5 | sha | sha224 | sha256 | sha348 | sha512</c></p></item> - - <tag><c>prf_random() =</c></tag> - <item><p><c>client_random | server_random</c></p></item> - - <tag><c>cipher_filters() =</c></tag> - <item><p><c> [{key_exchange | cipher | mac | prf, algo_filter()}])</c></p></item> - - <tag><c>algo_filter() =</c></tag> - <item><p>fun(key_exchange() | cipher() | hash() | aead | default_prf) -> true | false </p></item> - - <tag><c>srp_param_type() =</c></tag> - <item><p><c>srp_1024 | srp_1536 | srp_2048 | srp_3072 - | srp_4096 | srp_6144 | srp_8192</c></p></item> - - <tag><c>SNIfun::fun()</c></tag> - <item><p><c>= fun(ServerName :: string()) -> [ssl_option()]</c></p></item> - - <tag><c>named_curve() =</c></tag> - <item><p><c>sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 - | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 - | sect283k1 | sect283r1 | brainpoolP256r1 | secp256k1 | secp256r1 - | sect239k1 | sect233k1 | sect233r1 | secp224k1 | secp224r1 - | sect193r1 | sect193r2 | secp192k1 | secp192r1 | sect163k1 - | sect163r1 | sect163r2 | secp160k1 | secp160r1 | secp160r2</c></p></item> - - <tag><c>hello_extensions() =</c></tag> - <item><p><c>#{renegotiation_info => binary() | undefined, - signature_algs => [{hash(), ecsda| rsa| dsa}] | undefined - alpn => binary() | undefined, - next_protocol_negotiation => binary() | undefined, - srp => string() | undefined, - ec_point_formats => list() | undefined, - elliptic_curves => [oid] | undefined, - sni => string() | undefined} - }</c></p></item> + <datatype> + <name name="active_msgs"/> + <desc> + <p>When a TLS/DTLS socket is in active mode (the default), data from the + socket is delivered to the owner of the socket in the form of + messages as described above.</p> + <p>The <c>ssl_passive</c> message is sent only when the socket is in + <c>{active, N}</c> mode and the counter dropped to 0. It indicates + that the socket has transitioned to passive (<c>{active, false}</c>) mode.</p> + </desc> + </datatype> - - </taglist> - </section> - - <section> - <title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title> - - <p>The following options have the same meaning in the client and - the server:</p> + <datatype> + <name name="transport_option"/> + <desc> + <p>Defaults to <c>{gen_tcp, tcp, tcp_closed, tcp_error}</c> + for TLS and <c>{gen_udp, udp, udp_closed, udp_error}</c> for + DTLS. Can be used to customize the transport layer. The tag + values should be the values used by the underlying transport + in its active mode messages. For TLS the callback module must implement a + reliable transport protocol, behave as <c>gen_tcp</c>, and have functions + corresponding to <c>inet:setopts/2</c>, <c>inet:getopts/2</c>, + <c>inet:peername/1</c>, <c>inet:sockname/1</c>, and <c>inet:port/1</c>. + The callback <c>gen_tcp</c> is treated specially and calls <c>inet</c> + directly. For DTLS this feature must be considered exprimental. + </p> + </desc> + </datatype> + + <datatype> + <name name="host"/> + </datatype> + + <datatype> + <name name="hostname"/> + </datatype> + + <datatype> + <name name="ip_address"/> + </datatype> + + <datatype> + <name name="protocol_version"/> + </datatype> + + <datatype> + <name name="tls_version"/> + </datatype> + + <datatype> + <name name="dtls_version"/> + </datatype> + + <datatype> + <name name="legacy_version"/> + </datatype> + + <datatype> + <name name="prf_random"/> + </datatype> + + <datatype> + <name name="verify_type"/> + </datatype> + + <datatype> + <name name="ciphers"/> + </datatype> + + <datatype> + <name name="erl_cipher_suite"/> + </datatype> + + <datatype> + <name name="cipher"/> + </datatype> - <taglist> - - <tag><c>{protocol, tls | dtls}</c></tag> - <item><p>Choose TLS or DTLS protocol for the transport layer security. - Defaults to <c>tls</c> Introduced in OTP 20, DTLS support is considered - experimental in this release. Other transports than UDP are not yet supported.</p></item> - - <tag><c>{handshake, hello | full}</c></tag> - <item><p> Defaults to <c>full</c>. If hello is specified the handshake will - pause after the hello message and give the user a possibility make decisions - based on hello extensions before continuing or aborting the handshake by calling - <seealso marker="#handshake_continue-3"> handshake_continue/3</seealso> or - <seealso marker="#handshake_cancel-1"> handshake_cancel/1</seealso> - </p></item> - - <tag><c>{cert, public_key:der_encoded()}</c></tag> - <item><p>The DER-encoded users certificate. If this option - is supplied, it overrides option <c>certfile</c>.</p></item> - - <tag><c>{certfile, path()}</c></tag> - <item><p>Path to a file containing the user certificate.</p></item> - - <tag> - <marker id="key_option_def"/> - <c>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' - |'PrivateKeyInfo', public_key:der_encoded()} | #{algorithm := rsa | dss | ecdsa, - engine := crypto:engine_ref(), key_id := crypto:key_id(), password => crypto:password()}</c></tag> - <item><p>The DER-encoded user's private key or a map refering to a crypto - engine and its key reference that optionally can be password protected, - seealso <seealso marker="crypto:crypto#engine_load-4"> crypto:engine_load/4 - </seealso> and <seealso marker="crypto:engine_load"> Crypto's Users Guide</seealso>. If this option - is supplied, it overrides option <c>keyfile</c>.</p></item> - - <tag><c>{keyfile, path()}</c></tag> - <item><p>Path to the file containing the user's - private PEM-encoded key. As PEM-files can contain several - entries, this option defaults to the same file as given by - option <c>certfile</c>.</p></item> - - <tag><c>{password, string()}</c></tag> - <item><p>String containing the user's password. Only used if the - private keyfile is password-protected.</p></item> - - <tag><c>{ciphers, ciphers()}</c></tag> - <item><p>Supported cipher suites. The function - <c>cipher_suites/0</c> can be used to find all ciphers that are - supported by default. <c>cipher_suites(all)</c> can be called - to find all available cipher suites. Pre-Shared Key - (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC 4279</url> and - <url href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>), - Secure Remote Password - (<url href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>), RC4 cipher suites, - and anonymous cipher suites only work if explicitly enabled by - this option; they are supported/enabled by the peer also. - Anonymous cipher suites are supported for testing purposes - only and are not be used when security matters.</p></item> - - <tag><c>{eccs, [named_curve()]}</c></tag> - <item><p> Allows to specify the order of preference for named curves - and to restrict their usage when using a cipher suite supporting them. - </p></item> - - <tag><c>{secure_renegotiate, boolean()}</c></tag> - <item><p>Specifies if to reject renegotiation attempt that does - not live up to - <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. - By default <c>secure_renegotiate</c> is set to <c>true</c>, - that is, secure renegotiation is enforced. If set to <c>false</c> secure renegotiation - will still be used if possible, - but it falls back to insecure renegotiation if the peer - does not support - <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.</p> - </item> - - <tag><c>{depth, integer()}</c></tag> - <item><p>Maximum number of non-self-issued + <datatype> + <name name="legacy_cipher"/> + </datatype> + + <datatype> + <name name="cipher_filters"/> + </datatype> + + <datatype> + <name name="hash"/> + </datatype> + + <datatype> + <name name="sha2"/> + </datatype> + + <datatype> + <name name="legacy_hash"/> + </datatype> + + <datatype> + <name name="old_cipher_suite"/> + </datatype> + + <datatype> + <name name="signature_algs"/> + </datatype> + + <datatype> + <name name="sign_algo"/> + </datatype> + + <datatype> + <name name="kex_algo"/> + </datatype> + + <datatype> + <name name="algo_filter"/> + </datatype> + + <datatype> + <name name="eccs"/> + </datatype> + + <datatype> + <name name="named_curve"/> + </datatype> + + <datatype> + <name name="psk_identity"/> + </datatype> + + <datatype> + <name name="srp_identity"/> + </datatype> + + <datatype> + <name name="srp_param_type"/> + </datatype> + + <datatype> + <name name="app_level_protocol"/> + </datatype> + + <datatype> + <name name="protocol_extensions"/> + </datatype> + + <datatype> + <name name="error_alert"/> + </datatype> + + <datatype> + <name name="tls_alert"/> + </datatype> + + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</datatype_title> + + <datatype> + <name name="common_option"/> + </datatype> + + <datatype> + <name since="OTP 20" name="protocol"/> + <desc> + <p>Choose TLS or DTLS protocol for the transport layer security. + Defaults to <c>tls</c>. For DTLS other transports than UDP are not yet supported.</p> + </desc> + </datatype> + + <datatype> + <name name="handshake_completion"/> + <desc> + <p>Defaults to <c>full</c>. If hello is specified the handshake will + pause after the hello message and give the user a possibility make decisions + based on hello extensions before continuing or aborting the handshake by calling + <seealso marker="#handshake_continue-3"> handshake_continue/3</seealso> or + <seealso marker="#handshake_cancel-1"> handshake_cancel/1</seealso></p> + </desc> + </datatype> + + <datatype> + <name name="cert"/> + <desc> + <p>The DER-encoded users certificate. If this option + is supplied, it overrides option <c>certfile</c>.</p> + </desc> + </datatype> + + <datatype> + <name name="cert_pem"/> + <desc> + <p>Path to a file containing the user certificate on PEM format.</p> + </desc> + </datatype> + + <datatype> + <name name="key"/> + <desc> + <p>The DER-encoded user's private key or a map refering to a crypto + engine and its key reference that optionally can be password protected, + seealso <seealso marker="crypto:crypto#engine_load-4"> crypto:engine_load/4 + </seealso> and <seealso marker="crypto:engine_load"> Crypto's Users Guide</seealso>. If this option + is supplied, it overrides option <c>keyfile</c>.</p> + </desc> + </datatype> + + <datatype> + <name name="key_pem"/> + <desc> + <p>Path to the file containing the user's + private PEM-encoded key. As PEM-files can contain several + entries, this option defaults to the same file as given by + option <c>certfile</c>.</p> + </desc> + </datatype> + + <datatype> + <name name="key_password"/> + <desc> + <p>String containing the user's password. Only used if the + private keyfile is password-protected.</p> + </desc> + </datatype> + + <datatype> + <name name="cipher_suites"/> + <desc> + <p>Supported cipher suites. The function + <c>cipher_suites/2</c> can be used to find all ciphers that + are supported by default. <c>cipher_suites(all, 'tlsv1.2')</c> can be + called to find all available cipher suites. Pre-Shared Key + (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC + 4279</url> and <url + href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>), + Secure Remote Password (<url + href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>), + RC4, 3DES, DES cipher suites, and anonymous cipher suites only work if + explicitly enabled by this option; they are supported/enabled + by the peer also. Anonymous cipher suites are supported for + testing purposes only and are not be used when security + matters.</p> + </desc> + </datatype> + + <datatype> + <name name="eccs"/> + <desc><p> Allows to specify the order of preference for named curves + and to restrict their usage when using a cipher suite supporting them.</p> + </desc> + </datatype> + + <datatype> + <name name="secure_renegotiation"/> + <desc><p>Specifies if to reject renegotiation attempt that does + not live up to <url + href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. By + default <c>secure_renegotiate</c> is set to <c>true</c>, that + is, secure renegotiation is enforced. If set to <c>false</c> + secure renegotiation will still be used if possible, but it + falls back to insecure renegotiation if the peer does not + support <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC + 5746</url>.</p> + </desc> + </datatype> + + <datatype> + <name name="allowed_cert_chain_length"/> + <desc><p>Maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path. So, if depth is 0 the PEER must be signed by the trusted ROOT-CA directly; if 1 the path can be PEER, CA, ROOT-CA; if 2 the path can be PEER, CA, CA, - ROOT-CA, and so on. The default value is 1.</p></item> - - <tag><marker id="verify_fun"/><c>{verify_fun, {Verifyfun :: fun(), InitialUserState :: - term()}}</c></tag> - <item><p>The verification fun is to be defined as follows:</p> + ROOT-CA, and so on. The default value is 1.</p> + </desc> + </datatype> + + <datatype> + <name name="custom_verify"/> + <desc> + <p>The verification fun is to be defined as follows:</p> <code> -fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revoked, -atom()}} | +fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | + {revoked, atom()}} | {extension, #'Extension'{}}, InitialUserState :: term()) -> {valid, UserState :: term()} | {valid_peer, UserState :: term()} | {fail, Reason :: term()} | {unknown, UserState :: term()}. @@ -315,20 +375,21 @@ atom()}} | <p>The verification fun is called during the X509-path validation when an error or an extension unknown to the SSL - application is encountered. It is also called - when a certificate is considered valid by the path validation - to allow access to each certificate in the path to the user - application. It differentiates between the peer - certificate and the CA certificates by using <c>valid_peer</c> or - <c>valid</c> as second argument to the verification fun. See the - <seealso marker="public_key:public_key_records">public_key User's - Guide</seealso> for definition of <c>#'OTPCertificate'{}</c> and - <c>#'Extension'{}</c>.</p> + application is encountered. It is also called when a + certificate is considered valid by the path validation to + allow access to each certificate in the path to the user + application. It differentiates between the peer certificate + and the CA certificates by using <c>valid_peer</c> or + <c>valid</c> as second argument to the verification fun. See + the <seealso marker="public_key:public_key_records">public_key + User's Guide</seealso> for definition of + <c>#'OTPCertificate'{}</c> and <c>#'Extension'{}</c>.</p> <list type="bulleted"> - <item><p>If the verify callback fun returns <c>{fail, Reason}</c>, - the verification process is immediately stopped, an alert is - sent to the peer, and the TLS/DTLS handshake terminates.</p></item> + <item><p>If the verify callback fun returns <c>{fail, + Reason}</c>, the verification process is immediately + stopped, an alert is sent to the peer, and the TLS/DTLS + handshake terminates.</p></item> <item><p>If the verify callback fun returns <c>{valid, UserState}</c>, the verification process continues.</p></item> <item><p>If the verify callback fun always returns @@ -378,10 +439,12 @@ atom()}} | <taglist> <tag><c>unknown_ca</c></tag> - <item><p>No trusted CA was found in the trusted store. The trusted CA is - normally a so called ROOT CA, which is a self-signed certificate. Trust can - be claimed for an intermediate CA (trusted anchor does not have to be - self-signed according to X-509) by using option <c>partial_chain</c>.</p> + <item><p>No trusted CA was found in the trusted store. The + trusted CA is normally a so called ROOT CA, which is a + self-signed certificate. Trust can be claimed for an + intermediate CA (trusted anchor does not have to be + self-signed according to X-509) by using option + <c>partial_chain</c>.</p> </item> <tag><c>selfsigned_peer</c></tag> @@ -392,15 +455,17 @@ atom()}} | marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso> </p></item> </taglist> - </item> - - <tag><c>{crl_check, boolean() | peer | best_effort }</c></tag> - <item> + </desc> + </datatype> + + <datatype> + <name name="crl_check"/> + <desc> <p>Perform CRL (Certificate Revocation List) verification <seealso marker="public_key:public_key#pkix_crls_validate-3"> - (public_key:pkix_crls_validate/3)</seealso> on all the certificates during the path validation - <seealso - marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3) + (public_key:pkix_crls_validate/3)</seealso> on all the + certificates during the path validation <seealso + marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3) </seealso> of the certificate chain. Defaults to <c>false</c>.</p> @@ -412,112 +477,111 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid <item>if certificate revocation status can not be determined it will be accepted as valid.</item> </taglist> - + <p>The CA certificates specified for the connection will be used to construct the certificate chain validating the CRLs.</p> <p>The CRLs will be fetched from a local or external cache. See <seealso marker="ssl:ssl_crl_cache_api">ssl_crl_cache_api(3)</seealso>.</p> - </item> - - <tag><c>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</c></tag> - <item> - <p>Specify how to perform lookup and caching of certificate revocation lists. - <c>Module</c> defaults to <seealso marker="ssl:ssl_crl_cache">ssl_crl_cache</seealso> - with <c> DbHandle </c> being <c>internal</c> and an - empty argument list.</p> - - <p>There are two implementations available:</p> - - <taglist> - <tag><c>ssl_crl_cache</c></tag> - <item> - <p>This module maintains a cache of CRLs. CRLs can be - added to the cache using the function <seealso - marker="ssl:ssl_crl_cache#insert-1">ssl_crl_cache:insert/1</seealso>, - and optionally automatically fetched through HTTP if the - following argument is specified:</p> - - <taglist> - <tag><c>{http, timeout()}</c></tag> - <item><p> - Enables fetching of CRLs specified as http URIs in<seealso - marker="public_key:public_key_records">X509 certificate extensions</seealso>. - Requires the OTP inets application.</p> - </item> - </taglist> - </item> - - <tag><c>ssl_crl_hash_dir</c></tag> - <item> - <p>This module makes use of a directory where CRLs are - stored in files named by the hash of the issuer name.</p> - - <p>The file names consist of eight hexadecimal digits - followed by <c>.rN</c>, where <c>N</c> is an integer, - e.g. <c>1a2b3c4d.r0</c>. For the first version of the - CRL, <c>N</c> starts at zero, and for each new version, - <c>N</c> is incremented by one. The OpenSSL utility - <c>c_rehash</c> creates symlinks according to this - pattern.</p> - - <p>For a given hash value, this module finds all - consecutive <c>.r*</c> files starting from zero, and those - files taken together make up the revocation list. CRL - files whose <c>nextUpdate</c> fields are in the past, or - that are issued by a different CA that happens to have the - same name hash, are excluded.</p> - - <p>The following argument is required:</p> - - <taglist> - <tag><c>{dir, string()}</c></tag> - <item><p>Specifies the directory in which the CRLs can be found.</p></item> - </taglist> - - </item> - - <tag><c>max_handshake_size</c></tag> - <item> - <p>Integer (24 bits unsigned). Used to limit the size of - valid TLS handshake packets to avoid DoS attacks. - Defaults to 256*1024.</p> - </item> - - </taglist> - - </item> + </desc> + </datatype> - <tag><c>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} | - unknown_ca }</c></tag> - <item><p>Claim an intermediate CA in the chain as trusted. TLS then - performs <seealso - marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso> - with the selected CA as trusted anchor and the rest of the chain.</p></item> + <datatype> + <name name="crl_cache_opts"/> + <desc> + <p>Specify how to perform lookup and caching of certificate revocation lists. + <c>Module</c> defaults to <seealso marker="ssl:ssl_crl_cache">ssl_crl_cache</seealso> + with <c> DbHandle </c> being <c>internal</c> and an + empty argument list.</p> + + <p>There are two implementations available:</p> + + <taglist> + <tag><c>ssl_crl_cache</c></tag> + <item> + <p>This module maintains a cache of CRLs. CRLs can be + added to the cache using the function <seealso + marker="ssl:ssl_crl_cache#insert-1">ssl_crl_cache:insert/1</seealso>, + and optionally automatically fetched through HTTP if the + following argument is specified:</p> + + <taglist> + <tag><c>{http, timeout()}</c></tag> + <item><p> + Enables fetching of CRLs specified as http URIs in<seealso + marker="public_key:public_key_records">X509 certificate extensions</seealso>. + Requires the OTP inets application.</p> + </item> + </taglist> + </item> + + <tag><c>ssl_crl_hash_dir</c></tag> + <item> + <p>This module makes use of a directory where CRLs are + stored in files named by the hash of the issuer name.</p> + + <p>The file names consist of eight hexadecimal digits + followed by <c>.rN</c>, where <c>N</c> is an integer, + e.g. <c>1a2b3c4d.r0</c>. For the first version of the + CRL, <c>N</c> starts at zero, and for each new version, + <c>N</c> is incremented by one. The OpenSSL utility + <c>c_rehash</c> creates symlinks according to this + pattern.</p> + + <p>For a given hash value, this module finds all + consecutive <c>.r*</c> files starting from zero, and those + files taken together make up the revocation list. CRL + files whose <c>nextUpdate</c> fields are in the past, or + that are issued by a different CA that happens to have the + same name hash, are excluded.</p> + + <p>The following argument is required:</p> + + <taglist> + <tag><c>{dir, string()}</c></tag> + <item><p>Specifies the directory in which the CRLs can be found.</p></item> + </taglist> + </item> + </taglist> + </desc> + </datatype> + + <datatype> + <name name="root_fun"/> + <desc> + <code> +fun(Chain::[public_key:der_encoded()]) -> + {trusted_ca, DerCert::public_key:der_encoded()} | unknown_ca} + </code> + <p>Claim an intermediate CA in the chain as trusted. TLS then + performs <seealso + marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso> + with the selected CA as trusted anchor and the rest of the chain.</p> + </desc> + </datatype> - <tag><c>{versions, [protocol_version()]}</c></tag> - <item><p>TLS protocol versions supported by started clients and servers. + <datatype> + <name name="protocol_versions"/> + <desc><p>TLS protocol versions supported by started clients and servers. This option overrides the application environment option <c>protocol_version</c> and <c>dtls_protocol_version</c>. If the environment option is not set, it defaults to all versions, except SSL-3.0, supported by the SSL application. - See also <seealso marker="ssl:ssl_app">ssl(6).</seealso></p></item> + See also <seealso marker="ssl:ssl_app">ssl(6).</seealso></p> + </desc> + </datatype> - <tag><c>{hibernate_after, integer()|undefined}</c></tag> - <item><p>When an integer-value is specified, <c>TLS/DTLS-connection</c> - goes into hibernation after the specified number of milliseconds - of inactivity, thus reducing its memory footprint. When - <c>undefined</c> is specified (this is the default), the process - never goes into hibernation.</p></item> - <tag><c>{user_lookup_fun, {Lookupfun :: fun(), UserState :: term()}}</c></tag> - <item><p>The lookup fun is to defined as follows:</p> + <datatype> + <name name="custom_user_lookup"/> + <desc><p>The lookup fun is to defined as follows:</p> <code> fun(psk, PSKIdentity ::string(), UserState :: term()) -> {ok, SharedSecret :: binary()} | error; fun(srp, Username :: string(), UserState :: term()) -> - {ok, {SRPParams :: srp_param_type(), Salt :: binary(), DerivedKey :: binary()}} | error. + {ok, {SRPParams :: srp_param_type(), Salt :: binary(), + DerivedKey :: binary()}} | error. </code> <p>For Pre-Shared Key (PSK) cipher suites, the lookup fun is @@ -533,20 +597,54 @@ fun(srp, Username :: string(), UserState :: term()) -> <url href="http://tools.ietf.org/html/rfc5054#section-2.4"> RFC 5054</url>: <c>crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])])</c> </p> - </item> + </desc> + </datatype> - <tag><c>{padding_check, boolean()}</c></tag> - <item><p>Affects TLS-1.0 connections only. + <datatype> + <name name="session_id"/> + <desc> + <p>Identifies a TLS session.</p> + </desc> + </datatype> + + <datatype> + <name name="log_alert"/> + <desc><p>If set to <c>false</c>, error reports are not displayed.</p> + </desc> + </datatype> + + <datatype> + <name name="hibernate_after"/> + <desc><p>When an integer-value is specified, <c>TLS/DTLS-connection</c> + goes into hibernation after the specified number of milliseconds + of inactivity, thus reducing its memory footprint. When + <c>undefined</c> is specified (this is the default), the process + never goes into hibernation.</p> + </desc> + </datatype> + + <datatype> + <name name="handshake_size"/> + <desc> + <p>Integer (24 bits unsigned). Used to limit the size of + valid TLS handshake packets to avoid DoS attacks. + Defaults to 256*1024.</p> + </desc> + </datatype> + + <datatype> + <name name="padding_check"/> + <desc><p>Affects TLS-1.0 connections only. If set to <c>false</c>, it disables the block cipher padding check to be able to interoperate with legacy software.</p> <warning><p>Using <c>{padding_check, boolean()}</c> makes TLS vulnerable to the Poodle attack.</p></warning> - </item> - - + </desc> + </datatype> - <tag><c>{beast_mitigation, one_n_minus_one | zero_n | disabled}</c></tag> - <item><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST + <datatype> + <name name="beast_mitigation"/> + <desc><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST mitigation strategy to interoperate with legacy software. Defaults to <c>one_n_minus_one</c>.</p> @@ -556,127 +654,170 @@ fun(srp, Username :: string(), UserState :: term()) -> <p><c>disabled</c> - Disable BEAST mitigation.</p> - <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL or TLS + <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL-3.0 or TLS-1.0 vulnerable to the BEAST attack.</p></warning> - </item> - </taglist> - - </section> - - <section> - <title>TLS/DTLS OPTION DESCRIPTIONS - CLIENT SIDE</title> - - <p>The following options are client-specific or have a slightly different - meaning in the client than in the server:</p> - - <taglist> + </desc> + </datatype> + + <datatype> + <name name="ssl_imp"/> + <desc><p>Deprecated since OTP-17, has no affect.</p></desc> + </datatype> + + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - CLIENT</datatype_title> + + <datatype> + <name name="client_option"/> + </datatype> + + <datatype> + <name name="client_verify_type"/> + <desc><p>In mode <c>verify_none</c> the default behavior is to allow + all x509-path validation errors. See also option <seealso marker="#type-custom_verify">verify_fun</seealso>.</p> + </desc> + </datatype> - <tag><c>{verify, verify_type()}</c></tag> - <item><p>In mode <c>verify_none</c> the default behavior is to allow - all x509-path validation errors. See also option <c>verify_fun</c>.</p> - </item> + <datatype> + <name name="client_reuse_session"/> + <desc> + <p>Reuses a specific session earlier saved with the option + <c>{reuse_sessions, save} since OTP-21.3 </c> + </p> + </desc> + </datatype> - <tag><c>{reuse_sessions, boolean()}</c></tag> - <item><p>Specifies if the client is to try to reuse sessions - when possible.</p></item> + <datatype> + <name name="client_reuse_sessions"/> + <desc> + <p>When <c>save</c> is specified a new connection will be negotiated + and saved for later reuse. The session ID can be fetched with + <seealso marker="#connection_information-2">connection_information/2</seealso> + and used with the client option <seealso marker="#type-client_reuse_session">reuse_session</seealso> + The boolean value true specifies that if possible, automatized session reuse will + be performed. If a new session is created, and is unique in regard + to previous stored sessions, it will be saved for possible later reuse. Since OTP-21.3</p> + </desc> + </datatype> - <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag> - <item><p>The DER-encoded trusted certificates. If this option - is supplied it overrides option <c>cacertfile</c>.</p></item> - - <tag><c>{cacertfile, path()}</c></tag> - <item><p>Path to a file containing PEM-encoded CA certificates. The CA + <datatype> + <name name="client_cacerts"/> + <desc> + <p>The DER-encoded trusted certificates. If this option + is supplied it overrides option <c>cacertfile</c>.</p> + </desc> + </datatype> + + <datatype> + <name name="client_cafile"/> + <desc> + <p>Path to a file containing PEM-encoded CA certificates. The CA certificates are used during server authentication and when building the client certificate chain.</p> - </item> - - <tag><c>{alpn_advertised_protocols, [binary()]}</c></tag> - <item> - <p>The list of protocols supported by the client to be sent to the - server to be used for an Application-Layer Protocol Negotiation (ALPN). - If the server supports ALPN then it will choose a protocol from this - list; otherwise it will fail the connection with a "no_application_protocol" - alert. A server that does not support ALPN will ignore this value.</p> - - <p>The list of protocols must not contain an empty binary.</p> - - <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p> - </item> - - <tag><c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</c><br/> - <c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</c></tag> - <item> - <p>Indicates that the client is to try to perform Next Protocol - Negotiation.</p> - - <p>If precedence is server, the negotiated protocol is the - first protocol to be shown on the server advertised list, which is - also on the client preference list.</p> - - <p>If precedence is client, the negotiated protocol is the - first protocol to be shown on the client preference list, which is - also on the server advertised list.</p> - - <p>If the client does not support any of the server advertised - protocols or the server does not advertise any protocols, the - client falls back to the first protocol in its list or to the - default protocol (if a default is supplied). If the - server does not support Next Protocol Negotiation, the - connection terminates if no default protocol is supplied.</p> - </item> - - <tag><c>{psk_identity, string()}</c></tag> - <item><p>Specifies the identity the client presents to the server. - The matching secret is found by calling <c>user_lookup_fun</c>.</p> - </item> - - <tag><c>{srp_identity, {Username :: string(), Password :: string()} - </c></tag> - <item><p>Specifies the username and password to use to authenticate - to the server.</p></item> - - <tag><c>{server_name_indication, HostName :: hostname()}</c></tag> - <item><p>Specify the hostname to be used in TLS Server Name Indication extension. - If not specified it will default to the <c>Host</c> argument of <seealso marker="#connect-3">connect/[3,4]</seealso> - unless it is of type inet:ipaddress().</p> - <p> - The <c>HostName</c> will also be used in the hostname verification of the peer certificate using - <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>. - </p> - </item> - <tag><c>{server_name_indication, disable}</c></tag> - <item> - <p> Prevents the Server Name Indication extension from being sent and - disables the hostname verification check - <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> </p> - </item> - - <tag><c>{customize_hostname_check, Options::list()}</c></tag> - <item> - <p> Customizes the hostname verification of the peer certificate, as different protocols that use + </desc> + </datatype> + + <datatype> + <name name="client_alpn"/> + <desc> + <p>The list of protocols supported by the client to be sent to the + server to be used for an Application-Layer Protocol Negotiation (ALPN). + If the server supports ALPN then it will choose a protocol from this + list; otherwise it will fail the connection with a "no_application_protocol" + alert. A server that does not support ALPN will ignore this value.</p> + + <p>The list of protocols must not contain an empty binary.</p> + + <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p> + </desc> + </datatype> + + <datatype> + <name name="client_preferred_next_protocols"/> + <desc> + <p>Indicates that the client is to try to perform Next Protocol + Negotiation.</p> + + <p>If precedence is server, the negotiated protocol is the + first protocol to be shown on the server advertised list, which is + also on the client preference list.</p> + + <p>If precedence is client, the negotiated protocol is the + first protocol to be shown on the client preference list, which is + also on the server advertised list.</p> + + <p>If the client does not support any of the server advertised + protocols or the server does not advertise any protocols, the + client falls back to the first protocol in its list or to the + default protocol (if a default is supplied). If the + server does not support Next Protocol Negotiation, the + connection terminates if no default protocol is supplied.</p> + </desc> + </datatype> + + <datatype> + <name name="client_psk_identity"/> + <desc> + <p>Specifies the identity the client presents to the server. + The matching secret is found by calling <c>user_lookup_fun</c></p> + </desc> + </datatype> + + <datatype> + <name name="client_srp_identity"/> + <desc> + <p>Specifies the username and password to use to authenticate + to the server.</p> + </desc> + </datatype> + + <datatype> + <name name="sni"/> + <desc> + <p>Specify the hostname to be used in TLS Server Name Indication extension. + If not specified it will default to the <c>Host</c> argument of <seealso marker="#connect-3">connect/[3,4]</seealso> + unless it is of type inet:ipaddress().</p> + <p> + The <c>HostName</c> will also be used in the hostname verification of the peer certificate using + <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso>. + </p> + <p> The special value <c>disable</c> prevents the Server Name Indication extension from being sent and + disables the hostname verification check + <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> </p> + </desc> + </datatype> + + <datatype> + <name name="customize_hostname_check"/> + <desc> + <p> Customizes the hostname verification of the peer certificate, as different protocols that use TLS such as HTTP or LDAP may want to do it differently, for possible options see <seealso marker="public_key:public_key#pkix_verify_hostname-3">public_key:pkix_verify_hostname/3</seealso> </p> - </item> - - <tag><c>{fallback, boolean()}</c></tag> - <item> - <p> Send special cipher suite TLS_FALLBACK_SCSV to avoid undesired TLS version downgrade. - Defaults to false</p> - <warning><p>Note this option is not needed in normal TLS usage and should not be used - to implement new clients. But legacy clients that retries connections in the following manner</p> - - <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1', 'sslv3']}])</c></p> - <p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1', 'sslv3']}, {fallback, true}])</c></p> - <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv1', 'sslv3']}, {fallback, true}]) </c></p> - <p><c> ssl:connect(Host, Port, [...{versions, ['sslv3']}, {fallback, true}]) </c></p> - - <p>may use it to avoid undesired TLS version downgrade. Note that TLS_FALLBACK_SCSV must also - be supported by the server for the prevention to work. - </p></warning> - </item> - <tag><marker id="client_signature_algs"/><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag> - <item> - <p>In addition to the algorithms negotiated by the cipher + </desc> + </datatype> + + <datatype> + <name name="fallback"/> + <desc> + <p> Send special cipher suite TLS_FALLBACK_SCSV to avoid undesired TLS version downgrade. + Defaults to false</p> + <warning><p>Note this option is not needed in normal TLS usage and should not be used + to implement new clients. But legacy clients that retries connections in the following manner</p> + + <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1', 'sslv3']}])</c></p> + <p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1', 'sslv3']}, {fallback, true}])</c></p> + <p><c> ssl:connect(Host, Port, [...{versions, ['tlsv1', 'sslv3']}, {fallback, true}]) </c></p> + <p><c> ssl:connect(Host, Port, [...{versions, ['sslv3']}, {fallback, true}]) </c></p> + + <p>may use it to avoid undesired TLS version downgrade. Note that TLS_FALLBACK_SCSV must also + be supported by the server for the prevention to work. + </p></warning> + </desc> + </datatype> + + <datatype> + <name name="client_signature_algs"/> + <desc> + <p>In addition to the algorithms negotiated by the cipher suite used for key exchange, payload encryption, message authentication and pseudo random calculation, the TLS signature algorithm extension <url @@ -707,177 +848,230 @@ fun(srp, Username :: string(), UserState :: term()) -> Selected signature algorithm can restrict which hash functions that may be selected. Default support for {md5, rsa} removed in ssl-8.0 </p> - </item> - </taglist> - </section> - - <section> - <title>TLS/DTLS OPTION DESCRIPTIONS - SERVER SIDE</title> + </desc> + </datatype> + - <p>The following options are server-specific or have a slightly different - meaning in the server than in the client:</p> + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - SERVER </datatype_title> - <taglist> - <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag> - <item><p>The DER-encoded trusted certificates. If this option - is supplied it overrides option <c>cacertfile</c>.</p></item> + <datatype> + <name name="server_option"/> + </datatype> + + <datatype> + <name name="server_cacerts"/> + <desc><p>The DER-encoded trusted certificates. If this option + is supplied it overrides option <c>cacertfile</c>.</p> + </desc> + </datatype> - <tag><c>{cacertfile, path()}</c></tag> - <item><p>Path to a file containing PEM-encoded CA - certificates. The CA certificates are used to build the server - certificate chain and for client authentication. The CAs are - also used in the list of acceptable client CAs passed to the - client when a certificate is requested. Can be omitted if there - is no need to verify the client and if there are no - intermediate CAs for the server certificate.</p></item> - - <tag><c>{dh, public_key:der_encoded()}</c></tag> - <item><p>The DER-encoded Diffie-Hellman parameters. If specified, - it overrides option <c>dhfile</c>.</p></item> - - <tag><c>{dhfile, path()}</c></tag> - <item><p>Path to a file containing PEM-encoded Diffie Hellman parameters - to be used by the server if a cipher suite using Diffie Hellman key - exchange is negotiated. If not specified, default parameters are used. - </p></item> - - <tag><c>{verify, verify_type()}</c></tag> - <item><p>A server only does x509-path validation in mode <c>verify_peer</c>, - as it then sends a certificate request to the client - (this message is not sent if the verify option is <c>verify_none</c>). - You can then also want to specify option <c>fail_if_no_peer_cert</c>. - </p></item> - - <tag><c>{fail_if_no_peer_cert, boolean()}</c></tag> - <item><p>Used together with <c>{verify, verify_peer}</c> by an TLS/DTLS server. - If set to <c>true</c>, the server fails if the client does not have - a certificate to send, that is, sends an empty certificate. If set to - <c>false</c>, it fails only if the client sends an invalid - certificate (an empty certificate is considered valid). Defaults to false.</p> - </item> - - <tag><c>{reuse_sessions, boolean()}</c></tag> - <item><p>Specifies if the server is to agree to reuse sessions - when requested by the clients. See also option <c>reuse_session</c>. - </p></item> - - <tag><c>{reuse_session, fun(SuggestedSessionId, - PeerCert, Compression, CipherSuite) -> boolean()}</c></tag> - <item><p>Enables the TLS/DTLS server to have a local policy - for deciding if a session is to be reused or not. - Meaningful only if <c>reuse_sessions</c> is set to <c>true</c>. - <c>SuggestedSessionId</c> is a <c>binary()</c>, <c>PeerCert</c> is - a DER-encoded certificate, <c>Compression</c> is an enumeration integer, - and <c>CipherSuite</c> is of type <c>ciphersuite()</c>.</p></item> - - <tag><c>{alpn_preferred_protocols, [binary()]}</c></tag> - <item> - <p>Indicates the server will try to perform Application-Layer - Protocol Negotiation (ALPN).</p> - - <p>The list of protocols is in order of preference. The protocol - negotiated will be the first in the list that matches one of the - protocols advertised by the client. If no protocol matches, the - server will fail the connection with a "no_application_protocol" alert.</p> - - <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p> - </item> - - <tag><c>{next_protocols_advertised, Protocols :: [binary()]}</c></tag> - <item><p>List of protocols to send to the client if the client indicates that - it supports the Next Protocol extension. The client can select a protocol - that is not on this list. The list of protocols must not contain an empty - binary. If the server negotiates a Next Protocol, it can be accessed - using the <c>negotiated_next_protocol/1</c> method.</p></item> - - <tag><c>{psk_identity, string()}</c></tag> - <item><p>Specifies the server identity hint, which the server presents to - the client.</p></item> - - <tag><c>{log_alert, boolean()}</c></tag> - <item><p>If set to <c>false</c>, error reports are not displayed.</p></item> - - <tag><c>{honor_cipher_order, boolean()}</c></tag> - <item><p>If set to <c>true</c>, use the server preference for cipher - selection. If set to <c>false</c> (the default), use the client - preference.</p></item> - - <tag><c>{sni_hosts, [{hostname(), [ssl_option()]}]}</c></tag> - <item><p>If the server receives a SNI (Server Name Indication) from the client - matching a host listed in the <c>sni_hosts</c> option, the specific options for - that host will override previously specified options. - - The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item> - - <tag><c>{sni_fun, SNIfun::fun()}</c></tag> - <item><p>If the server receives a SNI (Server Name Indication) from the client, - the given function will be called to retrieve <c>[ssl_option()]</c> for the indicated server. - These options will be merged into predefined <c>[ssl_option()]</c>. - - The function should be defined as: - <c>fun(ServerName :: string()) -> [ssl_option()]</c> - and can be specified as a fun or as named <c>fun module:function/1</c> - - The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item> - - <tag><c>{client_renegotiation, boolean()}</c></tag> - <item>In protocols that support client-initiated renegotiation, the cost - of resources of such an operation is higher for the server than the - client. This can act as a vector for denial of service attacks. The SSL - application already takes measures to counter-act such attempts, - but client-initiated renegotiation can be strictly disabled by setting - this option to <c>false</c>. The default value is <c>true</c>. - Note that disabling renegotiation can result in long-lived connections - becoming unusable due to limits on the number of messages the underlying - cipher suite can encipher. - </item> - - <tag><c>{honor_cipher_order, boolean()}</c></tag> - <item>If true, use the server's preference for cipher selection. If false - (the default), use the client's preference. - </item> - <tag><c>{honor_ecc_order, boolean()}</c></tag> - <item>If true, use the server's preference for ECC curve selection. If false - (the default), use the client's preference. - </item> - - <tag><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag> - <item><p> The algorithms specified by - this option will be the ones accepted by the server in a signature algorithm - negotiation, introduced in TLS-1.2. The algorithms will also be offered to the client if a - client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>. - </p> </item> - - </taglist> - </section> - - <section> - <title>General</title> + <datatype> + <name name="server_cafile"/> + <desc><p>Path to a file containing PEM-encoded CA + certificates. The CA certificates are used to build the server + certificate chain and for client authentication. The CAs are + also used in the list of acceptable client CAs passed to the + client when a certificate is requested. Can be omitted if + there is no need to verify the client and if there are no + intermediate CAs for the server certificate.</p> + </desc> + </datatype> + + <datatype> + <name name="dh_der"/> + <desc><p>The DER-encoded Diffie-Hellman parameters. If + specified, it overrides option <c>dhfile</c>.</p> + </desc> + </datatype> + + <datatype> + <name name="dh_file"/> + <desc><p>Path to a file containing PEM-encoded Diffie Hellman + parameters to be used by the server if a cipher suite using + Diffie Hellman key exchange is negotiated. If not specified, + default parameters are used.</p> + </desc> + </datatype> - <p>When an TLS/DTLS socket is in active mode (the default), data from the - socket is delivered to the owner of the socket in the form of - messages:</p> - <list type="bulleted"> - <item><p><c>{ssl, Socket, Data}</c></p></item> - <item><p><c>{ssl_closed, Socket}</c></p></item> - <item><p><c>{ssl_error, Socket, Reason}</c></p></item> - </list> + <datatype> + <name name="server_verify_type"/> + <desc><p>A server only does x509-path validation in mode + <c>verify_peer</c>, as it then sends a certificate request to + the client (this message is not sent if the verify option is + <c>verify_none</c>). You can then also want to specify option + <c>fail_if_no_peer_cert</c>. </p> + </desc> + </datatype> + + <datatype> + <name name="fail_if_no_peer_cert"/> + <desc><p>Used together with <c>{verify, verify_peer}</c> by an + TLS/DTLS server. If set to <c>true</c>, the server fails if + the client does not have a certificate to send, that is, sends + an empty certificate. If set to <c>false</c>, it fails only if + the client sends an invalid certificate (an empty certificate + is considered valid). Defaults to false.</p> + </desc> + </datatype> + + <datatype> + <name name="server_reuse_sessions"/> + <desc><p>The boolean value true specifies that the server will + agree to reuse sessions. Setting it to false will result in an empty + session table, that is no sessions will be reused. + See also option <seealso marker="#type-server_reuse_session">reuse_session</seealso> + </p> + </desc> + </datatype> + + <datatype> + <name name="server_reuse_session"/> + <desc><p>Enables the TLS/DTLS server to have a local policy + for deciding if a session is to be reused or not. Meaningful + only if <c>reuse_sessions</c> is set to <c>true</c>. + <c>SuggestedSessionId</c> is a <c>binary()</c>, + <c>PeerCert</c> is a DER-encoded certificate, + <c>Compression</c> is an enumeration integer, and + <c>CipherSuite</c> is of type <c>ciphersuite()</c>.</p> + </desc> + </datatype> + + <datatype> + <name name="server_alpn"/> + <desc> + <p>Indicates the server will try to perform + Application-Layer Protocol Negotiation (ALPN).</p> + + <p>The list of protocols is in order of preference. The + protocol negotiated will be the first in the list that + matches one of the protocols advertised by the client. If no + protocol matches, the server will fail the connection with a + "no_application_protocol" alert.</p> + + <p>The negotiated protocol can be retrieved using the + <c>negotiated_protocol/1</c> function.</p> + </desc> + </datatype> + + <datatype> + <name name="server_next_protocol"/> + <desc><p>List of protocols to send to the client if the client + indicates that it supports the Next Protocol extension. The + client can select a protocol that is not on this list. The + list of protocols must not contain an empty binary. If the + server negotiates a Next Protocol, it can be accessed using + the <c>negotiated_next_protocol/1</c> method.</p> + </desc> + </datatype> + + <datatype> + <name name="server_psk_identity"/> + <desc> + <p>Specifies the server identity hint, which the server presents to + the client.</p> + </desc> + </datatype> + + <datatype> + <name name="honor_cipher_order"/> + <desc> + <p>If set to <c>true</c>, use the server preference for cipher + selection. If set to <c>false</c> (the default), use the client + preference.</p> + </desc> + </datatype> + + <datatype> + <name name="sni_hosts"/> + <desc><p>If the server receives a SNI (Server Name Indication) from the client + matching a host listed in the <c>sni_hosts</c> option, the specific options for + that host will override previously specified options. + + The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p> + </desc> + </datatype> + + <datatype> + <name name="sni_fun"/> + <desc> + <p>If the server receives a SNI (Server Name Indication) + from the client, the given function will be called to + retrieve <seealso marker="#type-server_option">[server_option()] </seealso> for the indicated server. + These options will be merged into predefined + <seealso marker="#type-server_option">[server_option()] </seealso> list. + + The function should be defined as: + fun(ServerName :: string()) -> <seealso marker="#type-server_option">[server_option()] </seealso> + and can be specified as a fun or as named <c>fun module:function/1</c> + + The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p> + </desc> + </datatype> + + <datatype> + <name name="client_renegotiation"/> + <desc><p>In protocols that support client-initiated + renegotiation, the cost of resources of such an operation is + higher for the server than the client. This can act as a + vector for denial of service attacks. The SSL application + already takes measures to counter-act such attempts, but + client-initiated renegotiation can be strictly disabled by + setting this option to <c>false</c>. The default value is + <c>true</c>. Note that disabling renegotiation can result in + long-lived connections becoming unusable due to limits on the + number of messages the underlying cipher suite can + encipher.</p> + </desc> + </datatype> + + <datatype> + <name name="honor_cipher_order"/> + <desc><p>If true, use the server's preference for cipher + selection. If false (the default), use the client's + preference.</p> + </desc> + </datatype> + + <datatype> + <name name="honor_ecc_order"/> + <desc><p>If true, use the server's preference for ECC curve + selection. If false (the default), use the client's + preference.</p> + </desc> + </datatype> + + <datatype> + <name name="server_signature_algs"/> + <desc><p> The algorithms specified by this option will be the + ones accepted by the server in a signature algorithm + negotiation, introduced in TLS-1.2. The algorithms will also + be offered to the client if a client certificate is + requested. For more details see the <seealso + marker="#type-client_signature_algs">corresponding client + option</seealso>. + </p> + </desc> + </datatype> + </datatypes> + +<!-- + ================================================================ + = Function definitions = + ================================================================ +--> - <p>A <c>Timeout</c> argument specifies a time-out in milliseconds. The - default value for argument <c>Timeout</c> is <c>infinity</c>.</p> - </section> - <funcs> <func> - <name>append_cipher_suites(Deferred, Suites) -> ciphers() </name> + <name since="OTP 20.3">append_cipher_suites(Deferred, Suites) -> ciphers() </name> <fsummary></fsummary> <type> - <v>Deferred = ciphers() | cipher_filters() </v> - <v>Suites = ciphers() </v> + <v>Deferred = <seealso marker="#type-ciphers">ciphers()</seealso> | + <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v> + <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></v> </type> <desc><p>Make <c>Deferred</c> suites become the least preferred suites, that is put them at the end of the cipher suite list @@ -889,8 +1083,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>cipher_suites() -></name> - <name>cipher_suites(Type) -> old_ciphers()</name> + <name since="OTP R14B">cipher_suites() -></name> + <name since="OTP R14B">cipher_suites(Type) -> [old_cipher_suite()]</name> <fsummary>Returns a list of supported cipher suites.</fsummary> <type> <v>Type = erlang | openssl | all</v> @@ -901,12 +1095,12 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>cipher_suites(Supported, Version) -> ciphers()</name> + <name since="OTP 20.3">cipher_suites(Supported, Version) -> ciphers()</name> <fsummary>Returns a list of all default or all supported cipher suites.</fsummary> <type> <v> Supported = default | all | anonymous </v> - <v> Version = protocol_version() </v> + <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v> </type> <desc><p>Returns all default or all supported (except anonymous), or all anonymous cipher suites for a @@ -915,10 +1109,16 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>eccs() -></name> - <name>eccs(protocol_version()) -> [named_curve()]</name> + <name since="OTP 19.2">eccs() -></name> + <name since="OTP 19.2">eccs(Version) -> NamedCurves</name> <fsummary>Returns a list of supported ECCs.</fsummary> + <type> + <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v> + <v> NamedCurves = <seealso marker="#type-named_curve">[named_curve()] </seealso></v> + + </type> + <desc><p>Returns a list of supported ECCs. <c>eccs()</c> is equivalent to calling <c>eccs(Protocol)</c> with all supported protocols and then deduplicating the output.</p> @@ -926,7 +1126,7 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>clear_pem_cache() -> ok </name> + <name since="OTP 17.5">clear_pem_cache() -> ok </name> <fsummary> Clears the pem cache</fsummary> <desc><p>PEM files, used by ssl API-functions, are cached. The @@ -938,61 +1138,68 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>connect(Socket, SslOptions) -> </name> - <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} + <name since="OTP R14B">connect(Socket, Options) -> </name> + <name since="">connect(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name> <fsummary>Upgrades a <c>gen_tcp</c>, or equivalent, connected socket to an TLS socket.</fsummary> <type> - <v>Socket = socket()</v> - <v>SslOptions = [{handshake, hello| full} | ssl_option()]</v> - <v>Timeout = integer() | infinity</v> - <v>SslSocket = sslsocket()</v> - <v>Ext = hello_extensions()</v> - <v>Reason = term()</v> + <v>Socket = <seealso marker="#type-socket"> socket() </seealso></v> + <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()] </seealso></v> + <v>Timeout = timeout()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc><p>Upgrades a <c>gen_tcp</c>, or equivalent, connected socket to an TLS socket, that is, performs the client-side TLS handshake.</p> - <note><p>If the option <c>verify</c> is set to <c>verify_peer</c> - the option <c>server_name_indication</c> shall also be specified, - if it is not no Server Name Indication extension will be sent, - and <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> - will be called with the IP-address of the connection as <c>ReferenceID</c>, which is proably not what you want.</p> + <note><p>If the option <c>verify</c> is set to + <c>verify_peer</c> the option <c>server_name_indication</c> + shall also be specified, if it is not no Server Name + Indication extension will be sent, and <seealso + marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> + will be called with the IP-address of the connection as + <c>ReferenceID</c>, which is proably not what you want.</p> </note> <p> If the option <c>{handshake, hello}</c> is used the handshake is paused after receiving the server hello message and the success response is <c>{ok, SslSocket, Ext}</c> - instead of <c>{ok, SslSocket}</c>. Thereafter the handshake is continued or - canceled by calling <seealso marker="#handshake_continue-3"> + instead of <c>{ok, SslSocket}</c>. Thereafter the handshake + is continued or canceled by calling <seealso + marker="#handshake_continue-3"> <c>handshake_continue/3</c></seealso> or <seealso - marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>. + marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>. </p> + <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value, + the process owning the sslsocket will receive messages of type + <seealso marker="#type-active_msgs"> active_msgs() </seealso> + </p> </desc> </func> <func> - <name>connect(Host, Port, Options) -></name> - <name>connect(Host, Port, Options, Timeout) -> + <name since="">connect(Host, Port, Options) -></name> + <name since="">connect(Host, Port, Options, Timeout) -> {ok, SslSocket}| {ok, SslSocket, Ext} | {error, Reason}</name> <fsummary>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</fsummary> <type> - <v>Host = host()</v> - <v>Port = integer()</v> - <v>Options = [option()]</v> - <v>Timeout = integer() | infinity</v> - <v>SslSocket = sslsocket()</v> - <v>Reason = term()</v> + <v>Host =<seealso marker="#type-host"> host() </seealso> </v> + <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> + <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()]</seealso></v> + <v>Timeout = timeout()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc><p>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</p> <p> When the option <c>verify</c> is set to <c>verify_peer</c> the check <seealso marker="public_key:public_key#pkix_verify_hostname-2">public_key:pkix_verify_hostname/2</seealso> will be performed in addition to the usual x509-path validation checks. If the check fails the error {bad_cert, hostname_check_failed} will - be propagated to the path validation fun <seealso marker="#verify_fun">verify_fun</seealso>, where it is possible to do customized + be propagated to the path validation fun <seealso marker="#type-custom_verify">verify_fun</seealso>, where it is possible to do customized checks by using the full possibilities of the <seealso marker="public_key:public_key#pkix_verify_hostname-3">public_key:pkix_verify_hostname/3</seealso> API. When the option <c>server_name_indication</c> is provided, its value (the DNS name) will be used as <c>ReferenceID</c> @@ -1014,14 +1221,19 @@ fun(srp, Username :: string(), UserState :: term()) -> <c>handshake_continue/3</c></seealso> or <seealso marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>. </p> + + <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value, + the process owning the sslsocket will receive messages of type + <seealso marker="#type-active_msgs"> active_msgs() </seealso> + </p> </desc> </func> <func> - <name>close(SslSocket) -> ok | {error, Reason}</name> + <name since="">close(SslSocket) -> ok | {error, Reason}</name> <fsummary>Closes an TLS/DTLS connection.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Reason = term()</v> </type> <desc><p>Closes an TLS/DTLS connection.</p> @@ -1029,10 +1241,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name> + <name since="OTP 18.1">close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name> <fsummary>Closes an TLS connection.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>How = timeout() | {NewController::pid(), timeout()} </v> <v>Reason = term()</v> </type> @@ -1044,12 +1256,12 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>controlling_process(SslSocket, NewOwner) -> + <name since="">controlling_process(SslSocket, NewOwner) -> ok | {error, Reason}</name> <fsummary>Assigns a new controlling process to the TLS/DTLS socket.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>NewOwner = pid()</v> <v>Reason = term()</v> </type> @@ -1060,12 +1272,12 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>connection_information(SslSocket) -> + <name since="OTP 18.0">connection_information(SslSocket) -> {ok, Result} | {error, Reason} </name> <fsummary>Returns all the connection information. </fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Item = protocol | selected_cipher_suite | sni_hostname | ecc | session_id | atom()</v> <d>Meaningful atoms, not specified above, are the ssl option names.</d> <v>Result = [{Item::atom(), Value::term()}]</v> @@ -1081,12 +1293,12 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>connection_information(SslSocket, Items) -> + <name since="OTP 18.0">connection_information(SslSocket, Items) -> {ok, Result} | {error, Reason} </name> <fsummary>Returns the requested connection information. </fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Items = [Item]</v> <v>Item = protocol | cipher_suite | sni_hostname | ecc | session_id | client_random | server_random | master_secret | atom()</v> @@ -1103,11 +1315,11 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>filter_cipher_suites(Suites, Filters) -> ciphers()</name> + <name since="OTP 20.3">filter_cipher_suites(Suites, Filters) -> ciphers()</name> <fsummary></fsummary> <type> - <v> Suites = ciphers()</v> - <v> Filters = cipher_filters()</v> + <v> Suites = <seealso marker="#type-ciphers"> ciphers() </seealso></v> + <v> Filters = <seealso marker="#type-cipher_filters"> cipher_filters() </seealso></v> </type> <desc><p>Removes cipher suites if any of the filter functions returns false for any part of the cipher suite. This function @@ -1118,7 +1330,7 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>format_error(Reason) -> string()</name> + <name since="">format_error(Reason) -> string()</name> <fsummary>Returns an error string.</fsummary> <type> <v>Reason = term()</v> @@ -1129,11 +1341,11 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>getopts(SslSocket, OptionNames) -> + <name since="">getopts(SslSocket, OptionNames) -> {ok, [socketoption()]} | {error, Reason}</name> <fsummary>Gets the values of the specified options.</fsummary> <type> - <v>Socket = sslsocket()</v> + <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>OptionNames = [atom()]</v> </type> <desc> @@ -1143,13 +1355,13 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>getstat(SslSocket) -> + <name since="OTP 19.0">getstat(SslSocket) -> {ok, OptionValues} | {error, inet:posix()}</name> - <name>getstat(SslSocket, OptionNames) -> + <name since="OTP 19.0">getstat(SslSocket, OptionNames) -> {ok, OptionValues} | {error, inet:posix()}</name> <fsummary>Get one or more statistic options for a socket</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>OptionNames = [atom()]</v> <v>OptionValues = [{inet:stat_option(), integer()}]</v> </type> @@ -1160,31 +1372,36 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>handshake(HsSocket) -> </name> - <name>handshake(HsSocket, Timeout) -> {ok, SslSocket} | {error, Reason}</name> + <name since="OTP 21.0">handshake(HsSocket) -> </name> + <name since="OTP 21.0">handshake(HsSocket, Timeout) -> {ok, SslSocket} | {error, Reason}</name> <fsummary>Performs server-side SSL/TLS handshake.</fsummary> <type> - <v>HsSocket = SslSocket = sslsocket()</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> + <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Timeout = timeout()</v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc> <p>Performs the SSL/TLS/DTLS server-side handshake.</p> <p>Returns a new TLS/DTLS socket if the handshake is successful.</p> + + <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value, + the process owning the sslsocket will receive messages of type + <seealso marker="#type-active_msgs"> active_msgs() </seealso> + </p> </desc> </func> <func> - <name>handshake(Socket, SslOptions) -> </name> - <name>handshake(Socket, SslOptions, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name> + <name since="OTP 21.0">handshake(Socket, Options) -> </name> + <name since="OTP 21.0">handshake(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name> <fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary> <type> - <v>Socket = socket() | sslsocket() </v> - <v>SslSocket = sslsocket() </v> - <v>Ext = hello_extensions()</v> - <v>SslOptions = [{handshake, hello| full} | ssl_option()]</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> + <v>Socket = socket() | <seealso marker="#type-sslsocket"> socket() </seealso> </v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v> + <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v> + <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v> + <v>Timeout = timeout()</v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc> <p>If <c>Socket</c> is a ordinary <c>socket()</c>: upgrades a <c>gen_tcp</c>, @@ -1196,7 +1413,8 @@ fun(srp, Username :: string(), UserState :: term()) -> is undefined. </p></warning> - <p>If <c>Socket</c> is an <c>sslsocket()</c>: provides extra SSL/TLS/DTLS + <p>If <c>Socket</c> is an + <seealso marker="#type-sslsocket"> sslsocket() </seealso>: provides extra SSL/TLS/DTLS options to those specified in <seealso marker="#listen-2">listen/2 </seealso> and then performs the SSL/TLS/DTLS handshake. Returns a new TLS/DTLS socket if the handshake is successful.</p> @@ -1210,14 +1428,20 @@ fun(srp, Username :: string(), UserState :: term()) -> <c>handshake_continue/3</c></seealso> or <seealso marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>. </p> + + <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value, + the process owning the sslsocket will receive messages of type + <seealso marker="#type-active_msgs"> active_msgs() </seealso> + </p> + </desc> </func> <func> - <name>handshake_cancel(SslSocket) -> ok </name> + <name since="OTP 21.0">handshake_cancel(SslSocket) -> ok </name> <fsummary>Cancel handshake with a fatal alert</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> </type> <desc> <p>Cancel the handshake with a fatal <c>USER_CANCELED</c> alert.</p> @@ -1225,14 +1449,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>handshake_continue(HsSocket, SSLOptions) -> {ok, SslSocket} | {error, Reason}</name> - <name>handshake_continue(HsSocket, SSLOptions, Timeout) -> {ok, SslSocket} | {error, Reason}</name> + <name since="OTP 21.0">handshake_continue(HsSocket, Options) -> {ok, SslSocket} | {error, Reason}</name> + <name since="OTP 21.0">handshake_continue(HsSocket, Options, Timeout) -> {ok, SslSocket} | {error, Reason}</name> <fsummary>Continue the SSL/TLS handshake.</fsummary> <type> - <v>HsSocket = SslSocket = sslsocket()</v> - <v>SslOptions = [ssl_option()]</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> + <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Options = <seealso marker="#type-tls_option"> tls_option() </seealso> </v> + <v>Timeout = timeout()</v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc> <p>Continue the SSL/TLS handshake possiby with new, additional or changed options.</p> @@ -1240,13 +1464,13 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>listen(Port, Options) -> + <name since="">listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name> <fsummary>Creates an SSL listen socket.</fsummary> <type> - <v>Port = integer()</v> - <v>Options = options()</v> - <v>ListenSocket = sslsocket()</v> + <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> + <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso></v> + <v>ListenSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> </type> <desc> <p>Creates an SSL listen socket.</p> @@ -1254,10 +1478,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, protocol_not_negotiated}</name> + <name since="OTP 18.0">negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, protocol_not_negotiated}</name> <fsummary>Returns the protocol negotiated through ALPN or NPN extensions.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Protocol = binary()</v> </type> <desc> @@ -1268,10 +1492,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>peercert(SslSocket) -> {ok, Cert} | {error, Reason}</name> + <name since="">peercert(SslSocket) -> {ok, Cert} | {error, Reason}</name> <fsummary>Returns the peer certificate.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Cert = binary()</v> </type> <desc> @@ -1283,13 +1507,13 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>peername(SslSocket) -> {ok, {Address, Port}} | + <name since="">peername(SslSocket) -> {ok, {Address, Port}} | {error, Reason}</name> <fsummary>Returns the peer address and port.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Address = ipaddress()</v> - <v>Port = integer()</v> + <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> </type> <desc> <p>Returns the address and port number of the peer.</p> @@ -1297,11 +1521,12 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>prepend_cipher_suites(Preferred, Suites) -> ciphers()</name> + <name since="OTP 20.3">prepend_cipher_suites(Preferred, Suites) -> ciphers()</name> <fsummary></fsummary> <type> - <v>Preferred = ciphers() | cipher_filters() </v> - <v>Suites = ciphers() </v> + <v>Preferred = <seealso marker="#type-ciphers">ciphers()</seealso> | + <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v> + <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></v> </type> <desc><p>Make <c>Preferred</c> suites become the most preferred suites that is put them at the head of the cipher suite list @@ -1313,13 +1538,13 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name> + <name since="OTP R15B01">prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name> <fsummary>Uses a session Pseudo-Random Function to generate key material.</fsummary> <type> - <v>Socket = sslsocket()</v> + <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Secret = binary() | master_secret</v> <v>Label = binary()</v> - <v>Seed = [binary() | prf_random()]</v> + <v>Seed = [binary() | <seealso marker="#type-prf_random"> prf_random()</seealso>]</v> <v>WantedLength = non_neg_integer()</v> </type> <desc> @@ -1333,14 +1558,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>recv(SslSocket, Length) -> </name> - <name>recv(SslSocket, Length, Timeout) -> {ok, Data} | {error, + <name since="">recv(SslSocket, Length) -> </name> + <name since="">recv(SslSocket, Length, Timeout) -> {ok, Data} | {error, Reason}</name> <fsummary>Receives data on a socket.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Length = integer()</v> - <v>Timeout = integer()</v> + <v>Timeout = timeout()</v> <v>Data = [char()] | binary()</v> </type> <desc> @@ -1360,10 +1585,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>renegotiate(SslSocket) -> ok | {error, Reason}</name> + <name since="OTP R14B">renegotiate(SslSocket) -> ok | {error, Reason}</name> <fsummary>Initiates a new handshake.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> </type> <desc><p>Initiates a new handshake. A notable return value is <c>{error, renegotiation_rejected}</c> indicating that the peer @@ -1373,10 +1598,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>send(SslSocket, Data) -> ok | {error, Reason}</name> + <name since="">send(SslSocket, Data) -> ok | {error, Reason}</name> <fsummary>Writes data to a socket.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>Data = iodata()</v> </type> <desc> @@ -1387,11 +1612,11 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>setopts(SslSocket, Options) -> ok | {error, Reason}</name> + <name since="">setopts(SslSocket, Options) -> ok | {error, Reason}</name> <fsummary>Sets socket options.</fsummary> <type> - <v>SslSocket = sslsocket()</v> - <v>Options = [socketoption]()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Options = <seealso marker="#type-socket_option"> [socket_option()] </seealso></v> </type> <desc> <p>Sets options according to <c>Options</c> for socket @@ -1400,10 +1625,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>shutdown(SslSocket, How) -> ok | {error, Reason}</name> + <name since="OTP R14B">shutdown(SslSocket, How) -> ok | {error, Reason}</name> <fsummary>Immediately closes a socket.</fsummary> <type> - <v>SslSocket = sslsocket()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> <v>How = read | write | read_write</v> <v>Reason = reason()</v> </type> @@ -1418,13 +1643,13 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>ssl_accept(SslSocket) -> </name> - <name>ssl_accept(SslSocket, Timeout) -> ok | {error, Reason}</name> + <name since="">ssl_accept(SslSocket) -> </name> + <name since="">ssl_accept(SslSocket, Timeout) -> ok | {error, Reason}</name> <fsummary>Performs server-side SSL/TLS handshake.</fsummary> <type> - <v>SslSocket = sslsocket()</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Timeout = timeout()</v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc> <p>Deprecated in OTP 21, use <seealso marker="#handshake-1">handshake/[1,2]</seealso> instead.</p> @@ -1433,14 +1658,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>ssl_accept(Socket, SslOptions) -> </name> - <name>ssl_accept(Socket, SslOptions, Timeout) -> {ok, Socket} | ok | {error, Reason}</name> + <name since="">ssl_accept(Socket, Options) -> </name> + <name since="OTP R14B">ssl_accept(Socket, Options, Timeout) -> {ok, Socket} | ok | {error, Reason}</name> <fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary> <type> - <v>Socket = socket() | sslsocket() </v> - <v>SslOptions = [ssl_option()]</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> + <v>Socket = socket() | <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v> + <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v> + <v>Timeout = timeout()</v> + <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v> </type> <desc> <p>Deprecated in OTP 21, use <seealso marker="#handshake-3">handshake/[2,3]</seealso> instead.</p> @@ -1449,13 +1674,13 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>sockname(SslSocket) -> {ok, {Address, Port}} | + <name since="">sockname(SslSocket) -> {ok, {Address, Port}} | {error, Reason}</name> <fsummary>Returns the local address and port.</fsummary> <type> - <v>SslSocket = sslsocket()</v> - <v>Address = ipaddress()</v> - <v>Port = integer()</v> + <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Address = <seealso marker="#type-ip_address">ip_address()</seealso></v> + <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v> </type> <desc> <p>Returns the local address and port number of socket @@ -1464,8 +1689,8 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>start() -> </name> - <name>start(Type) -> ok | {error, Reason}</name> + <name since="OTP R14B">start() -> </name> + <name since="OTP R14B">start(Type) -> ok | {error, Reason}</name> <fsummary>Starts the SSL application.</fsummary> <type> <v>Type = permanent | transient | temporary</v> @@ -1477,7 +1702,7 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>stop() -> ok </name> + <name since="OTP R14B">stop() -> ok </name> <fsummary>Stops the SSL application.</fsummary> <desc> <p>Stops the SSL application.</p> @@ -1485,10 +1710,10 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>suite_to_str(CipherSuite) -> String</name> + <name since="OTP 21.0">suite_to_str(CipherSuite) -> String</name> <fsummary>Returns the string representation of a cipher suite.</fsummary> <type> - <v>CipherSuite = erl_cipher_suite()</v> + <v>CipherSuite = <seealso marker="#type-erl_cipher_suite"> erl_cipher_suite() </seealso></v> <v>String = string()</v> </type> <desc> @@ -1497,14 +1722,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>transport_accept(ListenSocket) -></name> - <name>transport_accept(ListenSocket, Timeout) -> + <name since="">transport_accept(ListenSocket) -></name> + <name since="">transport_accept(ListenSocket, Timeout) -> {ok, SslSocket} | {error, Reason}</name> <fsummary>Accepts an incoming connection and prepares for <c>ssl_accept</c>.</fsummary> <type> - <v>ListenSocket = SslSocket = sslsocket()</v> - <v>Timeout = integer()</v> + <v>ListenSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v> + <v>Timeout = timeout()</v> <v>Reason = reason()</v> </type> <desc> @@ -1532,7 +1757,7 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> - <name>versions() -> [versions_info()]</name> + <name since="OTP R14B">versions() -> [versions_info()]</name> <fsummary>Returns version information relevant for the SSL application.</fsummary> <type> diff --git a/lib/ssl/doc/src/ssl_crl_cache.xml b/lib/ssl/doc/src/ssl_crl_cache.xml index 71c6d5e49e..a33aec62a7 100644 --- a/lib/ssl/doc/src/ssl_crl_cache.xml +++ b/lib/ssl/doc/src/ssl_crl_cache.xml @@ -24,7 +24,7 @@ <file>ssl_crl_cache.xml</file> </header> - <module>ssl_crl_cache</module> + <module since="OTP 18.0">ssl_crl_cache</module> <modulesummary>CRL cache </modulesummary> <description> <p> @@ -34,32 +34,43 @@ the following functions are available. </p> </description> + + <datatypes> + <datatype_title>DATA TYPES</datatype_title> + + <datatype> + <name name="crl_src"/> + </datatype> + + <datatype> + <name name="uri"/> + </datatype> + + </datatypes> <funcs> <func> - <name>delete(Entries) -> ok | {error, Reason} </name> + <name since="OTP 18.0">delete(Entries) -> ok | {error, Reason} </name> <fsummary> </fsummary> <type> - <v> Entries = <seealso marker="stdlib:uri_string">uri_string:uri_string()</seealso> | {file, string()} | {der, [<seealso - marker="public_key:public_key"> public_key:der_encoded() </seealso>]}</v> - <v> Reason = term()</v> + <v> Entries = <seealso marker="#type-crl_src">crl_src()</seealso>]}</v> + <v> Reason = crl_reason()</v> </type> <desc> <p>Delete CRLs from the ssl applications local cache. </p> </desc> </func> <func> - <name>insert(CRLSrc) -> ok | {error, Reason}</name> - <name>insert(URI, CRLSrc) -> ok | {error, Reason}</name> + <name since="OTP 18.0">insert(CRLSrc) -> ok | {error, Reason}</name> + <name since="OTP 18.0">insert(URI, CRLSrc) -> ok | {error, Reason}</name> <fsummary> </fsummary> <type> - <v> CRLSrc = {file, string()} | {der, [ <seealso - marker="public_key:public_key"> public_key:der_encoded() </seealso> ]}</v> - <v> URI = <seealso marker="stdlib:uri_string">uri_string:uri_string() </seealso> </v> + <v> CRLSrc = <seealso marker="#type-crl_src">crl_src()</seealso>]}</v> + <v> URI = <seealso marker="#type-uri">uri()</seealso> </v> <v> Reason = term()</v> </type> <desc> - <p>Insert CRLs into the ssl applications local cache. </p> + <p>Insert CRLs, available to fetch on DER format from <c>URI</c>, into the ssl applications local cache. </p> </desc> </func> </funcs> diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml index c6774b4df6..4cba4e1de1 100644 --- a/lib/ssl/doc/src/ssl_crl_cache_api.xml +++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml @@ -24,7 +24,7 @@ <file>ssl_crl_cache_api.xml</file> </header> - <module>ssl_crl_cache_api</module> + <module since="OTP 18.0">ssl_crl_cache_api</module> <modulesummary>API for a SSL/TLS CRL (Certificate Revocation List) cache.</modulesummary> <description> <p> @@ -39,35 +39,44 @@ a CRL cache. </p> </description> - - <section> - <title>DATA TYPES</title> - - <p>The following data types are used in the functions below: - </p> - - <taglist> - - <tag><c>cache_ref() =</c></tag> - <item>opaque()</item> - <tag><c>dist_point() =</c></tag> - <item><p>#'DistributionPoint'{} see <seealso - marker="public_key:public_key_records"> X509 certificates records</seealso></p></item> - - </taglist> + + + <!-- + ================================================================ + = Data types = + ================================================================ + --> + + <datatypes> - </section> + <datatype> + <name name="crl_cache_ref"/> + <desc> + <p>Reference to the CRL cache.</p> + </desc> + </datatype> + + + <datatype> + <name name="dist_point"/> + <desc> + <p>For description see <seealso + marker="public_key:public_key_records"> X509 certificates records</seealso></p> + </desc> + </datatype> + </datatypes> + <funcs> <func> - <name>fresh_crl(DistributionPoint, CRL) -> FreshCRL</name> + <name since="OTP 18.0">fresh_crl(DistributionPoint, CRL) -> FreshCRL</name> <fsummary> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to public_key:pkix_crls_validate/3 </fsummary> <type> - <v> DistributionPoint = dist_point() </v> + <v> DistributionPoint = <seealso marker="#type-dist_point"> dist_point() </seealso> </v> <v> CRL = [<seealso - marker="public_key:public_key">public_key:der_encoded()</seealso>] </v> + marker="public_key:public_key#type-der_encoded">public_key:der_encoded()</seealso>] </v> <v> FreshCRL = [<seealso - marker="public_key:public_key">public_key:der_encoded()</seealso>] </v> + marker="public_key:public_key#type-der_encoded">public_key:der_encoded()</seealso>] </v> </type> <desc> <p> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to @@ -76,16 +85,16 @@ </func> <func> - <name>lookup(DistributionPoint, Issuer, DbHandle) -> not_available | CRLs </name> - <name>lookup(DistributionPoint, DbHandle) -> not_available | CRLs </name> + <name since="OTP 19.0">lookup(DistributionPoint, Issuer, DbHandle) -> not_available | CRLs </name> + <name since="OTP 18.0">lookup(DistributionPoint, DbHandle) -> not_available | CRLs </name> <fsummary> </fsummary> <type> - <v> DistributionPoint = dist_point() </v> + <v> DistributionPoint = <seealso marker="#type-dist_point"> dist_point() </seealso> </v> <v> Issuer = <seealso - marker="public_key:public_key">public_key:issuer_name()</seealso> </v> - <v> DbHandle = cache_ref() </v> + marker="public_key:public_key#type-issuer_name">public_key:issuer_name()</seealso> </v> + <v> DbHandle = <seealso marker="#type-crl_cache_ref"> crl_cache_ref() </seealso></v> <v> CRLs = [<seealso - marker="public_key:public_key">public_key:der_encoded()</seealso>] </v> + marker="public_key:public_key#type-der_encoded">public_key:der_encoded()</seealso>] </v> </type> <desc> <p>Lookup the CRLs belonging to the distribution point <c> Distributionpoint</c>. This function may choose to only look in the cache or to follow distribution point @@ -106,12 +115,12 @@ </func> <func> - <name>select(Issuer, DbHandle) -> CRLs </name> + <name since="OTP 18.0">select(Issuer, DbHandle) -> CRLs </name> <fsummary>Select the CRLs in the cache that are issued by <c>Issuer</c></fsummary> <type> <v> Issuer = <seealso - marker="public_key:public_key">public_key:issuer_name()</seealso></v> - <v> DbHandle = cache_ref() </v> + marker="public_key:public_key#type-issuer_name">public_key:issuer_name()</seealso></v> + <v> DbHandle = <seealso marker="#type-crl_cache_ref"> cache_ref() </seealso></v> </type> <desc> <p>Select the CRLs in the cache that are issued by <c>Issuer</c> </p> diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml index a84a3dfce9..e841729e57 100644 --- a/lib/ssl/doc/src/ssl_session_cache_api.xml +++ b/lib/ssl/doc/src/ssl_session_cache_api.xml @@ -28,7 +28,7 @@ <rev></rev> <file>ssl_session_cache_api.xml</file> </header> - <module>ssl_session_cache_api</module> + <module since="OTP R14B">ssl_session_cache_api</module> <modulesummary>TLS session cache API</modulesummary> <description> @@ -38,39 +38,50 @@ defining a new callback module implementing this API. </p> </description> - <section> - <title>DATA TYPES</title> - <p>The following data types are used in the functions for - <c>ssl_session_cache_api</c>:</p> - - <taglist> - <tag><c>cache_ref() =</c></tag> - <item><p><c>opaque()</c></p></item> - - <tag><c>key() =</c></tag> - <item><p><c>{partialkey(), session_id()}</c></p></item> - - <tag><c>partialkey() =</c></tag> - <item><p><c>opaque()</c></p></item> - - <tag><c>session_id() =</c></tag> - <item><p><c>binary()</c></p></item> - - <tag><c>session()</c> =</tag> - <item><p><c>opaque()</c></p></item> - </taglist> - - </section> + <!-- + ================================================================ + = Data types = + ================================================================ + --> + + <datatypes> + + <datatype> + <name name="session_cache_ref"/> + </datatype> + + <datatype> + <name name="session_cache_key"/> + <desc> + <p>A key to an entry in the session cache.</p> + </desc> + </datatype> + + <datatype> + <name name="partial_key"/> + <desc> + <p>The opaque part of the key. Does not need to be handled + by the callback.</p> + </desc> + </datatype> + + <datatype> + <name name="session"/> + <desc> + <p>The session data that is stored for each session.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>delete(Cache, Key) -> _</name> + <name since="OTP R14B">delete(Cache, Key) -> _</name> <fsummary>Deletes a cache entry.</fsummary> <type> - <v>Cache = cache_ref()</v> - <v>Key = key()</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> + <v>Key = <seealso marker="#type-session_cache_key">session_cache_key() </seealso> </v> </type> <desc> <p>Deletes a cache entry. Is only called from the cache @@ -80,10 +91,12 @@ </func> <func> - <name>foldl(Fun, Acc0, Cache) -> Acc</name> + <name since="OTP R14B">foldl(Fun, Acc0, Cache) -> Acc</name> <fsummary></fsummary> <type> - <v></v> + <v>Fun = fun()</v> + <v>Acc0 = Acc = term()</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> </type> <desc> <p>Calls <c>Fun(Elem, AccIn)</c> on successive elements of the @@ -96,10 +109,11 @@ </func> <func> - <name>init(Args) -> opaque() </name> + <name since="OTP 18.0">init(Args) -> Cache </name> <fsummary>Returns cache reference.</fsummary> <type> - <v>Args = proplists:proplist()</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> + <v>Args = <seealso marker="stdlib:proplists#type-proplist">proplists:proplist()</seealso></v> </type> <desc> <p>Includes property <c>{role, client | server}</c>. @@ -121,12 +135,12 @@ </func> <func> - <name>lookup(Cache, Key) -> Entry</name> + <name since="OTP R14B">lookup(Cache, Key) -> Entry</name> <fsummary>Looks up a cache entry.</fsummary> <type> - <v>Cache = cache_ref()</v> - <v>Key = key()</v> - <v>Entry = session() | undefined</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> + <v>Key = <seealso marker="#type-session_cache_key">session_cache_key()</seealso> </v> + <v>Session = <seealso marker="#type-session">session()</seealso> | undefined</v> </type> <desc> <p>Looks up a cache entry. Is to be callable from any @@ -136,12 +150,12 @@ </func> <func> - <name>select_session(Cache, PartialKey) -> [session()]</name> + <name since="OTP R14B">select_session(Cache, PartialKey) -> [Session]</name> <fsummary>Selects sessions that can be reused.</fsummary> <type> - <v>Cache = cache_ref()</v> - <v>PartialKey = partialkey()</v> - <v>Session = session()</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> + <v>PartialKey = <seealso marker="#type-partial_key"> partial_key() </seealso></v> + <v>Session = <seealso marker="#type-session">session()</seealso></v> </type> <desc> <p>Selects sessions that can be reused. Is to be callable @@ -151,10 +165,10 @@ </func> <func> - <name>size(Cache) -> integer()</name> + <name since="OTP 19.3">size(Cache) -> integer()</name> <fsummary>Returns the number of sessions in the cache.</fsummary> <type> - <v>Cache = cache_ref()</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> </type> <desc> <p>Returns the number of sessions in the cache. If size @@ -166,11 +180,12 @@ </func> <func> - <name>terminate(Cache) -> _</name> + <name since="OTP R14B">terminate(Cache) -> _</name> <fsummary>Called by the process that handles the cache when it is about to terminate.</fsummary> <type> - <v>Cache = term() - as returned by init/0</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> + <d>As returned by init/0</d> </type> <desc> <p>Takes care of possible cleanup that is needed when the @@ -180,12 +195,12 @@ </func> <func> - <name>update(Cache, Key, Session) -> _</name> + <name since="OTP R14B">update(Cache, Key, Session) -> _</name> <fsummary>Caches a new session or updates an already cached one.</fsummary> <type> - <v>Cache = cache_ref()</v> - <v>Key = key()</v> - <v>Session = session()</v> + <v>Cache = <seealso marker="#type-session_cache_ref"> session_cache_ref() </seealso></v> + <v>Key = <seealso marker="#type-session_cache_key">session_cache_key()</seealso> </v> + <v>Session = <seealso marker="#type-session">session()</seealso></v> </type> <desc> <p>Caches a new session or updates an already cached one. Is diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 37719ad439..2c6b71c97a 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2018. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ -export([start_fsm/8, start_link/7, init/1, pids/1]). %% State transition handling --export([next_event/3, next_event/4, handle_common_event/4]). +-export([next_event/3, next_event/4, handle_protocol_record/3]). %% Handshake handling -export([renegotiate/2, send_handshake/2, @@ -50,8 +50,7 @@ -export([encode_alert/3, send_alert/2, send_alert_in_connection/2, close/5, protocol_name/0]). %% Data handling --export([encode_data/3, next_record/1, - send/3, socket/5, setopts/3, getopts/3]). +-export([next_record/1, socket/4, setopts/3, getopts/3]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states @@ -80,7 +79,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} end. %%-------------------------------------------------------------------- --spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) -> +-spec start_link(atom(), ssl:host(), inet:port_number(), port(), list(), pid(), tuple()) -> {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_statem process which calls Module:init/1 to @@ -107,9 +106,11 @@ pids(_) -> %%==================================================================== %% State transition handling %%==================================================================== -next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 -> - {no_record, State#state{unprocessed_handshake_events = N-1}}; - +next_record(#state{handshake_env = + #handshake_env{unprocessed_handshake_events = N} = HsEnv} + = State) when N > 0 -> + {no_record, State#state{handshake_env = + HsEnv#handshake_env{unprocessed_handshake_events = N-1}}}; next_record(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} = CT | Rest]} = Buffers, @@ -141,14 +142,14 @@ next_record(#state{protocol_buffers = next_record(State#state{protocol_buffers = Buffers#protocol_buffers{dtls_cipher_texts = Rest}, connection_states = ConnectionStates}); -next_record(#state{role = server, - socket = {Listener, {Client, _}}} = State) -> +next_record(#state{static_env = #static_env{role = server, + socket = {Listener, {Client, _}}}} = State) -> dtls_packet_demux:active_once(Listener, Client, self()), {no_record, State}; -next_record(#state{role = client, - socket = {_Server, Socket} = DTLSSocket, - close_tag = CloseTag, - transport_cb = Transport} = State) -> +next_record(#state{static_env = #static_env{role = client, + socket = {_Server, Socket} = DTLSSocket, + close_tag = CloseTag, + transport_cb = Transport}} = State) -> case dtls_socket:setopts(Transport, Socket, [{active,once}]) of ok -> {no_record, State}; @@ -234,37 +235,44 @@ next_event(StateName, Record, {next_state, StateName, State0, [{next_event, internal, Alert} | Actions]} end. -handle_common_event(internal, #alert{} = Alert, StateName, - #state{negotiated_version = Version} = State) -> - handle_own_alert(Alert, Version, StateName, State); +%%% DTLS record protocol level application data messages + +handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName0, State0) -> + case ssl_connection:read_application_data(Data, State0) of + {stop, _, _} = Stop-> + Stop; + {Record, State1} -> + {next_state, StateName, State, Actions} = next_event(StateName0, Record, State1), + ssl_connection:hibernate_after(StateName, State, Actions) + end; %%% DTLS record protocol level handshake messages -handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, +handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, StateName, #state{protocol_buffers = Buffers0, - negotiated_version = Version} = State0) -> + connection_env = #connection_env{negotiated_version = Version}} = State) -> try case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of {[], Buffers} -> - next_event(StateName, no_record, State0#state{protocol_buffers = Buffers}); + next_event(StateName, no_record, State#state{protocol_buffers = Buffers}); {Packets, Buffers} -> - State = State0#state{protocol_buffers = Buffers}, + HsEnv = State#state.handshake_env, Events = dtls_handshake_events(Packets), {next_state, StateName, - State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events} + State#state{protocol_buffers = Buffers, + handshake_env = + HsEnv#handshake_env{unprocessed_handshake_events + = unprocessed_events(Events)}}, Events} end catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State0) + handle_own_alert(Alert, Version, StateName, State) end; -%%% DTLS record protocol level application data messages -handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) -> - {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]}; %%% DTLS record protocol level change cipher messages -handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> +handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]}; %%% DTLS record protocol level Alert messages -handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, - #state{negotiated_version = Version} = State) -> +handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, + #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> case decode_alerts(EncAlerts) of Alerts = [_|_] -> handle_alerts(Alerts, {next_state, StateName, State}); @@ -272,18 +280,20 @@ handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, Sta handle_own_alert(Alert, Version, StateName, State) end; %% Ignore unknown TLS record level protocol messages -handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) -> - {next_state, StateName, State}. +handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) -> + {next_state, StateName, State, []}. %%==================================================================== %% Handshake handling %%==================================================================== -renegotiate(#state{role = client} = State, Actions) -> +renegotiate(#state{static_env = #static_env{role = client}} = State, Actions) -> %% Handle same way as if server requested %% the renegotiation - next_event(connection, no_record, State, [{next_event, internal, #hello_request{}} | Actions]); -renegotiate(#state{role = server} = State0, Actions) -> + {next_state, connection, State, + [{next_event, internal, #hello_request{}} | Actions]}; + +renegotiate(#state{static_env = #static_env{role = server}} = State0, Actions) -> HelloRequest = ssl_handshake:hello_request(), State1 = prepare_flight(State0), {State, MoreActions} = send_handshake(HelloRequest, State1), @@ -293,8 +303,8 @@ send_handshake(Handshake, #state{connection_states = ConnectionStates} = State) #{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write), send_handshake_flight(queue_handshake(Handshake, State), Epoch). -queue_handshake(Handshake0, #state{tls_handshake_history = Hist0, - negotiated_version = Version, +queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := HsBuffer0, change_cipher_spec := undefined, next_sequence := Seq} = Flight0} = State) -> @@ -302,17 +312,17 @@ queue_handshake(Handshake0, #state{tls_handshake_history = Hist0, Hist = update_handshake_history(Handshake0, Handshake, Hist0), State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0], next_sequence => Seq +1}, - tls_handshake_history = Hist}; + handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}; -queue_handshake(Handshake0, #state{tls_handshake_history = Hist0, - negotiated_version = Version, +queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0, next_sequence := Seq} = Flight0} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0], next_sequence => Seq +1}, - tls_handshake_history = Hist}. + handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}. queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight, connection_states = ConnectionStates0} = State) -> @@ -324,11 +334,14 @@ queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight, reinit(State) -> %% To be API compatible with TLS NOOP here reinit_handshake_data(State). -reinit_handshake_data(#state{protocol_buffers = Buffers} = State) -> - State#state{premaster_secret = undefined, - public_key_info = undefined, - tls_handshake_history = ssl_handshake:init_handshake_history(), - flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}, +reinit_handshake_data(#state{static_env = #static_env{data_tag = DataTag}, + protocol_buffers = Buffers, + protocol_specific = PS, + handshake_env = HsEnv} = State) -> + State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history(), + public_key_info = undefined, + premaster_secret = undefined}, + protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, flight_buffer = new_flight(), protocol_buffers = Buffers#protocol_buffers{ @@ -352,9 +365,9 @@ empty_connection_state(ConnectionEnd, BeastMitigation) -> encode_alert(#alert{} = Alert, Version, ConnectionStates) -> dtls_record:encode_alert_record(Alert, Version, ConnectionStates). -send_alert(Alert, #state{negotiated_version = Version, - socket = Socket, - transport_cb = Transport, +send_alert(Alert, #state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, connection_states = ConnectionStates0} = State0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), @@ -378,16 +391,13 @@ protocol_name() -> %% Data handling %%==================================================================== -encode_data(Data, Version, ConnectionStates0)-> - dtls_record:encode_data(Data, Version, ConnectionStates0). - -send(Transport, {_, {{_,_}, _} = Socket}, Data) -> - send(Transport, Socket, Data); -send(Transport, Socket, Data) -> - dtls_socket:send(Transport, Socket, Data). +send(Transport, {Listener, Socket}, Data) when is_pid(Listener) -> % Server socket + dtls_socket:send(Transport, Socket, Data); +send(Transport, Socket, Data) -> % Client socket + dtls_socket:send(Transport, Socket, Data). -socket(Pid, Transport, Socket, Connection, _) -> - dtls_socket:socket(Pid, Transport, Socket, Connection). +socket(Pid, Transport, Socket, _Tracker) -> + dtls_socket:socket(Pid, Transport, Socket, ?MODULE). setopts(Transport, Socket, Other) -> dtls_socket:setopts(Transport, Socket, Other). @@ -406,42 +416,39 @@ getopts(Transport, Socket, Tag) -> init(enter, _, State) -> {keep_state, State}; init({call, From}, {start, Timeout}, - #state{host = Host, port = Port, role = client, + #state{static_env = #static_env{host = Host, + port = Port, + role = client, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}}, + connection_env = CEnv, ssl_options = SslOpts, session = #session{own_certificate = Cert} = Session0, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb + connection_states = ConnectionStates0 } = State0) -> - Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From), Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), - State1 = prepare_flight(State0#state{negotiated_version = Version}), - {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}), - State = State2#state{negotiated_version = Version, %% Requested version - session = - Session0#session{session_id = Hello#client_hello.session_id}, - start_or_recv_from = From, - timer = Timer, - flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT} - }, - next_event(hello, no_record, State, Actions); -init({call, _} = Type, Event, #state{role = server, data_tag = udp} = State) -> + State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), + {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), + State3 = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion + session = + Session0#session{session_id = Hello#client_hello.session_id}, + start_or_recv_from = From}, + {Record, State} = next_record(State3), + next_event(hello, Record, State, [{{timeout, handshake}, Timeout, close} | Actions]); +init({call, _} = Type, Event, #state{static_env = #static_env{role = server}, + protocol_specific = PS} = State) -> Result = gen_handshake(?FUNCTION_NAME, Type, Event, - State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}, - protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(), - previous_cookie_secret => <<>>, - ignored_alerts => 0, - max_ignored_alerts => 10}}), + State#state{protocol_specific = PS#{current_cookie_secret => dtls_v1:cookie_secret(), + previous_cookie_secret => <<>>, + ignored_alerts => 0, + max_ignored_alerts => 10}}), erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret), Result; -init({call, _} = Type, Event, #state{role = server} = State) -> - %% I.E. DTLS over sctp - gen_handshake(?FUNCTION_NAME, Type, Event, State#state{flight_state = reliable}); init(Type, Event, State) -> gen_handshake(?FUNCTION_NAME, Type, Event, State). @@ -454,8 +461,8 @@ error(enter, _, State) -> {keep_state, State}; error({call, From}, {start, _Timeout}, #state{protocol_specific = #{error := Error}} = State) -> - ssl_connection:stop_and_reply( - normal, {reply, From, {error, Error}}, State); + {stop_and_reply, {shutdown, normal}, + [{reply, From, {error, Error}}], State}; error({call, _} = Call, Msg, State) -> gen_handshake(?FUNCTION_NAME, Call, Msg, State); error(_, _, _) -> @@ -467,16 +474,18 @@ error(_, _, _) -> #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- -hello(enter, _, #state{role = server} = State) -> +hello(enter, _, #state{static_env = #static_env{role = server}} = State) -> {keep_state, State}; -hello(enter, _, #state{role = client} = State0) -> +hello(enter, _, #state{static_env = #static_env{role = client}} = State0) -> {State, Actions} = handle_flight_timer(State0), {keep_state, State, Actions}; hello(internal, #client_hello{cookie = <<>>, client_version = Version} = Hello, - #state{role = server, - transport_cb = Transport, - socket = Socket, + #state{static_env = #static_env{role = server, + transport_cb = Transport, + socket = Socket}, + handshake_env = HsEnv, + connection_env = CEnv, protocol_specific = #{current_cookie_secret := Secret}} = State0) -> {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket), Cookie = dtls_handshake:cookie(Secret, IP, Port, Hello), @@ -487,51 +496,62 @@ hello(internal, #client_hello{cookie = <<>>, %% version 1.0 regardless of the version of TLS that is expected to be %% negotiated. VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION), - State1 = prepare_flight(State0#state{negotiated_version = Version}), - {State, Actions} = send_handshake(VerifyRequest, State1), - next_event(?FUNCTION_NAME, no_record, - State#state{tls_handshake_history = ssl_handshake:init_handshake_history()}, Actions); -hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client, - host = Host, port = Port, + State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), + {State2, Actions} = send_handshake(VerifyRequest, State1), + {Record, State} = next_record(State2), + next_event(?FUNCTION_NAME, Record, + State#state{handshake_env = HsEnv#handshake_env{ + tls_handshake_history = + ssl_handshake:init_handshake_history()}}, + Actions); +hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #static_env{role = client, + host = Host, + port = Port, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv, + connection_env = CEnv, ssl_options = SslOpts, session = #session{own_certificate = OwnCert} = Session0, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb + connection_states = ConnectionStates0 } = State0) -> Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, OwnCert), Version = Hello#client_hello.client_version, - State1 = prepare_flight(State0#state{tls_handshake_history = ssl_handshake:init_handshake_history()}), + State1 = prepare_flight(State0#state{handshake_env = + HsEnv#handshake_env{tls_handshake_history + = ssl_handshake:init_handshake_history()}}), {State2, Actions} = send_handshake(Hello, State1), - State = State2#state{negotiated_version = Version, %% Requested version + State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}}, next_event(?FUNCTION_NAME, no_record, State, Actions); hello(internal, #client_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello}, + handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, - hello = Hello}, + handshake_env = HsEnv#handshake_env{hello = Hello}}, [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello}, + handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, - hello = Hello}, + handshake_env = HsEnv#handshake_env{hello = Hello}}, [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; -hello(internal, #client_hello{cookie = Cookie} = Hello, - #state{role = server, - transport_cb = Transport, - socket = Socket, - protocol_specific = #{current_cookie_secret := Secret, - previous_cookie_secret := PSecret}} = State0) -> + +hello(internal, #client_hello{cookie = Cookie} = Hello, #state{static_env = #static_env{role = server, + transport_cb = Transport, + socket = Socket}, + protocol_specific = #{current_cookie_secret := Secret, + previous_cookie_secret := PSecret} + } = State0) -> {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket), case dtls_handshake:cookie(Secret, IP, Port, Hello) of Cookie -> @@ -546,11 +566,12 @@ hello(internal, #client_hello{cookie = Cookie} = Hello, end end; hello(internal, #server_hello{} = Hello, - #state{connection_states = ConnectionStates0, - negotiated_version = ReqVersion, - role = client, - renegotiation = {Renegotiation, _}, - ssl_options = SslOptions} = State) -> + #state{ + static_env = #static_env{role = client}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}}, + connection_env = #connection_env{negotiated_version = ReqVersion}, + connection_states = ConnectionStates0, + ssl_options = SslOptions} = State) -> case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> handle_own_alert(Alert, ReqVersion, ?FUNCTION_NAME, State); @@ -596,10 +617,11 @@ abbreviated(internal = Type, ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read), ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read), gen_handshake(?FUNCTION_NAME, Type, Event, State#state{connection_states = ConnectionStates}); -abbreviated(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) -> +abbreviated(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates, + protocol_specific = PS} = State) -> gen_handshake(?FUNCTION_NAME, Type, Event, prepare_flight(State#state{connection_states = ConnectionStates, - flight_state = connection})); + protocol_specific = PS#{flight_state => connection}})); abbreviated(state_timeout, Event, State) -> handle_state_timeout(Event, ?FUNCTION_NAME, State); abbreviated(Type, Event, State) -> @@ -639,10 +661,11 @@ cipher(internal = Type, #change_cipher_spec{type = <<1>>} = Event, ConnectionStates1 = dtls_record:save_current_connection_state(ConnectionStates0, read), ConnectionStates = dtls_record:next_epoch(ConnectionStates1, read), ssl_connection:?FUNCTION_NAME(Type, Event, State#state{connection_states = ConnectionStates}, ?MODULE); -cipher(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates} = State) -> +cipher(internal = Type, #finished{} = Event, #state{connection_states = ConnectionStates, + protocol_specific = PS} = State) -> ssl_connection:?FUNCTION_NAME(Type, Event, prepare_flight(State#state{connection_states = ConnectionStates, - flight_state = connection}), + protocol_specific = PS#{flight_state => connection}}), ?MODULE); cipher(state_timeout, Event, State) -> handle_state_timeout(Event, ?FUNCTION_NAME, State); @@ -658,33 +681,45 @@ connection(enter, _, State) -> {keep_state, State}; connection(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); -connection(internal, #hello_request{}, #state{host = Host, port = Port, +connection(internal, #hello_request{}, #state{static_env = #static_env{host = Host, + port = Port, + data_tag = DataTag, + session_cache = Cache, + session_cache_cb = CacheCb + }, + handshake_env = #handshake_env{ renegotiation = {Renegotiation, _}}, + connection_env = CEnv, session = #session{own_certificate = Cert} = Session0, - session_cache = Cache, session_cache_cb = CacheCb, ssl_options = SslOpts, connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}} = State0) -> + protocol_specific = PS + } = State0) -> Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), State1 = prepare_flight(State0), - {State, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}), - next_event(hello, no_record, State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}, - session = Session0#session{session_id - = Hello#client_hello.session_id}}, - Actions); -connection(internal, #client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> + {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), + {Record, State} = + next_record( + State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, + session = Session0#session{session_id + = Hello#client_hello.session_id}}), + next_event(hello, Record, State, Actions); +connection(internal, #client_hello{} = Hello, #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{allow_renegotiate = true} = HsEnv} = State) -> %% Mitigate Computational DoS attack %% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client %% initiated renegotiation we will disallow many client initiated %% renegotiations immediately after each other. erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate), - {next_state, hello, State#state{allow_renegotiate = false, renegotiation = {true, peer}}, + {next_state, hello, State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, peer}, + allow_renegotiate = false}}, [{next_event, internal, Hello}]}; -connection(internal, #client_hello{}, #state{role = server, allow_renegotiate = false} = State0) -> +connection(internal, #client_hello{}, #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{allow_renegotiate = false}} = State0) -> Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION), State1 = send_alert(Alert, State0), {Record, State} = ssl_connection:prepare_connection(State1, ?MODULE), @@ -739,33 +774,44 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, end, Monitor = erlang:monitor(process, User), - - #state{socket_options = SocketOptions, + InitStatEnv = #static_env{ + role = Role, + transport_cb = CbModule, + protocol_cb = ?MODULE, + data_tag = DataTag, + close_tag = CloseTag, + error_tag = ErrorTag, + host = Host, + port = Port, + socket = Socket, + session_cache_cb = SessionCacheCb + }, + + #state{static_env = InitStatEnv, + handshake_env = #handshake_env{ + tls_handshake_history = ssl_handshake:init_handshake_history(), + renegotiation = {false, first}, + allow_renegotiate = SSLOptions#ssl_options.client_renegotiation + }, + connection_env = #connection_env{user_application = {Monitor, User}}, + socket_options = SocketOptions, %% We do not want to save the password in the state so that %% could be written in the clear into error logs. ssl_options = SSLOptions#ssl_options{password = undefined}, session = #session{is_resumable = new}, - transport_cb = CbModule, - data_tag = DataTag, - close_tag = CloseTag, - error_tag = ErrorTag, - role = Role, - host = Host, - port = Port, - socket = Socket, connection_states = ConnectionStates, protocol_buffers = #protocol_buffers{}, - user_application = {Monitor, User}, - user_data_buffer = <<>>, - session_cache_cb = SessionCacheCb, - renegotiation = {false, first}, - allow_renegotiate = SSLOptions#ssl_options.client_renegotiation, + user_data_buffer = {[],0,[]}, start_or_recv_from = undefined, - protocol_cb = ?MODULE, flight_buffer = new_flight(), - flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT} + protocol_specific = #{flight_state => initial_flight_state(DataTag)} }. +initial_flight_state(udp)-> + {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}; +initial_flight_state(_) -> + reliable. + next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ dtls_record_buffer = Buf0, dtls_cipher_texts = CT0} = Buffers} = State0) -> @@ -783,7 +829,7 @@ next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ acceptable_record_versions(hello, _) -> [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_DATAGRAM_SUPPORTED_VERSIONS]; -acceptable_record_versions(_, #state{negotiated_version = Version}) -> +acceptable_record_versions(_, #state{connection_env = #connection_env{negotiated_version = Version}}) -> [Version]. dtls_handshake_events(Packets) -> @@ -802,19 +848,22 @@ decode_cipher_text(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts {Alert, State} end. -dtls_version(hello, Version, #state{role = server} = State) -> - State#state{negotiated_version = Version}; %%Inital version +dtls_version(hello, Version, #state{static_env = #static_env{role = server}, + connection_env = CEnv} = State) -> + State#state{connection_env = CEnv#connection_env{negotiated_version = Version}}; %%Inital version dtls_version(_,_, State) -> State. handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, #state{connection_states = ConnectionStates0, - port = Port, session = #session{own_certificate = Cert} = Session0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb, - negotiated_protocol = CurrentProtocol, - key_algorithm = KeyExAlg, + static_env = #static_env{port = Port, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{kex_algorithm = KeyExAlg, + renegotiation = {Renegotiation, _}, + negotiated_protocol = CurrentProtocol} = HsEnv, + connection_env = CEnv, + session = #session{own_certificate = Cert} = Session0, ssl_options = SslOpts} = State0) -> case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, @@ -829,11 +878,12 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, end, State = prepare_flight(State0#state{connection_states = ConnectionStates, - negotiated_version = Version, - hashsign_algorithm = HashSign, - client_hello_version = ClientVersion, - session = Session, - negotiated_protocol = Protocol}), + connection_env = CEnv#connection_env{negotiated_version = Version}, + handshake_env = HsEnv#handshake_env{ + hashsign_algorithm = HashSign, + client_hello_version = ClientVersion, + negotiated_protocol = Protocol}, + session = Session}), ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt}, State, ?MODULE) @@ -842,20 +892,20 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, %% raw data from socket, unpack records handle_info({Protocol, _, _, _, Data}, StateName, - #state{data_tag = Protocol} = State0) -> + #state{static_env = #static_env{data_tag = Protocol}} = State0) -> case next_dtls_record(Data, StateName, State0) of {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> ssl_connection:handle_normal_shutdown(Alert, StateName, State0), - ssl_connection:stop({shutdown, own_alert}, State0) + {stop, {shutdown, own_alert}, State0} end; handle_info({CloseTag, Socket}, StateName, - #state{socket = Socket, + #state{static_env = #static_env{socket = Socket, + close_tag = CloseTag}, + connection_env = #connection_env{negotiated_version = Version}, socket_options = #socket_options{active = Active}, - protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}, - close_tag = CloseTag, - negotiated_version = Version} = State) -> + protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}} = State) -> %% Note that as of DTLS 1.2 (TLS 1.1), %% failure to properly close a connection no longer requires that a %% session not be resumed. This is a change from DTLS 1.0 to conform @@ -873,7 +923,7 @@ handle_info({CloseTag, Socket}, StateName, ok end, ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), - ssl_connection:stop({shutdown, transport_closed}, State); + {stop, {shutdown, transport_closed}, State}; true -> %% Fixes non-delivery of final DTLS record in {active, once}. %% Basically allows the application the opportunity to set {active, once} again @@ -891,9 +941,10 @@ handle_info(Msg, StateName, State) -> ssl_connection:StateName(info, Msg, State, ?MODULE). handle_state_timeout(flight_retransmission_timeout, StateName, - #state{flight_state = {retransmit, NextTimeout}} = State0) -> - {State1, Actions0} = send_handshake_flight(State0#state{flight_state = {retransmit, NextTimeout}}, - retransmit_epoch(StateName, State0)), + #state{protocol_specific = + #{flight_state := {retransmit, _NextTimeout}}} = State0) -> + {State1, Actions0} = send_handshake_flight(State0, + retransmit_epoch(StateName, State0)), {next_state, StateName, State, Actions} = next_event(StateName, no_record, State1, Actions0), %% This will reset the retransmission timer by repeating the enter state event {repeat_state, State, Actions}. @@ -907,8 +958,8 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)). -handle_own_alert(Alert, Version, StateName, #state{data_tag = udp, - role = Role, +handle_own_alert(Alert, Version, StateName, #state{static_env = #static_env{data_tag = udp, + role = Role}, ssl_options = Options} = State0) -> case ignore_alert(Alert, State0) of {true, State} -> @@ -933,7 +984,7 @@ decode_alerts(Bin) -> ssl_alert:decode(Bin). gen_handshake(StateName, Type, Event, - #state{negotiated_version = Version} = State) -> + #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try ssl_connection:StateName(Type, Event, State, ?MODULE) of Result -> Result @@ -944,7 +995,7 @@ gen_handshake(StateName, Type, Event, Version, StateName, State) end. -gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) -> +gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try handle_info(Event, StateName, State) of Result -> Result @@ -955,7 +1006,7 @@ gen_info(Event, connection = StateName, #state{negotiated_version = Version} = Version, StateName, State) end; -gen_info(Event, StateName, #state{negotiated_version = Version} = State) -> +gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try handle_info(Event, StateName, State) of Result -> Result @@ -998,18 +1049,18 @@ next_flight(Flight) -> change_cipher_spec => undefined, handshakes_after_change_cipher_spec => []}. -handle_flight_timer(#state{data_tag = udp, - flight_state = {retransmit, Timeout}} = State) -> +handle_flight_timer(#state{static_env = #static_env{data_tag = udp}, + protocol_specific = #{flight_state := {retransmit, Timeout}}} = State) -> start_retransmision_timer(Timeout, State); -handle_flight_timer(#state{data_tag = udp, - flight_state = connection} = State) -> +handle_flight_timer(#state{static_env = #static_env{data_tag = udp}, + protocol_specific = #{flight_state := connection}} = State) -> {State, []}; -handle_flight_timer(State) -> +handle_flight_timer(#state{protocol_specific = #{flight_state := reliable}} = State) -> %% No retransmision needed i.e DTLS over SCTP - {State#state{flight_state = reliable}, []}. + {State, []}. -start_retransmision_timer(Timeout, State) -> - {State#state{flight_state = {retransmit, new_timeout(Timeout)}}, +start_retransmision_timer(Timeout, #state{protocol_specific = PS} = State) -> + {State#state{protocol_specific = PS#{flight_state => {retransmit, new_timeout(Timeout)}}}, [{state_timeout, Timeout, flight_retransmission_timeout}]}. new_timeout(N) when N =< 30 -> @@ -1017,11 +1068,11 @@ new_timeout(N) when N =< 30 -> new_timeout(_) -> 60. -send_handshake_flight(#state{socket = Socket, - transport_cb = Transport, - flight_buffer = #{handshakes := Flight, +send_handshake_flight(#state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, + flight_buffer = #{handshakes := Flight, change_cipher_spec := undefined}, - negotiated_version = Version, connection_states = ConnectionStates0} = State0, Epoch) -> %% TODO remove hardcoded Max size {Encoded, ConnectionStates} = @@ -1029,12 +1080,12 @@ send_handshake_flight(#state{socket = Socket, send(Transport, Socket, Encoded), {State0#state{connection_states = ConnectionStates}, []}; -send_handshake_flight(#state{socket = Socket, - transport_cb = Transport, +send_handshake_flight(#state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := [_|_] = Flight0, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := []}, - negotiated_version = Version, connection_states = ConnectionStates0} = State0, Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0), @@ -1043,12 +1094,12 @@ send_handshake_flight(#state{socket = Socket, send(Transport, Socket, [HsBefore, EncChangeCipher]), {State0#state{connection_states = ConnectionStates}, []}; -send_handshake_flight(#state{socket = Socket, - transport_cb = Transport, +send_handshake_flight(#state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := [_|_] = Flight0, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, - negotiated_version = Version, connection_states = ConnectionStates0} = State0, Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0), @@ -1059,12 +1110,12 @@ send_handshake_flight(#state{socket = Socket, send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]), {State0#state{connection_states = ConnectionStates}, []}; -send_handshake_flight(#state{socket = Socket, - transport_cb = Transport, +send_handshake_flight(#state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := [], change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, - negotiated_version = Version, connection_states = ConnectionStates0} = State0, Epoch) -> {EncChangeCipher, ConnectionStates1} = encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0), @@ -1117,22 +1168,22 @@ log_ignore_alert(false, _, _,_) -> ok. send_application_data(Data, From, _StateName, - #state{socket = Socket, - negotiated_version = Version, - protocol_cb = Connection, - transport_cb = Transport, + #state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, + handshake_env = HsEnv, connection_states = ConnectionStates0, ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> - renegotiate(State0#state{renegotiation = {true, internal}}, + renegotiate(State0#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, internal}}}, [{next_event, {call, From}, {application_data, Data}}]); false -> {Msgs, ConnectionStates} = - Connection:encode_data(Data, Version, ConnectionStates0), + dtls_record:encode_data(Data, Version, ConnectionStates0), State = State0#state{connection_states = ConnectionStates}, - case Connection:send(Transport, Socket, Msgs) of + case send(Transport, Socket, Msgs) of ok -> ssl_connection:hibernate_after(connection, State, [{reply, From, ok}]); Result -> @@ -1154,3 +1205,4 @@ is_time_to_renegotiate(N, M) when N < M-> false; is_time_to_renegotiate(_,_) -> true. + diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 3f70eaec8a..6e9bf99e52 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -46,7 +46,7 @@ %% Handshake handling %%==================================================================== %%-------------------------------------------------------------------- --spec client_hello(host(), inet:port_number(), ssl_record:connection_states(), +-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% @@ -59,7 +59,7 @@ client_hello(Host, Port, ConnectionStates, SslOpts, Cache, CacheCb, Renegotiation, OwnCert). %%-------------------------------------------------------------------- --spec client_hello(host(), inet:port_number(), term(), ssl_record:connection_states(), +-spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(), #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% @@ -123,7 +123,7 @@ cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor}, Random, SessionId, CipherSuites, CompressionMethods], crypto:hmac(sha, Key, CookieData). %%-------------------------------------------------------------------- --spec hello_verify_request(binary(), dtls_record:dtls_version()) -> #hello_verify_request{}. +-spec hello_verify_request(binary(), ssl_record:ssl_version()) -> #hello_verify_request{}. %% %% Description: Creates a hello verify request message sent by server to %% verify client @@ -151,7 +151,7 @@ encode_handshake(Handshake, Version, Seq) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec get_dtls_handshake(dtls_record:dtls_version(), binary(), #protocol_buffers{}) -> +-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}) -> {[dtls_handshake()], #protocol_buffers{}}. %% %% Description: Given buffered and new data from dtls_record, collects @@ -215,8 +215,6 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, dtls_v1:corresponding_tls_version(Version), SslOpts, Session0, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} catch throw:Alert -> @@ -225,17 +223,16 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> - case ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, - Compression, HelloExt, - dtls_v1:corresponding_tls_version(Version), - SslOpt, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; + try ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, + Compression, HelloExt, + dtls_v1:corresponding_tls_version(Version), + SslOpt, ConnectionStates0, Renegotiation) of {ConnectionStates, ProtoExt, Protocol} -> {Version, SessionId, ConnectionStates, ProtoExt, Protocol} + catch throw:Alert -> + Alert end. - %%-------------------------------------------------------------------- enc_handshake(#hello_verify_request{protocol_version = {Major, Minor}, diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl index 50e92027d2..41da8e5c8c 100644 --- a/lib/ssl/src/dtls_handshake.hrl +++ b/lib/ssl/src/dtls_handshake.hrl @@ -27,6 +27,7 @@ -define(dtls_handshake, true). -include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes +-include("ssl_api.hrl"). -define(HELLO_VERIFY_REQUEST, 3). -define(HELLO_VERIFY_REQUEST_VERSION, {254, 255}). diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index 1497c77cf3..092366b7c0 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -144,11 +144,11 @@ handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket %% UDP socket does not have a connection and should not receive an econnreset %% This does however happens on some windows versions. Just ignoring it %% appears to make things work as expected! -handle_info({Error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) -> +handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) -> Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]), error_logger:info_report(Report), {noreply, State}; -handle_info({Error, Socket, Error}, #state{listener = Socket, transport = {_,_,_, Error}} = State) -> +handle_info({ErrorTag, Socket, Error}, #state{listener = Socket, transport = {_,_,_, ErrorTag}} = State) -> Report = io_lib:format("SSL Packet muliplxer shutdown: Socket error: ~p ~n", [Error]), error_logger:info_report(Report), {noreply, State#state{close=true}}; @@ -297,6 +297,9 @@ do_set_emulated_opts([], Opts) -> Opts; do_set_emulated_opts([{mode, Value} | Rest], Opts) -> do_set_emulated_opts(Rest, Opts#socket_options{mode = Value}); +do_set_emulated_opts([{active, N0} | Rest], Opts=#socket_options{active = Active}) when is_integer(N0) -> + N = tls_socket:update_active_n(N0, Active), + do_set_emulated_opts(Rest, Opts#socket_options{active = N}); do_set_emulated_opts([{active, Value} | Rest], Opts) -> do_set_emulated_opts(Rest, Opts#socket_options{active = Value}). diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index b7346d3ec8..2fe875da31 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2018. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -49,9 +49,8 @@ is_acceptable_version/2, hello_version/2]). --export_type([dtls_version/0, dtls_atom_version/0]). +-export_type([dtls_atom_version/0]). --type dtls_version() :: ssl_record:ssl_version(). -type dtls_atom_version() :: dtlsv1 | 'dtlsv1.2'. -define(REPLAY_WINDOW_SIZE, 64). @@ -135,7 +134,7 @@ set_connection_state_by_epoch(ReadState, Epoch, #{saved_read := #{epoch := Epoch States#{saved_read := ReadState}. %%-------------------------------------------------------------------- --spec init_connection_state_seq(dtls_version(), ssl_record:connection_states()) -> +-spec init_connection_state_seq(ssl_record:ssl_version(), ssl_record:connection_states()) -> ssl_record:connection_state(). %% %% Description: Copy the read sequence number to the write sequence number @@ -163,7 +162,7 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, Epoch. %%-------------------------------------------------------------------- --spec get_dtls_records(binary(), [dtls_version()], binary()) -> {[binary()], binary()} | #alert{}. +-spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary()) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover @@ -188,7 +187,7 @@ get_dtls_records(Data, Versions, Buffer) -> %%==================================================================== %%-------------------------------------------------------------------- --spec encode_handshake(iolist(), dtls_version(), integer(), ssl_record:connection_states()) -> +-spec encode_handshake(iolist(), ssl_record:ssl_version(), integer(), ssl_record:connection_states()) -> {iolist(), ssl_record:connection_states()}. % %% Description: Encodes a handshake message to send on the ssl-socket. @@ -198,7 +197,7 @@ encode_handshake(Frag, Version, Epoch, ConnectionStates) -> %%-------------------------------------------------------------------- --spec encode_alert_record(#alert{}, dtls_version(), ssl_record:connection_states()) -> +-spec encode_alert_record(#alert{}, ssl_record:ssl_version(), ssl_record:connection_states()) -> {iolist(), ssl_record:connection_states()}. %% %% Description: Encodes an alert message to send on the ssl-socket. @@ -210,7 +209,7 @@ encode_alert_record(#alert{level = Level, description = Description}, ConnectionStates). %%-------------------------------------------------------------------- --spec encode_change_cipher_spec(dtls_version(), integer(), ssl_record:connection_states()) -> +-spec encode_change_cipher_spec(ssl_record:ssl_version(), integer(), ssl_record:connection_states()) -> {iolist(), ssl_record:connection_states()}. %% %% Description: Encodes a change_cipher_spec-message to send on the ssl socket. @@ -219,7 +218,7 @@ encode_change_cipher_spec(Version, Epoch, ConnectionStates) -> encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates). %%-------------------------------------------------------------------- --spec encode_data(binary(), dtls_version(), ssl_record:connection_states()) -> +-spec encode_data(binary(), ssl_record:ssl_version(), ssl_record:connection_states()) -> {iolist(),ssl_record:connection_states()}. %% %% Description: Encodes data to send on the ssl-socket. @@ -248,8 +247,8 @@ decode_cipher_text(#ssl_tls{epoch = Epoch} = CipherText, ConnnectionStates0) -> %%==================================================================== %%-------------------------------------------------------------------- --spec protocol_version(dtls_atom_version() | dtls_version()) -> - dtls_version() | dtls_atom_version(). +-spec protocol_version(dtls_atom_version() | ssl_record:ssl_version()) -> + ssl_record:ssl_version() | dtls_atom_version(). %% %% Description: Creates a protocol version record from a version atom %% or vice versa. @@ -263,7 +262,7 @@ protocol_version({254, 253}) -> protocol_version({254, 255}) -> dtlsv1. %%-------------------------------------------------------------------- --spec lowest_protocol_version(dtls_version(), dtls_version()) -> dtls_version(). +-spec lowest_protocol_version(ssl_record:ssl_version(), ssl_record:ssl_version()) -> ssl_record:ssl_version(). %% %% Description: Lowes protocol version of two given versions %%-------------------------------------------------------------------- @@ -277,7 +276,7 @@ lowest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- --spec lowest_protocol_version([dtls_version()]) -> dtls_version(). +-spec lowest_protocol_version([ssl_record:ssl_version()]) -> ssl_record:ssl_version(). %% %% Description: Lowest protocol version present in a list %%-------------------------------------------------------------------- @@ -288,7 +287,7 @@ lowest_protocol_version(Versions) -> lowest_list_protocol_version(Ver, Vers). %%-------------------------------------------------------------------- --spec highest_protocol_version([dtls_version()]) -> dtls_version(). +-spec highest_protocol_version([ssl_record:ssl_version()]) -> ssl_record:ssl_version(). %% %% Description: Highest protocol version present in a list %%-------------------------------------------------------------------- @@ -299,7 +298,7 @@ highest_protocol_version(Versions) -> highest_list_protocol_version(Ver, Vers). %%-------------------------------------------------------------------- --spec highest_protocol_version(dtls_version(), dtls_version()) -> dtls_version(). +-spec highest_protocol_version(ssl_record:ssl_version(), ssl_record:ssl_version()) -> ssl_record:ssl_version(). %% %% Description: Highest protocol version of two given versions %%-------------------------------------------------------------------- @@ -315,7 +314,7 @@ highest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- --spec is_higher(V1 :: dtls_version(), V2::dtls_version()) -> boolean(). +-spec is_higher(V1 :: ssl_record:ssl_version(), V2::ssl_record:ssl_version()) -> boolean(). %% %% Description: Is V1 > V2 %%-------------------------------------------------------------------- @@ -327,7 +326,7 @@ is_higher(_, _) -> false. %%-------------------------------------------------------------------- --spec supported_protocol_versions() -> [dtls_version()]. +-spec supported_protocol_versions() -> [ssl_record:ssl_version()]. %% %% Description: Protocol versions supported %%-------------------------------------------------------------------- @@ -370,7 +369,7 @@ supported_protocol_versions([_|_] = Vsns) -> end. %%-------------------------------------------------------------------- --spec is_acceptable_version(dtls_version(), Supported :: [dtls_version()]) -> boolean(). +-spec is_acceptable_version(ssl_record:ssl_version(), Supported :: [ssl_record:ssl_version()]) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. %% @@ -378,7 +377,7 @@ supported_protocol_versions([_|_] = Vsns) -> is_acceptable_version(Version, Versions) -> lists:member(Version, Versions). --spec hello_version(dtls_version(), [dtls_version()]) -> dtls_version(). +-spec hello_version(ssl_record:ssl_version(), [ssl_record:ssl_version()]) -> ssl_record:ssl_version(). hello_version(Version, Versions) -> case dtls_v1:corresponding_tls_version(Version) of TLSVersion when TLSVersion >= {3, 3} -> @@ -547,15 +546,15 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, compression_algorithm = CompAlg}} = ReadState0, ConnnectionStates0) -> AAD = start_additional_data(Type, Version, Epoch, Seq), - CipherS1 = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT16(Epoch), ?UINT48(Seq)>>, CipherS0), + CipherS = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT16(Epoch), ?UINT48(Seq)>>, CipherS0), TLSVersion = dtls_v1:corresponding_tls_version(Version), - case ssl_record:decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment, TLSVersion) of - {PlainFragment, CipherState} -> - {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, + case ssl_record:decipher_aead(BulkCipherAlgo, CipherS, AAD, CipherFragment, TLSVersion) of + PlainFragment when is_binary(PlainFragment) -> + {Plain, CompressionS} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), - ReadState0 = ReadState0#{compression_state => CompressionS1, - cipher_state => CipherState}, - ReadState = update_replay_window(Seq, ReadState0), + ReadState1 = ReadState0#{compression_state := CompressionS, + cipher_state := CipherS}, + ReadState = update_replay_window(Seq, ReadState1), ConnnectionStates = set_connection_state_by_epoch(ReadState, Epoch, ConnnectionStates0, read), {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; #alert{} = Alert -> diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl index 2001afd02f..4d07372e31 100644 --- a/lib/ssl/src/dtls_socket.erl +++ b/lib/ssl/src/dtls_socket.erl @@ -38,7 +38,9 @@ listen(Port, #config{transport_info = TransportInfo, case dtls_listener_sup:start_child([Port, TransportInfo, emulated_socket_options(EmOpts, #socket_options{}), Options ++ internal_inet_values(), SslOpts]) of {ok, Pid} -> - {ok, #sslsocket{pid = {dtls, Config#config{dtls_handler = {Pid, Port}}}}}; + Socket = #sslsocket{pid = {dtls, Config#config{dtls_handler = {Pid, Port}}}}, + check_active_n(EmOpts, Socket), + {ok, Socket}; Err = {error, _} -> Err end. @@ -81,8 +83,9 @@ socket(Pids, Transport, Socket, ConnectionCb) -> #sslsocket{pid = Pids, %% "The name "fd" is keept for backwards compatibility fd = {Transport, Socket, ConnectionCb}}. -setopts(_, #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) -> - SplitOpts = tls_socket:split_options(Options), +setopts(_, Socket = #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) -> + SplitOpts = {_, EmOpts} = tls_socket:split_options(Options), + check_active_n(EmOpts, Socket), dtls_packet_demux:set_sock_opts(ListenPid, SplitOpts); %%% Following clauses will not be called for emulated options, they are handled in the connection process setopts(gen_udp, Socket, Options) -> @@ -90,6 +93,32 @@ setopts(gen_udp, Socket, Options) -> setopts(Transport, Socket, Options) -> Transport:setopts(Socket, Options). +check_active_n(EmulatedOpts, Socket = #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}) -> + %% We check the resulting options to send an ssl_passive message if necessary. + case proplists:lookup(active, EmulatedOpts) of + %% The provided value is out of bound. + {_, N} when is_integer(N), N < -32768 -> + throw(einval); + {_, N} when is_integer(N), N > 32767 -> + throw(einval); + {_, N} when is_integer(N) -> + {ok, #socket_options{active = Active}, _} = dtls_packet_demux:get_all_opts(ListenPid), + case Active of + Atom when is_atom(Atom), N =< 0 -> + self() ! {ssl_passive, Socket}; + %% The result of the addition is out of bound. + %% We do not need to check < -32768 because Active can't be below 1. + A when is_integer(A), A + N > 32767 -> + throw(einval); + A when is_integer(A), A + N =< 0 -> + self() ! {ssl_passive, Socket}; + _ -> + ok + end; + _ -> + ok + end. + getopts(_, #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) -> SplitOpts = tls_socket:split_options(Options), dtls_packet_demux:get_sock_opts(ListenPid, SplitOpts); @@ -161,9 +190,18 @@ emulated_socket_options(InetValues, #socket_options{ mode = proplists:get_value(mode, InetValues, Mode), packet = proplists:get_value(packet, InetValues, Packet), packet_size = proplists:get_value(packet_size, InetValues, PacketSize), - active = proplists:get_value(active, InetValues, Active) + active = emulated_active_option(InetValues, Active) }. +emulated_active_option([], Active) -> + Active; +emulated_active_option([{active, Active} | _], _) when Active =< 0 -> + false; +emulated_active_option([{active, Active} | _], _) -> + Active; +emulated_active_option([_|Tail], Active) -> + emulated_active_option(Tail, Active). + emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) -> validate_inet_option(mode, Value), emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]); @@ -185,6 +223,9 @@ validate_inet_option(mode, Value) when Value =/= list, Value =/= binary -> throw({error, {options, {mode,Value}}}); validate_inet_option(active, Value) + when Value >= -32768, Value =< 32767 -> + ok; +validate_inet_option(active, Value) when Value =/= true, Value =/= false, Value =/= once -> throw({error, {options, {active,Value}}}); validate_inet_option(_, _) -> diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index a4f8bb7562..5cab35fd4b 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -480,22 +480,25 @@ allowed_nodes(PeerCert, Allowed, PeerIP, Node, Host) -> allowed_nodes(PeerCert, Allowed, PeerIP) end. - - setup(Node, Type, MyNode, LongOrShortNames, SetupTime) -> gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames, SetupTime). gen_setup(Driver, Node, Type, MyNode, LongOrShortNames, SetupTime) -> Kernel = self(), monitor_pid( - spawn_opt( - fun() -> - do_setup( - Driver, Kernel, Node, Type, - MyNode, LongOrShortNames, SetupTime) - end, - [link, {priority, max}])). + spawn_opt(setup_fun(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime), + [link, {priority, max}])). + +-spec setup_fun(_,_,_,_,_,_,_) -> fun(() -> no_return()). +setup_fun(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> + fun() -> + do_setup( + Driver, Kernel, Node, Type, + MyNode, LongOrShortNames, SetupTime) + end. + +-spec do_setup(_,_,_,_,_,_,_) -> no_return(). do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> {Name, Address} = split_node(Driver, Node, LongOrShortNames), ErlEpmd = net_kernel:epmd_module(), @@ -520,6 +523,8 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> trace({getaddr_failed, Driver, Address, Other})) end. +-spec do_setup_connect(_,_,_,_,_,_,_,_,_,_) -> no_return(). + do_setup_connect(Driver, Kernel, Node, Address, Ip, TcpPort, Version, Type, MyNode, Timer) -> Opts = trace(connect_options(get_ssl_options(client))), dist_util:reset_timer(Timer), @@ -564,7 +569,7 @@ gen_close(Driver, Socket) -> %% Determine if EPMD module supports address resolving. Default %% is to use inet_tcp:getaddr/2. %% ------------------------------------------------------------ -get_address_resolver(EpmdModule, Driver) -> +get_address_resolver(EpmdModule, _Driver) -> case erlang:function_exported(EpmdModule, address_please, 3) of true -> {EpmdModule, address_please}; _ -> {erl_epmd, address_please} diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 03a1e40bfc..fb6b7ba8e8 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2018. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -61,16 +61,317 @@ -deprecated({ssl_accept, 2, eventually}). -deprecated({ssl_accept, 3, eventually}). +-export_type([socket/0, + sslsocket/0, + socket_option/0, + active_msgs/0, + host/0, + tls_option/0, + tls_client_option/0, + tls_server_option/0, + erl_cipher_suite/0, + old_cipher_suite/0, + ciphers/0, + cipher/0, + hash/0, + kex_algo/0, + prf_random/0, + cipher_filters/0, + sign_algo/0, + protocol_version/0, + protocol_extensions/0, + session_id/0, + error_alert/0, + srp_param_type/0]). + +%% ------------------------------------------------------------------------------------------------------- +-type socket() :: gen_tcp:socket(). +-type socket_option() :: gen_tcp:connect_option() | gen_tcp:listen_option() | gen_udp:option(). +-type sslsocket() :: any(). +-type tls_option() :: tls_client_option() | tls_server_option(). +-type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option(). +-type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option(). +-type active_msgs() :: {ssl, sslsocket(), Data::binary() | list()} | {ssl_closed, sslsocket()} | + {ssl_error, sslsocket(), Reason::term()} | {ssl_passive, sslsocket()}. +-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), + ClosedTag::atom(), ErrTag::atom()}}. +-type host() :: hostname() | ip_address(). +-type hostname() :: string(). +-type ip_address() :: inet:ip_address(). +-type session_id() :: binary(). +-type protocol_version() :: tls_version() | dtls_version(). +-type tls_version() :: tlsv1 | 'tlsv1.1' | 'tlsv1.2' | 'tlsv1.3' | legacy_version(). +-type dtls_version() :: 'dtlsv1' | 'dtlsv1.2'. +-type legacy_version() :: sslv3. +-type verify_type() :: verify_none | verify_peer. +-type cipher() :: aes_128_cbc | + aes_256_cbc | + aes_128_gcm | + aes_256_gcm | + chacha20_poly1305 | + legacy_cipher(). +-type legacy_cipher() :: rc4_128 | + des_cbc | + '3des_ede_cbc'. + +-type hash() :: sha | + sha2() | + legacy_hash(). + +-type sha2() :: sha224 | + sha256 | + sha384 | + sha512. + +-type legacy_hash() :: md5. + +-type sign_algo() :: rsa | dsa | ecdsa. +-type kex_algo() :: rsa | + dhe_rsa | dhe_dss | + ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa | + srp_rsa| srp_dss | + psk | dhe_psk | rsa_psk | + dh_anon | ecdh_anon | srp_anon | + any. %% TLS 1.3 +-type erl_cipher_suite() :: #{key_exchange := kex_algo(), + cipher := cipher(), + mac := hash() | aead, + prf := hash() | default_prf %% Old cipher suites, version dependent + }. + +-type old_cipher_suite() :: {kex_algo(), cipher(), hash()} % Pre TLS 1.2 + %% TLS 1.2, internally PRE TLS 1.2 will use default_prf + | {kex_algo(), cipher(), hash() | aead, hash()}. + +-type named_curve() :: sect571r1 | + sect571k1 | + secp521r1 | + brainpoolP512r1 | + sect409k1 | + sect409r1 | + brainpoolP384r1 | + secp384r1 | + sect283k1 | + sect283r1 | + brainpoolP256r1 | + secp256k1 | + secp256r1 | + sect239k1 | + sect233k1 | + sect233r1 | + secp224k1 | + secp224r1 | + sect193r1 | + sect193r2 | + secp192k1 | + secp192r1 | + sect163k1 | + sect163r1 | + sect163r2 | + secp160k1 | + secp160r1 | + secp160r2. + +-type srp_param_type() :: srp_1024 | + srp_1536 | + srp_2048 | + srp_3072 | + srp_4096 | + srp_6144 | + srp_8192. + +-type error_alert() :: {tls_alert, {tls_alert(), Description::string()}}. + +-type tls_alert() :: close_notify | + unexpected_message | + bad_record_mac | + record_overflow | + handshake_failure | + bad_certificate | + unsupported_certificate | + certificate_revoked | + certificate_expired | + certificate_unknown | + illegal_parameter | + unknown_ca | + access_denied | + decode_error | + decrypt_error | + export_restriction| + protocol_version | + insufficient_security | + internal_error | + inappropriate_fallback | + user_canceled | + no_renegotiation | + unsupported_extension | + certificate_unobtainable | + unrecognized_name | + bad_certificate_status_response | + bad_certificate_hash_value | + unknown_psk_identity | + no_application_protocol. +%% ------------------------------------------------------------------------------------------------------- +-type common_option() :: {protocol, protocol()} | + {handshake, handshake_completion()} | + {cert, cert()} | + {certfile, cert_pem()} | + {key, key()} | + {keyfile, key_pem()} | + {password, key_password()} | + {ciphers, cipher_suites()} | + {eccs, eccs()} | + {secure_renegotiate, secure_renegotiation()} | + {depth, allowed_cert_chain_length()} | + {verify_fun, custom_verify()} | + {crl_check, crl_check()} | + {crl_cache, crl_cache_opts()} | + {max_handshake_size, handshake_size()} | + {partial_chain, root_fun()} | + {versions, protocol_versions()} | + {user_lookup_fun, custom_user_lookup()} | + {log_alert, log_alert()} | + {hibernate_after, hibernate_after()} | + {padding_check, padding_check()} | + {beast_mitigation, beast_mitigation()} | + {ssl_imp, ssl_imp()}. + +-type protocol() :: tls | dtls. +-type handshake_completion() :: hello | full. +-type cert() :: public_key:der_encoded(). +-type cert_pem() :: file:filename(). +-type key() :: {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', + public_key:der_encoded()} | + #{algorithm := rsa | dss | ecdsa, + engine := crypto:engine_ref(), + key_id := crypto:key_id(), + password => crypto:password()}. +-type key_pem() :: file:filename(). +-type key_password() :: string(). +-type cipher_suites() :: ciphers(). +-type ciphers() :: [erl_cipher_suite()] | + string(). % (according to old API) +-type cipher_filters() :: list({key_exchange | cipher | mac | prf, + algo_filter()}). +-type algo_filter() :: fun((kex_algo()|cipher()|hash()|aead|default_prf) -> true | false). +-type eccs() :: [named_curve()]. +-type secure_renegotiation() :: boolean(). +-type allowed_cert_chain_length() :: integer(). +-type custom_verify() :: {Verifyfun :: fun(), InitialUserState :: term()}. +-type crl_check() :: boolean() | peer | best_effort. +-type crl_cache_opts() :: [term()]. +-type handshake_size() :: integer(). +-type hibernate_after() :: timeout(). +-type root_fun() :: fun(). +-type protocol_versions() :: [protocol_version()]. +-type signature_algs() :: [{hash(), sign_algo()}]. +-type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: term()}. +-type padding_check() :: boolean(). +-type beast_mitigation() :: one_n_minus_one | zero_n | disabled. +-type srp_identity() :: {Username :: string(), Password :: string()}. +-type psk_identity() :: string(). +-type log_alert() :: boolean(). + +%% ------------------------------------------------------------------------------------------------------- + +-type client_option() :: {verify, client_verify_type()} | + {reuse_session, client_reuse_session()} | + {reuse_sessions, client_reuse_sessions()} | + {cacerts, client_cacerts()} | + {cacertfile, client_cafile()} | + {alpn_advertised_protocols, client_alpn()} | + {client_preferred_next_protocols, client_preferred_next_protocols()} | + {psk_identity, client_psk_identity()} | + {srp_identity, client_srp_identity()} | + {server_name_indication, sni()} | + {customize_hostname_check, customize_hostname_check()} | + {signature_algs, client_signature_algs()} | + {fallback, fallback()}. + +-type client_verify_type() :: verify_type(). +-type client_reuse_session() :: session_id(). +-type client_reuse_sessions() :: boolean() | save. +-type client_cacerts() :: [public_key:der_encoded()]. +-type client_cafile() :: file:filename(). +-type app_level_protocol() :: binary(). +-type client_alpn() :: [app_level_protocol()]. +-type client_preferred_next_protocols() :: {Precedence :: server | client, + ClientPrefs :: [app_level_protocol()]} | + {Precedence :: server | client, + ClientPrefs :: [app_level_protocol()], + Default::app_level_protocol()}. +-type client_psk_identity() :: psk_identity(). +-type client_srp_identity() :: srp_identity(). +-type customize_hostname_check() :: list(). +-type sni() :: HostName :: hostname() | disable. +-type client_signature_algs() :: signature_algs(). +-type fallback() :: boolean(). +-type ssl_imp() :: new | old. + +%% ------------------------------------------------------------------------------------------------------- + +-type server_option() :: {cacerts, server_cacerts()} | + {cacertfile, server_cafile()} | + {dh, dh_der()} | + {dhfile, dh_file()} | + {verify, server_verify_type()} | + {fail_if_no_peer_cert, fail_if_no_peer_cert()} | + {reuse_sessions, server_reuse_sessions()} | + {reuse_session, server_reuse_session()} | + {alpn_preferred_protocols, server_alpn()} | + {next_protocols_advertised, server_next_protocol()} | + {psk_identity, server_psk_identity()} | + {honor_cipher_order, boolean()} | + {sni_hosts, sni_hosts()} | + {sni_fun, sni_fun()} | + {honor_cipher_order, honor_cipher_order()} | + {honor_ecc_order, honor_ecc_order()} | + {client_renegotiation, client_renegotiation()}| + {signature_algs, server_signature_algs()}. + +-type server_cacerts() :: [public_key:der_encoded()]. +-type server_cafile() :: file:filename(). +-type server_alpn() :: [app_level_protocol()]. +-type server_next_protocol() :: [app_level_protocol()]. +-type server_psk_identity() :: psk_identity(). +-type dh_der() :: binary(). +-type dh_file() :: file:filename(). +-type server_verify_type() :: verify_type(). +-type fail_if_no_peer_cert() :: boolean(). +-type server_signature_algs() :: signature_algs(). +-type server_reuse_session() :: fun(). +-type server_reuse_sessions() :: boolean(). +-type sni_hosts() :: [{hostname(), [server_option() | common_option()]}]. +-type sni_fun() :: fun(). +-type honor_cipher_order() :: boolean(). +-type honor_ecc_order() :: boolean(). +-type client_renegotiation() :: boolean(). +%% ------------------------------------------------------------------------------------------------------- +-type prf_random() :: client_random | server_random. +-type protocol_extensions() :: #{renegotiation_info => binary(), + signature_algs => signature_algs(), + alpn => app_level_protocol(), + srp => binary(), + next_protocol => app_level_protocol(), + ec_point_formats => [0..2], + elliptic_curves => [public_key:oid()], + sni => hostname()}. +%% ------------------------------------------------------------------------------------------------------- + +%%%-------------------------------------------------------------------- +%%% API +%%%-------------------------------------------------------------------- + %%-------------------------------------------------------------------- --spec start() -> ok | {error, reason()}. --spec start(permanent | transient | temporary) -> ok | {error, reason()}. %% %% Description: Utility function that starts the ssl and applications %% that it depends on. %% see application(3) %%-------------------------------------------------------------------- +-spec start() -> ok | {error, reason()}. start() -> start(temporary). +-spec start(permanent | transient | temporary) -> ok | {error, reason()}. start(Type) -> case application:ensure_all_started(ssl, Type) of {ok, _} -> @@ -87,20 +388,17 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- --spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} | - {error, reason()}. --spec connect(host() | port(), [connect_option()] | inet:port_number(), - timeout() | list()) -> - {ok, #sslsocket{}} | {error, reason()}. --spec connect(host() | port(), inet:port_number(), list(), timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. - %% %% Description: Connect to an ssl server. %%-------------------------------------------------------------------- +-spec connect(host() | port(), [client_option()]) -> {ok, #sslsocket{}} | + {error, reason()}. connect(Socket, SslOptions) when is_port(Socket) -> connect(Socket, SslOptions, infinity). +-spec connect(host() | port(), [client_option()] | inet:port_number(), + timeout() | list()) -> + {ok, #sslsocket{}} | {error, reason()}. connect(Socket, SslOptions0, Timeout) when is_port(Socket), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0, @@ -117,6 +415,8 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket), connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). +-spec connect(host() | port(), inet:port_number(), [client_option()], timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> try {ok, Config} = handle_options(Options, client, Host), @@ -132,7 +432,7 @@ connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout end. %%-------------------------------------------------------------------- --spec listen(inet:port_number(), [listen_option()]) ->{ok, #sslsocket{}} | {error, reason()}. +-spec listen(inet:port_number(), [tls_server_option()]) ->{ok, #sslsocket{}} | {error, reason()}. %% %% Description: Creates an ssl listen socket. @@ -148,16 +448,16 @@ listen(Port, Options0) -> Error end. %%-------------------------------------------------------------------- --spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} | - {error, reason()}. --spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | - {error, reason()}. %% %% Description: Performs transport accept on an ssl listen socket %%-------------------------------------------------------------------- +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} | + {error, reason()}. transport_accept(ListenSocket) -> transport_accept(ListenSocket, infinity). +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | + {error, reason()}. transport_accept(#sslsocket{pid = {ListenSocket, #config{connection_cb = ConnectionCb} = Config}}, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> @@ -169,25 +469,25 @@ transport_accept(#sslsocket{pid = {ListenSocket, end. %%-------------------------------------------------------------------- --spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}. --spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option() - | transport_option()]) -> - ok | {ok, #sslsocket{}} | {error, reason()}. - --spec ssl_accept(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) -> - ok | {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- +-spec ssl_accept(#sslsocket{}) -> ok | {error, timeout | closed | {options, any()}| error_alert()}. ssl_accept(ListenSocket) -> ssl_accept(ListenSocket, [], infinity). + +-spec ssl_accept(#sslsocket{} | port(), timeout()| [tls_server_option()]) -> + ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}. ssl_accept(Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> ssl_accept(Socket, [], Timeout); ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> ssl_accept(ListenSocket, SslOptions, infinity); ssl_accept(Socket, Timeout) -> ssl_accept(Socket, [], Timeout). + +-spec ssl_accept(#sslsocket{} | port(), [tls_server_option()], timeout()) -> + ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}. ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> handshake(Socket, SslOptions, Timeout); ssl_accept(Socket, SslOptions, Timeout) -> @@ -198,20 +498,18 @@ ssl_accept(Socket, SslOptions, Timeout) -> Error end. %%-------------------------------------------------------------------- --spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. --spec handshake(#sslsocket{} | port(), timeout()| [ssl_option() - | transport_option()]) -> - {ok, #sslsocket{}} | {error, reason()}. - --spec handshake(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- + +%% Performs the SSL/TLS/DTLS server-side handshake. +-spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}. handshake(ListenSocket) -> handshake(ListenSocket, infinity). +-spec handshake(#sslsocket{} | port(), timeout()| [tls_server_option()]) -> + {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}. handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> ssl_connection:handshake(Socket, Timeout); @@ -219,6 +517,8 @@ handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Tim handshake(ListenSocket, SslOptions) when is_port(ListenSocket) -> handshake(ListenSocket, SslOptions, infinity). +-spec handshake(#sslsocket{} | port(), [tls_server_option()], timeout()) -> + {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}. handshake(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)-> handshake(Socket, Timeout); @@ -261,7 +561,7 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket), %%-------------------------------------------------------------------- --spec handshake_continue(#sslsocket{}, [ssl_option()]) -> +-spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()]) -> {ok, #sslsocket{}} | {error, reason()}. %% %% @@ -270,7 +570,7 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket), handshake_continue(Socket, SSLOptions) -> handshake_continue(Socket, SSLOptions, infinity). %%-------------------------------------------------------------------- --spec handshake_continue(#sslsocket{}, [ssl_option()], timeout()) -> +-spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% @@ -322,7 +622,7 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _} send(#sslsocket{pid = [Pid]}, Data) when is_pid(Pid) -> ssl_connection:send(Pid, Data); send(#sslsocket{pid = [_, Pid]}, Data) when is_pid(Pid) -> - tls_sender:send_data(Pid, erlang:iolist_to_binary(Data)); + tls_sender:send_data(Pid, erlang:iolist_to_iovec(Data)); send(#sslsocket{pid = {_, #config{transport_info={_, udp, _, _}}}}, _) -> {error,enotconn}; %% Emulate connection behaviour send(#sslsocket{pid = {dtls,_}}, _) -> @@ -331,13 +631,14 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _} Transport:send(ListenSocket, Data). %% {error,enotconn} %%-------------------------------------------------------------------- --spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. --spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- +-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. recv(Socket, Length) -> recv(Socket, Length, infinity). + +-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. recv(#sslsocket{pid = [Pid|_]}, Length, Timeout) when is_pid(Pid), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)-> ssl_connection:recv(Pid, Length, Timeout); @@ -440,13 +741,13 @@ negotiated_protocol(#sslsocket{pid = [Pid|_]}) when is_pid(Pid) -> ssl_connection:negotiated_protocol(Pid). %%-------------------------------------------------------------------- --spec cipher_suites() -> [ssl_cipher_format:old_erl_cipher_suite()] | [string()]. +-spec cipher_suites() -> [old_cipher_suite()] | [string()]. %%-------------------------------------------------------------------- cipher_suites() -> cipher_suites(erlang). %%-------------------------------------------------------------------- -spec cipher_suites(erlang | openssl | all) -> - [ssl_cipher_format:old_erl_cipher_suite() | string()]. + [old_cipher_suite() | string()]. %% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- cipher_suites(erlang) -> @@ -460,9 +761,9 @@ cipher_suites(all) -> [ssl_cipher_format:erl_suite_definition(Suite) || Suite <- available_suites(all)]. %%-------------------------------------------------------------------- --spec cipher_suites(default | all | anonymous, tls_record:tls_version() | dtls_record:dtls_version() | +-spec cipher_suites(default | all | anonymous, ssl_record:ssl_version() | tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) -> - [ssl_cipher_format:erl_cipher_suite()]. + [erl_cipher_suite()]. %% Description: Returns all default and all supported cipher suites for a %% TLS/DTLS version %%-------------------------------------------------------------------- @@ -478,9 +779,9 @@ cipher_suites(Base, Version) -> [ssl_cipher_format:suite_definition(Suite) || Suite <- supported_suites(Base, Version)]. %%-------------------------------------------------------------------- --spec filter_cipher_suites([ssl_cipher_format:erl_cipher_suite()], +-spec filter_cipher_suites([erl_cipher_suite()], [{key_exchange | cipher | mac | prf, fun()}] | []) -> - [ssl_cipher_format:erl_cipher_suite()]. + [erl_cipher_suite()]. %% Description: Removes cipher suites if any of the filter functions returns false %% for any part of the cipher suite. This function also calls default filter functions %% to make sure the cipher suite are supported by crypto. @@ -497,10 +798,10 @@ filter_cipher_suites(Suites, Filters0) -> prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)}, ssl_cipher:filter_suites(Suites, Filters). %%-------------------------------------------------------------------- --spec prepend_cipher_suites([ssl_cipher_format:erl_cipher_suite()] | +-spec prepend_cipher_suites([erl_cipher_suite()] | [{key_exchange | cipher | mac | prf, fun()}], - [ssl_cipher_format:erl_cipher_suite()]) -> - [ssl_cipher_format:erl_cipher_suite()]. + [erl_cipher_suite()]) -> + [erl_cipher_suite()]. %% Description: Make <Preferred> suites become the most prefered %% suites that is put them at the head of the cipher suite list %% and remove them from <Suites> if present. <Preferred> may be a @@ -515,10 +816,10 @@ prepend_cipher_suites(Filters, Suites) -> Preferred = filter_cipher_suites(Suites, Filters), Preferred ++ (Suites -- Preferred). %%-------------------------------------------------------------------- --spec append_cipher_suites(Deferred :: [ssl_cipher_format:erl_cipher_suite()] | +-spec append_cipher_suites(Deferred :: [erl_cipher_suite()] | [{key_exchange | cipher | mac | prf, fun()}], - [ssl_cipher_format:erl_cipher_suite()]) -> - [ssl_cipher_format:erl_cipher_suite()]. + [erl_cipher_suite()]) -> + [erl_cipher_suite()]. %% Description: Make <Deferred> suites suites become the %% least prefered suites that is put them at the end of the cipher suite list %% and removed them from <Suites> if present. @@ -540,8 +841,8 @@ eccs() -> eccs_filter_supported(Curves). %%-------------------------------------------------------------------- --spec eccs(tls_record:tls_version() | tls_record:tls_atom_version() | - dtls_record:dtls_version() | dtls_record:dtls_atom_version()) -> +-spec eccs(tls_record:tls_atom_version() | + ssl_record:ssl_version() | dtls_record:dtls_atom_version()) -> tls_v1:curves(). %% Description: returns the curves supported for a given version of %% ssl/tls. @@ -723,7 +1024,7 @@ versions() -> SupportedDTLSVsns = [dtls_record:protocol_version(Vsn) || Vsn <- DTLSVsns], AvailableTLSVsns = ?ALL_AVAILABLE_VERSIONS, AvailableDTLSVsns = ?ALL_AVAILABLE_DATAGRAM_VERSIONS, - [{ssl_app, ?VSN}, {supported, SupportedTLSVsns}, + [{ssl_app, "9.2"}, {supported, SupportedTLSVsns}, {supported_dtls, SupportedDTLSVsns}, {available, AvailableTLSVsns}, {available_dtls, AvailableDTLSVsns}]. @@ -783,8 +1084,8 @@ format_error(Reason) when is_list(Reason) -> Reason; format_error(closed) -> "TLS connection is closed"; -format_error({tls_alert, Description}) -> - "TLS Alert: " ++ Description; +format_error({tls_alert, {_, Description}}) -> + Description; format_error({options,{FileType, File, Reason}}) when FileType == cacertfile; FileType == certfile; FileType == keyfile; @@ -813,7 +1114,7 @@ tls_version({254, _} = Version) -> %%-------------------------------------------------------------------- --spec suite_to_str(ssl_cipher_format:erl_cipher_suite()) -> string(). +-spec suite_to_str(erl_cipher_suite()) -> string(). %% %% Description: Return the string representation of a cipher suite. %%-------------------------------------------------------------------- @@ -891,15 +1192,12 @@ handle_options(Opts0, Role, Host) -> {list, [{mode, list}]}], Opts0), assert_proplist(Opts), RecordCb = record_cb(Opts), - - ReuseSessionFun = fun(_, _, _, _) -> true end, CaCerts = handle_option(cacerts, Opts, undefined), {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, VerifyClientOnce} = handle_verify_options(Opts, CaCerts), CertFile = handle_option(certfile, Opts, <<>>), - RecordCb = record_cb(Opts), Versions = case handle_option(versions, Opts, []) of [] -> @@ -945,9 +1243,8 @@ handle_options(Opts0, Role, Host) -> default_option_role(server, tls_v1:default_signature_algs(Versions), Role)), tls_version(RecordCb:highest_protocol_version(Versions))), - %% Server side option - reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), - reuse_sessions = handle_option(reuse_sessions, Opts, true), + reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role), + reuse_session = handle_reuse_session_option(reuse_session, Opts, Role), secure_renegotiate = handle_option(secure_renegotiate, Opts, true), client_renegotiation = handle_option(client_renegotiation, Opts, default_option_role(server, true, Role), @@ -1003,8 +1300,8 @@ handle_options(Opts0, Role, Host) -> alpn_preferred_protocols, next_protocols_advertised, client_preferred_next_protocols, log_alert, server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, - fallback, signature_algs, eccs, honor_ecc_order, beast_mitigation, - max_handshake_size, handshake, customize_hostname_check], + fallback, signature_algs, eccs, honor_ecc_order, + beast_mitigation, max_handshake_size, handshake, customize_hostname_check], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) end, Opts, SslOptions), @@ -1138,11 +1435,16 @@ validate_option(srp_identity, {Username, Password}) {unicode:characters_to_binary(Username), unicode:characters_to_binary(Password)}; +validate_option(reuse_session, undefined) -> + undefined; validate_option(reuse_session, Value) when is_function(Value) -> Value; +validate_option(reuse_session, Value) when is_binary(Value) -> + Value; validate_option(reuse_sessions, Value) when is_boolean(Value) -> Value; - +validate_option(reuse_sessions, save = Value) -> + Value; validate_option(secure_renegotiate, Value) when is_boolean(Value) -> Value; validate_option(client_renegotiation, Value) when is_boolean(Value) -> @@ -1265,6 +1567,26 @@ handle_hashsigns_option(_, Version) when Version >= {3, 3} -> handle_hashsigns_option(_, _Version) -> undefined. +handle_reuse_sessions_option(Key, Opts, client) -> + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value; +handle_reuse_sessions_option(Key, Opts0, server) -> + Opts = proplists:delete({Key, save}, Opts0), + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value. + +handle_reuse_session_option(Key, Opts, client) -> + Value = proplists:get_value(Key, Opts, undefined), + validate_option(Key, Value), + Value; +handle_reuse_session_option(Key, Opts, server) -> + ReuseSessionFun = fun(_, _, _, _) -> true end, + Value = proplists:get_value(Key, Opts, ReuseSessionFun), + validate_option(Key, Value), + Value. + validate_options([]) -> []; validate_options([{Opt, Value} | Tail]) -> diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 34e9797f1f..2a20d13cd5 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -48,8 +48,8 @@ decode(Bin) -> decode(Bin, [], 0). %%-------------------------------------------------------------------- --spec reason_code(#alert{}, client | server) -> - closed | {tls_alert, unicode:chardata()}. +%% -spec reason_code(#alert{}, client | server) -> +%% {tls_alert, unicode:chardata()} | closed. %-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}. %% %% Description: Returns the error reason that will be returned to the @@ -58,8 +58,10 @@ decode(Bin) -> reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> closed; -reason_code(#alert{description = Description}, _) -> - {tls_alert, string:casefold(description_txt(Description))}. +reason_code(#alert{description = Description, role = Role} = Alert, Role) -> + {tls_alert, {description_atom(Description), own_alert_txt(Alert)}}; +reason_code(#alert{description = Description} = Alert, Role) -> + {tls_alert, {description_atom(Description), alert_txt(Alert#alert{role = Role})}}. %%-------------------------------------------------------------------- -spec own_alert_txt(#alert{}) -> string(). @@ -181,3 +183,70 @@ description_txt(?NO_APPLICATION_PROTOCOL) -> "No application protocol"; description_txt(Enum) -> lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])). + +description_atom(?CLOSE_NOTIFY) -> + close_notify; +description_atom(?UNEXPECTED_MESSAGE) -> + unexpected_message; +description_atom(?BAD_RECORD_MAC) -> + bad_record_mac; +description_atom(?DECRYPTION_FAILED_RESERVED) -> + decryption_failed_reserved; +description_atom(?RECORD_OVERFLOW) -> + record_overflow; +description_atom(?DECOMPRESSION_FAILURE) -> + decompression_failure; +description_atom(?HANDSHAKE_FAILURE) -> + handshake_failure; +description_atom(?NO_CERTIFICATE_RESERVED) -> + no_certificate_reserved; +description_atom(?BAD_CERTIFICATE) -> + bad_certificate; +description_atom(?UNSUPPORTED_CERTIFICATE) -> + unsupported_certificate; +description_atom(?CERTIFICATE_REVOKED) -> + certificate_revoked; +description_atom(?CERTIFICATE_EXPIRED) -> + certificate_expired; +description_atom(?CERTIFICATE_UNKNOWN) -> + certificate_unknown; +description_atom(?ILLEGAL_PARAMETER) -> + illegal_parameter; +description_atom(?UNKNOWN_CA) -> + unknown_ca; +description_atom(?ACCESS_DENIED) -> + access_denied; +description_atom(?DECODE_ERROR) -> + decode_error; +description_atom(?DECRYPT_ERROR) -> + decrypt_error; +description_atom(?EXPORT_RESTRICTION) -> + export_restriction; +description_atom(?PROTOCOL_VERSION) -> + protocol_version; +description_atom(?INSUFFICIENT_SECURITY) -> + insufficient_security; +description_atom(?INTERNAL_ERROR) -> + internal_error; +description_atom(?USER_CANCELED) -> + user_canceled; +description_atom(?NO_RENEGOTIATION) -> + no_renegotiation; +description_atom(?UNSUPPORTED_EXTENSION) -> + unsupported_extension; +description_atom(?CERTIFICATE_UNOBTAINABLE) -> + certificate_unobtainable; +description_atom(?UNRECOGNISED_NAME) -> + unrecognised_name; +description_atom(?BAD_CERTIFICATE_STATUS_RESPONSE) -> + bad_certificate_status_response; +description_atom(?BAD_CERTIFICATE_HASH_VALUE) -> + bad_certificate_hash_value; +description_atom(?UNKNOWN_PSK_IDENTITY) -> + unknown_psk_identity; +description_atom(?INAPPROPRIATE_FALLBACK) -> + inappropriate_fallback; +description_atom(?NO_APPLICATION_PROTOCOL) -> + no_application_protocol; +description_atom(_) -> + 'unsupported/unkonwn_alert'. diff --git a/lib/ssl/src/ssl_api.hrl b/lib/ssl/src/ssl_api.hrl index 7b7b1cbcd9..f4594912bd 100644 --- a/lib/ssl/src/ssl_api.hrl +++ b/lib/ssl/src/ssl_api.hrl @@ -21,56 +21,7 @@ -ifndef(ssl_api). -define(ssl_api, true). --include("ssl_cipher.hrl"). - -%% Visible in API --export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0, - prf_random/0, sslsocket/0]). - - %% Looks like it does for backwards compatibility reasons -record(sslsocket, {fd = nil, pid = nil}). - --type sslsocket() :: #sslsocket{}. --type connect_option() :: socket_connect_option() | ssl_option() | transport_option(). --type socket_connect_option() :: gen_tcp:connect_option(). --type listen_option() :: socket_listen_option() | ssl_option() | transport_option(). --type socket_listen_option() :: gen_tcp:listen_option(). - --type ssl_option() :: {versions, ssl_record:ssl_atom_version()} | - {verify, verify_type()} | - {verify_fun, {fun(), InitialUserState::term()}} | - {fail_if_no_peer_cert, boolean()} | {depth, integer()} | - {cert, Der::binary()} | {certfile, path()} | - {key, {private_key_type(), Der::binary()}} | - {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} | - {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} | - {user_lookup_fun, {fun(), InitialUserState::term()}} | - {psk_identity, string()} | - {srp_identity, {string(), string()}} | - {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | - {reuse_session, fun()} | {hibernate_after, integer()|undefined} | - {alpn_advertised_protocols, [binary()]} | - {alpn_preferred_protocols, [binary()]} | - {next_protocols_advertised, list(binary())} | - {client_preferred_next_protocols, binary(), client | server, list(binary())}. - --type verify_type() :: verify_none | verify_peer. --type path() :: string(). --type ciphers() :: [ssl_cipher_format:erl_cipher_suite()] | - string(). % (according to old API) --type ssl_imp() :: new | old. - --type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), - ClosedTag::atom(), ErrTag::atom()}}. --type prf_random() :: client_random | server_random. - --type private_key_type() :: rsa | %% Backwards compatibility - dsa | %% Backwards compatibility - 'RSAPrivateKey' | - 'DSAPrivateKey' | - 'ECPrivateKey' | - 'PrivateKeyInfo'. - -endif. % -ifdef(ssl_api). diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 54c04c13e5..fce48d1678 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -1,7 +1,7 @@ % %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ rc4_suites/1, des_suites/1, rsa_suites/1, filter/3, filter_suites/1, filter_suites/2, hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1, - random_bytes/1, calc_mac_hash/4, + random_bytes/1, calc_mac_hash/4, calc_mac_hash/6, is_stream_ciphersuite/1]). -compile(inline). @@ -97,7 +97,8 @@ cipher_init(?AES_GCM, IV, Key) -> cipher_init(?CHACHA20_POLY1305, IV, Key) -> #cipher_state{iv = IV, key = Key, tag_len = 16}; cipher_init(_BCA, IV, Key) -> - #cipher_state{iv = IV, key = Key}. + %% Initialize random IV cache, not used for aead ciphers + #cipher_state{iv = IV, key = Key, state = <<>>}. nonce_seed(Seed, CipherState) -> CipherState#cipher_state{nonce = Seed}. @@ -112,12 +113,11 @@ nonce_seed(Seed, CipherState) -> %% data is calculated and the data plus the HMAC is ecncrypted. %%------------------------------------------------------------------- cipher(?NULL, CipherState, <<>>, Fragment, _Version) -> - GenStreamCipherList = [Fragment, <<>>], - {GenStreamCipherList, CipherState}; + {iolist_to_binary(Fragment), CipherState}; cipher(?RC4, CipherState = #cipher_state{state = State0}, Mac, Fragment, _Version) -> GenStreamCipherList = [Fragment, Mac], {State1, T} = crypto:stream_encrypt(State0, GenStreamCipherList), - {T, CipherState#cipher_state{state = State1}}; + {iolist_to_binary(T), CipherState#cipher_state{state = State1}}; cipher(?DES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) -> crypto:block_encrypt(des_cbc, Key, IV, T) @@ -146,8 +146,7 @@ aead_type(?CHACHA20_POLY1305) -> build_cipher_block(BlockSz, Mac, Fragment) -> TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1, - {PaddingLength, Padding} = get_padding(TotSz, BlockSz), - [Fragment, Mac, PaddingLength, Padding]. + [Fragment, Mac, padding_with_len(TotSz, BlockSz)]. block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, Mac, Fragment, {3, N}) @@ -157,14 +156,21 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, NextIV = next_iv(T, IV), {T, CS0#cipher_state{iv=NextIV}}; -block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, +block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV, state = IV_Cache0} = CS0, Mac, Fragment, {3, N}) when N == 2; N == 3 -> - NextIV = random_iv(IV), + IV_Size = byte_size(IV), + <<NextIV:IV_Size/binary, IV_Cache/binary>> = + case IV_Cache0 of + <<>> -> + random_bytes(IV_Size bsl 5); % 32 IVs + _ -> + IV_Cache0 + end, L0 = build_cipher_block(BlockSz, Mac, Fragment), L = [NextIV|L0], T = Fun(Key, IV, L), - {T, CS0#cipher_state{iv=NextIV}}. + {T, CS0#cipher_state{iv=NextIV, state = IV_Cache}}. %%-------------------------------------------------------------------- -spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), @@ -481,8 +487,8 @@ filter(DerCert, Ciphers0, Version) -> filter_suites_signature(Sign, Ciphers, Version). %%-------------------------------------------------------------------- --spec filter_suites([ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()], map()) -> - [ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]. +-spec filter_suites([ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()], map()) -> + [ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]. %% %% Description: Filter suites using supplied filter funs %%------------------------------------------------------------------- @@ -508,8 +514,8 @@ filter_suite(Suite, Filters) -> filter_suite(ssl_cipher_format:suite_definition(Suite), Filters). %%-------------------------------------------------------------------- --spec filter_suites([ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]) -> - [ssl_cipher_format:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]. +-spec filter_suites([ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]) -> + [ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]. %% %% Description: Filter suites for algorithms supported by crypto. %%------------------------------------------------------------------- @@ -593,7 +599,7 @@ is_acceptable_cipher(rc4_128, Algos) -> is_acceptable_cipher(des_cbc, Algos) -> proplists:get_bool(des_cbc, Algos); is_acceptable_cipher('3des_ede_cbc', Algos) -> - proplists:get_bool(des3_cbc, Algos); + proplists:get_bool(des_ede3, Algos); is_acceptable_cipher(aes_128_cbc, Algos) -> proplists:get_bool(aes_cbc128, Algos); is_acceptable_cipher(aes_256_cbc, Algos) -> @@ -633,12 +639,13 @@ random_bytes(N) -> calc_mac_hash(Type, Version, PlainFragment, #{sequence_number := SeqNo, mac_secret := MacSecret, - security_parameters:= - SecPars}) -> + security_parameters := + #security_parameters{mac_algorithm = MacAlgorithm}}) -> + calc_mac_hash(Type, Version, PlainFragment, MacAlgorithm, MacSecret, SeqNo). +%% +calc_mac_hash(Type, Version, PlainFragment, MacAlgorithm, MacSecret, SeqNo) -> Length = erlang:iolist_size(PlainFragment), - mac_hash(Version, SecPars#security_parameters.mac_algorithm, - MacSecret, SeqNo, Type, - Length, PlainFragment). + mac_hash(Version, MacAlgorithm, MacSecret, SeqNo, Type, Length, PlainFragment). is_stream_ciphersuite(#{cipher := rc4_128}) -> true; @@ -722,7 +729,6 @@ expanded_key_material(Cipher) when Cipher == aes_128_cbc; Cipher == chacha20_poly1305 -> unknown. - effective_key_bits(null) -> 0; effective_key_bits(des_cbc) -> @@ -742,18 +748,15 @@ iv_size(Cipher) when Cipher == null; Cipher == rc4_128; Cipher == chacha20_poly1305-> 0; - iv_size(Cipher) when Cipher == aes_128_gcm; Cipher == aes_256_gcm -> 4; - iv_size(Cipher) -> block_size(Cipher). block_size(Cipher) when Cipher == des_cbc; Cipher == '3des_ede_cbc' -> 8; - block_size(Cipher) when Cipher == aes_128_cbc; Cipher == aes_256_cbc; Cipher == aes_128_gcm; @@ -888,21 +891,51 @@ is_correct_padding(GenBlockCipher, {3, 1}, false) -> %% Padding must be checked in TLS 1.1 and after is_correct_padding(#generic_block_cipher{padding_length = Len, padding = Padding}, _, _) -> - Len == byte_size(Padding) andalso - list_to_binary(lists:duplicate(Len, Len)) == Padding. - -get_padding(Length, BlockSize) -> - get_padding_aux(BlockSize, Length rem BlockSize). - -get_padding_aux(_, 0) -> - {0, <<>>}; -get_padding_aux(BlockSize, PadLength) -> - N = BlockSize - PadLength, - {N, list_to_binary(lists:duplicate(N, N))}. + (Len == byte_size(Padding)) andalso (padding(Len) == Padding). + +padding(PadLen) -> + case PadLen of + 0 -> <<>>; + 1 -> <<1>>; + 2 -> <<2,2>>; + 3 -> <<3,3,3>>; + 4 -> <<4,4,4,4>>; + 5 -> <<5,5,5,5,5>>; + 6 -> <<6,6,6,6,6,6>>; + 7 -> <<7,7,7,7,7,7,7>>; + 8 -> <<8,8,8,8,8,8,8,8>>; + 9 -> <<9,9,9,9,9,9,9,9,9>>; + 10 -> <<10,10,10,10,10,10,10,10,10,10>>; + 11 -> <<11,11,11,11,11,11,11,11,11,11,11>>; + 12 -> <<12,12,12,12,12,12,12,12,12,12,12,12>>; + 13 -> <<13,13,13,13,13,13,13,13,13,13,13,13,13>>; + 14 -> <<14,14,14,14,14,14,14,14,14,14,14,14,14,14>>; + 15 -> <<15,15,15,15,15,15,15,15,15,15,15,15,15,15,15>>; + _ -> + binary:copy(<<PadLen>>, PadLen) + end. -random_iv(IV) -> - IVSz = byte_size(IV), - random_bytes(IVSz). +padding_with_len(TextLen, BlockSize) -> + case BlockSize - (TextLen rem BlockSize) of + 0 -> <<0>>; + 1 -> <<1,1>>; + 2 -> <<2,2,2>>; + 3 -> <<3,3,3,3>>; + 4 -> <<4,4,4,4,4>>; + 5 -> <<5,5,5,5,5,5>>; + 6 -> <<6,6,6,6,6,6,6>>; + 7 -> <<7,7,7,7,7,7,7,7>>; + 8 -> <<8,8,8,8,8,8,8,8,8>>; + 9 -> <<9,9,9,9,9,9,9,9,9,9>>; + 10 -> <<10,10,10,10,10,10,10,10,10,10,10>>; + 11 -> <<11,11,11,11,11,11,11,11,11,11,11,11>>; + 12 -> <<12,12,12,12,12,12,12,12,12,12,12,12,12>>; + 13 -> <<13,13,13,13,13,13,13,13,13,13,13,13,13,13>>; + 14 -> <<14,14,14,14,14,14,14,14,14,14,14,14,14,14,14>>; + 15 -> <<15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15>>; + PadLen -> + binary:copy(<<PadLen>>, PadLen + 1) + end. next_iv(Bin, IV) -> BinSz = byte_size(Bin), diff --git a/lib/ssl/src/ssl_cipher_format.erl b/lib/ssl/src/ssl_cipher_format.erl index c311c0d097..1d28e1e3b4 100644 --- a/lib/ssl/src/ssl_cipher_format.erl +++ b/lib/ssl/src/ssl_cipher_format.erl @@ -25,26 +25,25 @@ %%---------------------------------------------------------------------- -module(ssl_cipher_format). +-include("ssl_api.hrl"). -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export_type([cipher_suite/0, - erl_cipher_suite/0, old_erl_cipher_suite/0, openssl_cipher_suite/0, - hash/0, key_algo/0, sign_algo/0]). +-export_type([old_erl_cipher_suite/0, openssl_cipher_suite/0, cipher_suite/0]). --type cipher() :: null |rc4_128 | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305. --type hash() :: null | md5 | sha | sha224 | sha256 | sha384 | sha512. --type sign_algo() :: rsa | dsa | ecdsa. --type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon. --type erl_cipher_suite() :: #{key_exchange := key_algo(), - cipher := cipher(), - mac := hash() | aead, - prf := hash() | default_prf %% Old cipher suites, version dependent +-type internal_cipher() :: null | ssl:cipher(). +-type internal_hash() :: null | ssl:hash(). +-type internal_kex_algo() :: null | ssl:kex_algo(). +-type internal_erl_cipher_suite() :: #{key_exchange := internal_kex_algo(), + cipher := internal_cipher(), + mac := internal_hash() | aead, + prf := internal_hash() | default_prf %% Old cipher suites, version dependent }. --type old_erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2 +-type old_erl_cipher_suite() :: {ssl:kex_algo(), internal_cipher(), internal_hash()} % Pre TLS 1.2 %% TLS 1.2, internally PRE TLS 1.2 will use default_prf - | {key_algo(), cipher(), hash(), hash() | default_prf}. + | {ssl:kex_algo(), internal_cipher(), internal_hash(), + internal_hash() | default_prf}. -type cipher_suite() :: binary(). -type openssl_cipher_suite() :: string(). @@ -53,7 +52,7 @@ openssl_suite/1, openssl_suite_name/1]). %%-------------------------------------------------------------------- --spec suite_to_str(erl_cipher_suite()) -> string(). +-spec suite_to_str(internal_erl_cipher_suite()) -> string(). %% %% Description: Return the string representation of a cipher suite. %%-------------------------------------------------------------------- @@ -77,7 +76,7 @@ suite_to_str(#{key_exchange := Kex, "_" ++ string:to_upper(atom_to_list(Mac)). %%-------------------------------------------------------------------- --spec suite_definition(cipher_suite()) -> erl_cipher_suite(). +-spec suite_definition(cipher_suite()) -> internal_erl_cipher_suite(). %% %% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. @@ -805,7 +804,7 @@ suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> prf => sha256}. %%-------------------------------------------------------------------- --spec erl_suite_definition(cipher_suite() | erl_cipher_suite()) -> old_erl_cipher_suite(). +-spec erl_suite_definition(cipher_suite() | internal_erl_cipher_suite()) -> old_erl_cipher_suite(). %% %% Description: Return erlang cipher suite definition. Filters last value %% for now (compatibility reasons). @@ -822,7 +821,7 @@ erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher, end. %%-------------------------------------------------------------------- --spec suite(erl_cipher_suite()) -> cipher_suite(). +-spec suite(internal_erl_cipher_suite()) -> cipher_suite(). %% %% Description: Return TLS cipher suite definition. %%-------------------------------------------------------------------- @@ -1585,7 +1584,7 @@ openssl_suite("ECDH-RSA-AES256-GCM-SHA384") -> ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384. %%-------------------------------------------------------------------- --spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | erl_cipher_suite(). +-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | internal_erl_cipher_suite(). %% %% Description: Return openssl cipher suite name if possible %%------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 4b406b4c1e..24fb091ddf 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2018. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ -export([connect/8, handshake/7, handshake/2, handshake/3, handshake_continue/3, handshake_cancel/1, - socket_control/4, socket_control/5, start_or_recv_cancel_timer/2]). + socket_control/4, socket_control/5]). %% User Events -export([send/2, recv/3, close/2, shutdown/2, @@ -51,7 +51,7 @@ %% Alert and close handling -export([handle_own_alert/4, handle_alert/3, - handle_normal_shutdown/3, stop/2, stop_and_reply/3, + handle_normal_shutdown/3, handle_trusted_certs_db/1]). %% Data handling @@ -70,14 +70,14 @@ -export([terminate/3, format_status/2]). %% Erlang Distribution export --export([get_sslsocket/1, dist_handshake_complete/2]). +-export([dist_handshake_complete/2]). %%==================================================================== %% Setup %%==================================================================== %%-------------------------------------------------------------------- -spec connect(tls_connection | dtls_connection, - host(), inet:port_number(), + ssl:host(), inet:port_number(), port() | {tuple(), port()}, %% TLS | DTLS {#ssl_options{}, #socket_options{}, %% Tracker only needed on server side @@ -143,7 +143,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, SslOptions, Timeout) -> end. %%-------------------------------------------------------------------- --spec handshake_continue(#sslsocket{}, [ssl_option()], +-spec handshake_continue(#sslsocket{}, [ssl:tls_server_option()], timeout()) -> {ok, #sslsocket{}}| {error, reason()}. %% %% Description: Continues handshake with new options @@ -182,27 +182,23 @@ socket_control(Connection, Socket, Pid, Transport) -> %%-------------------------------------------------------------------- socket_control(Connection, Socket, Pids, Transport, udp_listener) -> %% dtls listener process must have the socket control - {ok, Connection:socket(Pids, Transport, Socket, Connection, undefined)}; + {ok, Connection:socket(Pids, Transport, Socket, undefined)}; socket_control(tls_connection = Connection, Socket, [Pid|_] = Pids, Transport, ListenTracker) -> case Transport:controlling_process(Socket, Pid) of ok -> - {ok, Connection:socket(Pids, Transport, Socket, Connection, ListenTracker)}; + {ok, Connection:socket(Pids, Transport, Socket, ListenTracker)}; {error, Reason} -> {error, Reason} end; socket_control(dtls_connection = Connection, {_, Socket}, [Pid|_] = Pids, Transport, ListenTracker) -> case Transport:controlling_process(Socket, Pid) of ok -> - {ok, Connection:socket(Pids, Transport, Socket, Connection, ListenTracker)}; + {ok, Connection:socket(Pids, Transport, Socket, ListenTracker)}; {error, Reason} -> {error, Reason} end. -start_or_recv_cancel_timer(infinity, _RecvFrom) -> - undefined; -start_or_recv_cancel_timer(Timeout, RecvFrom) -> - erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}). %%==================================================================== %% User events @@ -215,9 +211,9 @@ start_or_recv_cancel_timer(Timeout, RecvFrom) -> %%-------------------------------------------------------------------- send(Pid, Data) -> call(Pid, {application_data, - %% iolist_to_binary should really - %% be called iodata_to_binary() - erlang:iolist_to_binary(Data)}). + %% iolist_to_iovec should really + %% be called iodata_to_iovec() + erlang:iolist_to_iovec(Data)}). %%-------------------------------------------------------------------- -spec recv(pid(), integer(), timeout()) -> @@ -315,9 +311,6 @@ renegotiation(ConnectionPid) -> internal_renegotiation(ConnectionPid, #{current_write := WriteState}) -> gen_statem:cast(ConnectionPid, {internal_renegotiate, WriteState}). -get_sslsocket(ConnectionPid) -> - call(ConnectionPid, get_sslsocket). - dist_handshake_complete(ConnectionPid, DHandle) -> gen_statem:cast(ConnectionPid, {dist_handshake_complete, DHandle}). @@ -335,8 +328,8 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> %% Alert and close handling %%==================================================================== handle_own_alert(Alert, _, StateName, - #state{role = Role, - protocol_cb = Connection, + #state{static_env = #static_env{role = Role, + protocol_cb = Connection}, ssl_options = SslOpts} = State) -> try %% Try to tell the other side send_alert(Alert, StateName, State) @@ -349,77 +342,96 @@ handle_own_alert(Alert, _, StateName, catch _:_ -> ok end, - stop({shutdown, own_alert}, State). - -handle_normal_shutdown(Alert, _, #state{socket = Socket, - transport_cb = Transport, - protocol_cb = Connection, - start_or_recv_from = StartFrom, - tracker = Tracker, - role = Role, renegotiation = {false, first}} = State) -> + {stop, {shutdown, own_alert}, State}. + +handle_normal_shutdown(Alert, _, #state{static_env = #static_env{role = Role, + socket = Socket, + transport_cb = Transport, + protocol_cb = Connection, + tracker = Tracker}, + handshake_env = #handshake_env{renegotiation = {false, first}}, + start_or_recv_from = StartFrom} = State) -> Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, Connection); -handle_normal_shutdown(Alert, StateName, #state{socket = Socket, - socket_options = Opts, - transport_cb = Transport, - protocol_cb = Connection, - user_application = {_Mon, Pid}, - tracker = Tracker, - start_or_recv_from = RecvFrom, role = Role} = State) -> +handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, + socket = Socket, + transport_cb = Transport, + protocol_cb = Connection, + tracker = Tracker}, + connection_env = #connection_env{user_application = {_Mon, Pid}}, + socket_options = Opts, + start_or_recv_from = RecvFrom} = State) -> Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{socket = Socket, transport_cb = Transport, - protocol_cb = Connection, - ssl_options = SslOpts, start_or_recv_from = From, host = Host, - port = Port, session = Session, user_application = {_Mon, Pid}, - role = Role, socket_options = Opts, tracker = Tracker} = State) -> + #state{static_env = #static_env{role = Role, + socket = Socket, + host = Host, + port = Port, + tracker = Tracker, + transport_cb = Transport, + protocol_cb = Connection}, + connection_env = #connection_env{user_application = {_Mon, Pid}}, + ssl_options = SslOpts, + start_or_recv_from = From, + session = Session, + socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection), - stop(normal, State); + {stop, {shutdown, normal}, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, - StateName, State) -> + downgrade= StateName, State) -> + {next_state, StateName, State, [{next_event, internal, Alert}]}; +handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, + StateName, State) -> handle_normal_shutdown(Alert, StateName, State), - stop({shutdown, peer_close}, State); - + {stop,{shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{role = Role, ssl_options = SslOpts, protocol_cb = Connection, - renegotiation = {true, internal}} = State) -> + #state{static_env = #static_env{role = Role, + protocol_cb = Connection}, + handshake_env = #handshake_env{renegotiation = {true, internal}}, + ssl_options = SslOpts} = State) -> log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), handle_normal_shutdown(Alert, StateName, State), - stop({shutdown, peer_close}, State); + {stop,{shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, connection = StateName, - #state{role = Role, - ssl_options = SslOpts, renegotiation = {true, From}, - protocol_cb = Connection} = State0) -> + #state{static_env = #static_env{role = Role, + protocol_cb = Connection}, + handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv, + ssl_options = SslOpts + } = State0) -> log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), gen_statem:reply(From, {error, renegotiation_rejected}), State = Connection:reinit_handshake_data(State0), - Connection:next_event(connection, no_record, State#state{renegotiation = undefined}); + Connection:next_event(connection, no_record, State#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}}); handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{role = Role, - ssl_options = SslOpts, renegotiation = {true, From}, - protocol_cb = Connection} = State0) -> + #state{static_env = #static_env{role = Role, + protocol_cb = Connection}, + handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv, + ssl_options = SslOpts + } = State0) -> log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), gen_statem:reply(From, {error, renegotiation_rejected}), %% Go back to connection! - State = Connection:reinit(State0#state{renegotiation = undefined}), + State = Connection:reinit(State0#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}}), Connection:next_event(connection, no_record, State); %% Gracefully log and ignore all other warning alerts handle_alert(#alert{level = ?WARNING} = Alert, StateName, - #state{ssl_options = SslOpts, protocol_cb = Connection, role = Role} = State) -> + #state{static_env = #static_env{role = Role, + protocol_cb = Connection}, + ssl_options = SslOpts} = State) -> log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), Connection:next_event(StateName, no_record, State). @@ -427,104 +439,251 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName, %%==================================================================== %% Data handling %%==================================================================== - -passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName, Connection) -> - case Buffer of - <<>> -> +passive_receive(State0 = #state{user_data_buffer = {_,BufferSize,_}}, StateName, Connection, StartTimerAction) -> + case BufferSize of + 0 -> {Record, State} = Connection:next_record(State0), - Connection:next_event(StateName, Record, State); + Connection:next_event(StateName, Record, State, StartTimerAction); _ -> case read_application_data(<<>>, State0) of {stop, _, _} = ShutdownError -> ShutdownError; {Record, State} -> - Connection:next_event(StateName, Record, State) + case State#state.start_or_recv_from of + undefined -> + %% Cancel recv timeout as data has been delivered + Connection:next_event(StateName, Record, State, + [{{timeout, recv}, infinity, timeout}]); + _ -> + Connection:next_event(StateName, Record, State, StartTimerAction) + end end end. -read_application_data(Data, #state{user_application = {_Mon, Pid}, - socket = Socket, - protocol_cb = Connection, - transport_cb = Transport, - socket_options = SOpts, - bytes_to_read = BytesToRead, - start_or_recv_from = RecvFrom, - timer = Timer, - user_data_buffer = Buffer0, - tracker = Tracker} = State0) -> - Buffer1 = if - Buffer0 =:= <<>> -> Data; - Data =:= <<>> -> Buffer0; - true -> <<Buffer0/binary, Data/binary>> - end, - case get_data(SOpts, BytesToRead, Buffer1) of - {ok, ClientData, Buffer} -> % Send data - #state{ssl_options = #ssl_options{erl_dist = Dist}, - erl_dist_data = DistData} = State0, - case Dist andalso is_dist_up(DistData) of - true -> - dist_app_data(ClientData, State0#state{user_data_buffer = Buffer, - bytes_to_read = undefined}); - _ -> - SocketOpt = - deliver_app_data(Connection:pids(State0), - Transport, Socket, SOpts, - ClientData, Pid, RecvFrom, Tracker, Connection), - cancel_timer(Timer), - State = - State0#state{ - user_data_buffer = Buffer, - start_or_recv_from = undefined, - timer = undefined, - bytes_to_read = undefined, - socket_options = SocketOpt - }, - if - SocketOpt#socket_options.active =:= false; - Buffer =:= <<>> -> - %% Passive mode, wait for active once or recv - %% Active and empty, get more data - {no_record, State}; - true -> %% We have more data - read_application_data(<<>>, State) - end - end; - {more, Buffer} -> % no reply, we need more data - {no_record, State0#state{user_data_buffer = Buffer}}; - {passive, Buffer} -> - {no_record, State0#state{user_data_buffer = Buffer}}; - {error,_Reason} -> %% Invalid packet in packet mode - deliver_packet_error(Connection:pids(State0), - Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection), - stop(normal, State0) +read_application_data( + Data, + #state{ + user_data_buffer = {Front0,BufferSize0,Rear0}, + connection_env = #connection_env{erl_dist_handle = DHandle}} = State) -> + %% + Front = Front0, + BufferSize = BufferSize0 + byte_size(Data), + Rear = [Data|Rear0], + case DHandle of + undefined -> + read_application_data(State, Front, BufferSize, Rear); + _ -> + try read_application_dist_data(DHandle, Front, BufferSize, Rear) of + Buffer -> + {no_record, State#state{user_data_buffer = Buffer}} + catch error:_ -> + {stop,disconnect, + State#state{user_data_buffer = {Front,BufferSize,Rear}}} + end end. -dist_app_data(ClientData, #state{erl_dist_data = #{dist_handle := undefined, - dist_buffer := DistBuff} = DistData} = State) -> - {no_record, State#state{erl_dist_data = DistData#{dist_buffer => [ClientData, DistBuff]}}}; -dist_app_data(ClientData, #state{erl_dist_data = #{dist_handle := DHandle, - dist_buffer := DistBuff} = ErlDistData, - user_data_buffer = Buffer, - socket_options = SOpts} = State) -> - Data = merge_dist_data(DistBuff, ClientData), - try erlang:dist_ctrl_put_data(DHandle, Data) of - _ when SOpts#socket_options.active =:= false; - Buffer =:= <<>> -> + +read_application_data(#state{ + socket_options = SocketOpts, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom} = State, Front, BufferSize, Rear) -> + read_application_data(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead). + +%% Pick binary from queue front, if empty wait for more data +read_application_data(State, [Bin|Front], BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead) -> + read_application_data_bin(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead, Bin); +read_application_data(State, [] = Front, BufferSize, [] = Rear, SocketOpts, RecvFrom, BytesToRead) -> + 0 = BufferSize, % Assert + {no_record, State#state{socket_options = SocketOpts, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = {Front,BufferSize,Rear}}}; +read_application_data(State, [], BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead) -> + [Bin|Front] = lists:reverse(Rear), + read_application_data_bin(State, Front, BufferSize, [], SocketOpts, RecvFrom, BytesToRead, Bin). + +read_application_data_bin(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead, <<>>) -> + %% Done with this binary - get next + read_application_data(State, Front, BufferSize, Rear, SocketOpts, RecvFrom, BytesToRead); +read_application_data_bin(State, Front0, BufferSize0, Rear0, SocketOpts0, RecvFrom, BytesToRead, Bin0) -> + %% Decode one packet from a binary + case get_data(SocketOpts0, BytesToRead, Bin0) of + {ok, Data, Bin} -> % Send data + BufferSize = BufferSize0 - (byte_size(Bin0) - byte_size(Bin)), + read_application_data_deliver( + State, [Bin|Front0], BufferSize, Rear0, SocketOpts0, RecvFrom, Data); + {more, undefined} -> + %% We need more data, do not know how much + if + byte_size(Bin0) < BufferSize0 -> + %% We have more data in the buffer besides the first binary - concatenate all and retry + Bin = iolist_to_binary([Bin0,Front0|lists:reverse(Rear0)]), + read_application_data_bin( + State, [], BufferSize0, [], SocketOpts0, RecvFrom, BytesToRead, Bin); + true -> + %% All data is in the first binary, no use to retry - wait for more + {no_record, State#state{socket_options = SocketOpts0, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = {[Bin0|Front0],BufferSize0,Rear0}}} + end; + {more, Size} when Size =< BufferSize0 -> + %% We have a packet in the buffer - collect it in a binary and decode + {Data,Front,Rear} = iovec_from_front(Size - byte_size(Bin0), Front0, Rear0, [Bin0]), + Bin = iolist_to_binary(Data), + read_application_data_bin( + State, Front, BufferSize0, Rear, SocketOpts0, RecvFrom, BytesToRead, Bin); + {more, _Size} -> + %% We do not have a packet in the buffer - wait for more + {no_record, State#state{socket_options = SocketOpts0, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = {[Bin0|Front0],BufferSize0,Rear0}}}; + passive -> + {no_record, State#state{socket_options = SocketOpts0, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = {[Bin0|Front0],BufferSize0,Rear0}}}; + {error,_Reason} -> + %% Invalid packet in packet mode + #state{ + static_env = + #static_env{ + socket = Socket, + protocol_cb = Connection, + transport_cb = Transport, + tracker = Tracker}, + connection_env = + #connection_env{user_application = {_Mon, Pid}}} = State, + Buffer = iolist_to_binary([Bin0,Front0|lists:reverse(Rear0)]), + deliver_packet_error( + Connection:pids(State), Transport, Socket, SocketOpts0, + Buffer, Pid, RecvFrom, Tracker, Connection), + {stop, {shutdown, normal}, State#state{socket_options = SocketOpts0, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = {[Buffer],BufferSize0,[]}}} + end. + +read_application_data_deliver(State, Front, BufferSize, Rear, SocketOpts0, RecvFrom, Data) -> + #state{ + static_env = + #static_env{ + socket = Socket, + protocol_cb = Connection, + transport_cb = Transport, + tracker = Tracker}, + connection_env = + #connection_env{user_application = {_Mon, Pid}}} = State, + SocketOpts = + deliver_app_data( + Connection:pids(State), Transport, Socket, SocketOpts0, Data, Pid, RecvFrom, Tracker, Connection), + if + SocketOpts#socket_options.active =:= false -> %% Passive mode, wait for active once or recv - %% Active and empty, get more data - {no_record, State#state{erl_dist_data = ErlDistData#{dist_buffer => <<>>}}}; - _ -> %% We have more data - read_application_data(<<>>, State) - catch error:_ -> - stop(State, disconnect) + {no_record, + State#state{ + user_data_buffer = {Front,BufferSize,Rear}, + start_or_recv_from = undefined, + bytes_to_read = undefined, + socket_options = SocketOpts + }}; + true -> %% Try to deliver more data + read_application_data(State, Front, BufferSize, Rear, SocketOpts, undefined, undefined) + end. + + +read_application_dist_data(DHandle, [Bin|Front], BufferSize, Rear) -> + read_application_dist_data(DHandle, Front, BufferSize, Rear, Bin); +read_application_dist_data(_DHandle, [] = Front, BufferSize, [] = Rear) -> + BufferSize = 0, + {Front,BufferSize,Rear}; +read_application_dist_data(DHandle, [], BufferSize, Rear) -> + [Bin|Front] = lists:reverse(Rear), + read_application_dist_data(DHandle, Front, BufferSize, [], Bin). +%% +read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) -> + case Bin0 of + %% + %% START Optimization + %% It is cheaper to match out several packets in one match operation than to loop for each + <<SizeA:32, DataA:SizeA/binary, + SizeB:32, DataB:SizeB/binary, + SizeC:32, DataC:SizeC/binary, + SizeD:32, DataD:SizeD/binary, Rest/binary>> -> + %% We have 4 complete packets in the first binary + erlang:dist_ctrl_put_data(DHandle, DataA), + erlang:dist_ctrl_put_data(DHandle, DataB), + erlang:dist_ctrl_put_data(DHandle, DataC), + erlang:dist_ctrl_put_data(DHandle, DataD), + read_application_dist_data( + DHandle, Front0, BufferSize - (4*4+SizeA+SizeB+SizeC+SizeD), Rear0, Rest); + <<SizeA:32, DataA:SizeA/binary, + SizeB:32, DataB:SizeB/binary, + SizeC:32, DataC:SizeC/binary, Rest/binary>> -> + %% We have 3 complete packets in the first binary + erlang:dist_ctrl_put_data(DHandle, DataA), + erlang:dist_ctrl_put_data(DHandle, DataB), + erlang:dist_ctrl_put_data(DHandle, DataC), + read_application_dist_data( + DHandle, Front0, BufferSize - (3*4+SizeA+SizeB+SizeC), Rear0, Rest); + <<SizeA:32, DataA:SizeA/binary, + SizeB:32, DataB:SizeB/binary, Rest/binary>> -> + %% We have 2 complete packets in the first binary + erlang:dist_ctrl_put_data(DHandle, DataA), + erlang:dist_ctrl_put_data(DHandle, DataB), + read_application_dist_data( + DHandle, Front0, BufferSize - (2*4+SizeA+SizeB), Rear0, Rest); + %% END Optimization + %% + %% Basic one packet code path + <<Size:32, Data:Size/binary, Rest/binary>> -> + %% We have a complete packet in the first binary + erlang:dist_ctrl_put_data(DHandle, Data), + read_application_dist_data(DHandle, Front0, BufferSize - (4+Size), Rear0, Rest); + <<Size:32, FirstData/binary>> when 4+Size =< BufferSize -> + %% We have a complete packet in the buffer + %% - fetch the missing content from the buffer front + {Data,Front,Rear} = iovec_from_front(Size - byte_size(FirstData), Front0, Rear0, [FirstData]), + erlang:dist_ctrl_put_data(DHandle, Data), + read_application_dist_data(DHandle, Front, BufferSize - (4+Size), Rear); + <<Bin/binary>> -> + %% In OTP-21 the match context reuse optimization fails if we use Bin0 in recursion, so here we + %% match out the whole binary which will trick the optimization into keeping the match context + %% for the first binary contains complete packet code above + case Bin of + <<_Size:32, _InsufficientData/binary>> -> + %% We have a length field in the first binary but there is not enough data + %% in the buffer to form a complete packet - await more data + {[Bin|Front0],BufferSize,Rear0}; + <<IncompleteLengthField/binary>> when 4 < BufferSize -> + %% We do not have a length field in the first binary but the buffer + %% contains enough data to maybe form a packet + %% - fetch a tiny binary from the buffer front to complete the length field + {LengthField,Front,Rear} = + iovec_from_front(4 - byte_size(IncompleteLengthField), Front0, Rear0, [IncompleteLengthField]), + LengthBin = iolist_to_binary(LengthField), + read_application_dist_data(DHandle, Front, BufferSize, Rear, LengthBin); + <<IncompleteLengthField/binary>> -> + %% We do not have enough data in the buffer to even form a length field - await more data + {[IncompleteLengthField|Front0],BufferSize,Rear0} + end + end. + +iovec_from_front(Size, [], Rear, Acc) -> + iovec_from_front(Size, lists:reverse(Rear), [], Acc); +iovec_from_front(Size, [Bin|Front], Rear, Acc) -> + case Bin of + <<Last:Size/binary>> -> % Just enough + {lists:reverse(Acc, [Last]),Front,Rear}; + <<Last:Size/binary, Rest/binary>> -> % More than enough, split here + {lists:reverse(Acc, [Last]),[Rest|Front],Rear}; + <<_/binary>> -> % Not enough + BinSize = byte_size(Bin), + iovec_from_front(Size - BinSize, Front, Rear, [Bin|Acc]) end. -merge_dist_data(<<>>, ClientData) -> - ClientData; -merge_dist_data(DistBuff, <<>>) -> - DistBuff; -merge_dist_data(DistBuff, ClientData) -> - [DistBuff, ClientData]. + %%==================================================================== %% Help functions for tls|dtls_connection.erl %%==================================================================== @@ -537,8 +696,8 @@ handle_session(#server_hello{cipher_suite = CipherSuite, compression_method = Compression}, Version, NewId, ConnectionStates, ProtoExt, Protocol0, #state{session = #session{session_id = OldId}, - negotiated_version = ReqVersion, - negotiated_protocol = CurrentProtocol} = State0) -> + handshake_env = #handshake_env{negotiated_protocol = CurrentProtocol} = HsEnv, + connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv} = State0) -> #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite), @@ -551,12 +710,12 @@ handle_session(#server_hello{cipher_suite = CipherSuite, {ProtoExt =:= npn, Protocol0} end, - State = State0#state{key_algorithm = KeyAlgorithm, - negotiated_version = Version, - connection_states = ConnectionStates, - premaster_secret = PremasterSecret, - expecting_next_protocol_negotiation = ExpectNPN, - negotiated_protocol = Protocol}, + State = State0#state{connection_states = ConnectionStates, + handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm, + premaster_secret = PremasterSecret, + expecting_next_protocol_negotiation = ExpectNPN, + negotiated_protocol = Protocol}, + connection_env = CEnv#connection_env{negotiated_version = Version}}, case ssl_session:is_new(OldId, NewId) of true -> @@ -570,10 +729,9 @@ handle_session(#server_hello{cipher_suite = CipherSuite, %%-------------------------------------------------------------------- -spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}. %%-------------------------------------------------------------------- -ssl_config(Opts, Role, State) -> - ssl_config(Opts, Role, State, new). - -ssl_config(Opts, Role, State0, Type) -> +ssl_config(Opts, Role, #state{static_env = InitStatEnv0, + handshake_env = HsEnv, + connection_env = CEnv} = State0) -> {ok, #{cert_db_ref := Ref, cert_db_handle := CertDbHandle, fileref_db_handle := FileRefHandle, @@ -585,24 +743,19 @@ ssl_config(Opts, Role, State0, Type) -> ssl_config:init(Opts, Role), TimeStamp = erlang:monotonic_time(), Session = State0#state.session, - State = State0#state{session = Session#session{own_certificate = OwnCert, - time_stamp = TimeStamp}, - file_ref_db = FileRefHandle, - cert_db_ref = Ref, - cert_db = CertDbHandle, - crl_db = CRLDbHandle, - session_cache = CacheHandle, - private_key = Key, - diffie_hellman_params = DHParams, - ssl_options = Opts}, - case Type of - new -> - Handshake = ssl_handshake:init_handshake_history(), - State#state{tls_handshake_history = Handshake}; - continue -> - State - end. - + + State0#state{session = Session#session{own_certificate = OwnCert, + time_stamp = TimeStamp}, + static_env = InitStatEnv0#static_env{ + file_ref_db = FileRefHandle, + cert_db_ref = Ref, + cert_db = CertDbHandle, + crl_db = CRLDbHandle, + session_cache = CacheHandle + }, + handshake_env = HsEnv#handshake_env{diffie_hellman_params = DHParams}, + connection_env = CEnv#connection_env{private_key = Key}, + ssl_options = Opts}. %%==================================================================== %% gen_statem general state functions with connection cb argument @@ -615,10 +768,11 @@ ssl_config(Opts, Role, State0, Type) -> %%-------------------------------------------------------------------- init({call, From}, {start, Timeout}, State0, Connection) -> - Timer = start_or_recv_cancel_timer(Timeout, From), - Connection:next_event(hello, no_record, State0#state{start_or_recv_from = From, timer = Timer}); + Connection:next_event(hello, no_record, State0#state{start_or_recv_from = From}, + [{{timeout, handshake}, Timeout, close}]); init({call, From}, {start, {Opts, EmOpts}, Timeout}, - #state{role = Role, ssl_options = OrigSSLOptions, + #state{static_env = #static_env{role = Role}, + ssl_options = OrigSSLOptions, socket_options = SockOpts} = State0, Connection) -> try SslOpts = ssl:handle_options(Opts, OrigSSLOptions), @@ -627,7 +781,7 @@ init({call, From}, {start, {Opts, EmOpts}, Timeout}, State#state{ssl_options = SslOpts, socket_options = new_emulated(EmOpts, SockOpts)}, Connection) catch throw:Error -> - stop_and_reply(normal, {reply, From, {error, Error}}, State0) + {stop_and_reply, {shutdown, normal}, {reply, From, {error, Error}}, State0} end; init({call, From}, {new_user, _} = Msg, State, Connection) -> handle_call(Msg, From, ?FUNCTION_NAME, State, Connection); @@ -643,7 +797,7 @@ init(_Type, _Event, _State, _Connection) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- error({call, From}, {close, _}, State, _Connection) -> - stop_and_reply(normal, {reply, From, ok}, State); + {stop_and_reply, {shutdown, normal}, {reply, From, ok}, State}; error({call, From}, _Msg, State, _Connection) -> {next_state, ?FUNCTION_NAME, State, [{reply, From, {error, closed}}]}. @@ -662,20 +816,18 @@ hello(info, Msg, State, _) -> hello(Type, Msg, State, Connection) -> handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). -user_hello({call, From}, cancel, #state{negotiated_version = Version} = State, _) -> +user_hello({call, From}, cancel, #state{connection_env = #connection_env{negotiated_version = Version}} = State, _) -> gen_statem:reply(From, ok), handle_own_alert(?ALERT_REC(?FATAL, ?USER_CANCELED, user_canceled), Version, ?FUNCTION_NAME, State); -user_hello({call, From}, {handshake_continue, NewOptions, Timeout}, #state{hello = Hello, - role = Role, - start_or_recv_from = RecvFrom, - ssl_options = Options0} = State0, _Connection) -> - Timer = start_or_recv_cancel_timer(Timeout, RecvFrom), +user_hello({call, From}, {handshake_continue, NewOptions, Timeout}, + #state{static_env = #static_env{role = Role}, + handshake_env = #handshake_env{hello = Hello}, + ssl_options = Options0} = State0, _Connection) -> Options = ssl:handle_options(NewOptions, Options0#ssl_options{handshake = full}), - State = ssl_config(Options, Role, State0, continue), - {next_state, hello, State#state{start_or_recv_from = From, - timer = Timer}, - [{next_event, internal, Hello}]}; + State = ssl_config(Options, Role, State0), + {next_state, hello, State#state{start_or_recv_from = From}, + [{next_event, internal, Hello}, {{timeout, handshake}, Timeout, close}]}; user_hello(_, _, _, _) -> {keep_state_and_data, [postpone]}. @@ -688,60 +840,63 @@ user_hello(_, _, _, _) -> abbreviated({call, From}, Msg, State, Connection) -> handle_call(Msg, From, ?FUNCTION_NAME, State, Connection); abbreviated(internal, #finished{verify_data = Data} = Finished, - #state{role = server, - negotiated_version = Version, - expecting_finished = true, - tls_handshake_history = Handshake, + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{tls_handshake_history = Hist, + expecting_finished = true} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, session = #session{master_secret = MasterSecret}, connection_states = ConnectionStates0} = State0, Connection) -> case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, client, get_current_prf(ConnectionStates0, write), - MasterSecret, Handshake) of + MasterSecret, Hist) of verified -> ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0), {Record, State} = prepare_connection(State0#state{connection_states = ConnectionStates, - expecting_finished = false}, Connection), - Connection:next_event(connection, Record, State); + handshake_env = HsEnv#handshake_env{expecting_finished = false}}, Connection), + Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close}]); #alert{} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) end; abbreviated(internal, #finished{verify_data = Data} = Finished, - #state{role = client, tls_handshake_history = Handshake0, + #state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{tls_handshake_history = Hist0}, + connection_env = #connection_env{negotiated_version = Version}, session = #session{master_secret = MasterSecret}, - negotiated_version = Version, connection_states = ConnectionStates0} = State0, Connection) -> case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, server, get_pending_prf(ConnectionStates0, write), - MasterSecret, Handshake0) of + MasterSecret, Hist0) of verified -> ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0), - {State1, Actions} = + {#state{handshake_env = HsEnv} = State1, Actions} = finalize_handshake(State0#state{connection_states = ConnectionStates1}, ?FUNCTION_NAME, Connection), - {Record, State} = prepare_connection(State1#state{expecting_finished = false}, Connection), - Connection:next_event(connection, Record, State, Actions); + {Record, State} = prepare_connection(State1#state{handshake_env = HsEnv#handshake_env{expecting_finished = false}}, Connection), + Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close} | Actions]); #alert{} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) end; %% only allowed to send next_protocol message after change cipher spec %% & before finished message and it is not allowed during renegotiation abbreviated(internal, #next_protocol{selected_protocol = SelectedProtocol}, - #state{role = server, expecting_next_protocol_negotiation = true} = State, + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{expecting_next_protocol_negotiation = true} = HsEnv} = State, Connection) -> Connection:next_event(?FUNCTION_NAME, no_record, - State#state{negotiated_protocol = SelectedProtocol, - expecting_next_protocol_negotiation = false}); + State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol, + expecting_next_protocol_negotiation = false}}); abbreviated(internal, #change_cipher_spec{type = <<1>>}, - #state{connection_states = ConnectionStates0} = State, Connection) -> + #state{connection_states = ConnectionStates0, + handshake_env = HsEnv} = State, Connection) -> ConnectionStates1 = ssl_record:activate_pending_connection_state(ConnectionStates0, read, Connection), Connection:next_event(?FUNCTION_NAME, no_record, State#state{connection_states = ConnectionStates1, - expecting_finished = true}); + handshake_env = HsEnv#handshake_env{expecting_finished = true}}); abbreviated(info, Msg, State, _) -> handle_info(Msg, ?FUNCTION_NAME, State); abbreviated(Type, Msg, State, Connection) -> @@ -759,32 +914,34 @@ certify({call, From}, Msg, State, Connection) -> certify(info, Msg, State, _) -> handle_info(Msg, ?FUNCTION_NAME, State); certify(internal, #certificate{asn1_certificates = []}, - #state{role = server, negotiated_version = Version, + #state{static_env = #static_env{role = server}, + connection_env = #connection_env{negotiated_version = Version}, ssl_options = #ssl_options{verify = verify_peer, fail_if_no_peer_cert = true}} = State, _) -> Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); certify(internal, #certificate{asn1_certificates = []}, - #state{role = server, + #state{static_env = #static_env{role = server}, ssl_options = #ssl_options{verify = verify_peer, fail_if_no_peer_cert = false}} = State0, Connection) -> Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_requested = false}); certify(internal, #certificate{}, - #state{role = server, - negotiated_version = Version, + #state{static_env = #static_env{role = server}, + connection_env = #connection_env{negotiated_version = Version}, ssl_options = #ssl_options{verify = verify_none}} = State, _) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate), handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); certify(internal, #certificate{} = Cert, - #state{negotiated_version = Version, - role = Role, - host = Host, - cert_db = CertDbHandle, - cert_db_ref = CertDbRef, - crl_db = CRLDbInfo, + #state{static_env = #static_env{ + role = Role, + host = Host, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + crl_db = CRLDbInfo}, + connection_env = #connection_env{negotiated_version = Version}, ssl_options = Opts} = State, Connection) -> case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts, CRLDbInfo, Role, Host) of @@ -795,112 +952,131 @@ certify(internal, #certificate{} = Cert, handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) end; certify(internal, #server_key_exchange{exchange_keys = Keys}, - #state{role = client, negotiated_version = Version, - key_algorithm = Alg, - public_key_info = PubKeyInfo, + #state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + public_key_info = PubKeyInfo} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, session = Session, connection_states = ConnectionStates} = State, Connection) - when Alg == dhe_dss; Alg == dhe_rsa; - Alg == ecdhe_rsa; Alg == ecdhe_ecdsa; - Alg == dh_anon; Alg == ecdh_anon; - Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk; - Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> - - Params = ssl_handshake:decode_server_key(Keys, Alg, ssl:tls_version(Version)), + when KexAlg == dhe_dss; + KexAlg == dhe_rsa; + KexAlg == ecdhe_rsa; + KexAlg == ecdhe_ecdsa; + KexAlg == dh_anon; + KexAlg == ecdh_anon; + KexAlg == psk; + KexAlg == dhe_psk; + KexAlg == ecdhe_psk; + KexAlg == rsa_psk; + KexAlg == srp_dss; + KexAlg == srp_rsa; + KexAlg == srp_anon -> + + Params = ssl_handshake:decode_server_key(Keys, KexAlg, ssl:tls_version(Version)), %% Use negotiated value if TLS-1.2 otherwhise return default - HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, ssl:tls_version(Version)), + HashSign = negotiated_hashsign(Params#server_key_params.hashsign, KexAlg, PubKeyInfo, ssl:tls_version(Version)), - case is_anonymous(Alg) of + case is_anonymous(KexAlg) of true -> calculate_secret(Params#server_key_params.params, - State#state{hashsign_algorithm = HashSign}, Connection); + State#state{handshake_env = HsEnv#handshake_env{hashsign_algorithm = HashSign}}, Connection); false -> case ssl_handshake:verify_server_key(Params, HashSign, ConnectionStates, ssl:tls_version(Version), PubKeyInfo) of true -> calculate_secret(Params#server_key_params.params, - State#state{hashsign_algorithm = HashSign, - session = session_handle_params(Params#server_key_params.params, Session)}, - Connection); + State#state{handshake_env = HsEnv#handshake_env{hashsign_algorithm = HashSign}, + session = session_handle_params(Params#server_key_params.params, Session)}, + Connection); false -> handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR), Version, ?FUNCTION_NAME, State) end end; certify(internal, #certificate_request{}, - #state{role = client, negotiated_version = Version, - key_algorithm = Alg} = State, _) - when Alg == dh_anon; Alg == ecdh_anon; - Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk; - Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> + #state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = KexAlg}, + connection_env = #connection_env{negotiated_version = Version}} = State, _) + when KexAlg == dh_anon; + KexAlg == ecdh_anon; + KexAlg == psk; + KexAlg == dhe_psk; + KexAlg == ecdhe_psk; + KexAlg == rsa_psk; + KexAlg == srp_dss; + KexAlg == srp_rsa; + KexAlg == srp_anon -> handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, ?FUNCTION_NAME, State); certify(internal, #certificate_request{}, - #state{session = #session{own_certificate = undefined}, - role = client} = State, Connection) -> + #state{static_env = #static_env{role = client}, + session = #session{own_certificate = undefined}} = State, Connection) -> %% The 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. Connection:next_event(?FUNCTION_NAME, no_record, State#state{client_certificate_requested = true}); certify(internal, #certificate_request{} = CertRequest, - #state{session = #session{own_certificate = Cert}, - role = client, - ssl_options = #ssl_options{signature_algs = SupportedHashSigns}, - negotiated_version = Version} = State, Connection) -> + #state{static_env = #static_env{role = client}, + handshake_env = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, + session = #session{own_certificate = Cert}, + ssl_options = #ssl_options{signature_algs = SupportedHashSigns}} = State, Connection) -> case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of #alert {} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); NegotiatedHashSign -> Connection:next_event(?FUNCTION_NAME, no_record, State#state{client_certificate_requested = true, - cert_hashsign_algorithm = NegotiatedHashSign}) + handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = NegotiatedHashSign}}) end; %% PSK and RSA_PSK might bypass the Server-Key-Exchange certify(internal, #server_hello_done{}, - #state{session = #session{master_secret = undefined}, - negotiated_version = Version, - psk_identity = PSKIdentity, - ssl_options = #ssl_options{user_lookup_fun = PSKLookup}, - premaster_secret = undefined, - role = client, - key_algorithm = Alg} = State0, Connection) - when Alg == psk -> - case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup) of + #state{static_env = #static_env{role = client}, + session = #session{master_secret = undefined}, + connection_env = #connection_env{negotiated_version = Version}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + premaster_secret = undefined, + server_psk_identity = PSKIdentity} = HsEnv, + ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) + when KexAlg == psk -> + case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup) of #alert{} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0); PremasterSecret -> State = master_secret(PremasterSecret, - State0#state{premaster_secret = PremasterSecret}), - client_certify_and_key_exchange(State, Connection) + State0#state{handshake_env = + HsEnv#handshake_env{premaster_secret = PremasterSecret}}), + client_certify_and_key_exchange(State, Connection) end; certify(internal, #server_hello_done{}, - #state{session = #session{master_secret = undefined}, - ssl_options = #ssl_options{user_lookup_fun = PSKLookup}, - negotiated_version = {Major, Minor} = Version, - psk_identity = PSKIdentity, - premaster_secret = undefined, - role = client, - key_algorithm = Alg} = State0, Connection) - when Alg == rsa_psk -> + #state{static_env = #static_env{role = client}, + connection_env = #connection_env{negotiated_version = {Major, Minor}} = Version, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + premaster_secret = undefined, + server_psk_identity = PSKIdentity} = HsEnv, + session = #session{master_secret = undefined}, + ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) + when KexAlg == rsa_psk -> Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), RSAPremasterSecret = <<?BYTE(Major), ?BYTE(Minor), Rand/binary>>, - case ssl_handshake:premaster_secret({Alg, PSKIdentity}, PSKLookup, + case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup, RSAPremasterSecret) of #alert{} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0); PremasterSecret -> State = master_secret(PremasterSecret, - State0#state{premaster_secret = RSAPremasterSecret}), + State0#state{handshake_env = + HsEnv#handshake_env{premaster_secret = RSAPremasterSecret}}), client_certify_and_key_exchange(State, Connection) end; %% Master secret was determined with help of server-key exchange msg certify(internal, #server_hello_done{}, - #state{session = #session{master_secret = MasterSecret} = Session, - connection_states = ConnectionStates0, - negotiated_version = Version, - premaster_secret = undefined, - role = client} = State0, Connection) -> + #state{static_env = #static_env{role = client}, + connection_env = #connection_env{negotiated_version = Version}, + handshake_env = #handshake_env{premaster_secret = undefined}, + session = #session{master_secret = MasterSecret} = Session, + connection_states = ConnectionStates0} = State0, Connection) -> case ssl_handshake:master_secret(ssl:tls_version(Version), Session, ConnectionStates0, client) of {MasterSecret, ConnectionStates} -> @@ -911,11 +1087,11 @@ certify(internal, #server_hello_done{}, end; %% Master secret is calculated from premaster_secret certify(internal, #server_hello_done{}, - #state{session = Session0, - connection_states = ConnectionStates0, - negotiated_version = Version, - premaster_secret = PremasterSecret, - role = client} = State0, Connection) -> + #state{static_env = #static_env{role = client}, + connection_env = #connection_env{negotiated_version = Version}, + handshake_env = #handshake_env{premaster_secret = PremasterSecret}, + session = Session0, + connection_states = ConnectionStates0} = State0, Connection) -> case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret, ConnectionStates0, client) of {MasterSecret, ConnectionStates} -> @@ -927,14 +1103,15 @@ certify(internal, #server_hello_done{}, handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) end; certify(internal = Type, #client_key_exchange{} = Msg, - #state{role = server, + #state{static_env = #static_env{role = server}, client_certificate_requested = true, ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State, Connection) -> %% We expect a certificate here handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection); certify(internal, #client_key_exchange{exchange_keys = Keys}, - State = #state{key_algorithm = KeyAlg, negotiated_version = Version}, Connection) -> + State = #state{handshake_env = #handshake_env{kex_algorithm = KeyAlg}, + connection_env = #connection_env{negotiated_version = Version}}, Connection) -> try certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, ssl:tls_version(Version)), State, Connection) @@ -957,69 +1134,72 @@ cipher(info, Msg, State, _) -> handle_info(Msg, ?FUNCTION_NAME, State); cipher(internal, #certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign}, - #state{role = server, - key_algorithm = KexAlg, - public_key_info = PublicKeyInfo, - negotiated_version = Version, - session = #session{master_secret = MasterSecret}, - tls_handshake_history = Handshake + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{tls_handshake_history = Hist, + kex_algorithm = KexAlg, + public_key_info = PubKeyInfo} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, + session = #session{master_secret = MasterSecret} } = State, Connection) -> TLSVersion = ssl:tls_version(Version), %% Use negotiated value if TLS-1.2 otherwhise return default - HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, TLSVersion), - case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - TLSVersion, HashSign, MasterSecret, Handshake) of + HashSign = negotiated_hashsign(CertHashSign, KexAlg, PubKeyInfo, TLSVersion), + case ssl_handshake:certificate_verify(Signature, PubKeyInfo, + TLSVersion, HashSign, MasterSecret, Hist) of valid -> Connection:next_event(?FUNCTION_NAME, no_record, - State#state{cert_hashsign_algorithm = HashSign}); + State#state{handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = HashSign}}); #alert{} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) end; %% client must send a next protocol message if we are expecting it cipher(internal, #finished{}, - #state{role = server, expecting_next_protocol_negotiation = true, - negotiated_protocol = undefined, negotiated_version = Version} = State0, + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{expecting_next_protocol_negotiation = true, + negotiated_protocol = undefined}, + connection_env = #connection_env{negotiated_version = Version}} = State0, _Connection) -> handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, ?FUNCTION_NAME, State0); cipher(internal, #finished{verify_data = Data} = Finished, - #state{negotiated_version = Version, - host = Host, - port = Port, - role = Role, - expecting_finished = true, + #state{static_env = #static_env{role = Role, + host = Host, + port = Port}, + handshake_env = #handshake_env{tls_handshake_history = Hist, + expecting_finished = true} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, session = #session{master_secret = MasterSecret} = Session0, ssl_options = SslOpts, - connection_states = ConnectionStates0, - tls_handshake_history = Handshake0} = State, Connection) -> + connection_states = ConnectionStates0} = State, Connection) -> case ssl_handshake:verify_connection(ssl:tls_version(Version), Finished, opposite_role(Role), get_current_prf(ConnectionStates0, read), - MasterSecret, Handshake0) of + MasterSecret, Hist) of verified -> - Session = register_session(Role, host_id(Role, Host, SslOpts), Port, Session0), + Session = handle_session(Role, SslOpts, Host, Port, Session0), cipher_role(Role, Data, Session, - State#state{expecting_finished = false}, Connection); + State#state{handshake_env = HsEnv#handshake_env{expecting_finished = false}}, Connection); #alert{} = Alert -> handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) end; %% only allowed to send next_protocol message after change cipher spec %% & before finished message and it is not allowed during renegotiation cipher(internal, #next_protocol{selected_protocol = SelectedProtocol}, - #state{role = server, expecting_next_protocol_negotiation = true, - expecting_finished = true} = State, Connection) -> - Connection:next_event(?FUNCTION_NAME, no_record, - State#state{expecting_next_protocol_negotiation = false, - negotiated_protocol = SelectedProtocol - }); -cipher(internal, #change_cipher_spec{type = <<1>>}, #state{connection_states = ConnectionStates0} = + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{expecting_finished = true, + expecting_next_protocol_negotiation = true} = HsEnv} = State0, Connection) -> + {Record, State} = + Connection:next_record(State0), + Connection:next_event(?FUNCTION_NAME, Record, + State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol, + expecting_next_protocol_negotiation = false}}); +cipher(internal, #change_cipher_spec{type = <<1>>}, #state{handshake_env = HsEnv, connection_states = ConnectionStates0} = State, Connection) -> ConnectionStates = ssl_record:activate_pending_connection_state(ConnectionStates0, read, Connection), - Connection:next_event(?FUNCTION_NAME, no_record, State#state{connection_states = - ConnectionStates, - expecting_finished = true}); + Connection:next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{expecting_finished = true}, + connection_states = ConnectionStates}); cipher(Type, Msg, State, Connection) -> handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). @@ -1029,15 +1209,17 @@ cipher(Type, Msg, State, Connection) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- connection({call, RecvFrom}, {recv, N, Timeout}, - #state{protocol_cb = Connection, socket_options = - #socket_options{active = false}} = State0, Connection) -> - Timer = start_or_recv_cancel_timer(Timeout, RecvFrom), + #state{static_env = #static_env{protocol_cb = Connection}, + socket_options = + #socket_options{active = false}} = State0, Connection) -> passive_receive(State0#state{bytes_to_read = N, - start_or_recv_from = RecvFrom, - timer = Timer}, ?FUNCTION_NAME, Connection); -connection({call, From}, renegotiate, #state{protocol_cb = Connection} = State, + start_or_recv_from = RecvFrom}, ?FUNCTION_NAME, Connection, + [{{timeout, recv}, Timeout, timeout}]); + +connection({call, From}, renegotiate, #state{static_env = #static_env{protocol_cb = Connection}, + handshake_env = HsEnv} = State, Connection) -> - Connection:renegotiate(State#state{renegotiation = {true, From}}, []); + Connection:renegotiate(State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, From}}}, []); connection({call, From}, peer_certificate, #state{session = #session{peer_certificate = Cert}} = State, _) -> hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, Cert}}]); @@ -1048,35 +1230,36 @@ connection({call, From}, {connection_information, false}, State, _) -> Info = connection_info(State), hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, Info}}]); connection({call, From}, negotiated_protocol, - #state{negotiated_protocol = undefined} = State, _) -> + #state{handshake_env = #handshake_env{negotiated_protocol = undefined}} = State, _) -> hibernate_after(?FUNCTION_NAME, State, [{reply, From, {error, protocol_not_negotiated}}]); connection({call, From}, negotiated_protocol, - #state{negotiated_protocol = SelectedProtocol} = State, _) -> + #state{handshake_env = #handshake_env{negotiated_protocol = SelectedProtocol}} = State, _) -> hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, SelectedProtocol}}]); connection({call, From}, Msg, State, Connection) -> handle_call(Msg, From, ?FUNCTION_NAME, State, Connection); -connection(cast, {internal_renegotiate, WriteState}, #state{protocol_cb = Connection, +connection(cast, {internal_renegotiate, WriteState}, #state{static_env = #static_env{protocol_cb = Connection}, + handshake_env = HsEnv, connection_states = ConnectionStates} = State, Connection) -> - Connection:renegotiate(State#state{renegotiation = {true, internal}, + Connection:renegotiate(State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, internal}}, connection_states = ConnectionStates#{current_write => WriteState}}, []); connection(cast, {dist_handshake_complete, DHandle}, #state{ssl_options = #ssl_options{erl_dist = true}, - erl_dist_data = ErlDistData, + connection_env = CEnv, socket_options = SockOpts} = State0, Connection) -> process_flag(priority, normal), State1 = State0#state{ - socket_options = - SockOpts#socket_options{active = true}, - erl_dist_data = ErlDistData#{dist_handle => DHandle}}, - {Record, State} = dist_app_data(<<>>, State1), + socket_options = SockOpts#socket_options{active = true}, + connection_env = CEnv#connection_env{erl_dist_handle = DHandle}, + bytes_to_read = undefined}, + {Record, State} = read_application_data(<<>>, State1), Connection:next_event(connection, Record, State); connection(info, Msg, State, _) -> handle_info(Msg, ?FUNCTION_NAME, State); -connection(internal, {recv, _}, State, Connection) -> - passive_receive(State, ?FUNCTION_NAME, Connection); +connection(internal, {recv, Timeout}, State, Connection) -> + passive_receive(State, ?FUNCTION_NAME, Connection, [{{timeout, recv}, Timeout, timeout}]); connection(Type, Msg, State, Connection) -> handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). @@ -1085,22 +1268,6 @@ connection(Type, Msg, State, Connection) -> #state{}, tls_connection | dtls_connection) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- -downgrade(internal, #alert{description = ?CLOSE_NOTIFY}, - #state{transport_cb = Transport, socket = Socket, - downgrade = {Pid, From}} = State, _) -> - tls_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]), - Transport:controlling_process(Socket, Pid), - gen_statem:reply(From, {ok, Socket}), - stop(normal, State); -downgrade(timeout, downgrade, #state{downgrade = {_, From}} = State, _) -> - gen_statem:reply(From, {error, timeout}), - stop(normal, State); -downgrade( - info, {CloseTag, Socket}, - #state{socket = Socket, close_tag = CloseTag, downgrade = {_, From}} = - State, _) -> - gen_statem:reply(From, {error, CloseTag}), - stop(normal, State); downgrade(Type, Event, State, Connection) -> handle_common_event(Type, Event, ?FUNCTION_NAME, State, Connection). @@ -1109,47 +1276,45 @@ downgrade(Type, Event, State, Connection) -> %% common or unexpected events for the state. %%-------------------------------------------------------------------- handle_common_event(internal, {handshake, {#hello_request{} = Handshake, _}}, connection = StateName, - #state{role = client} = State, _) -> + #state{static_env = #static_env{role = client}, + handshake_env = HsEnv} = State, _) -> %% Should not be included in handshake history - {next_state, StateName, State#state{renegotiation = {true, peer}}, [{next_event, internal, Handshake}]}; -handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #state{role = client}, _) + {next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, peer}}}, + [{next_event, internal, Handshake}]}; +handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, + #state{static_env = #static_env{role = client}}, _) when StateName =/= connection -> - {keep_state_and_data}; + keep_state_and_data; handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName, - #state{tls_handshake_history = Hs0} = State0, + #state{handshake_env = #handshake_env{tls_handshake_history = Hist0}} = State0, Connection) -> PossibleSNI = Connection:select_sni_extension(Handshake), %% This function handles client SNI hello extension when Handshake is %% a client_hello, which needs to be determined by the connection callback. %% In other cases this is a noop - State = handle_sni_extension(PossibleSNI, State0), - HsHist = ssl_handshake:update_handshake_history(Hs0, iolist_to_binary(Raw)), - {next_state, StateName, State#state{tls_handshake_history = HsHist}, + State = #state{handshake_env = HsEnv} = handle_sni_extension(PossibleSNI, State0), + + Hist = ssl_handshake:update_handshake_history(Hist0, Raw), + {next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}, [{next_event, internal, Handshake}]}; handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, State, Connection) -> - Connection:handle_common_event(internal, TLSorDTLSRecord, StateName, State); + Connection:handle_protocol_record(TLSorDTLSRecord, StateName, State); handle_common_event(timeout, hibernate, _, _, _) -> {keep_state_and_data, [hibernate]}; -handle_common_event(internal, {application_data, Data}, StateName, State0, Connection) -> - case read_application_data(Data, State0) of - {stop, _, _} = Stop-> - Stop; - {Record, State1} -> - case Connection:next_event(StateName, Record, State1) of - {next_state, StateName, State} -> - hibernate_after(StateName, State, []); - {next_state, StateName, State, Actions} -> - hibernate_after(StateName, State, Actions); - {stop, _, _} = Stop -> - Stop - end - end; handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName, - #state{negotiated_version = Version} = State, _) -> + #state{connection_env = #connection_env{negotiated_version = Version}} = State, _) -> handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, StateName, State); -handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State, +handle_common_event({timeout, handshake}, close, _StateName, #state{start_or_recv_from = StartFrom} = State, _) -> + {stop_and_reply, + {shutdown, user_timeout}, + {reply, StartFrom, {error, timeout}}, State#state{start_or_recv_from = undefined}}; +handle_common_event({timeout, recv}, timeout, StateName, #state{start_or_recv_from = RecvFrom} = State, _) -> + {next_state, StateName, State#state{start_or_recv_from = undefined, + bytes_to_read = undefined}, [{reply, RecvFrom, {error, timeout}}]}; +handle_common_event(_Type, Msg, StateName, #state{connection_env = + #connection_env{negotiated_version = Version}} = State, _) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, Msg}), handle_own_alert(Alert, Version, StateName, State). @@ -1157,47 +1322,41 @@ handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} handle_call({application_data, _Data}, _, _, _, _) -> %% In renegotiation priorities handshake, send data when handshake is finished {keep_state_and_data, [postpone]}; -handle_call({close, {Pid, Timeout}}, From, StateName, State0, Connection) when is_pid(Pid) -> - %% terminate will send close alert to peer - State = State0#state{downgrade = {Pid, From}}, - Connection:terminate(downgrade, StateName, State), - %% User downgrades connection - %% When downgrading an TLS connection to a transport connection - %% we must recive the close alert from the peer before releasing the - %% transport socket. - {next_state, downgrade, State#state{terminated = true}, [{timeout, Timeout, downgrade}]}; -handle_call({close, _} = Close, From, StateName, State, _Connection) -> +handle_call({close, _} = Close, From, StateName, #state{connection_env = CEnv} = State, _Connection) -> %% Run terminate before returning so that the reuseaddr %% inet-option works properly Result = terminate(Close, StateName, State), - stop_and_reply( - {shutdown, normal}, - {reply, From, Result}, State#state{terminated = true}); + {stop_and_reply, + {shutdown, normal}, + {reply, From, Result}, State#state{connection_env = CEnv#connection_env{terminated = true}}}; handle_call({shutdown, read_write = How}, From, StateName, - #state{transport_cb = Transport, - socket = Socket} = State, _) -> - + #state{static_env = #static_env{transport_cb = Transport, + socket = Socket}, + connection_env = CEnv} = State, _) -> try send_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), - StateName, State) of + StateName, State) of _ -> case Transport:shutdown(Socket, How) of ok -> - {next_state, StateName, State#state{terminated = true}, [{reply, From, ok}]}; + {next_state, StateName, State#state{connection_env = + CEnv#connection_env{terminated = true}}, + [{reply, From, ok}]}; Error -> - {stop, StateName, State#state{terminated = true}, [{reply, From, Error}]} + {stop_and_reply, {shutdown, normal}, {reply, From, Error}, + State#state{connection_env = CEnv#connection_env{terminated = true}}} end catch throw:Return -> Return end; handle_call({shutdown, How0}, From, StateName, - #state{transport_cb = Transport, - socket = Socket} = State, _) -> + #state{static_env = #static_env{transport_cb = Transport, + socket = Socket}} = State, _) -> case Transport:shutdown(Socket, How0) of ok -> {next_state, StateName, State, [{reply, From, ok}]}; Error -> - {stop, StateName, State, [{reply, From, Error}]} + {stop_and_reply, {shutdown, normal}, {reply, From, Error}, State} end; handle_call({recv, _N, _Timeout}, From, _, #state{socket_options = @@ -1206,40 +1365,47 @@ handle_call({recv, _N, _Timeout}, From, _, handle_call({recv, N, Timeout}, RecvFrom, StateName, State, _) -> %% Doing renegotiate wait with handling request until renegotiate is %% finished. - Timer = start_or_recv_cancel_timer(Timeout, RecvFrom), - {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom, - timer = Timer}, - [{next_event, internal, {recv, RecvFrom}}]}; + {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, + [{next_event, internal, {recv, RecvFrom}} , {{timeout, recv}, Timeout, timeout}]}; handle_call({new_user, User}, From, StateName, - State =#state{user_application = {OldMon, _}}, _) -> + State = #state{connection_env = #connection_env{user_application = {OldMon, _}} = CEnv}, _) -> NewMon = erlang:monitor(process, User), erlang:demonitor(OldMon, [flush]), - {next_state, StateName, State#state{user_application = {NewMon,User}}, + {next_state, StateName, State#state{connection_env = CEnv#connection_env{user_application = {NewMon, User}}}, [{reply, From, ok}]}; handle_call({get_opts, OptTags}, From, _, - #state{socket = Socket, - transport_cb = Transport, + #state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, socket_options = SockOpts}, Connection) -> OptsReply = get_socket_opts(Connection, Transport, Socket, OptTags, SockOpts, []), {keep_state_and_data, [{reply, From, OptsReply}]}; handle_call({set_opts, Opts0}, From, StateName, - #state{socket_options = Opts1, - socket = Socket, - transport_cb = Transport} = State0, Connection) -> + #state{static_env = #static_env{socket = Socket, + transport_cb = Transport, + tracker = Tracker}, + connection_env = + #connection_env{user_application = {_Mon, Pid}}, + socket_options = Opts1 + } = State0, Connection) -> {Reply, Opts} = set_socket_opts(Connection, Transport, Socket, Opts0, Opts1, []), + case {proplists:lookup(active, Opts0), Opts} of + {{_, N}, #socket_options{active=false}} when is_integer(N) -> + send_user( + Pid, + format_passive( + Connection:pids(State0), Transport, Socket, Tracker, Connection)); + _ -> + ok + end, State = State0#state{socket_options = Opts}, handle_active_option(Opts#socket_options.active, StateName, From, Reply, State); handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection -> {keep_state_and_data, [{reply, From, {error, already_renegotiating}}]}; -handle_call(get_sslsocket, From, _StateName, State, Connection) -> - SslSocket = Connection:socket(State), - {keep_state_and_data, [{reply, From, SslSocket}]}; - handle_call({prf, Secret, Label, Seed, WantedLength}, From, _, #state{connection_states = ConnectionStates, - negotiated_version = Version}, _) -> + connection_env = #connection_env{negotiated_version = Version}}, _) -> #{security_parameters := SecParams} = ssl_record:current_connection_state(ConnectionStates, read), #security_parameters{master_secret = MasterSecret, @@ -1266,62 +1432,51 @@ handle_call(_,_,_,_,_) -> {keep_state_and_data, [postpone]}. handle_info({ErrorTag, Socket, econnaborted}, StateName, - #state{socket = Socket, transport_cb = Transport, - protocol_cb = Connection, - start_or_recv_from = StartFrom, role = Role, - error_tag = ErrorTag, - tracker = Tracker} = State) when StateName =/= connection -> + #state{static_env = #static_env{role = Role, + socket = Socket, + transport_cb = Transport, + error_tag = ErrorTag, + tracker = Tracker, + protocol_cb = Connection}, + start_or_recv_from = StartFrom + } = State) when StateName =/= connection -> Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker,Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection), - stop(normal, State); + {stop, {shutdown, normal}, State}; -handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, - error_tag = ErrorTag} = State) -> +handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{socket = Socket, + error_tag = ErrorTag}} = State) -> Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]), error_logger:error_report(Report), handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), - stop(normal, State); + {stop, {shutdown,normal}, State}; handle_info({'DOWN', MonitorRef, _, _, Reason}, _, - #state{user_application = {MonitorRef, _Pid}, + #state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}, ssl_options = #ssl_options{erl_dist = true}}) -> {stop, {shutdown, Reason}}; handle_info({'DOWN', MonitorRef, _, _, _}, _, - #state{user_application = {MonitorRef, _Pid}}) -> - {stop, normal}; + #state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}}) -> + {stop, {shutdown, normal}}; handle_info({'EXIT', Pid, _Reason}, StateName, - #state{user_application = {_MonitorRef, Pid}} = State) -> + #state{connection_env = #connection_env{user_application = {_MonitorRef, Pid}}} = State) -> %% It seems the user application has linked to us %% - ignore that and let the monitor handle this {next_state, StateName, State}; %%% So that terminate will be run when supervisor issues shutdown handle_info({'EXIT', _Sup, shutdown}, _StateName, State) -> - stop(shutdown, State); -handle_info({'EXIT', Socket, normal}, _StateName, #state{socket = Socket} = State) -> + {stop, shutdown, State}; +handle_info({'EXIT', Socket, normal}, _StateName, #state{static_env = #static_env{socket = Socket}} = State) -> %% Handle as transport close" - stop({shutdown, transport_closed}, State); -handle_info({'EXIT', Socket, Reason}, _StateName, #state{socket = Socket} = State) -> - stop({shutdown, Reason}, State); - -handle_info(allow_renegotiate, StateName, State) -> - {next_state, StateName, State#state{allow_renegotiate = true}}; - -handle_info({cancel_start_or_recv, StartFrom}, StateName, - #state{renegotiation = {false, first}} = State) when StateName =/= connection -> - stop_and_reply( - {shutdown, user_timeout}, - {reply, StartFrom, {error, timeout}}, - State#state{timer = undefined}); -handle_info({cancel_start_or_recv, RecvFrom}, StateName, - #state{start_or_recv_from = RecvFrom} = State) when RecvFrom =/= undefined -> - {next_state, StateName, State#state{start_or_recv_from = undefined, - bytes_to_read = undefined, - timer = undefined}, [{reply, RecvFrom, {error, timeout}}]}; -handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) -> - {next_state, StateName, State#state{timer = undefined}}; + {stop,{shutdown, transport_closed}, State}; +handle_info({'EXIT', Socket, Reason}, _StateName, #state{static_env = #static_env{socket = Socket}} = State) -> + {stop,{shutdown, Reason}, State}; + +handle_info(allow_renegotiate, StateName, #state{handshake_env = HsEnv} = State) -> + {next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{allow_renegotiate = true}}}; -handle_info(Msg, StateName, #state{socket = Socket, error_tag = Tag} = State) -> +handle_info(Msg, StateName, #state{static_env = #static_env{socket = Socket, error_tag = Tag}} = State) -> Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [{Msg, Tag, Socket}]), error_logger:info_report(Report), {next_state, StateName, State}. @@ -1329,7 +1484,7 @@ handle_info(Msg, StateName, #state{socket = Socket, error_tag = Tag} = State) -> %%==================================================================== %% general gen_statem callbacks %%==================================================================== -terminate(_, _, #state{terminated = true}) -> +terminate(_, _, #state{connection_env = #connection_env{terminated = true}}) -> %% Happens when user closes the connection using ssl:close/1 %% we want to guarantee that Transport:close has been called %% when ssl:close/1 returns unless it is a downgrade where @@ -1338,14 +1493,15 @@ terminate(_, _, #state{terminated = true}) -> %% before run by gen_statem which will end up here ok; terminate({shutdown, transport_closed} = Reason, - _StateName, #state{protocol_cb = Connection, - socket = Socket, transport_cb = Transport} = State) -> + _StateName, #state{static_env = #static_env{protocol_cb = Connection, + socket = Socket, + transport_cb = Transport}} = State) -> handle_trusted_certs_db(State), Connection:close(Reason, Socket, Transport, undefined, undefined); terminate({shutdown, own_alert}, _StateName, #state{ - protocol_cb = Connection, - socket = Socket, - transport_cb = Transport} = State) -> + static_env = #static_env{protocol_cb = Connection, + socket = Socket, + transport_cb = Transport}} = State) -> handle_trusted_certs_db(State), case application:get_env(ssl, alert_timeout) of {ok, Timeout} when is_integer(Timeout) -> @@ -1353,23 +1509,27 @@ terminate({shutdown, own_alert}, _StateName, #state{ _ -> Connection:close({timeout, ?DEFAULT_TIMEOUT}, Socket, Transport, undefined, undefined) end; -terminate(downgrade = Reason, connection, #state{protocol_cb = Connection, - transport_cb = Transport, socket = Socket - } = State) -> +terminate({shutdown, downgrade = Reason}, downgrade, #state{static_env = #static_env{protocol_cb = Connection, + transport_cb = Transport, + socket = Socket} + } = State) -> handle_trusted_certs_db(State), Connection:close(Reason, Socket, Transport, undefined, undefined); -terminate(Reason, connection, #state{protocol_cb = Connection, +terminate(Reason, connection, #state{static_env = #static_env{ + protocol_cb = Connection, + transport_cb = Transport, + socket = Socket}, connection_states = ConnectionStates, - ssl_options = #ssl_options{padding_check = Check}, - transport_cb = Transport, socket = Socket + ssl_options = #ssl_options{padding_check = Check} } = State) -> handle_trusted_certs_db(State), Alert = terminate_alert(Reason), %% Send the termination ALERT if possible - catch (Connection:send_alert_in_connection(Alert, State)), + catch (ok = Connection:send_alert_in_connection(Alert, State)), Connection:close({timeout, ?DEFAULT_TIMEOUT}, Socket, Transport, ConnectionStates, Check); -terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection, - socket = Socket +terminate(Reason, _StateName, #state{static_env = #static_env{transport_cb = Transport, + protocol_cb = Connection, + socket = Socket} } = State) -> handle_trusted_certs_db(State), Connection:close(Reason, Socket, Transport, undefined, undefined). @@ -1388,14 +1548,9 @@ format_status(terminate, [_, StateName, State]) -> [{data, [{"State", {StateName, State#state{connection_states = ?SECRET_PRINTOUT, protocol_buffers = ?SECRET_PRINTOUT, user_data_buffer = ?SECRET_PRINTOUT, - tls_handshake_history = ?SECRET_PRINTOUT, + handshake_env = ?SECRET_PRINTOUT, + connection_env = ?SECRET_PRINTOUT, session = ?SECRET_PRINTOUT, - private_key = ?SECRET_PRINTOUT, - diffie_hellman_params = ?SECRET_PRINTOUT, - diffie_hellman_keys = ?SECRET_PRINTOUT, - srp_params = ?SECRET_PRINTOUT, - srp_keys = ?SECRET_PRINTOUT, - premaster_secret = ?SECRET_PRINTOUT, ssl_options = NewOptions, flight_buffer = ?SECRET_PRINTOUT} }}]}]. @@ -1403,16 +1558,16 @@ format_status(terminate, [_, StateName, State]) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -send_alert(Alert, connection, #state{protocol_cb = Connection} = State) -> +send_alert(Alert, connection, #state{static_env = #static_env{protocol_cb = Connection}} = State) -> Connection:send_alert_in_connection(Alert, State); -send_alert(Alert, _, #state{protocol_cb = Connection} = State) -> +send_alert(Alert, _, #state{static_env = #static_env{protocol_cb = Connection}} = State) -> Connection:send_alert(Alert, State). -connection_info(#state{sni_hostname = SNIHostname, - session = #session{session_id = SessionId, +connection_info(#state{static_env = #static_env{protocol_cb = Connection}, + handshake_env = #handshake_env{sni_hostname = SNIHostname}, + session = #session{session_id = SessionId, cipher_suite = CipherSuite, ecc = ECCCurve}, - protocol_cb = Connection, - negotiated_version = {_,_} = Version, + connection_env = #connection_env{negotiated_version = {_,_} = Version}, ssl_options = Opts}) -> RecordCB = record_cb(Connection), CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher_format:suite_definition(CipherSuite), @@ -1440,16 +1595,17 @@ security_info(#state{connection_states = ConnectionStates}) -> do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} = ServerHelloExt, - #state{negotiated_version = Version, + #state{connection_env = #connection_env{negotiated_version = Version}, + handshake_env = HsEnv, session = #session{session_id = SessId}, connection_states = ConnectionStates0} = State0, Connection) when is_atom(Type) -> - + ServerHello = ssl_handshake:server_hello(SessId, ssl:tls_version(Version), ConnectionStates0, ServerHelloExt), State = server_hello(ServerHello, - State0#state{expecting_next_protocol_negotiation = - NextProtocols =/= undefined}, Connection), + State0#state{handshake_env = HsEnv#handshake_env{expecting_next_protocol_negotiation = + NextProtocols =/= undefined}}, Connection), case Type of new -> new_server_hello(ServerHello, State, Connection); @@ -1460,8 +1616,8 @@ do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocol new_server_hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression, session_id = SessionId}, - #state{session = Session0, - negotiated_version = Version} = State0, Connection) -> + #state{session = Session0, + connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) -> try server_certify_and_key_exchange(State0, Connection) of #state{} = State1 -> {State, Actions} = server_hello_done(State1, Connection), @@ -1477,7 +1633,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, resumed_server_hello(#state{session = Session, connection_states = ConnectionStates0, - negotiated_version = Version} = State0, Connection) -> + connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) -> case ssl_handshake:master_secret(ssl:tls_version(Version), Session, ConnectionStates0, server) of @@ -1494,19 +1650,20 @@ resumed_server_hello(#state{session = Session, server_hello(ServerHello, State0, Connection) -> CipherSuite = ServerHello#server_hello.cipher_suite, #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite), - State = Connection:queue_handshake(ServerHello, State0), - State#state{key_algorithm = KeyAlgorithm}. + #state{handshake_env = HsEnv} = State = Connection:queue_handshake(ServerHello, State0), + State#state{handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm}}. server_hello_done(State, Connection) -> HelloDone = ssl_handshake:server_hello_done(), Connection:send_handshake(HelloDone, State). handle_peer_cert(Role, PeerCert, PublicKeyInfo, - #state{session = #session{cipher_suite = CipherSuite} = Session} = State0, + #state{handshake_env = HsEnv, + session = #session{cipher_suite = CipherSuite} = Session} = State0, Connection) -> - State1 = State0#state{session = - Session#session{peer_certificate = PeerCert}, - public_key_info = PublicKeyInfo}, + State1 = State0#state{handshake_env = HsEnv#handshake_env{public_key_info = PublicKeyInfo}, + session = + Session#session{peer_certificate = PeerCert}}, #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite), State = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlgorithm, State1), Connection:next_event(certify, no_record, State). @@ -1514,27 +1671,20 @@ handle_peer_cert(Role, PeerCert, PublicKeyInfo, handle_peer_cert_key(client, _, {?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey, PublicKeyParams}, - KeyAlg, #state{session = Session} = State) when KeyAlg == ecdh_rsa; + KeyAlg, #state{handshake_env = HsEnv, + session = Session} = State) when KeyAlg == ecdh_rsa; KeyAlg == ecdh_ecdsa -> ECDHKey = public_key:generate_key(PublicKeyParams), PremasterSecret = ssl_handshake:premaster_secret(PublicKey, ECDHKey), - master_secret(PremasterSecret, State#state{diffie_hellman_keys = ECDHKey, + master_secret(PremasterSecret, State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKey}, session = Session#session{ecc = PublicKeyParams}}); -%% We do currently not support cipher suites that use fixed DH. -%% If we want to implement that the following clause can be used -%% to extract DH parameters form cert. -%% handle_peer_cert_key(client, _PeerCert, {?dhpublicnumber, PublicKey, PublicKeyParams}, -%% {_,SignAlg}, -%% #state{diffie_hellman_keys = {_, MyPrivatKey}} = State) when -%% SignAlg == dh_rsa; -%% SignAlg == dh_dss -> -%% dh_master_secret(PublicKeyParams, PublicKey, MyPrivatKey, State); handle_peer_cert_key(_, _, _, _, State) -> State. -certify_client(#state{client_certificate_requested = true, role = client, - cert_db = CertDbHandle, - cert_db_ref = CertDbRef, +certify_client(#state{static_env = #static_env{role = client, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef}, + client_certificate_requested = true, session = #session{own_certificate = OwnCert}} = State, Connection) -> Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), @@ -1542,16 +1692,17 @@ certify_client(#state{client_certificate_requested = true, role = client, certify_client(#state{client_certificate_requested = false} = State, _) -> State. -verify_client_cert(#state{client_certificate_requested = true, role = client, - negotiated_version = Version, - private_key = PrivateKey, +verify_client_cert(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{tls_handshake_history = Hist, + cert_hashsign_algorithm = HashSign}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, + client_certificate_requested = true, session = #session{master_secret = MasterSecret, - own_certificate = OwnCert}, - cert_hashsign_algorithm = HashSign, - tls_handshake_history = Handshake0} = State, Connection) -> + own_certificate = OwnCert}} = State, Connection) -> case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - ssl:tls_version(Version), HashSign, PrivateKey, Handshake0) of + ssl:tls_version(Version), HashSign, PrivateKey, Hist) of #certificate_verify{} = Verified -> Connection:queue_handshake(Verified, State); ignore -> @@ -1562,7 +1713,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, verify_client_cert(#state{client_certificate_requested = false} = State, _) -> State. -client_certify_and_key_exchange(#state{negotiated_version = Version} = +client_certify_and_key_exchange(#state{connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) -> try do_client_certify_and_key_exchange(State0, Connection) of State1 = #state{} -> @@ -1587,7 +1738,9 @@ server_certify_and_key_exchange(State0, Connection) -> request_client_cert(State2, Connection). certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS}, - #state{private_key = Key, client_hello_version = {Major, Minor} = Version} = State, Connection) -> + #state{connection_env = #connection_env{private_key = Key}, + handshake_env = #handshake_env{client_hello_version = {Major, Minor} = Version}} + = State, Connection) -> FakeSecret = make_premaster_secret(Version, rsa), %% Countermeasure for Bleichenbacher attack always provide some kind of premaster secret %% and fail handshake later.RFC 5246 section 7.4.7.1. @@ -1608,14 +1761,15 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS end, calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, - #state{diffie_hellman_params = #'DHParameter'{} = Params, - diffie_hellman_keys = {_, ServerDhPrivateKey}} = State, + #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params, + kex_keys = {_, ServerDhPrivateKey}} + } = State, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientPublicDhKey, ServerDhPrivateKey, Params), calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientPublicEcDhPoint}, - #state{diffie_hellman_keys = ECDHKey} = State, Connection) -> + #state{handshake_env = #handshake_env{kex_keys = ECDHKey}} = State, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(#'ECPoint'{point = ClientPublicEcDhPoint}, ECDHKey), calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); certify_client_key_exchange(#client_psk_identity{} = ClientKey, @@ -1625,8 +1779,8 @@ certify_client_key_exchange(#client_psk_identity{} = ClientKey, PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup), calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher); certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey, - #state{diffie_hellman_params = #'DHParameter'{} = Params, - diffie_hellman_keys = {_, ServerDhPrivateKey}, + #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params, + kex_keys = {_, ServerDhPrivateKey}}, ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) -> @@ -1634,7 +1788,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey, ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup), calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher); certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey, - #state{diffie_hellman_keys = ServerEcDhPrivateKey, + #state{handshake_env = #handshake_env{kex_keys = ServerEcDhPrivateKey}, ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State, Connection) -> @@ -1642,28 +1796,29 @@ certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey, ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup), calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey, - #state{private_key = Key, + #state{connection_env = #connection_env{private_key = Key}, ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup), calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher); certify_client_key_exchange(#client_srp_public{} = ClientKey, - #state{srp_params = Params, - srp_keys = Key + #state{handshake_env = #handshake_env{srp_params = Params, + kex_keys = Key} } = State0, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, Params), calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher). -certify_server(#state{key_algorithm = Algo} = State, _) when Algo == dh_anon; - Algo == ecdh_anon; - Algo == psk; - Algo == dhe_psk; - Algo == ecdhe_psk; - Algo == srp_anon -> +certify_server(#state{handshake_env = #handshake_env{kex_algorithm = KexAlg}} = + State, _) when KexAlg == dh_anon; + KexAlg == ecdh_anon; + KexAlg == psk; + KexAlg == dhe_psk; + KexAlg == ecdhe_psk; + KexAlg == srp_anon -> State; -certify_server(#state{cert_db = CertDbHandle, - cert_db_ref = CertDbRef, +certify_server(#state{static_env = #static_env{cert_db = CertDbHandle, + cert_db_ref = CertDbRef}, session = #session{own_certificate = OwnCert}} = State, Connection) -> case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of Cert = #certificate{} -> @@ -1672,18 +1827,19 @@ certify_server(#state{cert_db = CertDbHandle, throw(Alert) end. -key_exchange(#state{role = server, key_algorithm = rsa} = State,_) -> +key_exchange(#state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{kex_algorithm = rsa}} = State,_) -> State; -key_exchange(#state{role = server, key_algorithm = Algo, - hashsign_algorithm = HashSignAlgo, - diffie_hellman_params = #'DHParameter'{} = Params, - private_key = PrivateKey, - connection_states = ConnectionStates0, - negotiated_version = Version - } = State0, Connection) - when Algo == dhe_dss; - Algo == dhe_rsa; - Algo == dh_anon -> +key_exchange(#state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + diffie_hellman_params = #'DHParameter'{} = Params, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, + connection_states = ConnectionStates0} = State0, Connection) + when KexAlg == dhe_dss; + KexAlg == dhe_rsa; + KexAlg == dh_anon -> DHKeys = public_key:generate_key(Params), #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), @@ -1693,22 +1849,26 @@ key_exchange(#state{role = server, key_algorithm = Algo, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - State = Connection:queue_handshake(Msg, State0), - State#state{diffie_hellman_keys = DHKeys}; -key_exchange(#state{role = server, private_key = #'ECPrivateKey'{parameters = ECCurve} = Key, key_algorithm = Algo, + #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0), + State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}}; +key_exchange(#state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{kex_algorithm = KexAlg} = HsEnv, + connection_env = #connection_env{private_key = #'ECPrivateKey'{parameters = ECCurve} = Key}, session = Session} = State, _) - when Algo == ecdh_ecdsa; Algo == ecdh_rsa -> - State#state{diffie_hellman_keys = Key, + when KexAlg == ecdh_ecdsa; + KexAlg == ecdh_rsa -> + State#state{handshake_env = HsEnv#handshake_env{kex_keys = Key}, session = Session#session{ecc = ECCurve}}; -key_exchange(#state{role = server, key_algorithm = Algo, - hashsign_algorithm = HashSignAlgo, - private_key = PrivateKey, +key_exchange(#state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, session = #session{ecc = ECCCurve}, - connection_states = ConnectionStates0, - negotiated_version = Version - } = State0, Connection) - when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa; - Algo == ecdh_anon -> + connection_states = ConnectionStates0} = State0, Connection) + when KexAlg == ecdhe_ecdsa; + KexAlg == ecdhe_rsa; + KexAlg == ecdh_anon -> ECDHKeys = public_key:generate_key(ECCCurve), #{security_parameters := SecParams} = @@ -1720,18 +1880,19 @@ key_exchange(#state{role = server, key_algorithm = Algo, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - State = Connection:queue_handshake(Msg, State0), - State#state{diffie_hellman_keys = ECDHKeys}; -key_exchange(#state{role = server, key_algorithm = psk, + #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0), + State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}}; +key_exchange(#state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{kex_algorithm = psk}, ssl_options = #ssl_options{psk_identity = undefined}} = State, _) -> State; -key_exchange(#state{role = server, key_algorithm = psk, +key_exchange(#state{static_env = #static_env{role = server}, ssl_options = #ssl_options{psk_identity = PskIdentityHint}, - hashsign_algorithm = HashSignAlgo, - private_key = PrivateKey, - connection_states = ConnectionStates0, - negotiated_version = Version - } = State0, Connection) -> + handshake_env = #handshake_env{kex_algorithm = psk, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, + connection_states = ConnectionStates0} = State0, Connection) -> #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), #security_parameters{client_random = ClientRandom, @@ -1740,15 +1901,16 @@ key_exchange(#state{role = server, key_algorithm = psk, {psk, PskIdentityHint, HashSignAlgo, ClientRandom, ServerRandom, - PrivateKey}), + PrivateKey}), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = server, key_algorithm = dhe_psk, +key_exchange(#state{static_env = #static_env{role = server}, ssl_options = #ssl_options{psk_identity = PskIdentityHint}, - hashsign_algorithm = HashSignAlgo, - diffie_hellman_params = #'DHParameter'{} = Params, - private_key = PrivateKey, - connection_states = ConnectionStates0, - negotiated_version = Version + handshake_env = #handshake_env{kex_algorithm = dhe_psk, + diffie_hellman_params = #'DHParameter'{} = Params, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, + connection_states = ConnectionStates0 } = State0, Connection) -> DHKeys = public_key:generate_key(Params), #{security_parameters := SecParams} = @@ -1761,15 +1923,16 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - State = Connection:queue_handshake(Msg, State0), - State#state{diffie_hellman_keys = DHKeys}; -key_exchange(#state{role = server, key_algorithm = ecdhe_psk, + #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0), + State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}}; +key_exchange(#state{static_env = #static_env{role = server}, ssl_options = #ssl_options{psk_identity = PskIdentityHint}, - hashsign_algorithm = HashSignAlgo, - private_key = PrivateKey, + handshake_env = #handshake_env{kex_algorithm = ecdhe_psk, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, session = #session{ecc = ECCCurve}, - connection_states = ConnectionStates0, - negotiated_version = Version + connection_states = ConnectionStates0 } = State0, Connection) -> ECDHKeys = public_key:generate_key(ECCCurve), #{security_parameters := SecParams} = @@ -1782,17 +1945,19 @@ key_exchange(#state{role = server, key_algorithm = ecdhe_psk, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - State = Connection:queue_handshake(Msg, State0), - State#state{diffie_hellman_keys = ECDHKeys}; -key_exchange(#state{role = server, key_algorithm = rsa_psk, + #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0), + State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}}; +key_exchange(#state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{kex_algorithm = rsa_psk}, ssl_options = #ssl_options{psk_identity = undefined}} = State, _) -> State; -key_exchange(#state{role = server, key_algorithm = rsa_psk, +key_exchange(#state{static_env = #static_env{role = server}, ssl_options = #ssl_options{psk_identity = PskIdentityHint}, - hashsign_algorithm = HashSignAlgo, - private_key = PrivateKey, - connection_states = ConnectionStates0, - negotiated_version = Version + handshake_env = #handshake_env{kex_algorithm = rsa_psk, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, + connection_states = ConnectionStates0 } = State0, Connection) -> #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), @@ -1804,17 +1969,18 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk, ServerRandom, PrivateKey}), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = server, key_algorithm = Algo, +key_exchange(#state{static_env = #static_env{role = server}, ssl_options = #ssl_options{user_lookup_fun = LookupFun}, - hashsign_algorithm = HashSignAlgo, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + hashsign_algorithm = HashSignAlgo}, + connection_env = #connection_env{negotiated_version = Version, + private_key = PrivateKey}, session = #session{srp_username = Username}, - private_key = PrivateKey, - connection_states = ConnectionStates0, - negotiated_version = Version + connection_states = ConnectionStates0 } = State0, Connection) - when Algo == srp_dss; - Algo == srp_rsa; - Algo == srp_anon -> + when KexAlg == srp_dss; + KexAlg == srp_rsa; + KexAlg == srp_anon -> SrpParams = handle_srp_identity(Username, LookupFun), Keys = case generate_srp_server_keys(SrpParams, 0) of Alert = #alert{} -> @@ -1831,82 +1997,86 @@ key_exchange(#state{role = server, key_algorithm = Algo, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - State = Connection:queue_handshake(Msg, State0), - State#state{srp_params = SrpParams, - srp_keys = Keys}; -key_exchange(#state{role = client, - key_algorithm = rsa, - public_key_info = PublicKeyInfo, - negotiated_version = Version, - premaster_secret = PremasterSecret} = State0, Connection) -> + #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0), + State#state{handshake_env = HsEnv#handshake_env{srp_params = SrpParams, + kex_keys = Keys}}; +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = rsa, + public_key_info = PublicKeyInfo, + premaster_secret = PremasterSecret}, + connection_env = #connection_env{negotiated_version = Version} + } = State0, Connection) -> Msg = rsa_key_exchange(ssl:tls_version(Version), PremasterSecret, PublicKeyInfo), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = client, - key_algorithm = Algorithm, - negotiated_version = Version, - diffie_hellman_keys = {DhPubKey, _} - } = State0, Connection) - when Algorithm == dhe_dss; - Algorithm == dhe_rsa; - Algorithm == dh_anon -> +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + kex_keys = {DhPubKey, _}}, + connection_env = #connection_env{negotiated_version = Version} + } = State0, Connection) + when KexAlg == dhe_dss; + KexAlg == dhe_rsa; + KexAlg == dh_anon -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dh, DhPubKey}), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = client, - key_algorithm = Algorithm, - negotiated_version = Version, - session = Session, - diffie_hellman_keys = #'ECPrivateKey'{parameters = ECCurve} = Key} = State0, Connection) - when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa; - Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa; - Algorithm == ecdh_anon -> +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + kex_keys = #'ECPrivateKey'{parameters = ECCurve} = Key}, + connection_env = #connection_env{negotiated_version = Version}, + session = Session + } = State0, Connection) + when KexAlg == ecdhe_ecdsa; + KexAlg == ecdhe_rsa; + KexAlg == ecdh_ecdsa; + KexAlg == ecdh_rsa; + KexAlg == ecdh_anon -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdh, Key}), Connection:queue_handshake(Msg, State0#state{session = Session#session{ecc = ECCurve}}); -key_exchange(#state{role = client, - ssl_options = SslOpts, - key_algorithm = psk, - negotiated_version = Version} = State0, Connection) -> +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = psk}, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = SslOpts} = State0, Connection) -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {psk, SslOpts#ssl_options.psk_identity}), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = client, - ssl_options = SslOpts, - key_algorithm = dhe_psk, - negotiated_version = Version, - diffie_hellman_keys = {DhPubKey, _}} = State0, Connection) -> +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = dhe_psk, + kex_keys = {DhPubKey, _}}, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = SslOpts} = State0, Connection) -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = client, - ssl_options = SslOpts, - key_algorithm = ecdhe_psk, - negotiated_version = Version, - diffie_hellman_keys = ECDHKeys} = State0, Connection) -> +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = ecdhe_psk, + kex_keys = ECDHKeys}, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = SslOpts} = State0, Connection) -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdhe_psk, SslOpts#ssl_options.psk_identity, ECDHKeys}), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = client, - ssl_options = SslOpts, - key_algorithm = rsa_psk, - public_key_info = PublicKeyInfo, - negotiated_version = Version, - premaster_secret = PremasterSecret} +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = rsa_psk, + public_key_info = PublicKeyInfo, + premaster_secret = PremasterSecret}, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = SslOpts} = State0, Connection) -> Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity, PremasterSecret, PublicKeyInfo), Connection:queue_handshake(Msg, State0); -key_exchange(#state{role = client, - key_algorithm = Algorithm, - negotiated_version = Version, - srp_keys = {ClientPubKey, _}} +key_exchange(#state{static_env = #static_env{role = client}, + handshake_env = #handshake_env{kex_algorithm = KexAlg, + kex_keys = {ClientPubKey, _}}, + connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) - when Algorithm == srp_dss; - Algorithm == srp_rsa; - Algorithm == srp_anon -> + when KexAlg == srp_dss; + KexAlg == srp_rsa; + KexAlg == srp_anon -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {srp, ClientPubKey}), Connection:queue_handshake(Msg, State0). @@ -1943,18 +2113,24 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, rsa_psk_key_exchange(_, _, _, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)). -request_client_cert(#state{key_algorithm = Alg} = State, _) - when Alg == dh_anon; Alg == ecdh_anon; - Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk; - Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> +request_client_cert(#state{handshake_env = #handshake_env{kex_algorithm = Alg}} = State, _) + when Alg == dh_anon; + Alg == ecdh_anon; + Alg == psk; + Alg == dhe_psk; + Alg == ecdhe_psk; + Alg == rsa_psk; + Alg == srp_dss; + Alg == srp_rsa; + Alg == srp_anon -> State; -request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer, - signature_algs = SupportedHashSigns}, - connection_states = ConnectionStates0, - cert_db = CertDbHandle, - cert_db_ref = CertDbRef, - negotiated_version = Version} = State0, Connection) -> +request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle, + cert_db_ref = CertDbRef}, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = #ssl_options{verify = verify_peer, + signature_algs = SupportedHashSigns}, + connection_states = ConnectionStates0} = State0, Connection) -> #{security_parameters := #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates0, read), @@ -1971,7 +2147,7 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = State. calculate_master_secret(PremasterSecret, - #state{negotiated_version = Version, + #state{connection_env = #connection_env{negotiated_version = Version}, connection_states = ConnectionStates0, session = Session0} = State0, Connection, _Current, Next) -> @@ -1998,27 +2174,29 @@ finalize_handshake(State0, StateName, Connection) -> State = next_protocol(State2, Connection), finished(State, StateName, Connection). -next_protocol(#state{role = server} = State, _) -> +next_protocol(#state{static_env = #static_env{role = server}} = State, _) -> State; -next_protocol(#state{negotiated_protocol = undefined} = State, _) -> +next_protocol(#state{handshake_env = #handshake_env{negotiated_protocol = undefined}} = State, _) -> State; -next_protocol(#state{expecting_next_protocol_negotiation = false} = State, _) -> +next_protocol(#state{handshake_env = #handshake_env{expecting_next_protocol_negotiation = false}} = State, _) -> State; -next_protocol(#state{negotiated_protocol = NextProtocol} = State0, Connection) -> +next_protocol(#state{handshake_env = #handshake_env{negotiated_protocol = NextProtocol}} = State0, Connection) -> NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol), Connection:queue_handshake(NextProtocolMessage, State0). cipher_protocol(State, Connection) -> Connection:queue_change_cipher(#change_cipher_spec{}, State). -finished(#state{role = Role, negotiated_version = Version, +finished(#state{static_env = #static_env{role = Role}, + handshake_env = #handshake_env{tls_handshake_history = Hist}, + connection_env = #connection_env{negotiated_version = Version}, session = Session, - connection_states = ConnectionStates0, - tls_handshake_history = Handshake0} = State0, StateName, Connection) -> + connection_states = ConnectionStates0} = State0, + StateName, Connection) -> MasterSecret = Session#session.master_secret, Finished = ssl_handshake:finished(ssl:tls_version(Version), Role, get_current_prf(ConnectionStates0, write), - MasterSecret, Handshake0), + MasterSecret, Hist), ConnectionStates = save_verify_data(Role, Finished, ConnectionStates0, StateName), Connection:send_handshake(Finished, State0#state{connection_states = ConnectionStates}). @@ -2034,64 +2212,71 @@ save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbrev calculate_secret(#server_dh_params{dh_p = Prime, dh_g = Base, dh_y = ServerPublicDhKey} = Params, - State, Connection) -> + #state{handshake_env = HsEnv} = State, Connection) -> Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]), PremasterSecret = ssl_handshake:premaster_secret(ServerPublicDhKey, PrivateDhKey, Params), calculate_master_secret(PremasterSecret, - State#state{diffie_hellman_keys = Keys}, + State#state{handshake_env = HsEnv#handshake_env{kex_keys = Keys}}, Connection, certify, certify); calculate_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey}, - State=#state{session=Session}, Connection) -> + #state{handshake_env = HsEnv, + session = Session} = State, Connection) -> ECDHKeys = public_key:generate_key(ECCurve), PremasterSecret = ssl_handshake:premaster_secret(#'ECPoint'{point = ECServerPubKey}, ECDHKeys), calculate_master_secret(PremasterSecret, - State#state{diffie_hellman_keys = ECDHKeys, + State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}, session = Session#session{ecc = ECCurve}}, Connection, certify, certify); calculate_secret(#server_psk_params{ hint = IdentityHint}, - State, Connection) -> + #state{handshake_env = HsEnv} = State, Connection) -> %% store for later use - Connection:next_event(certify, no_record, State#state{psk_identity = IdentityHint}); + Connection:next_event(certify, no_record, + State#state{handshake_env = + HsEnv#handshake_env{server_psk_identity = IdentityHint}}); calculate_secret(#server_dhe_psk_params{ dh_params = #server_dh_params{dh_p = Prime, dh_g = Base}} = ServerKey, - #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = + #state{handshake_env = HsEnv, + ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State, Connection) -> Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]), PremasterSecret = ssl_handshake:premaster_secret(ServerKey, PrivateDhKey, PSKLookup), - calculate_master_secret(PremasterSecret, State#state{diffie_hellman_keys = Keys}, + calculate_master_secret(PremasterSecret, State#state{handshake_env = HsEnv#handshake_env{kex_keys = Keys}}, Connection, certify, certify); calculate_secret(#server_ecdhe_psk_params{ dh_params = #server_ecdh_params{curve = ECCurve}} = ServerKey, #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = - State=#state{session=Session}, Connection) -> + #state{handshake_env = HsEnv, + session = Session} = State, Connection) -> ECDHKeys = public_key:generate_key(ECCurve), PremasterSecret = ssl_handshake:premaster_secret(ServerKey, ECDHKeys, PSKLookup), calculate_master_secret(PremasterSecret, - State#state{diffie_hellman_keys = ECDHKeys, + State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}, session = Session#session{ecc = ECCurve}}, Connection, certify, certify); calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey, - #state{ssl_options = #ssl_options{srp_identity = SRPId}} = State, + #state{handshake_env = HsEnv, + ssl_options = #ssl_options{srp_identity = SRPId}} = State, Connection) -> Keys = generate_srp_client_keys(Generator, Prime, 0), PremasterSecret = ssl_handshake:premaster_secret(ServerKey, Keys, SRPId), - calculate_master_secret(PremasterSecret, State#state{srp_keys = Keys}, Connection, + calculate_master_secret(PremasterSecret, State#state{handshake_env = HsEnv#handshake_env{kex_keys = Keys}}, Connection, certify, certify). master_secret(#alert{} = Alert, _) -> Alert; -master_secret(PremasterSecret, #state{session = Session, - negotiated_version = Version, role = Role, +master_secret(PremasterSecret, #state{static_env = #static_env{role = Role}, + connection_env = #connection_env{negotiated_version = Version}, + session = Session, connection_states = ConnectionStates0} = State) -> case ssl_handshake:master_secret(ssl:tls_version(Version), PremasterSecret, ConnectionStates0, Role) of @@ -2151,7 +2336,7 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} {Record, State} = prepare_connection(State0#state{session = Session, connection_states = ConnectionStates}, Connection), - Connection:next_event(connection, Record, State); + Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close}]); cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State0, Connection) -> ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, @@ -2160,15 +2345,15 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0 finalize_handshake(State0#state{connection_states = ConnectionStates1, session = Session}, cipher, Connection), {Record, State} = prepare_connection(State1, Connection), - Connection:next_event(connection, Record, State, Actions). - -is_anonymous(Algo) when Algo == dh_anon; - Algo == ecdh_anon; - Algo == psk; - Algo == dhe_psk; - Algo == ecdhe_psk; - Algo == rsa_psk; - Algo == srp_anon -> + Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close} | Actions]). + +is_anonymous(KexAlg) when KexAlg == dh_anon; + KexAlg == ecdh_anon; + KexAlg == psk; + KexAlg == dhe_psk; + KexAlg == ecdhe_psk; + KexAlg == rsa_psk; + KexAlg == srp_anon -> true; is_anonymous(_) -> false. @@ -2282,6 +2467,30 @@ set_socket_opts(ConnectionCb, Transport, Socket, [{active, Active}| Opts], SockO Active == false -> set_socket_opts(ConnectionCb, Transport, Socket, Opts, SockOpts#socket_options{active = Active}, Other); +set_socket_opts(ConnectionCb, Transport, Socket, [{active, Active1} = Opt| Opts], + SockOpts=#socket_options{active = Active0}, Other) + when Active1 >= -32768, Active1 =< 32767 -> + Active = if + is_integer(Active0), Active0 + Active1 < -32768 -> + error; + is_integer(Active0), Active0 + Active1 =< 0 -> + false; + is_integer(Active0), Active0 + Active1 > 32767 -> + error; + Active1 =< 0 -> + false; + is_integer(Active0) -> + Active0 + Active1; + true -> + Active1 + end, + case Active of + error -> + {{error, {options, {socket_options, Opt}} }, SockOpts}; + _ -> + set_socket_opts(ConnectionCb, Transport, Socket, Opts, + SockOpts#socket_options{active = Active}, Other) + end; set_socket_opts(_,_, _, [{active, _} = Opt| _], SockOpts, _) -> {{error, {options, {socket_options, Opt}} }, SockOpts}; set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other) -> @@ -2325,18 +2534,18 @@ handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) -> %% No trusted certs specified ok; -handle_trusted_certs_db(#state{cert_db_ref = Ref, - cert_db = CertDb, - ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined -> +handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref, + cert_db = CertDb}, + ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined -> %% Certs provided as DER directly can not be shared %% with other connections and it is safe to delete them when the connection ends. ssl_pkix_db:remove_trusted_certs(Ref, CertDb); -handle_trusted_certs_db(#state{file_ref_db = undefined}) -> +handle_trusted_certs_db(#state{static_env = #static_env{file_ref_db = undefined}}) -> %% Something went wrong early (typically cacertfile does not %% exist) so there is nothing to handle ok; -handle_trusted_certs_db(#state{cert_db_ref = Ref, - file_ref_db = RefDb, +handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref, + file_ref_db = RefDb}, ssl_options = #ssl_options{cacertfile = File}}) -> case ssl_pkix_db:ref_count(Ref, RefDb, -1) of 0 -> @@ -2345,7 +2554,7 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref, ok end. -prepare_connection(#state{renegotiation = Renegotiate, +prepare_connection(#state{handshake_env = #handshake_env{renegotiation = Renegotiate}, start_or_recv_from = RecvFrom} = State0, Connection) when Renegotiate =/= {false, first}, RecvFrom =/= undefined -> @@ -2355,42 +2564,54 @@ prepare_connection(State0, Connection) -> State = Connection:reinit(State0), {no_record, ack_connection(State)}. -ack_connection(#state{renegotiation = {true, Initiater}} = State) when Initiater == peer; - Initiater == internal -> - State#state{renegotiation = undefined}; -ack_connection(#state{renegotiation = {true, From}} = State) -> +ack_connection(#state{handshake_env = #handshake_env{renegotiation = {true, Initiater}} = HsEnv} = State) when Initiater == peer; + Initiater == internal -> + State#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}}; +ack_connection(#state{handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv} = State) -> gen_statem:reply(From, ok), - State#state{renegotiation = undefined}; -ack_connection(#state{renegotiation = {false, first}, - start_or_recv_from = StartFrom, - timer = Timer} = State) when StartFrom =/= undefined -> + State#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}}; +ack_connection(#state{handshake_env = #handshake_env{renegotiation = {false, first}} = HsEnv, + start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined -> gen_statem:reply(StartFrom, connected), - cancel_timer(Timer), - State#state{renegotiation = undefined, - start_or_recv_from = undefined, timer = undefined}; + State#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}, + start_or_recv_from = undefined}; ack_connection(State) -> State. -cancel_timer(undefined) -> - ok; -cancel_timer(Timer) -> - erlang:cancel_timer(Timer), - ok. - session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) -> Session#session{ecc = ECCurve}; session_handle_params(_, Session) -> Session. -register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> +handle_session(Role = server, #ssl_options{reuse_sessions = true} = SslOpts, + Host, Port, Session0) -> + register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, true); +handle_session(Role = client, #ssl_options{verify = verify_peer, + reuse_sessions = Reuse} = SslOpts, + Host, Port, Session0) when Reuse =/= false -> + register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, reg_type(Reuse)); +handle_session(server, _, Host, Port, Session) -> + %% Remove "session of type new" entry from session DB + ssl_manager:invalidate_session(Host, Port, Session), + Session; +handle_session(client, _,_,_, Session) -> + %% In client case there is no entry yet, so nothing to remove + Session. + +reg_type(save) -> + true; +reg_type(true) -> + unique. + +register_session(client, Host, Port, #session{is_resumable = new} = Session0, Save) -> Session = Session0#session{is_resumable = true}, - ssl_manager:register_session(Host, Port, Session), + ssl_manager:register_session(Host, Port, Session, Save), Session; -register_session(server, _, Port, #session{is_resumable = new} = Session0) -> +register_session(server, _, Port, #session{is_resumable = new} = Session0, _) -> Session = Session0#session{is_resumable = true}, ssl_manager:register_session(Port, Session), Session; -register_session(_, _, _, Session) -> +register_session(_, _, _, Session, _) -> Session. %% Already registered host_id(client, _Host, #ssl_options{server_name_indication = Hostname}) when is_list(Hostname) -> @@ -2399,19 +2620,21 @@ host_id(_, Host, _) -> Host. handle_new_session(NewId, CipherSuite, Compression, - #state{session = Session0, - protocol_cb = Connection} = State0) -> + #state{static_env = #static_env{protocol_cb = Connection}, + session = Session0 + } = State0) -> Session = Session0#session{session_id = NewId, cipher_suite = CipherSuite, compression_method = Compression}, Connection:next_event(certify, no_record, State0#state{session = Session}). -handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, - negotiated_version = Version, - host = Host, port = Port, - protocol_cb = Connection, - session_cache = Cache, - session_cache_cb = CacheCb} = State) -> +handle_resumed_session(SessId, #state{static_env = #static_env{host = Host, + port = Port, + protocol_cb = Connection, + session_cache = Cache, + session_cache_cb = CacheCb}, + connection_env = #connection_env{negotiated_version = Version}, + connection_states = ConnectionStates0} = State) -> Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), case ssl_handshake:master_secret(ssl:tls_version(Version), Session, ConnectionStates0, client) of @@ -2468,8 +2691,8 @@ ssl_options_list([Key | Keys], [Value | Values], Acc) -> handle_active_option(false, connection = StateName, To, Reply, State) -> hibernate_after(StateName, State, [{reply, To, Reply}]); -handle_active_option(_, connection = StateName0, To, Reply, #state{protocol_cb = Connection, - user_data_buffer = <<>>} = State0) -> +handle_active_option(_, connection = StateName0, To, Reply, #state{static_env = #static_env{protocol_cb = Connection}, + user_data_buffer = {_,0,_}} = State0) -> case Connection:next_event(StateName0, no_record, State0) of {next_state, StateName, State} -> hibernate_after(StateName, State, [{reply, To, Reply}]); @@ -2478,12 +2701,13 @@ handle_active_option(_, connection = StateName0, To, Reply, #state{protocol_cb = {stop, _, _} = Stop -> Stop end; -handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = <<>>} = State) -> +handle_active_option(_, StateName, To, Reply, #state{user_data_buffer = {_,0,_}} = State) -> %% Active once already set {next_state, StateName, State, [{reply, To, Reply}]}; -%% user_data_buffer =/= <<>> -handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection} = State0) -> +%% user_data_buffer nonempty +handle_active_option(_, StateName0, To, Reply, + #state{static_env = #static_env{protocol_cb = Connection}} = State0) -> case read_application_data(<<>>, State0) of {stop, _, _} = Stop -> Stop; @@ -2501,33 +2725,25 @@ handle_active_option(_, StateName0, To, Reply, #state{protocol_cb = Connection} %% Picks ClientData -get_data(_, _, <<>>) -> - {more, <<>>}; -%% Recv timed out save buffer data until next recv -get_data(#socket_options{active=false}, undefined, Buffer) -> - {passive, Buffer}; -get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) +get_data(#socket_options{active=false}, undefined, _Bin) -> + %% Recv timed out save buffer data until next recv + passive; +get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Bin) when Raw =:= raw; Raw =:= 0 -> %% Raw Mode - if - Active =/= false orelse BytesToRead =:= 0 -> + case Bin of + <<_/binary>> when Active =/= false orelse BytesToRead =:= 0 -> %% Active true or once, or passive mode recv(0) - {ok, Buffer, <<>>}; - byte_size(Buffer) >= BytesToRead -> + {ok, Bin, <<>>}; + <<Data:BytesToRead/binary, Rest/binary>> -> %% Passive Mode, recv(Bytes) - <<Data:BytesToRead/binary, Rest/binary>> = Buffer, - {ok, Data, Rest}; - true -> + {ok, Data, Rest}; + <<_/binary>> -> %% Passive Mode not enough data - {more, Buffer} + {more, BytesToRead} end; -get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) -> +get_data(#socket_options{packet=Type, packet_size=Size}, _, Bin) -> PacketOpts = [{packet_size, Size}], - case decode_packet(Type, Buffer, PacketOpts) of - {more, _} -> - {more, Buffer}; - Decoded -> - Decoded - end. + decode_packet(Type, Bin, PacketOpts). decode_packet({http, headers}, Buffer, PacketOpts) -> decode_packet(httph, Buffer, PacketOpts); @@ -2545,24 +2761,39 @@ decode_packet(Type, Buffer, PacketOpts) -> %% Note that if the user has explicitly configured the socket to expect %% HTTP headers using the {packet, httph} option, we don't do any automatic %% switching of states. -deliver_app_data(CPids, Transport, Socket, SOpts = #socket_options{active=Active, packet=Type}, - Data, Pid, From, Tracker, Connection) -> - send_or_reply(Active, Pid, From, - format_reply(CPids, Transport, Socket, SOpts, Data, Tracker, Connection)), - SO = case Data of - {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), - ((Type =:= http) or (Type =:= http_bin)) -> - SOpts#socket_options{packet={Type, headers}}; - http_eoh when tuple_size(Type) =:= 2 -> - % End of headers - expect another Request/Response line - {Type1, headers} = Type, - SOpts#socket_options{packet=Type1}; - _ -> - SOpts - end, +deliver_app_data( + CPids, Transport, Socket, + #socket_options{active=Active, packet=Type} = SOpts, + Data, Pid, From, Tracker, Connection) -> + %% + send_or_reply( + Active, Pid, From, + format_reply( + CPids, Transport, Socket, SOpts, Data, Tracker, Connection)), + SO = + case Data of + {P, _, _, _} + when ((P =:= http_request) or (P =:= http_response)), + ((Type =:= http) or (Type =:= http_bin)) -> + SOpts#socket_options{packet={Type, headers}}; + http_eoh when tuple_size(Type) =:= 2 -> + %% End of headers - expect another Request/Response line + {Type1, headers} = Type, + SOpts#socket_options{packet=Type1}; + _ -> + SOpts + end, case Active of once -> SO#socket_options{active=false}; + 1 -> + send_user( + Pid, + format_passive( + CPids, Transport, Socket, Tracker, Connection)), + SO#socket_options{active=false}; + N when is_integer(N) -> + SO#socket_options{active=N - 1}; _ -> SO end. @@ -2572,7 +2803,7 @@ format_reply(_, _, _,#socket_options{active = false, mode = Mode, packet = Packe {ok, do_format_reply(Mode, Packet, Header, Data)}; format_reply(CPids, Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet, header = Header}, Data, Tracker, Connection) -> - {ssl, Connection:socket(CPids, Transport, Socket, Connection, Tracker), + {ssl, Connection:socket(CPids, Transport, Socket, Tracker), do_format_reply(Mode, Packet, Header, Data)}. deliver_packet_error(CPids, Transport, Socket, @@ -2584,7 +2815,7 @@ format_packet_error(_, _, _,#socket_options{active = false, mode = Mode}, Data, {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}; format_packet_error(CPids, Transport, Socket, #socket_options{active = _, mode = Mode}, Data, Tracker, Connection) -> - {ssl_error, Connection:socket(CPids, Transport, Socket, Connection, Tracker), + {ssl_error, Connection:socket(CPids, Transport, Socket, Tracker), {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}. do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode @@ -2599,6 +2830,9 @@ do_format_reply(list, Packet, _, Data) do_format_reply(list, _,_, Data) -> binary_to_list(Data). +format_passive(CPids, Transport, Socket, Tracker, Connection) -> + {ssl_passive, Connection:socket(CPids, Transport, Socket, Tracker)}. + header(0, <<>>) -> <<>>; header(_, <<>>) -> @@ -2640,12 +2874,10 @@ alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Con case ssl_alert:reason_code(Alert, Role) of closed -> send_or_reply(Active, Pid, From, - {ssl_closed, Connection:socket(Pids, - Transport, Socket, Connection, Tracker)}); + {ssl_closed, Connection:socket(Pids, Transport, Socket, Tracker)}); ReasonCode -> send_or_reply(Active, Pid, From, - {ssl_error, Connection:socket(Pids, - Transport, Socket, Connection, Tracker), ReasonCode}) + {ssl_error, Connection:socket(Pids, Transport, Socket, Tracker), ReasonCode}) end. log_alert(true, Role, ProtocolName, StateName, #alert{role = Role} = Alert) -> @@ -2664,7 +2896,9 @@ invalidate_session(server, _, Port, Session) -> handle_sni_extension(undefined, State) -> State; -handle_sni_extension(#sni{hostname = Hostname}, State0) -> +handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{role = Role} = InitStatEnv0, + handshake_env = HsEnv, + connection_env = CEnv} = State0) -> NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname), case NewOptions of undefined -> @@ -2678,19 +2912,21 @@ handle_sni_extension(#sni{hostname = Hostname}, State0) -> private_key := Key, dh_params := DHParams, own_certificate := OwnCert}} = - ssl_config:init(NewOptions, State0#state.role), + ssl_config:init(NewOptions, Role), State0#state{ session = State0#state.session#session{own_certificate = OwnCert}, - file_ref_db = FileRefHandle, - cert_db_ref = Ref, - cert_db = CertDbHandle, - crl_db = CRLDbHandle, - session_cache = CacheHandle, - private_key = Key, - diffie_hellman_params = DHParams, - ssl_options = NewOptions, - sni_hostname = Hostname - } + static_env = InitStatEnv0#static_env{ + file_ref_db = FileRefHandle, + cert_db_ref = Ref, + cert_db = CertDbHandle, + crl_db = CRLDbHandle, + session_cache = CacheHandle + }, + connection_env = CEnv#connection_env{private_key = Key}, + ssl_options = NewOptions, + handshake_env = HsEnv#handshake_env{sni_hostname = Hostname, + diffie_hellman_params = DHParams} + } end. update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) -> @@ -2713,14 +2949,3 @@ new_emulated([], EmOpts) -> EmOpts; new_emulated(NewEmOpts, _) -> NewEmOpts. - -stop(Reason, State) -> - {stop, Reason, State}. - -stop_and_reply(Reason, Replies, State) -> - {stop_and_reply, Reason, Replies, State}. - -is_dist_up(#{dist_handle := Handle}) when Handle =/= undefined -> - true; -is_dist_up(_) -> - false. diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index 66e3182313..9efd65b2d2 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2018. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -33,71 +33,90 @@ -include("ssl_cipher.hrl"). -include_lib("public_key/include/public_key.hrl"). +-record(static_env, { + role :: client | server, + transport_cb :: atom(), % callback module + protocol_cb :: tls_connection | dtls_connection, + data_tag :: atom(), % ex tcp. + close_tag :: atom(), % ex tcp_closed + error_tag :: atom(), % ex tcp_error + host :: string() | inet:ip_address(), + port :: integer(), + socket :: port() | tuple(), %% TODO: dtls socket + cert_db :: reference() | 'undefined', + session_cache :: db_handle(), + session_cache_cb :: atom(), + crl_db :: term(), + file_ref_db :: db_handle(), + cert_db_ref :: certdb_ref() | 'undefined', + tracker :: pid() | 'undefined' %% Tracker process for listen socket + }). + +-record(handshake_env, { + client_hello_version :: ssl_record:ssl_version() | 'undefined', + unprocessed_handshake_events = 0 :: integer(), + tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout() + | 'undefined', + expecting_finished = false ::boolean(), + renegotiation :: undefined | {boolean(), From::term() | internal | peer}, + allow_renegotiate = true ::boolean(), + %% Ext handling + hello, %%:: #client_hello{} | #server_hello{} + sni_hostname = undefined, + expecting_next_protocol_negotiation = false ::boolean(), + next_protocol = undefined :: undefined | binary(), + negotiated_protocol, + hashsign_algorithm = {undefined, undefined}, + cert_hashsign_algorithm = {undefined, undefined}, + %% key exchange + kex_algorithm :: ssl:kex_algo(), + kex_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(), + diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(), + srp_params :: #srp_user{} | secret_printout() | 'undefined', + public_key_info :: ssl_handshake:public_key_info() | 'undefined', + premaster_secret :: binary() | secret_printout() | 'undefined', + server_psk_identity :: binary() | 'undefined' % server psk identity hint + }). + +-record(connection_env, { + user_application :: {Monitor::reference(), User::pid()}, + downgrade, + terminated = false ::boolean() | closed, + negotiated_version :: ssl_record:ssl_version() | 'undefined', + erl_dist_handle = undefined :: erlang:dist_handle() | 'undefined', + private_key :: public_key:private_key() | secret_printout() | 'undefined' + }). + -record(state, { - role :: client | server, - user_application :: {Monitor::reference(), User::pid()}, - transport_cb :: atom(), % callback module - protocol_cb :: tls_connection | dtls_connection, - data_tag :: atom(), % ex tcp. - close_tag :: atom(), % ex tcp_closed - error_tag :: atom(), % ex tcp_error - host :: string() | inet:ip_address(), - port :: integer(), - socket :: port() | tuple(), %% TODO: dtls socket - sender :: pid() | undefined, - ssl_options :: #ssl_options{}, - socket_options :: #socket_options{}, - connection_states :: ssl_record:connection_states() | secret_printout(), - protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl - unprocessed_handshake_events = 0 :: integer(), - tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout() - | 'undefined', - cert_db :: reference() | 'undefined', - session :: #session{} | secret_printout(), - session_cache :: db_handle(), - session_cache_cb :: atom(), - crl_db :: term(), - negotiated_version :: ssl_record:ssl_version() | 'undefined', - client_hello_version :: ssl_record:ssl_version() | 'undefined', - client_certificate_requested = false :: boolean(), - key_algorithm :: ssl_cipher_format:key_algo(), - hashsign_algorithm = {undefined, undefined}, - cert_hashsign_algorithm = {undefined, undefined}, - public_key_info :: ssl_handshake:public_key_info() | 'undefined', - private_key :: public_key:private_key() | secret_printout() | 'undefined', - diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(), - diffie_hellman_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(), - psk_identity :: binary() | 'undefined', % server psk identity hint - srp_params :: #srp_user{} | secret_printout() | 'undefined', - srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout() | 'undefined', - premaster_secret :: binary() | secret_printout() | 'undefined', - file_ref_db :: db_handle(), - cert_db_ref :: certdb_ref() | 'undefined', - bytes_to_read :: undefined | integer(), %% bytes to read in passive mode - user_data_buffer :: undefined | binary() | secret_printout(), - erl_dist_data = #{} :: map(), - renegotiation :: undefined | {boolean(), From::term() | internal | peer}, - start_or_recv_from :: term(), - timer :: undefined | reference(), % start_or_recive_timer - %%send_queue :: queue:queue(), - hello, %%:: #client_hello{} | #server_hello{}, - terminated = false ::boolean(), - allow_renegotiate = true ::boolean(), - expecting_next_protocol_negotiation = false ::boolean(), - expecting_finished = false ::boolean(), - next_protocol = undefined :: undefined | binary(), - negotiated_protocol, - tracker :: pid() | 'undefined', %% Tracker process for listen socket - sni_hostname = undefined, - downgrade, - flight_buffer = [] :: list() | map(), %% Buffer of TLS/DTLS records, used during the TLS handshake - %% to when possible pack more than one TLS record into the - %% underlaying packet format. Introduced by DTLS - RFC 4347. - %% The mecahnism is also usefull in TLS although we do not - %% need to worry about packet loss in TLS. In DTLS we need to track DTLS handshake seqnr - flight_state = reliable, %% reliable | {retransmit, integer()}| {waiting, ref(), integer()} - last two is used in DTLS over udp. - protocol_specific = #{} :: map() - }). + static_env :: #static_env{}, + connection_env :: #connection_env{} | secret_printout(), + ssl_options :: #ssl_options{}, + socket_options :: #socket_options{}, + + %% Hanshake %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + handshake_env :: #handshake_env{} | secret_printout(), + %% Buffer of TLS/DTLS records, used during the TLS + %% handshake to when possible pack more than one TLS + %% record into the underlaying packet + %% format. Introduced by DTLS - RFC 4347. The + %% mecahnism is also usefull in TLS although we do not + %% need to worry about packet loss in TLS. In DTLS we + %% need to track DTLS handshake seqnr + flight_buffer = [] :: list() | map(), + client_certificate_requested = false :: boolean(), + protocol_specific = #{} :: map(), + session :: #session{} | secret_printout(), + %% Data shuffling %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + connection_states :: ssl_record:connection_states() | secret_printout(), + protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hr + user_data_buffer :: undefined | {[binary()],non_neg_integer(),[binary()]} | secret_printout(), + bytes_to_read :: undefined | integer(), %% bytes to read in passive mode + + %% recv and start handling + start_or_recv_from :: term() + }). + + -define(DEFAULT_DIFFIE_HELLMAN_PARAMS, #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). diff --git a/lib/ssl/src/ssl_crl_cache.erl b/lib/ssl/src/ssl_crl_cache.erl index 9c1af86eeb..841620ce57 100644 --- a/lib/ssl/src/ssl_crl_cache.erl +++ b/lib/ssl/src/ssl_crl_cache.erl @@ -28,6 +28,10 @@ -behaviour(ssl_crl_cache_api). +-export_type([crl_src/0, uri/0]). +-type crl_src() :: {file, file:filename()} | {der, public_key:der_encoded()}. +-type uri() :: uri_string:uri_string(). + -export([lookup/3, select/2, fresh_crl/2]). -export([insert/1, insert/2, delete/1]). diff --git a/lib/ssl/src/ssl_crl_cache_api.erl b/lib/ssl/src/ssl_crl_cache_api.erl index d5380583e7..8a750b3929 100644 --- a/lib/ssl/src/ssl_crl_cache_api.erl +++ b/lib/ssl/src/ssl_crl_cache_api.erl @@ -21,12 +21,15 @@ %% -module(ssl_crl_cache_api). - -include_lib("public_key/include/public_key.hrl"). --type db_handle() :: term(). --type issuer_name() :: {rdnSequence, [#'AttributeTypeAndValue'{}]}. +-export_type([dist_point/0, crl_cache_ref/0]). + +-type crl_cache_ref() :: any(). +-type issuer_name() :: {rdnSequence,[#'AttributeTypeAndValue'{}]}. +-type dist_point() :: #'DistributionPoint'{}. --callback lookup(#'DistributionPoint'{}, issuer_name(), db_handle()) -> not_available | [public_key:der_encoded()]. --callback select(issuer_name(), db_handle()) -> [public_key:der_encoded()]. --callback fresh_crl(#'DistributionPoint'{}, public_key:der_encoded()) -> public_key:der_encoded(). + +-callback lookup(dist_point(), issuer_name(), crl_cache_ref()) -> not_available | [public_key:der_encoded()]. +-callback select(issuer_name(), crl_cache_ref()) -> [public_key:der_encoded()]. +-callback fresh_crl(dist_point(), public_key:der_encoded()) -> public_key:der_encoded(). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 14df1d2e02..9ba62b3a12 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -610,7 +610,7 @@ encode_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>); encode_hello_extensions([#srp{username = UserName} | Rest], Acc) -> SRPLen = byte_size(UserName), - Len = SRPLen + 2, + Len = SRPLen + 1, encode_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); encode_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> @@ -729,7 +729,7 @@ decode_hello_extensions(Extensions) -> dec_hello_extensions(Extensions, #hello_extensions{}). %%-------------------------------------------------------------------- --spec decode_server_key(binary(), ssl_cipher_format:key_algo(), ssl_record:ssl_version()) -> +-spec decode_server_key(binary(), ssl:kex_algo(), ssl_record:ssl_version()) -> #server_key_params{}. %% %% Description: Decode server_key data and return appropriate type @@ -738,7 +738,7 @@ decode_server_key(ServerKey, Type, Version) -> dec_server_key(ServerKey, key_exchange_alg(Type), Version). %%-------------------------------------------------------------------- --spec decode_client_key(binary(), ssl_cipher_format:key_algo(), ssl_record:ssl_version()) -> +-spec decode_client_key(binary(), ssl:kex_algo(), ssl_record:ssl_version()) -> #encrypted_premaster_secret{} | #client_diffie_hellman_public{} | #client_ec_diffie_hellman_public{} @@ -972,34 +972,30 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, #session{cipher_suite = NegotiatedCipherSuite, compression_method = Compression} = Session0, ConnectionStates0, Renegotiation) -> - Session = handle_srp_extension(SRP, Session0), - ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, Info, - Random, NegotiatedCipherSuite, + Session = handle_srp_extension(SRP, Session0), + ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, Info, + Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, - ConnectionStates0, Renegotiation, SecureRenegotation), - - ServerHelloExtensions = #hello_extensions{ - renegotiation_info = renegotiation_info(RecordCB, server, - ConnectionStates, Renegotiation), - ec_point_formats = server_ecc_extension(Version, ECCFormat) - }, - + ConnectionStates0, Renegotiation, SecureRenegotation), + + ServerHelloExtensions = #hello_extensions{ + renegotiation_info = renegotiation_info(RecordCB, server, + ConnectionStates, Renegotiation), + ec_point_formats = server_ecc_extension(Version, ECCFormat) + }, + %% If we receive an ALPN extension and have ALPN configured for this connection, %% we handle it. Otherwise we check for the NPN extension. if ALPN =/= undefined, ALPNPreferredProtocols =/= undefined -> - case handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)) of - #alert{} = Alert -> - Alert; - Protocol -> - {Session, ConnectionStates, Protocol, - ServerHelloExtensions#hello_extensions{alpn=encode_alpn([Protocol], Renegotiation)}} - end; + Protocol = handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)), + {Session, ConnectionStates, Protocol, + ServerHelloExtensions#hello_extensions{alpn=encode_alpn([Protocol], Renegotiation)}}; true -> - ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), + ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), {Session, ConnectionStates, undefined, - ServerHelloExtensions#hello_extensions{next_protocol_negotiation= - encode_protocols_advertised_on_server(ProtocolsToAdvertise)}} + ServerHelloExtensions#hello_extensions{next_protocol_negotiation= + encode_protocols_advertised_on_server(ProtocolsToAdvertise)}} end. handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, @@ -1022,12 +1018,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, [Protocol] when not Renegotiation -> {ConnectionStates, alpn, Protocol}; undefined -> - case handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation) of - #alert{} = Alert -> - Alert; - Protocol -> - {ConnectionStates, npn, Protocol} - end; + Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation), + {ConnectionStates, npn, Protocol}; {error, Reason} -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); [] -> @@ -1941,7 +1933,7 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar RenegotiateInfo}}); dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc) - when Len == SRPLen + 2 -> + when Len == SRPLen + 1 -> dec_hello_extensions(Rest, Acc#hello_extensions{srp = #srp{username = SRP}}); dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), @@ -2189,30 +2181,26 @@ filter_unavailable_ecc_suites(_, Suites) -> handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, ConnectionStates0, Renegotiation, SecureRenegotation) -> - case handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, - Renegotiation, SecureRenegotation, - ClientCipherSuites) of - {ok, ConnectionStates} -> - hello_pending_connection_states(RecordCB, Role, - Version, - NegotiatedCipherSuite, - Random, - Compression, - ConnectionStates); - #alert{} = Alert -> - throw(Alert) - end. + {ok, ConnectionStates} = handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, + ClientCipherSuites), + hello_pending_connection_states(RecordCB, Role, + Version, + NegotiatedCipherSuite, + Random, + Compression, + ConnectionStates). %% Receive protocols, choose one from the list, return it. handle_alpn_extension(_, {error, Reason}) -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason)); handle_alpn_extension([], _) -> - ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL); + throw(?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL)); handle_alpn_extension([ServerProtocol|Tail], ClientProtocols) -> - case lists:member(ServerProtocol, ClientProtocols) of - true -> ServerProtocol; - false -> handle_alpn_extension(Tail, ClientProtocols) - end. + case lists:member(ServerProtocol, ClientProtocols) of + true -> ServerProtocol; + false -> handle_alpn_extension(Tail, ClientProtocols) + end. handle_next_protocol(undefined, _NextProtocolSelector, _Renegotiating) -> @@ -2225,14 +2213,14 @@ handle_next_protocol(#next_protocol_negotiation{} = NextProtocols, true -> select_next_protocol(decode_next_protocols(NextProtocols), NextProtocolSelector); false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension)) end. handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, SslOpts)-> case handle_next_protocol_on_server(NextProtocolNegotiation, Renegotiation, SslOpts) of #alert{} = Alert -> - Alert; + throw(Alert); ProtocolsToAdvertise -> ProtocolsToAdvertise end. @@ -2428,14 +2416,14 @@ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_co true -> {ok, ConnectionStates}; false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation)) end; handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify}, ConnectionStates, true, _, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); false -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), Data = maps:get(client_verify_data, ConnectionState), @@ -2443,7 +2431,7 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co true -> {ok, ConnectionStates}; false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation)) end end; @@ -2453,7 +2441,7 @@ handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, S handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); false -> handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation) end. @@ -2462,9 +2450,9 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of {_, true} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure)); {true, false} -> - ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION); + throw(?ALERT_REC(?FATAL, ?NO_RENEGOTIATION)); {false, false} -> {ok, ConnectionStates} end. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 63e751440a..57c72aa122 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -31,8 +31,6 @@ -type reply() :: term(). -type msg() :: term(). -type from() :: term(). --type host() :: inet:ip_address() | inet:hostname(). --type session_id() :: 0 | binary(). -type certdb_ref() :: reference(). -type db_handle() :: term(). -type der_cert() :: binary(). @@ -111,10 +109,10 @@ %% Local policy for the server if it want's to reuse the session %% or not. Defaluts to allways returning true. %% fun(SessionId, PeerCert, Compression, CipherSuite) -> boolean() - reuse_session, + reuse_session :: fun() | binary() | undefined, %% Server side is a fun() %% If false sessions will never be reused, if true they %% will be reused if possible. - reuse_sessions :: boolean(), + reuse_sessions :: boolean() | save, %% Only client side can use value save renegotiate_at, secure_renegotiate, client_renegotiation, @@ -148,6 +146,8 @@ max_handshake_size :: integer(), handshake, customize_hostname_check + %% , + %% save_session :: boolean() }). -record(socket_options, diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 4b735b2400..c56675b691 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -30,7 +30,7 @@ connection_init/3, cache_pem_file/2, lookup_trusted_cert/4, new_session_id/1, clean_cert_db/2, - register_session/2, register_session/3, invalidate_session/2, + register_session/2, register_session/4, invalidate_session/2, insert_crls/2, insert_crls/3, delete_crls/1, delete_crls/2, invalidate_session/3, name/1]). @@ -42,6 +42,8 @@ -include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). +-include("ssl_api.hrl"). + -include_lib("kernel/include/file.hrl"). -record(state, { @@ -148,7 +150,7 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> ssl_pkix_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec new_session_id(integer()) -> session_id(). +-spec new_session_id(integer()) -> ssl:session_id(). %% %% Description: Creates a session id for the server. %%-------------------------------------------------------------------- @@ -170,9 +172,11 @@ clean_cert_db(Ref, File) -> %% %% Description: Make the session available for reuse. %%-------------------------------------------------------------------- --spec register_session(host(), inet:port_number(), #session{}) -> ok. -register_session(Host, Port, Session) -> - cast({register_session, Host, Port, Session}). +-spec register_session(ssl:host(), inet:port_number(), #session{}, unique | true) -> ok. +register_session(Host, Port, Session, true) -> + call({register_session, Host, Port, Session}); +register_session(Host, Port, Session, unique = Save) -> + cast({register_session, Host, Port, Session, Save}). -spec register_session(inet:port_number(), #session{}) -> ok. register_session(Port, Session) -> @@ -183,7 +187,7 @@ register_session(Port, Session) -> %% a the session has been marked "is_resumable = false" for some while %% it will be safe to remove the data from the session database. %%-------------------------------------------------------------------- --spec invalidate_session(host(), inet:port_number(), #session{}) -> ok. +-spec invalidate_session(ssl:host(), inet:port_number(), #session{}) -> ok. invalidate_session(Host, Port, Session) -> load_mitigation(), cast({invalidate_session, Host, Port, Session}). @@ -301,7 +305,10 @@ handle_call({{new_session_id, Port}, _}, _, #state{session_cache_cb = CacheCb, session_cache_server = Cache} = State) -> Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), - {reply, Id, State}. + {reply, Id, State}; +handle_call({{register_session, Host, Port, Session},_}, _, State0) -> + State = client_register_session(Host, Port, Session, State0), + {reply, ok, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -311,8 +318,12 @@ handle_call({{new_session_id, Port}, _}, %% %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast({register_session, Host, Port, Session}, State0) -> - State = ssl_client_register_session(Host, Port, Session, State0), +handle_cast({register_session, Host, Port, Session, unique}, State0) -> + State = client_register_unique_session(Host, Port, Session, State0), + {noreply, State}; + +handle_cast({register_session, Host, Port, Session, true}, State0) -> + State = client_register_session(Host, Port, Session, State0), {noreply, State}; handle_cast({register_session, Port, Session}, State0) -> @@ -540,10 +551,10 @@ clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File) -> ok end. -ssl_client_register_session(Host, Port, Session, #state{session_cache_client = Cache, - session_cache_cb = CacheCb, - session_cache_client_max = Max, - session_client_invalidator = Pid0} = State) -> +client_register_unique_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> TimeStamp = erlang:monotonic_time(), NewSession = Session#session{time_stamp = TimeStamp}, @@ -557,6 +568,17 @@ ssl_client_register_session(Host, Port, Session, #state{session_cache_client = C register_unique_session(Sessions, NewSession, {Host, Port}, State) end. +client_register_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + Pid = do_register_session({{Host, Port}, + NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid}. + server_register_session(Port, Session, #state{session_cache_server_max = Max, session_cache_server = Cache, session_cache_cb = CacheCb, diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index b9d1320ef3..1a36b2dba8 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2018. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -45,14 +45,16 @@ -export([compress/3, uncompress/3, compressions/0]). %% Payload encryption/decryption --export([cipher/4, decipher/4, cipher_aead/4, decipher_aead/5, is_correct_mac/2, nonce_seed/3]). +-export([cipher/4, cipher/5, decipher/4, + cipher_aead/4, cipher_aead/5, decipher_aead/5, + is_correct_mac/2, nonce_seed/3]). -export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]). -type ssl_version() :: {integer(), integer()}. -type ssl_atom_version() :: tls_record:tls_atom_version(). --type connection_states() :: term(). %% Map --type connection_state() :: term(). %% Map +-type connection_states() :: map(). %% Map +-type connection_state() :: map(). %% Map %%==================================================================== %% Connection state handling @@ -302,27 +304,49 @@ cipher(Version, Fragment, #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo} } = WriteState0, MacHash) -> - + %% {CipherFragment, CipherS1} = ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version), {CipherFragment, WriteState0#{cipher_state => CipherS1}}. + +%%-------------------------------------------------------------------- +-spec cipher(ssl_version(), iodata(), #cipher_state{}, MacHash::binary(), #security_parameters{}) -> + {CipherFragment::binary(), #cipher_state{}}. +%% +%% Description: Payload encryption +%%-------------------------------------------------------------------- +cipher(Version, Fragment, CipherS0, MacHash, + #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo}) -> + %% + ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version). + %%-------------------------------------------------------------------- -spec cipher_aead(ssl_version(), iodata(), connection_state(), AAD::binary()) -> {CipherFragment::binary(), connection_state()}. %% Description: Payload encryption %% %%-------------------------------------------------------------------- -cipher_aead(Version, Fragment, +cipher_aead(_Version, Fragment, #{cipher_state := CipherS0, security_parameters := #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo} } = WriteState0, AAD) -> {CipherFragment, CipherS1} = - cipher_aead(BulkCipherAlgo, CipherS0, AAD, Fragment, Version), + do_cipher_aead(BulkCipherAlgo, Fragment, CipherS0, AAD), {CipherFragment, WriteState0#{cipher_state => CipherS1}}. %%-------------------------------------------------------------------- +-spec cipher_aead(ssl_version(), iodata(), #cipher_state{}, AAD::binary(), #security_parameters{}) -> + {CipherFragment::binary(), #cipher_state{}}. + +%% Description: Payload encryption +%% %%-------------------------------------------------------------------- +cipher_aead(_Version, Fragment, CipherS, AAD, + #security_parameters{bulk_cipher_algorithm = BulkCipherAlgo}) -> + do_cipher_aead(BulkCipherAlgo, Fragment, CipherS, AAD). + +%%-------------------------------------------------------------------- -spec decipher(ssl_version(), binary(), connection_state(), boolean()) -> {binary(), binary(), connection_state()} | #alert{}. %% @@ -343,9 +367,8 @@ decipher(Version, CipherFragment, Alert end. %%-------------------------------------------------------------------- --spec decipher_aead(ssl_cipher:cipher_enum(), #cipher_state{}, - binary(), binary(), ssl_record:ssl_version()) -> - {binary(), #cipher_state{}} | #alert{}. +-spec decipher_aead(ssl_cipher:cipher_enum(), #cipher_state{}, binary(), binary(), ssl_record:ssl_version()) -> + binary() | #alert{}. %% %% Description: Decrypts the data and checks the associated data (AAD) MAC using %% cipher described by cipher_enum() and updating the cipher state. @@ -357,7 +380,7 @@ decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment {AAD, CipherText, CipherTag} = aead_ciphertext_split(Type, CipherState, CipherFragment, AAD0), case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of Content when is_binary(Content) -> - {Content, CipherState}; + Content; _ -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) end @@ -399,11 +422,13 @@ random() -> Random_28_bytes = ssl_cipher:random_bytes(28), <<?UINT32(Secs_since_1970), Random_28_bytes/binary>>. +-compile({inline, [is_correct_mac/2]}). is_correct_mac(Mac, Mac) -> true; is_correct_mac(_M,_H) -> false. +-compile({inline, [record_protocol_role/1]}). record_protocol_role(client) -> ?CLIENT; record_protocol_role(server) -> @@ -427,13 +452,15 @@ initial_security_params(ConnectionEnd) -> compression_algorithm = ?NULL}, ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, SecParams). -cipher_aead(?CHACHA20_POLY1305 = Type, #cipher_state{key=Key} = CipherState, AAD0, Fragment, _Version) -> - AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)), +-define(end_additional_data(AAD, Len), << (begin(AAD)end)/binary, ?UINT16(begin(Len)end) >>). + +do_cipher_aead(?CHACHA20_POLY1305 = Type, Fragment, #cipher_state{key=Key} = CipherState, AAD0) -> + AAD = ?end_additional_data(AAD0, erlang:iolist_size(Fragment)), Nonce = encrypt_nonce(Type, CipherState), {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD), {<<Content/binary, CipherTag/binary>>, CipherState}; -cipher_aead(Type, #cipher_state{key=Key, nonce = ExplicitNonce} = CipherState, AAD0, Fragment, _Version) -> - AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)), +do_cipher_aead(Type, Fragment, #cipher_state{key=Key, nonce = ExplicitNonce} = CipherState, AAD0) -> + AAD = ?end_additional_data(AAD0, erlang:iolist_size(Fragment)), Nonce = encrypt_nonce(Type, CipherState), {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD), {<<ExplicitNonce:64/integer, Content/binary, CipherTag/binary>>, CipherState#cipher_state{nonce = ExplicitNonce + 1}}. @@ -449,15 +476,12 @@ decrypt_nonce(?CHACHA20_POLY1305, #cipher_state{nonce = Nonce, iv = IV}, _) -> decrypt_nonce(?AES_GCM, #cipher_state{iv = <<Salt:4/bytes, _/binary>>}, <<ExplicitNonce:8/bytes, _/binary>>) -> <<Salt/binary, ExplicitNonce/binary>>. +-compile({inline, [aead_ciphertext_split/4]}). aead_ciphertext_split(?CHACHA20_POLY1305, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> - CipherLen = size(CipherTextFragment) - Len, + CipherLen = byte_size(CipherTextFragment) - Len, <<CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment, - {end_additional_data(AAD, CipherLen), CipherText, CipherTag}; + {?end_additional_data(AAD, CipherLen), CipherText, CipherTag}; aead_ciphertext_split(?AES_GCM, #cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> - CipherLen = size(CipherTextFragment) - (Len + 8), %% 8 is length of explicit Nonce + CipherLen = byte_size(CipherTextFragment) - (Len + 8), %% 8 is length of explicit Nonce << _:8/bytes, CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment, - {end_additional_data(AAD, CipherLen), CipherText, CipherTag}. - -end_additional_data(AAD, Len) -> - <<AAD/binary, ?UINT16(Len)>>. - + {?end_additional_data(AAD, CipherLen), CipherText, CipherTag}. diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index ed007f58d7..a927fba0de 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -140,6 +140,8 @@ -define(ALERT, 21). -define(HANDSHAKE, 22). -define(APPLICATION_DATA, 23). +-define(KNOWN_RECORD_TYPE(Type), + (is_integer(Type) andalso (20 =< (Type)) andalso ((Type) =< 23))). -define(MAX_PLAIN_TEXT_LENGTH, 16384). -define(MAX_COMPRESSED_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+1024)). -define(MAX_CIPHER_TEXT_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+2048)). diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index c9607489e9..44305c65fe 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -27,6 +27,7 @@ -include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). +-include("ssl_api.hrl"). %% Internal application API -export([is_new/2, client_id/4, server_id/6, valid_session/2]). @@ -34,7 +35,7 @@ -type seconds() :: integer(). %%-------------------------------------------------------------------- --spec is_new(session_id(), session_id()) -> boolean(). +-spec is_new(ssl:session_id(), ssl:session_id()) -> boolean(). %% %% Description: Checks if the session id decided by the server is a %% new or resumed sesion id. @@ -47,12 +48,19 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec client_id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), +-spec client_id({ssl:host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), undefined | binary()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- +client_id({Host, Port, #ssl_options{reuse_session = SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)-> + case CacheCb:lookup(Cache, {{Host, Port}, SessionId}) of + undefined -> + <<>>; + #session{} -> + SessionId + end; client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> @@ -91,7 +99,8 @@ server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) -> +select_session({_, _, #ssl_options{reuse_sessions = Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true -> + %% If reuse_sessions == true | save a new session should be created no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), @@ -132,7 +141,7 @@ is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = false -> {false, undefined} end; undefined -> - {false, undefined} + {false, undefined} end. resumable(new) -> diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl index b68c75a09b..5f96f905b1 100644 --- a/lib/ssl/src/ssl_session_cache_api.erl +++ b/lib/ssl/src/ssl_session_cache_api.erl @@ -23,14 +23,20 @@ -module(ssl_session_cache_api). -include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). +-include("ssl_api.hrl"). --type key() :: {{host(), inet:port_number()}, session_id()} | {inet:port_number(), session_id()}. +-export_type([session_cache_key/0, session/0, partial_key/0, session_cache_ref/0]). --callback init(list()) -> db_handle(). --callback terminate(db_handle()) -> any(). --callback lookup(db_handle(), key()) -> #session{} | undefined. --callback update(db_handle(), key(), #session{}) -> any(). --callback delete(db_handle(), key()) -> any(). --callback foldl(fun(), term(), db_handle()) -> term(). --callback select_session(db_handle(), {host(), inet:port_number()} | inet:port_number()) -> [#session{}]. --callback size(db_handle()) -> integer(). +-type session_cache_ref() :: any(). +-type session_cache_key() :: {partial_key(), ssl:session_id()}. +-opaque session() :: #session{}. +-opaque partial_key() :: {ssl:host(), inet:port_number()} | inet:port_number(). + +-callback init(list()) -> session_cache_ref(). +-callback terminate(session_cache_ref()) -> any(). +-callback lookup(session_cache_ref(), session_cache_key()) -> #session{} | undefined. +-callback update(session_cache_ref(), session_cache_key(), #session{}) -> any(). +-callback delete(session_cache_ref(), session_cache_key()) -> any(). +-callback foldl(fun(), term(), session_cache_ref()) -> term(). +-callback select_session(session_cache_ref(), {ssl:host(), inet:port_number()} | inet:port_number()) -> [#session{}]. +-callback size(session_cache_ref()) -> integer(). diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 0f986b5e21..3229004c9d 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ %% %% %CopyrightEnd% %% - %% %%---------------------------------------------------------------------- %% Purpose: Handles an ssl connection, e.i. both the setup @@ -47,7 +46,7 @@ %% State transition handling -export([next_event/3, next_event/4, - handle_common_event/4]). + handle_protocol_record/3]). %% Handshake handling -export([renegotiation/2, renegotiate/2, send_handshake/2, @@ -58,11 +57,10 @@ %% Alert and close handling -export([send_alert/2, send_alert_in_connection/2, send_sync_alert/2, - encode_alert/3, close/5, protocol_name/0]). + close/5, protocol_name/0]). %% Data handling --export([encode_data/3, next_record/1, - send/3, socket/5, setopts/3, getopts/3]). +-export([next_record/1, socket/4, setopts/3, getopts/3]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states @@ -109,7 +107,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = end. %%-------------------------------------------------------------------- --spec start_link(atom(), pid(), host(), inet:port_number(), port(), list(), pid(), tuple()) -> +-spec start_link(atom(), pid(), ssl:host(), inet:port_number(), port(), list(), pid(), tuple()) -> {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_statem process which calls Module:init/1 to @@ -144,28 +142,23 @@ pids(#state{protocol_specific = #{sender := Sender}}) -> %%==================================================================== %% State transition handling %%==================================================================== -next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 -> - {no_record, State#state{unprocessed_handshake_events = N-1}}; - +next_record(#state{handshake_env = + #handshake_env{unprocessed_handshake_events = N} = HsEnv} + = State) when N > 0 -> + {no_record, State#state{handshake_env = + HsEnv#handshake_env{unprocessed_handshake_events = N-1}}}; next_record(#state{protocol_buffers = - #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]} - = Buffers, - connection_states = ConnStates0, - ssl_options = #ssl_options{padding_check = Check}} = State) -> - case tls_record:decode_cipher_text(CT, ConnStates0, Check) of - {Plain, ConnStates} -> - {Plain, State#state{protocol_buffers = - Buffers#protocol_buffers{tls_cipher_texts = Rest}, - connection_states = ConnStates}}; - #alert{} = Alert -> - {Alert, State} - end; -next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []}, + #protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts}, + connection_states = ConnectionStates, + ssl_options = #ssl_options{padding_check = Check}} = State) -> + next_record(State, CipherTexts, ConnectionStates, Check); +next_record(#state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []}, protocol_specific = #{active_n_toggle := true, active_n := N} = ProtocolSpec, - socket = Socket, - close_tag = CloseTag, - transport_cb = Transport} = State) -> - case tls_socket:setopts(Transport, Socket, [{active, N}]) of + static_env = #static_env{socket = Socket, + close_tag = CloseTag, + transport_cb = Transport} + } = State) -> + case tls_socket:setopts(Transport, Socket, [{active, N}]) of ok -> {no_record, State#state{protocol_specific = ProtocolSpec#{active_n_toggle => false}}}; _ -> @@ -175,16 +168,48 @@ next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_ci next_record(State) -> {no_record, State}. +%% Decipher next record and concatenate consecutive ?APPLICATION_DATA records into one +%% +next_record(State, CipherTexts, ConnectionStates, Check) -> + next_record(State, CipherTexts, ConnectionStates, Check, []). +%% +next_record(State, [#ssl_tls{type = ?APPLICATION_DATA} = CT|CipherTexts], ConnectionStates0, Check, Acc) -> + case tls_record:decode_cipher_text(CT, ConnectionStates0, Check) of + {#ssl_tls{fragment = Fragment}, ConnectionStates} -> + next_record(State, CipherTexts, ConnectionStates, Check, [Fragment|Acc]); + #alert{} = Alert -> + Alert + end; +next_record(State, [CT|CipherTexts], ConnectionStates0, Check, []) -> + case tls_record:decode_cipher_text(CT, ConnectionStates0, Check) of + {Record, ConnectionStates} -> + next_record_done(State, CipherTexts, ConnectionStates, Record); + #alert{} = Alert -> + Alert + end; +next_record(State, CipherTexts, ConnectionStates, _Check, Acc) -> + %% Not ?APPLICATION_DATA but we have a nonempty Acc + %% -> build an ?APPLICATION_DATA record with the accumulated fragments + next_record_done(State, CipherTexts, ConnectionStates, + #ssl_tls{type = ?APPLICATION_DATA, fragment = iolist_to_binary(lists:reverse(Acc))}). + +next_record_done(#state{protocol_buffers = Buffers} = State, CipherTexts, ConnectionStates, Record) -> + {Record, + State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts}, + connection_states = ConnectionStates}}. + + next_event(StateName, Record, State) -> next_event(StateName, Record, State, []). +%% next_event(StateName, no_record, State0, Actions) -> case next_record(State0) of {no_record, State} -> {next_state, StateName, State, Actions}; {#ssl_tls{} = Record, State} -> {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; - {#alert{} = Alert, State} -> - {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} + #alert{} = Alert -> + {next_state, StateName, State0, [{next_event, internal, Alert} | Actions]} end; next_event(StateName, Record, State, Actions) -> case Record of @@ -196,14 +221,22 @@ next_event(StateName, Record, State, Actions) -> {next_state, StateName, State, [{next_event, internal, Alert} | Actions]} end. -handle_common_event(internal, #alert{} = Alert, StateName, - #state{negotiated_version = Version} = State) -> - ssl_connection:handle_own_alert(Alert, Version, StateName, State); + +%%% TLS record protocol level application data messages + +handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State0) -> + case ssl_connection:read_application_data(Data, State0) of + {stop, _, _} = Stop-> + Stop; + {Record, State1} -> + {next_state, StateName, State, Actions} = next_event(StateName, Record, State1), + ssl_connection:hibernate_after(StateName, State, Actions) + end; %%% TLS record protocol level handshake messages -handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, +handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, StateName, #state{protocol_buffers = #protocol_buffers{tls_handshake_buffer = Buf0} = Buffers, - negotiated_version = Version, + connection_env = #connection_env{negotiated_version = Version}, ssl_options = Options} = State0) -> try {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options), @@ -220,22 +253,23 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, connection -> ssl_connection:hibernate_after(StateName, State, Events); _ -> + HsEnv = State#state.handshake_env, {next_state, StateName, - State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events} + State#state{protocol_buffers = Buffers, + handshake_env = + HsEnv#handshake_env{unprocessed_handshake_events + = unprocessed_events(Events)}}, Events} end end catch throw:#alert{} = Alert -> ssl_connection:handle_own_alert(Alert, Version, StateName, State0) end; -%%% TLS record protocol level application data messages -handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) -> - {next_state, StateName, State, [{next_event, internal, {application_data, Data}}]}; %%% TLS record protocol level change cipher messages -handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> +handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]}; %%% TLS record protocol level Alert messages -handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, - #state{negotiated_version = Version} = State) -> +handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, + #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try decode_alerts(EncAlerts) of Alerts = [_|_] -> handle_alerts(Alerts, {next_state, StateName, State}); @@ -251,77 +285,79 @@ handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, Sta end; %% Ignore unknown TLS record level protocol messages -handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) -> - {next_state, StateName, State}. +handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) -> + {next_state, StateName, State, []}. %%==================================================================== %% Handshake handling %%==================================================================== renegotiation(Pid, WriteState) -> gen_statem:call(Pid, {user_renegotiate, WriteState}). -renegotiate(#state{role = client} = State, Actions) -> +renegotiate(#state{static_env = #static_env{role = client}, + handshake_env = HsEnv} = State, Actions) -> %% Handle same way as if server requested %% the renegotiation Hs0 = ssl_handshake:init_handshake_history(), - {next_state, connection, State#state{tls_handshake_history = Hs0}, + {next_state, connection, State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hs0}}, [{next_event, internal, #hello_request{}} | Actions]}; -renegotiate(#state{role = server, - socket = Socket, - transport_cb = Transport, - negotiated_version = Version, +renegotiate(#state{static_env = #static_env{role = server, + socket = Socket, + transport_cb = Transport}, + handshake_env = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, connection_states = ConnectionStates0} = State0, Actions) -> HelloRequest = ssl_handshake:hello_request(), Frag = tls_handshake:encode_handshake(HelloRequest, Version), Hs0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates} = tls_record:encode_handshake(Frag, Version, ConnectionStates0), - send(Transport, Socket, BinMsg), + tls_socket:send(Transport, Socket, BinMsg), State = State0#state{connection_states = ConnectionStates, - tls_handshake_history = Hs0}, + handshake_env = HsEnv#handshake_env{tls_handshake_history = Hs0}}, next_event(hello, no_record, State, Actions). send_handshake(Handshake, State) -> send_handshake_flight(queue_handshake(Handshake, State)). -queue_handshake(Handshake, #state{negotiated_version = Version, - tls_handshake_history = Hist0, - flight_buffer = Flight0, +queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, + connection_env = #connection_env{negotiated_version = Version}, + flight_buffer = Flight0, connection_states = ConnectionStates0} = State0) -> {BinHandshake, ConnectionStates, Hist} = encode_handshake(Handshake, Version, ConnectionStates0, Hist0), State0#state{connection_states = ConnectionStates, - tls_handshake_history = Hist, + handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, flight_buffer = Flight0 ++ [BinHandshake]}. -send_handshake_flight(#state{socket = Socket, - transport_cb = Transport, +send_handshake_flight(#state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, flight_buffer = Flight} = State0) -> - send(Transport, Socket, Flight), + tls_socket:send(Transport, Socket, Flight), {State0#state{flight_buffer = []}, []}. -queue_change_cipher(Msg, #state{negotiated_version = Version, - flight_buffer = Flight0, - connection_states = ConnectionStates0} = State0) -> +queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_version = Version}, + flight_buffer = Flight0, + connection_states = ConnectionStates0} = State0) -> {BinChangeCipher, ConnectionStates} = encode_change_cipher(Msg, Version, ConnectionStates0), State0#state{connection_states = ConnectionStates, flight_buffer = Flight0 ++ [BinChangeCipher]}. reinit(#state{protocol_specific = #{sender := Sender}, - negotiated_version = Version, + connection_env = #connection_env{negotiated_version = Version}, connection_states = #{current_write := Write}} = State) -> tls_sender:update_connection_state(Sender, Write, Version), reinit_handshake_data(State). -reinit_handshake_data(State) -> +reinit_handshake_data(#state{handshake_env = HsEnv} =State) -> %% premaster_secret, public_key_info and tls_handshake_info %% are only needed during the handshake phase. %% To reduce memory foot print of a connection reinitialize them. State#state{ - premaster_secret = undefined, - public_key_info = undefined, - tls_handshake_history = ssl_handshake:init_handshake_history() + handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history(), + public_key_info = undefined, + premaster_secret = undefined} }. select_sni_extension(#client_hello{extensions = HelloExtensions}) -> @@ -344,13 +380,13 @@ empty_connection_state(ConnectionEnd, BeastMitigation) -> encode_alert(#alert{} = Alert, Version, ConnectionStates) -> tls_record:encode_alert_record(Alert, Version, ConnectionStates). -send_alert(Alert, #state{negotiated_version = Version, - socket = Socket, - transport_cb = Transport, +send_alert(Alert, #state{static_env = #static_env{socket = Socket, + transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, connection_states = ConnectionStates0} = StateData0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), - send(Transport, Socket, BinMsg), + tls_socket:send(Transport, Socket, BinMsg), StateData0#state{connection_states = ConnectionStates}. %% If an ALERT sent in the connection state, should cause the TLS @@ -404,14 +440,9 @@ protocol_name() -> %%==================================================================== %% Data handling %%==================================================================== -encode_data(Data, Version, ConnectionStates0)-> - tls_record:encode_data(Data, Version, ConnectionStates0). - -send(Transport, Socket, Data) -> - tls_socket:send(Transport, Socket, Data). -socket(Pids, Transport, Socket, Connection, Tracker) -> - tls_socket:socket(Pids, Transport, Socket, Connection, Tracker). +socket(Pids, Transport, Socket, Tracker) -> + tls_socket:socket(Pids, Transport, Socket, ?MODULE, Tracker). setopts(Transport, Socket, Other) -> tls_socket:setopts(Transport, Socket, Other). @@ -429,16 +460,19 @@ getopts(Transport, Socket, Tag) -> %%-------------------------------------------------------------------- init({call, From}, {start, Timeout}, - #state{host = Host, port = Port, role = client, + #state{static_env = #static_env{role = client, + host = Host, + port = Port, + transport_cb = Transport, + socket = Socket, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv, + connection_env = CEnv, ssl_options = SslOpts, session = #session{own_certificate = Cert} = Session0, - transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb + connection_states = ConnectionStates0 } = State0) -> - Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From), Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), @@ -447,15 +481,14 @@ init({call, From}, {start, Timeout}, Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), - send(Transport, Socket, BinMsg), + tls_socket:send(Transport, Socket, BinMsg), State = State0#state{connection_states = ConnectionStates, - negotiated_version = Version, %% Requested version + connection_env = CEnv#connection_env{negotiated_version = Version}, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}, - tls_handshake_history = Handshake, - start_or_recv_from = From, - timer = Timer}, - next_event(hello, no_record, State); + handshake_env = HsEnv#handshake_env{tls_handshake_history = Handshake}, + start_or_recv_from = From}, + next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close}]); init(Type, Event, State) -> gen_handshake(?FUNCTION_NAME, Type, Event, State). @@ -466,8 +499,9 @@ init(Type, Event, State) -> %%-------------------------------------------------------------------- error({call, From}, {start, _Timeout}, #state{protocol_specific = #{error := Error}} = State) -> - ssl_connection:stop_and_reply( - normal, {reply, From, {error, Error}}, State); + {stop_and_reply, {shutdown, normal}, + [{reply, From, {error, Error}}], State}; + error({call, _} = Call, Msg, State) -> gen_handshake(?FUNCTION_NAME, Call, Msg, State); error(_, _, _) -> @@ -481,31 +515,36 @@ error(_, _, _) -> %%-------------------------------------------------------------------- hello(internal, #client_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello}, + handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, - hello = Hello}, + handshake_env = HsEnv#handshake_env{hello = Hello}}, [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #ssl_options{handshake = hello}, + handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, State#state{start_or_recv_from = undefined, - hello = Hello}, + handshake_env = HsEnv#handshake_env{hello = Hello}}, [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]}; hello(internal, #client_hello{client_version = ClientVersion} = Hello, #state{connection_states = ConnectionStates0, - port = Port, session = #session{own_certificate = Cert} = Session0, - renegotiation = {Renegotiation, _}, - session_cache = Cache, - session_cache_cb = CacheCb, - negotiated_protocol = CurrentProtocol, - key_algorithm = KeyExAlg, + static_env = #static_env{ + port = Port, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{kex_algorithm = KeyExAlg, + renegotiation = {Renegotiation, _}, + negotiated_protocol = CurrentProtocol} = HsEnv, + connection_env = CEnv, + session = #session{own_certificate = Cert} = Session0, ssl_options = SslOpts} = State) -> case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of #alert{} = Alert -> ssl_connection:handle_own_alert(Alert, ClientVersion, hello, - State#state{negotiated_version - = ClientVersion}); + State#state{connection_env = + CEnv#connection_env{negotiated_version = ClientVersion}}); {Version, {Type, Session}, ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> Protocol = case Protocol0 of @@ -514,22 +553,24 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello, end, gen_handshake(?FUNCTION_NAME, internal, {common_client_hello, Type, ServerHelloExt}, State#state{connection_states = ConnectionStates, - negotiated_version = Version, - hashsign_algorithm = HashSign, - client_hello_version = ClientVersion, - session = Session, - negotiated_protocol = Protocol}) + connection_env = CEnv#connection_env{negotiated_version = Version}, + handshake_env = HsEnv#handshake_env{ + hashsign_algorithm = HashSign, + client_hello_version = ClientVersion, + negotiated_protocol = Protocol}, + session = Session + }) end; hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, - negotiated_version = ReqVersion, - role = client, - renegotiation = {Renegotiation, _}, + connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv, + static_env = #static_env{role = client}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}}, ssl_options = SslOptions} = State) -> case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> + #alert{} = Alert -> %%TODO ssl_connection:handle_own_alert(Alert, ReqVersion, hello, - State#state{negotiated_version = ReqVersion}); + State#state{connection_env = CEnv#connection_env{negotiated_version = ReqVersion}}); {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, Version, NewId, ConnectionStates, ProtoExt, Protocol, State) @@ -580,22 +621,76 @@ connection({call, From}, {user_renegotiate, WriteState}, #state{connection_states = ConnectionStates} = State) -> {next_state, ?FUNCTION_NAME, State#state{connection_states = ConnectionStates#{current_write => WriteState}}, [{next_event,{call, From}, renegotiate}]}; +connection({call, From}, + {close, {Pid, _Timeout}}, + #state{connection_env = #connection_env{terminated = closed} =CEnv} = State) -> + {next_state, downgrade, State#state{connection_env = + CEnv#connection_env{terminated = true, + downgrade = {Pid, From}}}, + [{next_event, internal, ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY)}]}; +connection({call, From}, + {close,{Pid, Timeout}}, + #state{connection_states = ConnectionStates, + protocol_specific = #{sender := Sender}, + connection_env = CEnv + } = State0) -> + case tls_sender:downgrade(Sender, Timeout) of + {ok, Write} -> + %% User downgrades connection + %% When downgrading an TLS connection to a transport connection + %% we must recive the close alert from the peer before releasing the + %% transport socket. + State = send_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + State0#state{connection_states = + ConnectionStates#{current_write => Write}}), + {next_state, downgrade, State#state{connection_env = + CEnv#connection_env{downgrade = {Pid, From}, + terminated = true}}, + [{timeout, Timeout, downgrade}]}; + {error, timeout} -> + {stop_and_reply, {shutdown, downgrade_fail}, [{reply, From, {error, timeout}}]} + end; connection(internal, #hello_request{}, - #state{role = client, - renegotiation = {Renegotiation, _}, - host = Host, port = Port, + #state{static_env = #static_env{role = client, + host = Host, + port = Port, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, peer}}, session = #session{own_certificate = Cert} = Session0, - session_cache = Cache, session_cache_cb = CacheCb, - ssl_options = SslOpts, + ssl_options = SslOpts, + protocol_specific = #{sender := Pid}, + connection_states = ConnectionStates} = State0) -> + try tls_sender:peer_renegotiate(Pid) of + {ok, Write} -> + Hello = tls_handshake:client_hello(Host, Port, ConnectionStates, SslOpts, + Cache, CacheCb, Renegotiation, Cert), + {State, Actions} = send_handshake(Hello, State0#state{connection_states = ConnectionStates#{current_write => Write}}), + next_event(hello, no_record, State#state{session = Session0#session{session_id + = Hello#client_hello.session_id}}, Actions) + catch + _:_ -> + {stop, {shutdown, sender_blocked}, State0} + end; +connection(internal, #hello_request{}, + #state{static_env = #static_env{role = client, + host = Host, + port = Port, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}}, + session = #session{own_certificate = Cert} = Session0, + ssl_options = SslOpts, connection_states = ConnectionStates} = State0) -> Hello = tls_handshake:client_hello(Host, Port, ConnectionStates, SslOpts, - Cache, CacheCb, Renegotiation, Cert), + Cache, CacheCb, Renegotiation, Cert), {State, Actions} = send_handshake(Hello, State0), next_event(hello, no_record, State#state{session = Session0#session{session_id = Hello#client_hello.session_id}}, Actions); connection(internal, #client_hello{} = Hello, - #state{role = server, allow_renegotiate = true, connection_states = CS, - %%protocol_cb = Connection, + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{allow_renegotiate = true}= HsEnv, + connection_states = CS, protocol_specific = #{sender := Sender} } = State) -> %% Mitigate Computational DoS attack @@ -606,17 +701,18 @@ connection(internal, #client_hello{} = Hello, erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate), {ok, Write} = tls_sender:renegotiate(Sender), next_event(hello, no_record, State#state{connection_states = CS#{current_write => Write}, - allow_renegotiate = false, - renegotiation = {true, peer} + handshake_env = HsEnv#handshake_env{renegotiation = {true, peer}, + allow_renegotiate = false} }, [{next_event, internal, Hello}]); connection(internal, #client_hello{}, - #state{role = server, allow_renegotiate = false, - protocol_cb = Connection} = State0) -> + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{allow_renegotiate = false}} = State0) -> Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION), send_alert_in_connection(Alert, State0), - State = Connection:reinit_handshake_data(State0), + State = reinit_handshake_data(State0), next_event(?FUNCTION_NAME, no_record, State); + connection(Type, Event, State) -> ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). @@ -624,19 +720,35 @@ connection(Type, Event, State) -> -spec downgrade(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- +downgrade(internal, #alert{description = ?CLOSE_NOTIFY}, + #state{static_env = #static_env{transport_cb = Transport, + socket = Socket}, + connection_env = #connection_env{downgrade = {Pid, From}}} = State) -> + tls_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]), + Transport:controlling_process(Socket, Pid), + {stop_and_reply, {shutdown, downgrade},[{reply, From, {ok, Socket}}], State}; +downgrade(timeout, downgrade, #state{ connection_env = #connection_env{downgrade = {_, From}}} = State) -> + {stop_and_reply, {shutdown, normal},[{reply, From, {error, timeout}}], State}; +downgrade(info, {CloseTag, Socket}, + #state{static_env = #static_env{socket = Socket, + close_tag = CloseTag}, + connection_env = #connection_env{downgrade = {_, From}}} = + State) -> + {stop_and_reply, {shutdown, normal},[{reply, From, {error, CloseTag}}], State}; +downgrade(info, Info, State) -> + handle_info(Info, ?FUNCTION_NAME, State); downgrade(Type, Event, State) -> - ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). - + ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). %-------------------------------------------------------------------- %% gen_statem callbacks %%-------------------------------------------------------------------- callback_mode() -> state_functions. - -terminate( - {shutdown, sender_died, Reason}, _StateName, - #state{socket = Socket, transport_cb = Transport} = State) -> +terminate({shutdown, sender_died, Reason}, _StateName, + #state{static_env = #static_env{socket = Socket, + transport_cb = Transport}} + = State) -> ssl_connection:handle_trusted_certs_db(State), close(Reason, Socket, Transport, undefined, undefined); terminate(Reason, StateName, State) -> @@ -658,65 +770,62 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac erl_dist = IsErlDist} = SSLOptions, ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation), - ErlDistData = erl_dist_data(IsErlDist), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> Cb; _ -> ssl_session_cache end, - InternalActiveN = case application:get_env(ssl, internal_active_n) of {ok, N} when is_integer(N) andalso (not IsErlDist) -> N; _ -> ?INTERNAL_ACTIVE_N end, - UserMonitor = erlang:monitor(process, User), - - #state{socket_options = SocketOptions, - ssl_options = SSLOptions, - session = #session{is_resumable = new}, - transport_cb = CbModule, - data_tag = DataTag, - close_tag = CloseTag, - error_tag = ErrorTag, - role = Role, - host = Host, - port = Port, - socket = Socket, - erl_dist_data = ErlDistData, - connection_states = ConnectionStates, - protocol_buffers = #protocol_buffers{}, - user_application = {UserMonitor, User}, - user_data_buffer = <<>>, - session_cache_cb = SessionCacheCb, - renegotiation = {false, first}, - allow_renegotiate = SSLOptions#ssl_options.client_renegotiation, - start_or_recv_from = undefined, - protocol_cb = ?MODULE, - tracker = Tracker, - flight_buffer = [], - protocol_specific = #{sender => Sender, - active_n => InternalActiveN, - active_n_toggle => true - } - }. - -erl_dist_data(true) -> - #{dist_handle => undefined, - dist_buffer => <<>>}; -erl_dist_data(false) -> - #{}. - -initialize_tls_sender(#state{role = Role, - socket = Socket, - socket_options = SockOpts, - tracker = Tracker, - protocol_cb = Connection, - transport_cb = Transport, - negotiated_version = Version, + InitStatEnv = #static_env{ + role = Role, + transport_cb = CbModule, + protocol_cb = ?MODULE, + data_tag = DataTag, + close_tag = CloseTag, + error_tag = ErrorTag, + host = Host, + port = Port, + socket = Socket, + session_cache_cb = SessionCacheCb, + tracker = Tracker + }, + #state{ + static_env = InitStatEnv, + handshake_env = #handshake_env{ + tls_handshake_history = ssl_handshake:init_handshake_history(), + renegotiation = {false, first}, + allow_renegotiate = SSLOptions#ssl_options.client_renegotiation + }, + connection_env = #connection_env{user_application = {UserMonitor, User}}, + socket_options = SocketOptions, + ssl_options = SSLOptions, + session = #session{is_resumable = new}, + connection_states = ConnectionStates, + protocol_buffers = #protocol_buffers{}, + user_data_buffer = {[],0,[]}, + start_or_recv_from = undefined, + flight_buffer = [], + protocol_specific = #{sender => Sender, + active_n => InternalActiveN, + active_n_toggle => true + } + }. + +initialize_tls_sender(#state{static_env = #static_env{ + role = Role, + transport_cb = Transport, + socket = Socket, + tracker = Tracker + }, + connection_env = #connection_env{negotiated_version = Version}, + socket_options = SockOpts, ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}, connection_states = #{current_write := ConnectionWriteState}, protocol_specific = #{sender := Sender}}) -> @@ -725,19 +834,23 @@ initialize_tls_sender(#state{role = Role, socket => Socket, socket_options => SockOpts, tracker => Tracker, - protocol_cb => Connection, transport_cb => Transport, negotiated_version => Version, renegotiate_at => RenegotiateAt}, tls_sender:initialize(Sender, Init). - -next_tls_record(Data, StateName, #state{protocol_buffers = - #protocol_buffers{tls_record_buffer = Buf0, - tls_cipher_texts = CT0} = Buffers} - = State0) -> - case tls_record:get_tls_records(Data, - acceptable_record_versions(StateName, State0), - Buf0) of + +next_tls_record(Data, StateName, + #state{protocol_buffers = + #protocol_buffers{tls_record_buffer = Buf0, + tls_cipher_texts = CT0} = Buffers} = State0) -> + Versions = + case StateName of + hello -> + [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS]; + _ -> + State0#state.connection_env#connection_env.negotiated_version + end, + case tls_record:get_tls_records(Data, Versions, Buf0) of {Records, Buf1} -> CT1 = CT0 ++ Records, next_record(State0#state{protocol_buffers = @@ -748,10 +861,6 @@ next_tls_record(Data, StateName, #state{protocol_buffers = end. -acceptable_record_versions(hello, _) -> - [tls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_VERSIONS]; -acceptable_record_versions(_, #state{negotiated_version = Version}) -> - [Version]. handle_record_alert(Alert, _) -> Alert. @@ -762,33 +871,34 @@ tls_handshake_events(Packets) -> %% raw data from socket, upack records handle_info({Protocol, _, Data}, StateName, - #state{data_tag = Protocol - } = State0) -> + #state{static_env = #static_env{data_tag = Protocol}} = State0) -> case next_tls_record(Data, StateName, State0) of {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> ssl_connection:handle_normal_shutdown(Alert, StateName, State0), - ssl_connection:stop({shutdown, own_alert}, State0) + {stop, {shutdown, own_alert}, State0} end; -handle_info({tcp_passive, Socket}, StateName, #state{socket = Socket, - protocol_specific = PS - } = State) -> - next_event(StateName, no_record, State#state{protocol_specific = PS#{active_n_toggle => true}}); +handle_info({tcp_passive, Socket}, StateName, + #state{static_env = #static_env{socket = Socket}, + protocol_specific = PS + } = State) -> + next_event(StateName, no_record, + State#state{protocol_specific = PS#{active_n_toggle => true}}); handle_info({CloseTag, Socket}, StateName, - #state{socket = Socket, close_tag = CloseTag, + #state{static_env = #static_env{socket = Socket, close_tag = CloseTag}, + connection_env = #connection_env{negotiated_version = Version}, socket_options = #socket_options{active = Active}, protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}, - user_data_buffer = Buffer, - protocol_specific = PS, - negotiated_version = Version} = State) -> + user_data_buffer = {_,BufferSize,_}, + protocol_specific = PS} = State) -> %% Note that as of TLS 1.1, %% failure to properly close a connection no longer requires that a %% session not be resumed. This is a change from TLS 1.0 to conform %% with widespread implementation practice. - case (Active == false) andalso ((CTs =/= []) or (Buffer =/= <<>>)) of + case (Active == false) andalso ((CTs =/= []) or (BufferSize =/= 0)) of false -> case Version of {1, N} when N >= 1 -> @@ -802,7 +912,7 @@ handle_info({CloseTag, Socket}, StateName, end, ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), - ssl_connection:stop({shutdown, transport_closed}, State); + {stop, {shutdown, transport_closed}, State}; true -> %% Fixes non-delivery of final TLS record in {active, once}. %% Basically allows the application the opportunity to set {active, once} again @@ -820,6 +930,14 @@ handle_alerts([], Result) -> Result; handle_alerts(_, {stop, _, _} = Stop) -> Stop; +handle_alerts([#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} | _Alerts], + {next_state, connection = StateName, #state{connection_env = CEnv, + socket_options = #socket_options{active = false}, + user_data_buffer = {_,BufferSize,_}, + protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}} = + State}) when (BufferSize =/= 0) orelse + (CTs =/= []) -> + {next_state, StateName, State#state{connection_env = CEnv#connection_env{terminated = true}}}; handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)); handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> @@ -839,7 +957,7 @@ decode_alerts(Bin) -> ssl_alert:decode(Bin). gen_handshake(StateName, Type, Event, - #state{negotiated_version = Version} = State) -> + #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try ssl_connection:StateName(Type, Event, State, ?MODULE) of Result -> Result @@ -850,7 +968,7 @@ gen_handshake(StateName, Type, Event, Version, StateName, State) end. -gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) -> +gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try handle_info(Event, StateName, State) of Result -> Result @@ -861,7 +979,7 @@ gen_info(Event, connection = StateName, #state{negotiated_version = Version} = Version, StateName, State) end; -gen_info(Event, StateName, #state{negotiated_version = Version} = State) -> +gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> try handle_info(Event, StateName, State) of Result -> Result diff --git a/lib/ssl/src/tls_connection.hrl b/lib/ssl/src/tls_connection.hrl index 0af2258932..9063b1b736 100644 --- a/lib/ssl/src/tls_connection.hrl +++ b/lib/ssl/src/tls_connection.hrl @@ -30,7 +30,6 @@ -include("tls_record.hrl"). -record(protocol_buffers, { - tls_packets = [], %% :: [#ssl_tls{}], % Not yet handled decode SSL/TLS packets. tls_record_buffer = <<>>, %% :: binary(), % Buffer of incomplete records tls_handshake_buffer = <<>>, %% :: binary(), % Buffer of incomplete handshakes tls_cipher_texts = [] %%:: [binary()] diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 19a5eb0348..0f0de5936a 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -30,6 +30,7 @@ -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_api.hrl"). -include_lib("public_key/include/public_key.hrl"). %% Handshake handling @@ -47,7 +48,7 @@ %% Handshake handling %%==================================================================== %%-------------------------------------------------------------------- --spec client_hello(host(), inet:port_number(), ssl_record:connection_states(), +-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% @@ -81,13 +82,13 @@ client_hello(Host, Port, ConnectionStates, -spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(), atom(), ssl_record:connection_states(), - binary() | undefined, ssl_cipher_format:key_algo()}, + binary() | undefined, ssl:kex_algo()}, boolean()) -> - {tls_record:tls_version(), session_id(), + {tls_record:tls_version(), ssl:session_id(), ssl_record:connection_states(), alpn | npn, binary() | undefined}| {tls_record:tls_version(), {resumed | new, #session{}}, ssl_record:connection_states(), binary() | undefined, - #hello_extensions{}, {ssl_cipher_format:hash(), ssl_cipher_format:sign_algo()} | + #hello_extensions{}, {ssl:hash(), ssl:sign_algo()} | undefined} | #alert{}. %% %% Description: Handles a received hello message @@ -223,8 +224,6 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, Version, SslOpts, Session0, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} @@ -235,14 +234,14 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> - case ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, + try ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, Compression, HelloExt, Version, SslOpt, ConnectionStates0, - Renegotiation) of - #alert{} = Alert -> - Alert; + Renegotiation) of {ConnectionStates, ProtoExt, Protocol} -> {Version, SessionId, ConnectionStates, ProtoExt, Protocol} + catch throw:Alert -> + Alert end. %%-------------------------------------------------------------------- enc_handshake(#hello_request{}, _Version) -> diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index cf0690f2a5..b456197398 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -75,25 +75,22 @@ init_connection_states(Role, BeastMitigation) -> pending_write => Pending}. %%-------------------------------------------------------------------- --spec get_tls_records(binary(), [tls_version()], binary()) -> {[binary()], binary()} | #alert{}. +-spec get_tls_records( + binary(), [tls_version()] | tls_version(), + Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}) -> + {Records :: [#ssl_tls{}], + Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} | + #alert{}. %% %% and returns it as a list of tls_compressed binaries also returns leftover %% Description: Given old buffer and new data from TCP, packs up a records %% data %%-------------------------------------------------------------------- -get_tls_records(Data, Versions, Buffer) -> - BinData = list_to_binary([Buffer, Data]), - case erlang:byte_size(BinData) of - N when N >= 3 -> - case assert_version(BinData, Versions) of - true -> - get_tls_records_aux(BinData, []); - false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) - end; - _ -> - get_tls_records_aux(BinData, []) - end. + +get_tls_records(Data, Versions, Buffer) when is_binary(Buffer) -> + parse_tls_records(Versions, {[Data],byte_size(Data),[]}, undefined); +get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}) -> + parse_tls_records(Versions, {Front,Size + byte_size(Data),[Data|Rear]}, Hdr). %%==================================================================== %% Encoding @@ -113,8 +110,8 @@ encode_handshake(Frag, Version, ConnectionStates) -> case iolist_size(Frag) of N when N > ?MAX_PLAIN_TEXT_LENGTH -> - Data = split_bin(iolist_to_binary(Frag), Version, BCA, BeastMitigation), - encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates); + Data = split_iovec(erlang:iolist_to_iovec(Frag), Version, BCA, BeastMitigation), + encode_fragments(?HANDSHAKE, Version, Data, ConnectionStates); _ -> encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates) end. @@ -140,18 +137,18 @@ encode_change_cipher_spec(Version, ConnectionStates) -> encode_plain_text(?CHANGE_CIPHER_SPEC, Version, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates). %%-------------------------------------------------------------------- --spec encode_data(binary(), tls_version(), ssl_record:connection_states()) -> - {iolist(), ssl_record:connection_states()}. +-spec encode_data([binary()], tls_version(), ssl_record:connection_states()) -> + {[[binary()]], ssl_record:connection_states()}. %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- -encode_data(Frag, Version, +encode_data(Data, Version, #{current_write := #{beast_mitigation := BeastMitigation, security_parameters := #security_parameters{bulk_cipher_algorithm = BCA}}} = ConnectionStates) -> - Data = split_bin(Frag, Version, BCA, BeastMitigation), - encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates). + Fragments = split_iovec(Data, Version, BCA, BeastMitigation), + encode_fragments(?APPLICATION_DATA, Version, Fragments, ConnectionStates). %%==================================================================== %% Decoding @@ -163,57 +160,59 @@ encode_data(Frag, Version, %% %% Description: Decode cipher text %%-------------------------------------------------------------------- -decode_cipher_text(#ssl_tls{type = Type, version = Version, - fragment = CipherFragment} = CipherText, +decode_cipher_text(CipherText, #{current_read := - #{compression_state := CompressionS0, - sequence_number := Seq, - cipher_state := CipherS0, + #{sequence_number := Seq, security_parameters := - #security_parameters{ - cipher_type = ?AEAD, - bulk_cipher_algorithm = - BulkCipherAlgo, - compression_algorithm = CompAlg} - } = ReadState0} = ConnnectionStates0, _) -> - AAD = start_additional_data(Type, Version, ReadState0), - CipherS1 = ssl_record:nonce_seed(BulkCipherAlgo, <<?UINT64(Seq)>>, CipherS0), - case ssl_record:decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment, Version) of - {PlainFragment, CipherState} -> - {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, - PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#{ + #security_parameters{cipher_type = ?AEAD, + bulk_cipher_algorithm = BulkCipherAlgo}, + cipher_state := CipherS0 + } + } = ConnectionStates0, _) -> + SeqBin = <<?UINT64(Seq)>>, + #ssl_tls{type = Type, version = {MajVer,MinVer} = Version, fragment = Fragment} = CipherText, + StartAdditionalData = <<SeqBin/binary, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>, + CipherS = ssl_record:nonce_seed(BulkCipherAlgo, SeqBin, CipherS0), + case ssl_record:decipher_aead( + BulkCipherAlgo, CipherS, StartAdditionalData, Fragment, Version) + of + PlainFragment when is_binary(PlainFragment) -> + #{current_read := + #{security_parameters := SecParams, + compression_state := CompressionS0} = ReadState0} = ConnectionStates0, + {Plain, CompressionS} = ssl_record:uncompress(SecParams#security_parameters.compression_algorithm, + PlainFragment, CompressionS0), + ConnectionStates = ConnectionStates0#{ current_read => ReadState0#{ - cipher_state => CipherState, + cipher_state => CipherS, sequence_number => Seq + 1, - compression_state => CompressionS1}}, - {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; + compression_state => CompressionS}}, + {CipherText#ssl_tls{fragment = Plain}, ConnectionStates}; #alert{} = Alert -> Alert end; -decode_cipher_text(#ssl_tls{type = Type, version = Version, +decode_cipher_text(#ssl_tls{version = Version, fragment = CipherFragment} = CipherText, - #{current_read := - #{compression_state := CompressionS0, - sequence_number := Seq, - security_parameters := - #security_parameters{compression_algorithm = CompAlg} - } = ReadState0} = ConnnectionStates0, PaddingCheck) -> + #{current_read := ReadState0} = ConnnectionStates0, PaddingCheck) -> case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of {PlainFragment, Mac, ReadState1} -> - MacHash = ssl_cipher:calc_mac_hash(Type, Version, PlainFragment, ReadState1), + MacHash = ssl_cipher:calc_mac_hash(CipherText#ssl_tls.type, Version, PlainFragment, ReadState1), case ssl_record:is_correct_mac(Mac, MacHash) of true -> + #{sequence_number := Seq, + compression_state := CompressionS0, + security_parameters := + #security_parameters{compression_algorithm = CompAlg}} = ReadState0, {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#{ - current_read => ReadState1#{ - sequence_number => Seq + 1, - compression_state => CompressionS1}}, + ConnnectionStates = + ConnnectionStates0#{current_read => + ReadState1#{sequence_number => Seq + 1, + compression_state => CompressionS1}}, {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; #alert{} = Alert -> Alert @@ -395,117 +394,222 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> server_verify_data => undefined }. -assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) -> - is_acceptable_version({MajVer, MinVer}, Versions). - -get_tls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Length), Data:Length/binary, Rest/binary>>, - Acc) -> - get_tls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA, - version = {MajVer, MinVer}, - fragment = Data} | Acc]); -get_tls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Length), - Data:Length/binary, Rest/binary>>, Acc) -> - get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE, - version = {MajVer, MinVer}, - fragment = Data} | Acc]); -get_tls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Length), Data:Length/binary, - Rest/binary>>, Acc) -> - get_tls_records_aux(Rest, [#ssl_tls{type = ?ALERT, - version = {MajVer, MinVer}, - fragment = Data} | Acc]); -get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Length), Data:Length/binary, Rest/binary>>, - Acc) -> - get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, - version = {MajVer, MinVer}, - fragment = Data} | Acc]); -get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer), - ?UINT16(Length), _/binary>>, - _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH -> - ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); -get_tls_records_aux(Data, Acc) -> - case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of - true -> - {lists:reverse(Acc), Data}; - false -> - ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE) - end. -%%-------------------------------------------------------------------- -encode_plain_text(Type, Version, Data, #{current_write := Write0} = ConnectionStates) -> - {CipherFragment, Write1} = do_encode_plain_text(Type, Version, Data, Write0), - {CipherText, Write} = encode_tls_cipher_text(Type, Version, CipherFragment, Write1), - {CipherText, ConnectionStates#{current_write => Write}}. - -encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment, #{sequence_number := Seq} = Write) -> - Length = erlang:iolist_size(Fragment), - {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment], - Write#{sequence_number => Seq +1}}. - -encode_iolist(Type, Data, Version, ConnectionStates0) -> - {ConnectionStates, EncodedMsg} = - lists:foldl(fun(Text, {CS0, Encoded}) -> - {Enc, CS1} = - encode_plain_text(Type, Version, Text, CS0), - {CS1, [Enc | Encoded]} - end, {ConnectionStates0, []}, Data), - {lists:reverse(EncodedMsg), ConnectionStates}. -%%-------------------------------------------------------------------- -do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0, - cipher_state := CipherS0, - sequence_number := Seq, - security_parameters := - #security_parameters{ - cipher_type = ?AEAD, - bulk_cipher_algorithm = BCAlg, - compression_algorithm = CompAlg} - } = WriteState0) -> - {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - CipherS = ssl_record:nonce_seed(BCAlg, <<?UINT64(Seq)>>, CipherS0), - WriteState = WriteState0#{compression_state => CompS1, - cipher_state => CipherS}, - AAD = start_additional_data(Type, Version, WriteState), - ssl_record:cipher_aead(Version, Comp, WriteState, AAD); -do_encode_plain_text(Type, Version, Data, #{compression_state := CompS0, - security_parameters := - #security_parameters{compression_algorithm = CompAlg} - }= WriteState0) -> - {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - WriteState1 = WriteState0#{compression_state => CompS1}, - MacHash = ssl_cipher:calc_mac_hash(Type, Version, Comp, WriteState1), - ssl_record:cipher(Version, Comp, WriteState1, MacHash); -do_encode_plain_text(_,_,_,CS) -> + +parse_tls_records(Versions, Q, undefined) -> + decode_tls_records(Versions, Q, [], undefined, undefined, undefined); +parse_tls_records(Versions, Q, #ssl_tls{type = Type, version = Version, fragment = Length}) -> + decode_tls_records(Versions, Q, [], Type, Version, Length). + +%% Generic code path +decode_tls_records(Versions, {_,Size,_} = Q0, Acc, undefined, _Version, _Length) -> + if + 5 =< Size -> + {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(5, Q0), + validate_tls_records_type(Versions, Q, Acc, Type, {MajVer,MinVer}, Length); + 3 =< Size -> + {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(3, Q0), + validate_tls_records_type(Versions, Q, Acc, Type, {MajVer,MinVer}, undefined); + 1 =< Size -> + {<<?BYTE(Type)>>, Q} = binary_from_front(1, Q0), + validate_tls_records_type(Versions, Q, Acc, Type, undefined, undefined); + true -> + validate_tls_records_type(Versions, Q0, Acc, undefined, undefined, undefined) + end; +decode_tls_records(Versions, {_,Size,_} = Q0, Acc, Type, undefined, _Length) -> + if + 4 =< Size -> + {<<?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(4, Q0), + validate_tls_record_version(Versions, Q, Acc, Type, {MajVer,MinVer}, Length); + 2 =< Size -> + {<<?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(2, Q0), + validate_tls_record_version(Versions, Q, Acc, Type, {MajVer,MinVer}, undefined); + true -> + validate_tls_record_version(Versions, Q0, Acc, Type, undefined, undefined) + end; +decode_tls_records(Versions, {_,Size,_} = Q0, Acc, Type, Version, undefined) -> + if + 2 =< Size -> + {<<?UINT16(Length)>>, Q} = binary_from_front(2, Q0), + validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + true -> + validate_tls_record_length(Versions, Q0, Acc, Type, Version, undefined) + end; +decode_tls_records(Versions, Q, Acc, Type, Version, Length) -> + validate_tls_record_length(Versions, Q, Acc, Type, Version, Length). + +validate_tls_records_type(_Versions, Q, Acc, undefined, _Version, _Length) -> + {lists:reverse(Acc), + {undefined, Q}}; +validate_tls_records_type(Versions, Q, Acc, Type, Version, Length) -> + if + ?KNOWN_RECORD_TYPE(Type) -> + validate_tls_record_version(Versions, Q, Acc, Type, Version, Length); + true -> + %% Not ?KNOWN_RECORD_TYPE(Type) + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE) + end. + +validate_tls_record_version(_Versions, Q, Acc, Type, undefined, _Length) -> + {lists:reverse(Acc), + {#ssl_tls{type = Type, version = undefined, fragment = undefined}, Q}}; +validate_tls_record_version(Versions, Q, Acc, Type, Version, Length) -> + if + is_list(Versions) -> + case is_acceptable_version(Version, Versions) of + true -> + validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; + Version =:= Versions -> + %% Exact version match + validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + true -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end. + +validate_tls_record_length(_Versions, Q, Acc, Type, Version, undefined) -> + {lists:reverse(Acc), + {#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}}; +validate_tls_record_length(Versions, {_,Size0,_} = Q0, Acc, Type, Version, Length) -> + if + Length =< ?MAX_CIPHER_TEXT_LENGTH -> + if + Length =< Size0 -> + %% Complete record + {Fragment, Q} = binary_from_front(Length, Q0), + Record = #ssl_tls{type = Type, version = Version, fragment = Fragment}, + decode_tls_records(Versions, Q, [Record|Acc], undefined, undefined, undefined); + true -> + {lists:reverse(Acc), + {#ssl_tls{type = Type, version = Version, fragment = Length}, Q0}} + end; + true -> + ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW) + end. + + +binary_from_front(SplitSize, {Front,Size,Rear}) -> + binary_from_front(SplitSize, Front, Size, Rear, []). +%% +binary_from_front(SplitSize, [], Size, [_] = Rear, Acc) -> + %% Optimize a simple case + binary_from_front(SplitSize, Rear, Size, [], Acc); +binary_from_front(SplitSize, [], Size, Rear, Acc) -> + binary_from_front(SplitSize, lists:reverse(Rear), Size, [], Acc); +binary_from_front(SplitSize, [Bin|Front], Size, Rear, []) -> + %% Optimize a frequent case + BinSize = byte_size(Bin), + if + SplitSize < BinSize -> + {RetBin, Rest} = erlang:split_binary(Bin, SplitSize), + {RetBin, {[Rest|Front],Size - SplitSize,Rear}}; + BinSize < SplitSize -> + binary_from_front(SplitSize - BinSize, Front, Size, Rear, [Bin]); + true -> % Perfect fit + {Bin, {Front,Size - SplitSize,Rear}} + end; +binary_from_front(SplitSize, [Bin|Front], Size, Rear, Acc) -> + BinSize = byte_size(Bin), + if + SplitSize < BinSize -> + {Last, Rest} = erlang:split_binary(Bin, SplitSize), + RetBin = iolist_to_binary(lists:reverse(Acc, [Last])), + {RetBin, {[Rest|Front],Size - byte_size(RetBin),Rear}}; + BinSize < SplitSize -> + binary_from_front(SplitSize - BinSize, Front, Size, Rear, [Bin|Acc]); + true -> % Perfect fit + RetBin = iolist_to_binary(lists:reverse(Acc, [Bin])), + {RetBin, {Front,Size - byte_size(RetBin),Rear}} + end. + +%%-------------------------------------------------------------------- +encode_plain_text(Type, Version, Data, ConnectionStates0) -> + {[CipherText],ConnectionStates} = encode_fragments(Type, Version, [Data], ConnectionStates0), + {CipherText,ConnectionStates}. +%%-------------------------------------------------------------------- +encode_fragments(Type, Version, Data, + #{current_write := #{compression_state := CompS, + cipher_state := CipherS, + sequence_number := Seq}} = ConnectionStates) -> + encode_fragments(Type, Version, Data, ConnectionStates, CompS, CipherS, Seq, []). +%% +encode_fragments(_Type, _Version, [], #{current_write := WriteS} = CS, + CompS, CipherS, Seq, CipherFragments) -> + {lists:reverse(CipherFragments), + CS#{current_write := WriteS#{compression_state := CompS, + cipher_state := CipherS, + sequence_number := Seq}}}; +encode_fragments(Type, Version, [Text|Data], + #{current_write := #{security_parameters := + #security_parameters{cipher_type = ?AEAD, + bulk_cipher_algorithm = BCAlg, + compression_algorithm = CompAlg} = SecPars}} = CS, + CompS0, CipherS0, Seq, CipherFragments) -> + {CompText, CompS} = ssl_record:compress(CompAlg, Text, CompS0), + SeqBin = <<?UINT64(Seq)>>, + CipherS1 = ssl_record:nonce_seed(BCAlg, SeqBin, CipherS0), + {MajVer, MinVer} = Version, + VersionBin = <<?BYTE(MajVer), ?BYTE(MinVer)>>, + StartAdditionalData = <<SeqBin/binary, ?BYTE(Type), VersionBin/binary>>, + {CipherFragment,CipherS} = ssl_record:cipher_aead(Version, CompText, CipherS1, StartAdditionalData, SecPars), + Length = byte_size(CipherFragment), + CipherHeader = <<?BYTE(Type), VersionBin/binary, ?UINT16(Length)>>, + encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1, + [[CipherHeader, CipherFragment] | CipherFragments]); +encode_fragments(Type, Version, [Text|Data], + #{current_write := #{security_parameters := + #security_parameters{compression_algorithm = CompAlg, + mac_algorithm = MacAlgorithm} = SecPars, + mac_secret := MacSecret}} = CS, + CompS0, CipherS0, Seq, CipherFragments) -> + {CompText, CompS} = ssl_record:compress(CompAlg, Text, CompS0), + MacHash = ssl_cipher:calc_mac_hash(Type, Version, CompText, MacAlgorithm, MacSecret, Seq), + {CipherFragment,CipherS} = ssl_record:cipher(Version, CompText, CipherS0, MacHash, SecPars), + Length = byte_size(CipherFragment), + {MajVer, MinVer} = Version, + CipherHeader = <<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, + encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1, + [[CipherHeader, CipherFragment] | CipherFragments]); +encode_fragments(_Type, _Version, _Data, CS, _CompS, _CipherS, _Seq, _CipherFragments) -> exit({cs, CS}). %%-------------------------------------------------------------------- -start_additional_data(Type, {MajVer, MinVer}, - #{sequence_number := SeqNo}) -> - <<?UINT64(SeqNo), ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>. %% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are %% not vulnerable to this attack. -split_bin(<<FirstByte:8, Rest/binary>>, Version, BCA, one_n_minus_one) when - BCA =/= ?RC4 andalso ({3, 1} == Version orelse - {3, 0} == Version) -> - [[FirstByte]|do_split_bin(Rest)]; +split_iovec([<<FirstByte:8, Rest/binary>>|Data], Version, BCA, one_n_minus_one) + when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse + {3, 0} == Version) -> + [[FirstByte]|split_iovec([Rest|Data])]; %% 0/n splitting countermeasure for clients that are incompatible with 1/n-1 %% splitting. -split_bin(Bin, Version, BCA, zero_n) when - BCA =/= ?RC4 andalso ({3, 1} == Version orelse - {3, 0} == Version) -> - [<<>>|do_split_bin(Bin)]; -split_bin(Bin, _, _, _) -> - do_split_bin(Bin). - -do_split_bin(<<>>) -> []; -do_split_bin(Bin) -> - case Bin of - <<Chunk:?MAX_PLAIN_TEXT_LENGTH/binary, Rest/binary>> -> - [Chunk|do_split_bin(Rest)]; - _ -> - [Bin] - end. +split_iovec(Data, Version, BCA, zero_n) + when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse + {3, 0} == Version) -> + [<<>>|split_iovec(Data)]; +split_iovec(Data, _Version, _BCA, _BeatMitigation) -> + split_iovec(Data). + +split_iovec([]) -> + []; +split_iovec(Data) -> + {Part,Rest} = split_iovec(Data, ?MAX_PLAIN_TEXT_LENGTH, []), + [Part|split_iovec(Rest)]. +%% +split_iovec([Bin|Data], SplitSize, Acc) -> + BinSize = byte_size(Bin), + if + SplitSize < BinSize -> + {Last, Rest} = erlang:split_binary(Bin, SplitSize), + {lists:reverse(Acc, [Last]), [Rest|Data]}; + BinSize < SplitSize -> + split_iovec(Data, SplitSize - BinSize, [Bin|Acc]); + true -> % Perfect match + {lists:reverse(Acc, [Bin]), Data} + end; +split_iovec([], _SplitSize, Acc) -> + {lists:reverse(Acc),[]}. + %%-------------------------------------------------------------------- lowest_list_protocol_version(Ver, []) -> Ver; diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index ee2b7be4f4..c07b7f49cd 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ %% API -export([start/0, start/1, initialize/2, send_data/2, send_alert/2, - send_and_ack_alert/2, setopts/2, renegotiate/1, + send_and_ack_alert/2, setopts/2, renegotiate/1, peer_renegotiate/1, downgrade/2, update_connection_state/3, dist_tls_socket/1, dist_handshake_complete/3]). %% gen_statem callbacks @@ -38,19 +38,23 @@ -define(SERVER, ?MODULE). --record(data, {connection_pid, - connection_states = #{}, - role, - socket, - socket_options, - tracker, - protocol_cb, - transport_cb, - negotiated_version, - renegotiate_at, - connection_monitor, - dist_handle - }). +-record(static, + {connection_pid, + role, + socket, + socket_options, + tracker, + transport_cb, + negotiated_version, + renegotiate_at, + connection_monitor, + dist_handle + }). + +-record(data, + {static = #static{}, + connection_states = #{} + }). %%%=================================================================== %%% API @@ -118,6 +122,15 @@ setopts(Pid, Opts) -> renegotiate(Pid) -> %% Needs error handling for external API call(Pid, renegotiate). + +%%-------------------------------------------------------------------- +-spec peer_renegotiate(pid()) -> {ok, WriteState::map()} | {error, term()}. +%% Description: So TLS connection process can synchronize the +%% encryption state to be used when handshaking. +%%-------------------------------------------------------------------- +peer_renegotiate(Pid) -> + gen_statem:call(Pid, renegotiate, ?DEFAULT_TIMEOUT). + %%-------------------------------------------------------------------- -spec update_connection_state(pid(), WriteState::map(), tls_record:tls_version()) -> ok. %% Description: So TLS connection process can synchronize the @@ -125,6 +138,21 @@ renegotiate(Pid) -> %%-------------------------------------------------------------------- update_connection_state(Pid, NewState, Version) -> gen_statem:cast(Pid, {new_write, NewState, Version}). + +%%-------------------------------------------------------------------- +-spec downgrade(pid(), integer()) -> {ok, ssl_record:connection_state()} + | {error, timeout}. +%% Description: So TLS connection process can synchronize the +%% encryption state to be used when sending application data. +%%-------------------------------------------------------------------- +downgrade(Pid, Timeout) -> + try gen_statem:call(Pid, downgrade, Timeout) of + Result -> + Result + catch + _:_ -> + {error, timeout} + end. %%-------------------------------------------------------------------- -spec dist_handshake_complete(pid(), node(), term()) -> ok. %% Description: Erlang distribution callback @@ -147,6 +175,10 @@ dist_tls_socket(Pid) -> callback_mode() -> state_functions. + +-define(HANDLE_COMMON, + ?FUNCTION_NAME(Type, Msg, StateData) -> + handle_common(Type, Msg, StateData)). %%-------------------------------------------------------------------- -spec init(Args :: term()) -> gen_statem:init_result(atom()). @@ -168,39 +200,35 @@ init({call, From}, {Pid, #{current_write := WriteState, socket := Socket, socket_options := SockOpts, tracker := Tracker, - protocol_cb := Connection, transport_cb := Transport, negotiated_version := Version, renegotiate_at := RenegotiateAt}}, - #data{connection_states = ConnectionStates} = StateData0) -> + #data{connection_states = ConnectionStates, static = Static0} = StateData0) -> Monitor = erlang:monitor(process, Pid), StateData = - StateData0#data{connection_pid = Pid, - connection_monitor = Monitor, - connection_states = - ConnectionStates#{current_write => WriteState}, - role = Role, - socket = Socket, - socket_options = SockOpts, - tracker = Tracker, - protocol_cb = Connection, - transport_cb = Transport, - negotiated_version = Version, - renegotiate_at = RenegotiateAt}, + StateData0#data{connection_states = ConnectionStates#{current_write => WriteState}, + static = Static0#static{connection_pid = Pid, + connection_monitor = Monitor, + role = Role, + socket = Socket, + socket_options = SockOpts, + tracker = Tracker, + transport_cb = Transport, + negotiated_version = Version, + renegotiate_at = RenegotiateAt}}, {next_state, handshake, StateData, [{reply, From, ok}]}; -init(info, Msg, StateData) -> - handle_info(Msg, ?FUNCTION_NAME, StateData). +init(_, _, _) -> + %% Just in case anything else sneeks through + {keep_state_and_data, [postpone]}. + %%-------------------------------------------------------------------- -spec connection(gen_statem:event_type(), Msg :: term(), StateData :: term()) -> gen_statem:event_handler_result(atom()). %%-------------------------------------------------------------------- -connection({call, From}, renegotiate, - #data{connection_states = #{current_write := Write}} = StateData) -> - {next_state, handshake, StateData, [{reply, From, {ok, Write}}]}; connection({call, From}, {application_data, AppData}, - #data{socket_options = #socket_options{packet = Packet}} = + #data{static = #static{socket_options = #socket_options{packet = Packet}}} = StateData) -> case encode_packet(Packet, AppData) of {error, _} = Error -> @@ -208,37 +236,40 @@ connection({call, From}, {application_data, AppData}, Data -> send_application_data(Data, From, ?FUNCTION_NAME, StateData) end; -connection({call, From}, {set_opts, _} = Call, StateData) -> - handle_call(From, Call, ?FUNCTION_NAME, StateData); +connection({call, From}, {ack_alert, #alert{} = Alert}, StateData0) -> + StateData = send_tls_alert(Alert, StateData0), + {next_state, ?FUNCTION_NAME, StateData, + [{reply,From,ok}]}; +connection({call, From}, renegotiate, + #data{connection_states = #{current_write := Write}} = StateData) -> + {next_state, handshake, StateData, [{reply, From, {ok, Write}}]}; +connection({call, From}, downgrade, #data{connection_states = + #{current_write := Write}} = StateData) -> + {next_state, death_row, StateData, [{reply,From, {ok, Write}}]}; +connection({call, From}, {set_opts, Opts}, StateData) -> + handle_set_opts(From, Opts, StateData); connection({call, From}, dist_get_tls_socket, - #data{protocol_cb = Connection, - transport_cb = Transport, - socket = Socket, - connection_pid = Pid, - tracker = Tracker} = StateData) -> - TLSSocket = Connection:socket([Pid, self()], Transport, Socket, Connection, Tracker), + #data{static = #static{transport_cb = Transport, + socket = Socket, + connection_pid = Pid, + tracker = Tracker}} = StateData) -> + TLSSocket = tls_connection:socket([Pid, self()], Transport, Socket, Tracker), {next_state, ?FUNCTION_NAME, StateData, [{reply, From, {ok, TLSSocket}}]}; connection({call, From}, {dist_handshake_complete, _Node, DHandle}, - #data{connection_pid = Pid, - socket_options = #socket_options{packet = Packet}} = - StateData) -> + #data{static = #static{connection_pid = Pid} = Static} = StateData) -> ok = erlang:dist_ctrl_input_handler(DHandle, Pid), ok = ssl_connection:dist_handshake_complete(Pid, DHandle), %% From now on we execute on normal priority process_flag(priority, normal), - {next_state, ?FUNCTION_NAME, StateData#data{dist_handle = DHandle}, - [{reply, From, ok} - | case dist_data(DHandle, Packet) of - [] -> - []; - Data -> - [{next_event, internal, - {application_packets,{self(),undefined},Data}}] - end]}; -connection({call, From}, {ack_alert, #alert{} = Alert}, StateData0) -> - StateData = send_tls_alert(Alert, StateData0), - {next_state, ?FUNCTION_NAME, StateData, - [{reply,From,ok}]}; + {keep_state, StateData#data{static = Static#static{dist_handle = DHandle}}, + [{reply,From,ok}| + case dist_data(DHandle) of + [] -> + []; + Data -> + [{next_event, internal, + {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}] + end]}; connection(internal, {application_packets, From, Data}, StateData) -> send_application_data(Data, From, ?FUNCTION_NAME, StateData); %% @@ -246,29 +277,26 @@ connection(cast, #alert{} = Alert, StateData0) -> StateData = send_tls_alert(Alert, StateData0), {next_state, ?FUNCTION_NAME, StateData}; connection(cast, {new_write, WritesState, Version}, - #data{connection_states = ConnectionStates0} = StateData) -> + #data{connection_states = ConnectionStates, static = Static} = StateData) -> {next_state, connection, StateData#data{connection_states = - ConnectionStates0#{current_write => WritesState}, - negotiated_version = Version}}; + ConnectionStates#{current_write => WritesState}, + static = Static#static{negotiated_version = Version}}}; %% -connection(info, dist_data, - #data{dist_handle = DHandle, - socket_options = #socket_options{packet = Packet}} = - StateData) -> - {next_state, ?FUNCTION_NAME, StateData, - case dist_data(DHandle, Packet) of +connection(info, dist_data, #data{static = #static{dist_handle = DHandle}}) -> + {keep_state_and_data, + case dist_data(DHandle) of [] -> []; Data -> [{next_event, internal, - {application_packets,{self(),undefined},Data}}] + {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}] end}; connection(info, tick, StateData) -> consume_ticks(), - {next_state, ?FUNCTION_NAME, StateData, - [{next_event, {call, {self(), undefined}}, - {application_data, <<>>}}]}; + Data = [<<0:32>>], % encode_packet(4, <<>>) + From = {self(), undefined}, + send_application_data(Data, From, ?FUNCTION_NAME, StateData); connection(info, {send, From, Ref, Data}, _StateData) -> %% This is for testing only! %% @@ -277,29 +305,37 @@ connection(info, {send, From, Ref, Data}, _StateData) -> From ! {Ref, ok}, {keep_state_and_data, [{next_event, {call, {self(), undefined}}, - {application_data, iolist_to_binary(Data)}}]}; -connection(info, Msg, StateData) -> - handle_info(Msg, ?FUNCTION_NAME, StateData). + {application_data, erlang:iolist_to_iovec(Data)}}]}; +?HANDLE_COMMON. + %%-------------------------------------------------------------------- -spec handshake(gen_statem:event_type(), Msg :: term(), StateData :: term()) -> gen_statem:event_handler_result(atom()). %%-------------------------------------------------------------------- -handshake({call, From}, {set_opts, _} = Call, StateData) -> - handle_call(From, Call, ?FUNCTION_NAME, StateData); +handshake({call, From}, {set_opts, Opts}, StateData) -> + handle_set_opts(From, Opts, StateData); handshake({call, _}, _, _) -> + %% Postpone all calls to the connection state + {keep_state_and_data, [postpone]}; +handshake(internal, {application_packets,_,_}, _) -> {keep_state_and_data, [postpone]}; handshake(cast, {new_write, WritesState, Version}, - #data{connection_states = ConnectionStates0} = StateData) -> + #data{connection_states = ConnectionStates, static = Static} = StateData) -> {next_state, connection, - StateData#data{connection_states = - ConnectionStates0#{current_write => WritesState}, - negotiated_version = Version}}; -handshake(internal, {application_packets,_,_}, _) -> + StateData#data{connection_states = ConnectionStates#{current_write => WritesState}, + static = Static#static{negotiated_version = Version}}}; +handshake(info, dist_data, _) -> {keep_state_and_data, [postpone]}; -handshake(info, Msg, StateData) -> - handle_info(Msg, ?FUNCTION_NAME, StateData). +handshake(info, tick, _) -> + %% Ignore - data is sent anyway during handshake + consume_ticks(), + keep_state_and_data; +handshake(info, {send, _, _, _}, _) -> + %% Testing only, OTP distribution test suites... + {keep_state_and_data, [postpone]}; +?HANDLE_COMMON. %%-------------------------------------------------------------------- -spec death_row(gen_statem:event_type(), @@ -334,49 +370,66 @@ code_change(_OldVsn, State, Data, _Extra) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -handle_call(From, {set_opts, Opts}, StateName, #data{socket_options = SockOpts} = StateData) -> - {next_state, StateName, StateData#data{socket_options = set_opts(SockOpts, Opts)}, [{reply, From, ok}]}. - -handle_info({'DOWN', Monitor, _, _, Reason}, _, - #data{connection_monitor = Monitor, - dist_handle = Handle} = StateData) when Handle =/= undefined-> - {next_state, death_row, StateData, [{state_timeout, 5000, Reason}]}; -handle_info({'DOWN', Monitor, _, _, _}, _, - #data{connection_monitor = Monitor} = StateData) -> + +handle_set_opts( + From, Opts, #data{static = #static{socket_options = SockOpts} = Static} = StateData) -> + {keep_state, StateData#data{static = Static#static{socket_options = set_opts(SockOpts, Opts)}}, + [{reply, From, ok}]}. + +handle_common( + {call, From}, {set_opts, Opts}, + #data{static = #static{socket_options = SockOpts} = Static} = StateData) -> + {keep_state, StateData#data{static = Static#static{socket_options = set_opts(SockOpts, Opts)}}, + [{reply, From, ok}]}; +handle_common( + info, {'DOWN', Monitor, _, _, Reason}, + #data{static = #static{connection_monitor = Monitor, + dist_handle = Handle}} = StateData) when Handle =/= undefined -> + {next_state, death_row, StateData, + [{state_timeout, 5000, Reason}]}; +handle_common( + info, {'DOWN', Monitor, _, _, _}, + #data{static = #static{connection_monitor = Monitor}} = StateData) -> {stop, normal, StateData}; -handle_info(_,_,_) -> +handle_common(info, Msg, _) -> + Report = + io_lib:format("TLS sender: Got unexpected info: ~p ~n", [Msg]), + error_logger:info_report(Report), + keep_state_and_data; +handle_common(Type, Msg, _) -> + Report = + io_lib:format( + "TLS sender: Got unexpected event: ~p ~n", [{Type,Msg}]), + error_logger:error_report(Report), keep_state_and_data. -send_tls_alert(Alert, #data{negotiated_version = Version, - socket = Socket, - protocol_cb = Connection, - transport_cb = Transport, - connection_states = ConnectionStates0} = StateData0) -> +send_tls_alert(#alert{} = Alert, + #data{static = #static{negotiated_version = Version, + socket = Socket, + transport_cb = Transport}, + connection_states = ConnectionStates0} = StateData0) -> {BinMsg, ConnectionStates} = - Connection:encode_alert(Alert, Version, ConnectionStates0), - Connection:send(Transport, Socket, BinMsg), + tls_record:encode_alert_record(Alert, Version, ConnectionStates0), + tls_socket:send(Transport, Socket, BinMsg), StateData0#data{connection_states = ConnectionStates}. send_application_data(Data, From, StateName, - #data{connection_pid = Pid, - socket = Socket, - dist_handle = DistHandle, - negotiated_version = Version, - protocol_cb = Connection, - transport_cb = Transport, - connection_states = ConnectionStates0, - renegotiate_at = RenegotiateAt} = StateData0) -> + #data{static = #static{connection_pid = Pid, + socket = Socket, + dist_handle = DistHandle, + negotiated_version = Version, + transport_cb = Transport, + renegotiate_at = RenegotiateAt}, + connection_states = ConnectionStates0} = StateData0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> ssl_connection:internal_renegotiation(Pid, ConnectionStates0), {next_state, handshake, StateData0, [{next_event, internal, {application_packets, From, Data}}]}; false -> - {Msgs, ConnectionStates} = - Connection:encode_data( - iolist_to_binary(Data), Version, ConnectionStates0), + {Msgs, ConnectionStates} = tls_record:encode_data(Data, Version, ConnectionStates0), StateData = StateData0#data{connection_states = ConnectionStates}, - case Connection:send(Transport, Socket, Msgs) of + case tls_socket:send(Transport, Socket, Msgs) of ok when DistHandle =/= undefined -> {next_state, StateName, StateData, []}; Reason when DistHandle =/= undefined -> @@ -392,9 +445,9 @@ send_application_data(Data, From, StateName, encode_packet(Packet, Data) -> Len = iolist_size(Data), case Packet of - 1 when Len < (1 bsl 8) -> [<<Len:8>>,Data]; - 2 when Len < (1 bsl 16) -> [<<Len:16>>,Data]; - 4 when Len < (1 bsl 32) -> [<<Len:32>>,Data]; + 1 when Len < (1 bsl 8) -> [<<Len:8>>|Data]; + 2 when Len < (1 bsl 16) -> [<<Len:16>>|Data]; + 4 when Len < (1 bsl 32) -> [<<Len:32>>|Data]; N when N =:= 1; N =:= 2; N =:= 4 -> {error, {badarg, {packet_to_large, Len, (1 bsl (Packet bsl 3)) - 1}}}; @@ -431,22 +484,30 @@ call(FsmPid, Event) -> {error, closed} end. -%%---------------Erlang distribution -------------------------------------- +%%-------------- Erlang distribution helpers ------------------------------ -dist_data(DHandle, Packet) -> +dist_data(DHandle) -> case erlang:dist_ctrl_get_data(DHandle) of none -> erlang:dist_ctrl_get_data_notification(DHandle), []; - Data -> - %% This is encode_packet(4, Data) without Len check - %% since the emulator will always deliver a Data - %% smaller than 4 GB, and the distribution will - %% therefore always have to use {packet,4} + %% This is encode_packet(4, Data) without Len check + %% since the emulator will always deliver a Data + %% smaller than 4 GB, and the distribution will + %% therefore always have to use {packet,4} + Data when is_binary(Data) -> + Len = byte_size(Data), + [[<<Len:32>>,Data]|dist_data(DHandle)]; + [BA,BB] = Data -> + Len = byte_size(BA) + byte_size(BB), + [[<<Len:32>>|Data]|dist_data(DHandle)]; + Data when is_list(Data) -> Len = iolist_size(Data), - [<<Len:32>>,Data|dist_data(DHandle, Packet)] + [[<<Len:32>>|Data]|dist_data(DHandle)] end. + +%% Empty the inbox from distribution ticks - do not let them accumulate consume_ticks() -> receive tick -> consume_ticks() diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl index a391bc53de..c3c41d3e12 100644 --- a/lib/ssl/src/tls_socket.erl +++ b/lib/ssl/src/tls_socket.erl @@ -32,6 +32,7 @@ emulated_socket_options/2, get_emulated_opts/1, set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3]). +-export([update_active_n/2]). -record(state, { emulated_opts, @@ -51,7 +52,9 @@ listen(Transport, Port, #config{transport_info = {Transport, _, _, _}, case Transport:listen(Port, Options ++ internal_inet_values()) of {ok, ListenSocket} -> {ok, Tracker} = inherit_tracker(ListenSocket, EmOpts, SslOpts), - {ok, #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}}}; + Socket = #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}}, + check_active_n(EmOpts, Socket), + {ok, Socket}; Err = {error, _} -> Err end. @@ -117,14 +120,16 @@ socket(Pids, Transport, Socket, ConnectionCb, Tracker) -> #sslsocket{pid = Pids, %% "The name "fd" is keept for backwards compatibility fd = {Transport, Socket, ConnectionCb, Tracker}}. -setopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) -> +setopts(gen_tcp, Socket = #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) -> {SockOpts, EmulatedOpts} = split_options(Options), ok = set_emulated_opts(Tracker, EmulatedOpts), + check_active_n(EmulatedOpts, Socket), inet:setopts(ListenSocket, SockOpts); -setopts(_, #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}, +setopts(_, Socket = #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}, emulated = Tracker}}}, Options) -> {SockOpts, EmulatedOpts} = split_options(Options), ok = set_emulated_opts(Tracker, EmulatedOpts), + check_active_n(EmulatedOpts, Socket), Transport:setopts(ListenSocket, SockOpts); %%% Following clauses will not be called for emulated options, they are handled in the connection process setopts(gen_tcp, Socket, Options) -> @@ -132,6 +137,31 @@ setopts(gen_tcp, Socket, Options) -> setopts(Transport, Socket, Options) -> Transport:setopts(Socket, Options). +check_active_n(EmulatedOpts, Socket = #sslsocket{pid = {_, #config{emulated = Tracker}}}) -> + %% We check the resulting options to send an ssl_passive message if necessary. + case proplists:lookup(active, EmulatedOpts) of + %% The provided value is out of bound. + {_, N} when is_integer(N), N < -32768 -> + throw(einval); + {_, N} when is_integer(N), N > 32767 -> + throw(einval); + {_, N} when is_integer(N) -> + case get_emulated_opts(Tracker, [active]) of + [{_, false}] -> + self() ! {ssl_passive, Socket}, + ok; + %% The result of the addition is out of bound. + [{_, A}] when is_integer(A), A < -32768 -> + throw(einval); + [{_, A}] when is_integer(A), A > 32767 -> + throw(einval); + _ -> + ok + end; + _ -> + ok + end. + getopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) -> {SockOptNames, EmulatedOptNames} = split_options(Options), EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames), @@ -209,7 +239,7 @@ start_link(Port, SockOpts, SslOpts) -> init([Port, Opts, SslOpts]) -> process_flag(trap_exit, true), true = link(Port), - {ok, #state{emulated_opts = Opts, port = Port, ssl_opts = SslOpts}}. + {ok, #state{emulated_opts = do_set_emulated_opts(Opts, []), port = Port, ssl_opts = SslOpts}}. %%-------------------------------------------------------------------- -spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}. @@ -304,9 +334,24 @@ split_options([Name | Opts], Emu, SocketOptNames, EmuOptNames) -> do_set_emulated_opts([], Opts) -> Opts; +do_set_emulated_opts([{active, N0} | Rest], Opts) when is_integer(N0) -> + N = update_active_n(N0, proplists:get_value(active, Opts, false)), + do_set_emulated_opts(Rest, [{active, N} | proplists:delete(active, Opts)]); do_set_emulated_opts([{Name,_} = Opt | Rest], Opts) -> do_set_emulated_opts(Rest, [Opt | proplists:delete(Name, Opts)]). +update_active_n(New, Current) -> + if + is_integer(Current), New + Current =< 0 -> + false; + is_integer(Current) -> + New + Current; + New =< 0 -> + false; + true -> + New + end. + get_socket_opts(_, [], _) -> []; get_socket_opts(ListenSocket, SockOptNames, Cb) -> @@ -366,6 +411,9 @@ validate_inet_option(header, Value) when not is_integer(Value) -> throw({error, {options, {header,Value}}}); validate_inet_option(active, Value) + when Value >= -32768, Value =< 32767 -> + ok; +validate_inet_option(active, Value) when Value =/= true, Value =/= false, Value =/= once -> throw({error, {options, {active,Value}}}); validate_inet_option(_, _) -> diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index 9dfb2eba53..a10f71a3de 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -29,7 +29,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # Application version # ---------------------------------------------------- include ../vsn.mk -VSN=$(GS_VSN) +VSN=$(SSL_VSN) # ---------------------------------------------------- # Target Specs diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 8fe7c54549..7f3371da9a 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -189,6 +189,18 @@ gencrl(Root, CA, C, CrlHours) -> Env = [{"ROOTDIR", filename:absname(Root)}], cmd(Cmd, Env). +%% This function sets the number of seconds until the next CRL is due. +gencrl_sec(Root, CA, C, CrlSecs) -> + CACnfFile = filename:join([Root, CA, "ca.cnf"]), + CACRLFile = filename:join([Root, CA, "crl.pem"]), + Cmd = [C#config.openssl_cmd, " ca" + " -gencrl ", + " -crlsec ", integer_to_list(CrlSecs), + " -out ", CACRLFile, + " -config ", CACnfFile], + Env = [{"ROOTDIR", filename:absname(Root)}], + cmd(Cmd, Env). + can_generate_expired_crls(C) -> %% OpenSSL can generate CRLs with an expiration date in the past, %% if we pass a negative number for -crlhours. However, LibreSSL diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl index a5309e866b..ca8d0ec70c 100644 --- a/lib/ssl/test/ssl_ECC_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_SUITE.erl @@ -212,53 +212,61 @@ client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) -> ecc_default_order(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], - ecdhe_ecdsa, ecdhe_ecdsa, Config), + ecdhe_ecdsa, ecdhe_ecdsa, + Config, DefaultCurve), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), ECCOpts = [], - case ssl_test_lib:supported_eccs([{eccs, [sect571r1]}]) of - true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config); + case ssl_test_lib:supported_eccs([{eccs, [DefaultCurve]}]) of + true -> ssl_test_lib:ecc_test(DefaultCurve, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} end. ecc_default_order_custom_curves(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], - ecdhe_ecdsa, ecdhe_ecdsa, Config), + ecdhe_ecdsa, ecdhe_ecdsa, + Config, DefaultCurve), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{eccs, [secp256r1, sect571r1]}], + ECCOpts = [{eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of - true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config); + true -> ssl_test_lib:ecc_test(DefaultCurve, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} end. ecc_client_order(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], - ecdhe_ecdsa, ecdhe_ecdsa, Config), + ecdhe_ecdsa, ecdhe_ecdsa, + Config, DefaultCurve), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), ECCOpts = [{honor_ecc_order, false}], - case ssl_test_lib:supported_eccs([{eccs, [sect571r1]}]) of - true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config); + case ssl_test_lib:supported_eccs([{eccs, [DefaultCurve]}]) of + true -> ssl_test_lib:ecc_test(DefaultCurve, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} end. ecc_client_order_custom_curves(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], - ecdhe_ecdsa, ecdhe_ecdsa, Config), + ecdhe_ecdsa, ecdhe_ecdsa, + Config, DefaultCurve), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, false}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, false}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of - true -> ssl_test_lib:ecc_test(sect571r1, COpts, SOpts, [], ECCOpts, Config); + true -> ssl_test_lib:ecc_test(DefaultCurve, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} end. @@ -274,12 +282,13 @@ ecc_unknown_curve(Config) -> client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdh_rsa, ecdhe_ecdsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} @@ -287,12 +296,13 @@ client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) -> client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdh_rsa, ecdhe_rsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config); @@ -301,12 +311,13 @@ client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) -> client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_rsa, ecdhe_ecdsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} @@ -314,19 +325,21 @@ client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) -> client_ecdhe_rsa_server_ecdhe_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_rsa, ecdhe_rsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} end. client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, [[], [], [{extensions, Ext}]]}, {client_chain, Default}], @@ -334,8 +347,8 @@ client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) -> COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], - Expected = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), %% The certificate curve + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], + Expected = secp256r1, %% The certificate curve case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(Expected, COpts, SOpts, [], ECCOpts, Config); @@ -344,12 +357,13 @@ client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) -> client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} @@ -357,12 +371,13 @@ client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) -> client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_rsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, sect571r1]}], + ECCOpts = [{honor_ecc_order, true}, {eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, [], ECCOpts, Config); false -> {skip, "unsupported named curves"} @@ -370,12 +385,13 @@ client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) -> client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_ecdsa, ecdhe_ecdsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{eccs, [secp256r1, sect571r1]}], + ECCOpts = [{eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config); false -> {skip, "unsupported named curves"} @@ -383,12 +399,13 @@ client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) -> client_ecdhe_rsa_server_ecdhe_ecdsa_client_custom(Config) -> Default = ssl_test_lib:default_cert_chain_conf(), + DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))), {COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default}, {client_chain, Default}], ecdhe_rsa, ecdhe_ecdsa, Config), COpts = ssl_test_lib:ssl_options(COpts0, Config), SOpts = ssl_test_lib:ssl_options(SOpts0, Config), - ECCOpts = [{eccs, [secp256r1, sect571r1]}], + ECCOpts = [{eccs, [secp256r1, DefaultCurve]}], case ssl_test_lib:supported_eccs(ECCOpts) of true -> ssl_test_lib:ecc_test(secp256r1, COpts, SOpts, ECCOpts, [], Config); false -> {skip, "unsupported named curves"} diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl index 04c4b257d9..dfc780479e 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl @@ -153,41 +153,41 @@ protocols_must_be_a_binary_list(Config) when is_list(Config) -> empty_client(Config) when is_list(Config) -> run_failing_handshake(Config, - [{alpn_advertised_protocols, []}], - [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}], - {error,{tls_alert,"no application protocol"}}). + [{alpn_advertised_protocols, []}], + [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}], + no_application_protocol). %-------------------------------------------------------------------------------- empty_server(Config) when is_list(Config) -> run_failing_handshake(Config, - [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}], - [{alpn_preferred_protocols, []}], - {error,{tls_alert,"no application protocol"}}). + [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}], + [{alpn_preferred_protocols, []}], + no_application_protocol). %-------------------------------------------------------------------------------- empty_client_empty_server(Config) when is_list(Config) -> run_failing_handshake(Config, - [{alpn_advertised_protocols, []}], - [{alpn_preferred_protocols, []}], - {error,{tls_alert,"no application protocol"}}). + [{alpn_advertised_protocols, []}], + [{alpn_preferred_protocols, []}], + no_application_protocol). %-------------------------------------------------------------------------------- no_matching_protocol(Config) when is_list(Config) -> run_failing_handshake(Config, - [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}], - [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}], - {error,{tls_alert,"no application protocol"}}). + [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}], + [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}], + no_application_protocol). %-------------------------------------------------------------------------------- client_alpn_and_server_alpn(Config) when is_list(Config) -> run_handshake(Config, - [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}], - [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}], - {ok, <<"http/1.1">>}). + [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}], + [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}], + {ok, <<"http/1.1">>}). %-------------------------------------------------------------------------------- @@ -262,52 +262,12 @@ client_renegotiate(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- session_reused(Config) when is_list(Config)-> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{alpn_advertised_protocols, [<<"http/1.0">>]}] ++ ClientOpts0, ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result_msg, []}}, - {options, ClientOpts}]), - - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:fail(Other) - end, - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). %-------------------------------------------------------------------------------- alpn_not_supported_client(Config) when is_list(Config) -> @@ -337,7 +297,7 @@ alpn_not_supported_server(Config) when is_list(Config)-> %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- -run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedResult) -> +run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedAlert) -> ClientOpts = ClientExtraOpts ++ ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ServerExtraOpts ++ ssl_test_lib:ssl_options(server_rsa_opts, Config), @@ -353,8 +313,7 @@ run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedResult) {from, self()}, {mfa, {?MODULE, placeholder, []}}, {options, ClientOpts}]), - ssl_test_lib:check_result(Server, ExpectedResult, - Client, ExpectedResult). + ssl_test_lib:check_client_alert(Server, Client, ExpectedAlert). run_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> Data = "hello world", diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 9633800da5..e86d2eaf9e 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -164,8 +164,10 @@ api_tests() -> accept_pool, prf, socket_options, + active_n, cipher_suites, handshake_continue, + handshake_continue_timeout, hello_client_cancel, hello_server_cancel ]. @@ -242,6 +244,7 @@ error_handling_tests()-> [close_transport_accept, recv_active, recv_active_once, + recv_active_n, recv_error_handling, call_in_error_state, close_in_error_state, @@ -654,8 +657,8 @@ new_options_in_accept(Config) when is_list(Config) -> handshake_continue() -> [{doc, "Test API function ssl:handshake_continue/3"}]. handshake_continue(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), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -681,6 +684,34 @@ handshake_continue(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%------------------------------------------------------------------ +handshake_continue_timeout() -> + [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}]. +handshake_continue_timeout(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), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 1}, + {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}], + Config)}, + {continue_options, proplists:delete(reuseaddr, ServerOpts)} + ]), + + Port = ssl_test_lib:inet_port(Server), + + + {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, {error,timeout}), + ssl_test_lib:close(Server). + + %%-------------------------------------------------------------------- hello_client_cancel() -> [{doc, "Test API function ssl:handshake_cancel/1 on the client side"}]. @@ -702,19 +733,12 @@ hello_client_cancel(Config) when is_list(Config) -> {from, self()}, {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)}, {continue_options, cancel}]), - receive - {Server, {error, {tls_alert, "user canceled"}}} -> - ok; - {Server, {error, closed}} -> - ct:pal("Did not receive the ALERT"), - ok - end. - + ssl_test_lib:check_server_alert(Server, user_canceled). %%-------------------------------------------------------------------- hello_server_cancel() -> [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}]. hello_server_cancel(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -756,8 +780,8 @@ prf(Config) when is_list(Config) -> secret_connection_info() -> [{doc,"Test the API function ssl:connection_information/2"}]. secret_connection_info(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), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -838,42 +862,30 @@ controlling_process(Config) when is_list(Config) -> ClientMsg = "Server hello", ServerMsg = "Client hello", - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - controlling_process_result, [self(), - ServerMsg]}}, - {options, ServerOpts}]), + Server = ssl_test_lib:start_server([ + {node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + controlling_process_result, [self(), + ServerMsg]}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, + {Client, CSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, {mfa, {?MODULE, controlling_process_result, [self(), ClientMsg]}}, {options, ClientOpts}]), - + ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), + [self(), Client, Server]), - receive - {ssl, _, "S"} -> - receive_s_rizzo_duong_beast(); - {ssl, _, ServerMsg} -> - receive - {ssl, _, ClientMsg} -> - ok - end; - {ssl, _, "C"} -> - receive_c_rizzo_duong_beast(); - {ssl, _, ClientMsg} -> - receive - {ssl, _, ServerMsg} -> - ok - end; - Unexpected -> - ct:fail(Unexpected) - end, + ServerMsg = ssl_test_lib:active_recv(CSocket, length(ServerMsg)), + %% We do not have the TLS server socket but all messages form the client + %% socket are now read, so ramining are form the server socket + ClientMsg = ssl_active_recv(length(ClientMsg)), ssl_test_lib:close(Server), ssl_test_lib:close(Client). @@ -1195,9 +1207,8 @@ fallback(Config) when is_list(Config) -> [{fallback, true}, {versions, ['tlsv1']} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, {error,{tls_alert,"inappropriate fallback"}}, - Client, {error,{tls_alert,"inappropriate fallback"}}). + ssl_test_lib:check_server_alert(Server, Client, inappropriate_fallback). + %%-------------------------------------------------------------------- cipher_format() -> @@ -1458,8 +1469,8 @@ cipher_suites_mix() -> cipher_suites_mix(Config) when is_list(Config) -> CipherSuites = [{dhe_rsa,aes_128_cbc,sha256,sha256}, {dhe_rsa,aes_128_cbc,sha}], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1969,7 +1980,7 @@ recv_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- recv_active_once() -> - [{doc,"Test recv on active socket"}]. + [{doc,"Test recv on active (once) socket"}]. recv_active_once(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), @@ -1994,6 +2005,178 @@ recv_active_once(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- +recv_active_n() -> + [{doc,"Test recv on active (n) socket"}]. + +recv_active_n(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_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([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, try_recv_active_once, []}}, + {options, [{active, 1} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, try_recv_active_once, []}}, + {options, [{active, 1} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +%% Test case adapted from gen_tcp_misc_SUITE. +active_n() -> + [{doc,"Test {active,N} option"}]. + +active_n(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + Port = ssl_test_lib:inet_port(node()), + N = 3, + LS = ok(ssl:listen(Port, [{active,N}|ServerOpts])), + [{active,N}] = ok(ssl:getopts(LS, [active])), + active_n_common(LS, N), + Self = self(), + spawn_link(fun() -> + S0 = ok(ssl:transport_accept(LS)), + {ok, S} = ssl:handshake(S0), + ok = ssl:setopts(S, [{active,N}]), + [{active,N}] = ok(ssl:getopts(S, [active])), + ssl:controlling_process(S, Self), + Self ! {server, S} + end), + C = ok(ssl:connect("localhost", Port, [{active,N}|ClientOpts])), + [{active,N}] = ok(ssl:getopts(C, [active])), + S = receive + {server, S0} -> S0 + after + 1000 -> + exit({error, connect}) + end, + active_n_common(C, N), + active_n_common(S, N), + ok = ssl:setopts(C, [{active,N}]), + ok = ssl:setopts(S, [{active,N}]), + ReceiveMsg = fun(Socket, Msg) -> + receive + {ssl,Socket,Msg} -> + ok; + {ssl,Socket,Begin} -> + receive + {ssl,Socket,End} -> + Msg = Begin ++ End, + ok + after 1000 -> + exit(timeout) + end + after 1000 -> + exit(timeout) + end + end, + repeat(3, fun(I) -> + Msg = "message "++integer_to_list(I), + ok = ssl:send(C, Msg), + ReceiveMsg(S, Msg), + ok = ssl:send(S, Msg), + ReceiveMsg(C, Msg) + end), + receive + {ssl_passive,S} -> + [{active,false}] = ok(ssl:getopts(S, [active])) + after + 1000 -> + exit({error,ssl_passive}) + end, + receive + {ssl_passive,C} -> + [{active,false}] = ok(ssl:getopts(C, [active])) + after + 1000 -> + exit({error,ssl_passive}) + end, + LS2 = ok(ssl:listen(0, [{active,0}])), + receive + {ssl_passive,LS2} -> + [{active,false}] = ok(ssl:getopts(LS2, [active])) + after + 1000 -> + exit({error,ssl_passive}) + end, + ok = ssl:close(LS2), + ok = ssl:close(C), + ok = ssl:close(S), + ok = ssl:close(LS), + ok. + +active_n_common(S, N) -> + ok = ssl:setopts(S, [{active,-N}]), + receive + {ssl_passive, S} -> ok + after + 1000 -> + error({error,ssl_passive_failure}) + end, + [{active,false}] = ok(ssl:getopts(S, [active])), + ok = ssl:setopts(S, [{active,0}]), + receive + {ssl_passive, S} -> ok + after + 1000 -> + error({error,ssl_passive_failure}) + end, + ok = ssl:setopts(S, [{active,32767}]), + {error,{options,_}} = ssl:setopts(S, [{active,1}]), + {error,{options,_}} = ssl:setopts(S, [{active,-32769}]), + ok = ssl:setopts(S, [{active,-32768}]), + receive + {ssl_passive, S} -> ok + after + 1000 -> + error({error,ssl_passive_failure}) + end, + [{active,false}] = ok(ssl:getopts(S, [active])), + ok = ssl:setopts(S, [{active,N}]), + ok = ssl:setopts(S, [{active,true}]), + [{active,true}] = ok(ssl:getopts(S, [active])), + receive + _ -> error({error,active_n}) + after + 0 -> + ok + end, + ok = ssl:setopts(S, [{active,N}]), + ok = ssl:setopts(S, [{active,once}]), + [{active,once}] = ok(ssl:getopts(S, [active])), + receive + _ -> error({error,active_n}) + after + 0 -> + ok + end, + {error,{options,_}} = ssl:setopts(S, [{active,32768}]), + ok = ssl:setopts(S, [{active,false}]), + [{active,false}] = ok(ssl:getopts(S, [active])), + ok. + +ok({ok,V}) -> V. + +repeat(N, Fun) -> + repeat(N, N, Fun). + +repeat(N, T, Fun) when is_integer(N), N > 0 -> + Fun(T-N), + repeat(N-1, T, Fun); +repeat(_, _, _) -> + ok. + +%%-------------------------------------------------------------------- dh_params() -> [{doc,"Test to specify DH-params file in server."}]. @@ -2118,15 +2301,21 @@ tls_downgrade(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, tls_downgrade_result, []}}, + {mfa, {?MODULE, tls_downgrade_result, [self()]}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, tls_downgrade_result, []}}, + {mfa, {?MODULE, tls_downgrade_result, [self()]}}, {options, [{active, false} |ClientOpts]}]), + + ssl_test_lib:check_result(Server, ready, Client, ready), + + Server ! go, + Client ! go, + ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). @@ -2364,8 +2553,8 @@ invalid_options() -> [{doc,"Test what happens when we give invalid options"}]. invalid_options(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_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) -> @@ -2380,27 +2569,28 @@ invalid_options(Config) when is_list(Config) -> {error, {options, Option}}) end, - TestOpts = [{versions, [sslv2, sslv3]}, - {verify, 4}, - {verify_fun, function}, - {fail_if_no_peer_cert, 0}, - {verify_client_once, 1}, - {depth, four}, - {certfile, 'cert.pem'}, - {keyfile,'key.pem' }, - {password, foo}, - {cacertfile, ""}, - {dhfile,'dh.pem' }, - {ciphers, [{foo, bar, sha, ignore}]}, - {reuse_session, foo}, - {reuse_sessions, 0}, - {renegotiate_at, "10"}, - {mode, depech}, - {packet, 8.0}, - {packet_size, "2"}, - {header, a}, - {active, trice}, - {key, 'key.pem' }], + TestOpts = + [{versions, [sslv2, sslv3]}, + {verify, 4}, + {verify_fun, function}, + {fail_if_no_peer_cert, 0}, + {verify_client_once, 1}, + {depth, four}, + {certfile, 'cert.pem'}, + {keyfile,'key.pem' }, + {password, foo}, + {cacertfile, ""}, + {dhfile,'dh.pem' }, + {ciphers, [{foo, bar, sha, ignore}]}, + {reuse_session, foo}, + {reuse_sessions, 0}, + {renegotiate_at, "10"}, + {mode, depech}, + {packet, 8.0}, + {packet_size, "2"}, + {header, a}, + {active, trice}, + {key, 'key.pem' }], [begin Server = @@ -2656,8 +2846,7 @@ default_reject_anonymous(Config) when is_list(Config) -> [{ciphers,[CipherSuite]} | ClientOpts]}]), - ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}}, - Client, {error, {tls_alert, "insufficient security"}}). + ssl_test_lib:check_server_alert(Server, Client, insufficient_security). %%-------------------------------------------------------------------- ciphers_ecdsa_signed_certs() -> @@ -2693,175 +2882,69 @@ ciphers_ecdh_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> reuse_session() -> [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_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([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:log("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - ct:fail(session_not_reused) - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - Client2 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, [{reuse_sessions, false} - | ClientOpts]}]), - receive - {Client2, SessionInfo} -> - ct:fail( - session_reused_when_session_reuse_disabled_by_client); - {Client2, _} -> - ok - end, - - ssl_test_lib:close(Server), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, [{reuse_sessions, false} | ServerOpts]}]), - - Port1 = ssl_test_lib:inet_port(Server1), - Client3 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - - SessionInfo1 = - receive - {Server1, Info1} -> - Info1 - end, - - Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client4 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client4, SessionInfo1} -> - ct:fail( - session_reused_when_session_reuse_disabled_by_server); - {Client4, _Other} -> - ct:log("OTHER: ~p ~n", [_Other]), - ok - end, - - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - ssl_test_lib:close(Client3), - ssl_test_lib:close(Client4). - + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). %%-------------------------------------------------------------------- reuse_session_expired() -> [{doc,"Test sessions is not reused when it has expired"}]. reuse_session_expired(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_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port0 = ssl_test_lib:inet_port(Server0), - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + Server0 ! listen, + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + SID = receive + {Client0, Id0} -> + Id0 + end, + receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:log("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - ct:fail(session_not_reused) + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) end, - Server ! listen, - + Server0 ! listen, + %% Make sure session is unregistered due to expiration - ct:sleep((?EXPIRE+1)), - [{session_id, Id} |_] = SessionInfo, + ct:sleep((?EXPIRE*2)), - make_sure_expired(Hostname, Port, Id), + make_sure_expired(Hostname, Port0, SID), Client2 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, {from, self()}, {options, ClientOpts}]), receive - {Client2, SessionInfo} -> + {Client2, SID} -> ct:fail(session_reused_when_session_expired); {Client2, _} -> ok end, process_flag(trap_exit, false), - ssl_test_lib:close(Server), + ssl_test_lib:close(Server0), ssl_test_lib:close(Client0), ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). @@ -2870,16 +2953,16 @@ make_sure_expired(Host, Port, Id) -> {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), - Cache = element(2, State), + ClientCache = element(2, State), - case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of + case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of undefined -> - ok; + ok; #session{is_resumable = false} -> - ok; + ok; _ -> ct:sleep(?SLEEP), - make_sure_expired(Host, Port, Id) + make_sure_expired(Host, Port, Id) end. %%-------------------------------------------------------------------- @@ -3615,8 +3698,7 @@ no_common_signature_algs(Config) when is_list(Config) -> {options, [{signature_algs, [{sha384, rsa}]} | ClientOpts]}]), - ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}}, - Client, {error, {tls_alert, "insufficient security"}}). + ssl_test_lib:check_server_alert(Server, Client, insufficient_security). %%-------------------------------------------------------------------- @@ -3647,7 +3729,7 @@ tls_dont_crash_on_handshake_garbage(Config) -> <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage ]), % Send unexpected change_cipher_spec - ok = gen_tcp:send(Socket, <<20, 0,0,12, 111,40,244,7,137,224,16,109,197,110,249,152>>), + ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>), % Ensure we receive an alert, not sudden disconnect {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000). @@ -3980,8 +4062,8 @@ tls_tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> {status, _, _, StatusInfo} = sys:get_status(Pid), [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), - Socket = element(11, State), - + StaticEnv = element(2, State), + Socket = element(10, StaticEnv), %% Fake tcp error Pid ! {tcp_error, Socket, etimedout}, @@ -4131,6 +4213,8 @@ rizzo(Config) when is_list(Config) -> {cipher, fun(rc4_128) -> false; + (chacha20_poly1305) -> + false; (_) -> true end}]), @@ -4173,6 +4257,9 @@ rizzo_one_n_minus_one(Config) when is_list(Config) -> {cipher, fun(rc4_128) -> false; + %% TODO: remove this clause when chacha is fixed! + (chacha20_poly1305) -> + false; (_) -> true end}]), @@ -4314,8 +4401,7 @@ tls_versions_option(Config) when is_list(Config) -> {Server, _} -> ok end, - - ssl_test_lib:check_result(ErrClient, {error, {tls_alert, "protocol version"}}). + ssl_test_lib:check_client_alert(ErrClient, protocol_version). %%-------------------------------------------------------------------- @@ -4490,8 +4576,8 @@ tcp_send_recv_result(Socket) -> ok. basic_verify_test_no_close(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -4634,19 +4720,24 @@ recv_close(Socket) -> send_recv_result_active_rizzo(Socket) -> ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end - end. + "Hello world" = ssl_test_lib:active_recv(Socket, 11), + ok. send_recv_result_active_no_rizzo(Socket) -> ssl:send(Socket, "Hello world"), + "Hello world" = ssl_test_lib:active_recv(Socket, 11), + ok. + + +ssl_active_recv(N) -> + ssl_active_recv(N, []). + +ssl_active_recv(0, Acc) -> + Acc; +ssl_active_recv(N, Acc) -> receive - {ssl, Socket, "Hello world"} -> - ok + {ssl, _, Bytes} -> + ssl_active_recv(N-length(Bytes), Acc ++ Bytes) end. result_ok(_Socket) -> @@ -4670,16 +4761,7 @@ renegotiate_reuse_session(Socket, Data) -> renegotiate(Socket, Data). renegotiate_immediately(Socket) -> - receive - {ssl, Socket, "Hello world"} -> - ok; - %% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end - end, + _ = ssl_test_lib:active_recv(Socket, 11), ok = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP), @@ -4689,17 +4771,7 @@ renegotiate_immediately(Socket) -> ok. renegotiate_rejected(Socket) -> - receive - {ssl, Socket, "Hello world"} -> - ok; - %% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast - {ssl, Socket, "H"} -> - - receive - {ssl, Socket, "ello world"} -> - ok - end - end, + _ = ssl_test_lib:active_recv(Socket, 11), {error, renegotiation_rejected} = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), @@ -4874,17 +4946,11 @@ session_loop(Sess) -> erlang_ssl_receive(Socket, Data) -> - receive - {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), - ok; - {ssl, Socket, Byte} when length(Byte) == 1 -> %% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast - io:format("Received ~p~n",[Byte]), - erlang_ssl_receive(Socket, tl(Data)); - Other -> - ct:fail({unexpected_message, Other}) - after timer:seconds(?SEC_RENEGOTIATION_TIMEOUT) * test_server:timetrap_scale_factor() -> - ct:fail({did_not_get, Data}) + case ssl_test_lib:active_recv(Socket, length(Data)) of + Data -> + ok; + Other -> + ct:fail({{expected, Data}, {got, Other}}) end. receive_msg(_) -> @@ -4901,28 +4967,6 @@ controlling_process_result(Socket, Pid, Msg) -> ssl:send(Socket, Msg), no_result_msg. -receive_s_rizzo_duong_beast() -> - receive - {ssl, _, "erver hello"} -> - receive - {ssl, _, "C"} -> - receive - {ssl, _, "lient hello"} -> - ok - end - end - end. -receive_c_rizzo_duong_beast() -> - receive - {ssl, _, "lient hello"} -> - receive - {ssl, _, "S"} -> - receive - {ssl, _, "erver hello"} -> - ok - end - end - end. controller_dies_result(_Socket, _Pid, _Msg) -> receive Result -> Result end. @@ -5008,16 +5052,16 @@ run_suites(Ciphers, Config, Type) -> {ClientOpts, ServerOpts} = case Type of rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_opts, Config)]}; dsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_dsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_dsa_opts, Config)]}; anonymous -> %% No certs in opts! - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options([], Config)]}; psk -> @@ -5039,46 +5083,50 @@ run_suites(Ciphers, Config, Type) -> ssl_test_lib:ssl_options(server_psk_anon_hint, Config)]}; srp -> {ssl_test_lib:ssl_options(client_srp, Config), - ssl_test_lib:ssl_options(server_srp, Config)}; + [{ciphers, Ciphers} | + ssl_test_lib:ssl_options(server_srp, Config)]}; srp_anon -> {ssl_test_lib:ssl_options(client_srp, Config), - ssl_test_lib:ssl_options(server_srp_anon, Config)}; + [{ciphers, Ciphers} | + ssl_test_lib:ssl_options(server_srp_anon, Config)]}; srp_dsa -> {ssl_test_lib:ssl_options(client_srp_dsa, Config), - ssl_test_lib:ssl_options(server_srp_dsa, Config)}; + [{ciphers, Ciphers} | + ssl_test_lib:ssl_options(server_srp_dsa, Config)]}; ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; ecdh_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), - ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}; + {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), + [{ciphers, Ciphers} | + ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; rc4_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; rc4_ecdh_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; rc4_ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; des_dhe_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_verification_opts, Config)]}; des_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; chacha_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; chacha_ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]} end, @@ -5176,23 +5224,28 @@ connect_dist_c(S) -> {ok, Test} = ssl:recv(S, 0, 10000), ok. -tls_downgrade_result(Socket) -> +tls_downgrade_result(Socket, Pid) -> ok = ssl_test_lib:send_recv_result(Socket), + Pid ! {self(), ready}, + receive + go -> + ok + end, case ssl:close(Socket, {self(), 10000}) of {ok, TCPSocket} -> - inet:setopts(TCPSocket, [{active, true}]), + inet:setopts(TCPSocket, [{active, true}]), gen_tcp:send(TCPSocket, "Downgraded"), - receive - {tcp, TCPSocket, <<"Downgraded">>} -> - ok; - {tcp_closed, TCPSocket} -> - ct:pal("Peer timed out, downgrade aborted"), - ok; - Other -> - {error, Other} - end; + receive + {tcp, TCPSocket, <<"Downgraded">>} -> + ok; + {tcp_closed, TCPSocket} -> + ct:fail("Peer timed out, downgrade aborted"), + ok; + Other -> + {error, Other} + end; {error, timeout} -> - ct:pal("Timed out, downgrade aborted"), + ct:fail("Timed out, downgrade aborted"), ok; Fail -> {error, Fail} diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 588ca153a9..c0a5367a57 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -298,15 +298,8 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {options, [{active, Active} | BadClientOpts]}]), - receive - {Server, {error, {tls_alert, "handshake failure"}}} -> - receive - {Client, {error, {tls_alert, "handshake failure"}}} -> - ok; - {Client, {error, closed}} -> - ok - end - end. + + ssl_test_lib:check_server_alert(Server, Client, handshake_failure). %%-------------------------------------------------------------------- server_require_peer_cert_empty_ok() -> @@ -365,15 +358,8 @@ server_require_peer_cert_partial_chain(Config) when is_list(Config) -> {options, [{active, Active}, {cacerts, [RootCA]} | proplists:delete(cacertfile, ClientOpts)]}]), - receive - {Server, {error, {tls_alert, "unknown ca"}}} -> - receive - {Client, {error, {tls_alert, "unknown ca"}}} -> - ok; - {Client, {error, closed}} -> - ok - end - end. + ssl_test_lib:check_server_alert(Server, Client, unknown_ca). + %%-------------------------------------------------------------------- server_require_peer_cert_allow_partial_chain() -> [{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}]. @@ -446,17 +432,7 @@ server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - - receive - {Server, {error, {tls_alert, "unknown ca"}}} -> - receive - {Client, {error, {tls_alert, "unknown ca"}}} -> - ok; - {Client, {error, closed}} -> - ok - end - end. - + ssl_test_lib:check_server_alert(Server, Client, unknown_ca). %%-------------------------------------------------------------------- server_require_peer_cert_partial_chain_fun_fail() -> [{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}]. @@ -487,16 +463,7 @@ server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - - receive - {Server, {error, {tls_alert, "unknown ca"}}} -> - receive - {Client, {error, {tls_alert, "unknown ca"}}} -> - ok; - {Client, {error, closed}} -> - ok - end - end. + ssl_test_lib:check_server_alert(Server, Client, unknown_ca). %%-------------------------------------------------------------------- verify_fun_always_run_client() -> @@ -535,14 +502,8 @@ verify_fun_always_run_client(Config) when is_list(Config) -> [{verify, verify_peer}, {verify_fun, FunAndState} | ClientOpts]}]), - %% Server 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! - receive - {Server, ServerError} -> - ct:log("Server Error ~p~n", [ServerError]) - end, - ssl_test_lib:check_result(Client, {error, {tls_alert, "handshake failure"}}). + ssl_test_lib:check_client_alert(Server, Client, handshake_failure). %%-------------------------------------------------------------------- verify_fun_always_run_server() -> @@ -581,16 +542,8 @@ verify_fun_always_run_server(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {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! - receive - {Client, ClientError} -> - ct:log("Client Error ~p~n", [ClientError]) - end, - - ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}). - + + ssl_test_lib:check_client_alert(Server, Client, handshake_failure). %%-------------------------------------------------------------------- cert_expired() -> @@ -620,8 +573,7 @@ cert_expired(Config) when is_list(Config) -> {from, self()}, {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]), - ssl_test_lib:check_result(Server, {error, {tls_alert, "certificate expired"}}, - Client, {error, {tls_alert, "certificate expired"}}). + ssl_test_lib:check_client_alert(Server, Client, certificate_expired). two_digits_str(N) when N < 10 -> lists:flatten(io_lib:format("0~p", [N])); @@ -727,12 +679,8 @@ critical_extension_verify_server(Config) when is_list(Config) -> {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]), %% This certificate has a critical extension that we don't - %% understand. Therefore, verification should fail. - - ssl_test_lib:check_result(Server, {error, {tls_alert, "unsupported certificate"}}, - Client, {error, {tls_alert, "unsupported certificate"}}), - - ssl_test_lib:close(Server). + %% understand. Therefore, verification should fail. + ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate). %%-------------------------------------------------------------------- critical_extension_verify_client() -> @@ -763,12 +711,7 @@ critical_extension_verify_client(Config) when is_list(Config) -> {mfa, {ssl_test_lib, ReceiveFunction, []}}, {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]), - %% This certificate has a critical extension that we don't - %% understand. Therefore, verification should fail. - ssl_test_lib:check_result(Server, {error, {tls_alert, "unsupported certificate"}}, - Client, {error, {tls_alert, "unsupported certificate"}}), - - ssl_test_lib:close(Server). + ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate). %%-------------------------------------------------------------------- critical_extension_verify_none() -> @@ -908,10 +851,7 @@ invalid_signature_server(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {options, [{verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}}, - Client, {error, {tls_alert, "unknown ca"}}). - + ssl_test_lib:check_server_alert(Server, Client, unknown_ca). %%-------------------------------------------------------------------- invalid_signature_client() -> @@ -946,9 +886,7 @@ invalid_signature_client(Config) when is_list(Config) -> {from, self()}, {options, NewClientOpts}]), - ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}}, - Client, {error, {tls_alert, "unknown ca"}}). - + ssl_test_lib:check_client_alert(Server, Client, unknown_ca). %%-------------------------------------------------------------------- @@ -1034,16 +972,7 @@ unknown_server_ca_fail(Config) when is_list(Config) -> [{verify, verify_peer}, {verify_fun, FunAndState} | ClientOpts]}]), - receive - {Client, {error, {tls_alert, "unknown ca"}}} -> - receive - {Server, {error, {tls_alert, "unknown ca"}}} -> - ok; - {Server, {error, closed}} -> - ok - end - end. - + ssl_test_lib:check_client_alert(Server, Client, unknown_ca). %%-------------------------------------------------------------------- unknown_server_ca_accept_verify_none() -> @@ -1193,11 +1122,7 @@ customize_hostname_check(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts} ]), - ssl_test_lib:check_result(Client1, {error, {tls_alert, "handshake failure"}}, - Server, {error, {tls_alert, "handshake failure"}}), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + ssl_test_lib:check_client_alert(Server, Client1, handshake_failure). incomplete_chain() -> [{doc,"Test option verify_peer"}]. diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl index 23c5eaf84d..b2fd3874a8 100644 --- a/lib/ssl/test/ssl_crl_SUITE.erl +++ b/lib/ssl/test/ssl_crl_SUITE.erl @@ -238,7 +238,7 @@ crl_verify_revoked(Config) when is_list(Config) -> end, crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, - "certificate revoked"). + certificate_revoked). crl_verify_no_crl() -> [{doc,"Verify a simple CRL chain when the CRL is missing"}]. @@ -277,10 +277,10 @@ crl_verify_no_crl(Config) when is_list(Config) -> %% The error "revocation status undetermined" gets turned %% into "bad certificate". crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, - "bad certificate"); + bad_certificate); peer -> crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, - "bad certificate"); + bad_certificate); best_effort -> %% In "best effort" mode, we consider the certificate not %% to be revoked if we can't find the appropriate CRL. @@ -341,7 +341,7 @@ crl_hash_dir_collision(Config) when is_list(Config) -> %% First certificate revoked; first fails, second succeeds. crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts, - "certificate revoked"), + certificate_revoked), crl_verify_valid(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts), make_certs:revoke(PrivDir, CA2, "collision-client-2", CertsConfig), @@ -352,9 +352,9 @@ crl_hash_dir_collision(Config) when is_list(Config) -> %% Second certificate revoked; both fail. crl_verify_error(Hostname, ServerNode, ServerOpts1, ClientNode, ClientOpts, - "certificate revoked"), + certificate_revoked), crl_verify_error(Hostname, ServerNode, ServerOpts2, ClientNode, ClientOpts, - "certificate revoked"), + certificate_revoked), ok. @@ -383,8 +383,11 @@ crl_hash_dir_expired(Config) when is_list(Config) -> {verify, verify_peer}], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - %% First make a CRL that expired yesterday. - make_certs:gencrl(PrivDir, CA, CertsConfig, -24), + %% First make a CRL that will expire in one second. + make_certs:gencrl_sec(PrivDir, CA, CertsConfig, 1), + %% Sleep until the next CRL is due + ct:sleep({seconds, 1}), + CrlDir = filename:join(PrivDir, "crls"), populate_crl_hash_dir(PrivDir, CrlDir, [{CA, "1627b4b0"}], @@ -397,10 +400,10 @@ crl_hash_dir_expired(Config) when is_list(Config) -> %% The error "revocation status undetermined" gets turned %% into "bad certificate". crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, - "bad certificate"); + bad_certificate); peer -> crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, - "bad certificate"); + bad_certificate); best_effort -> %% In "best effort" mode, we consider the certificate not %% to be revoked if we can't find the appropriate CRL. @@ -448,11 +451,8 @@ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, Expec {host, Hostname}, {from, self()}, {options, ClientOpts}]), - receive - {Server, AlertOrClose} -> - ct:pal("Server Alert or Close ~p", [AlertOrClose]) - end, - ssl_test_lib:check_result(Client, {error, {tls_alert, ExpectedAlert}}). + + ssl_test_lib:check_client_alert(Server, Client, ExpectedAlert). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ diff --git a/lib/ssl/test/ssl_dist_bench_SUITE.erl b/lib/ssl/test/ssl_dist_bench_SUITE.erl index 7409b69639..618ad0789e 100644 --- a/lib/ssl/test/ssl_dist_bench_SUITE.erl +++ b/lib/ssl/test/ssl_dist_bench_SUITE.erl @@ -1,7 +1,7 @@ %%%------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017-2018. All Rights Reserved. +%% Copyright Ericsson AB 2017-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ throughput_1048576/1]). %% Debug --export([payload/1]). +-export([payload/1, roundtrip_runner/3, setup_runner/3, throughput_runner/4]). %%%------------------------------------------------------------------- @@ -407,17 +407,19 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) -> [] = ssl_apply(HA, erlang, nodes, []), [] = ssl_apply(HB, erlang, nodes, []), #{time := Time, - dist_stats := DistStats, + client_dist_stats := ClientDistStats, client_msacc_stats := ClientMsaccStats, client_prof := ClientProf, server_msacc_stats := ServerMsaccStats, - server_prof := ServerProf} = + server_prof := ServerProf, + server_gc_before := Server_GC_Before, + server_gc_after := Server_GC_After} = ssl_apply(HA, fun () -> throughput_runner(A, B, Packets, Size) end), [B] = ssl_apply(HA, erlang, nodes, []), [A] = ssl_apply(HB, erlang, nodes, []), ClientMsaccStats =:= undefined orelse msacc:print(ClientMsaccStats), - io:format("DistStats: ~p~n", [DistStats]), + io:format("ClientDistStats: ~p~n", [ClientDistStats]), Overhead = 50 % Distribution protocol headers (empirical) (TLS+=54) + byte_size(erlang:term_to_binary([0|<<>>])), % Benchmark overhead @@ -436,6 +438,8 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) -> end, io:format("******* ClientProf:~n", []), prof_print(ClientProf), io:format("******* ServerProf:~n", []), prof_print(ServerProf), + io:format("******* Server GC Before:~n~p~n", [Server_GC_Before]), + io:format("******* Server GC After:~n~p~n", [Server_GC_After]), Speed = round((Bytes * 1000000) / (1024 * Time)), report(Prefix++" Throughput_"++integer_to_list(Size), Speed, "kB/s"). @@ -457,10 +461,10 @@ throughput_runner(A, B, Rounds, Size) -> ok end, prof_start(), - {Time,ServerMsaccStats,ServerProf} = + #{time := Time} = Result = throughput_client(ServerPid, ServerMon, Payload, Rounds), prof_stop(), - ClientMsaccStats = + MsaccStats = case msacc:available() of true -> MStats = msacc:stats(), @@ -469,15 +473,13 @@ throughput_runner(A, B, Rounds, Size) -> false -> undefined end, - ClientProf = prof_end(), + Prof = prof_end(), [{_Node,Socket}] = dig_dist_node_sockets(), DistStats = inet:getstat(Socket), - #{time => microseconds(Time), - dist_stats => DistStats, - client_msacc_stats => ClientMsaccStats, - client_prof => ClientProf, - server_msacc_stats => ServerMsaccStats, - server_prof => ServerProf}. + Result#{time := microseconds(Time), + client_dist_stats => DistStats, + client_msacc_stats => MsaccStats, + client_prof => Prof}. dig_dist_node_sockets() -> [case DistCtrl of @@ -500,6 +502,9 @@ dig_dist_node_sockets() -> throughput_server(Pid, N) -> + GC_Before = get_server_gc_info(), + %% dbg:tracer(port, dbg:trace_port(file, "throughput_server_gc.log")), + %% dbg:p(TLSDistReceiver, garbage_collection), msacc:available() andalso begin msacc:stop(), @@ -508,9 +513,9 @@ throughput_server(Pid, N) -> ok end, prof_start(), - throughput_server_loop(Pid, N). + throughput_server_loop(Pid, GC_Before, N). -throughput_server_loop(_Pid, 0) -> +throughput_server_loop(_Pid, GC_Before, 0) -> prof_stop(), MsaccStats = case msacc:available() of @@ -523,11 +528,26 @@ throughput_server_loop(_Pid, 0) -> undefined end, Prof = prof_end(), - exit({ok,MsaccStats,Prof}); -throughput_server_loop(Pid, N) -> + %% dbg:flush_trace_port(), + exit(#{server_msacc_stats => MsaccStats, + server_prof => Prof, + server_gc_before => GC_Before, + server_gc_after => get_server_gc_info()}); +throughput_server_loop(Pid, GC_Before, N) -> receive {Pid, N, _} -> - throughput_server_loop(Pid, N-1) + throughput_server_loop(Pid, GC_Before, N-1) + end. + +get_server_gc_info() -> + case whereis(ssl_connection_sup_dist) of + undefined -> + undefined; + SupPid -> + [{_Id,TLSDistReceiver,_Type,_Modules}|_] = + supervisor:which_children(SupPid), + erlang:process_info( + TLSDistReceiver, [garbage_collection,garbage_collection_info]) end. throughput_client(Pid, Mon, Payload, N) -> @@ -535,8 +555,8 @@ throughput_client(Pid, Mon, Payload, N) -> throughput_client_loop(_Pid, Mon, _Payload, 0, StartTime) -> receive - {'DOWN', Mon, _, _, {ok,MsaccStats,Prof}} -> - {elapsed_time(StartTime),MsaccStats,Prof}; + {'DOWN', Mon, _, _, #{} = Result} -> + Result#{time => elapsed_time(StartTime)}; {'DOWN', Mon, _, _, Other} -> exit(Other) end; @@ -554,6 +574,7 @@ prof_start() -> ok. -elif(?prof =:= eprof). prof_start() -> + catch eprof:stop(), {ok,_} = eprof:start(), profiling = eprof:start_profiling(processes()), ok. diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index b8b9989d30..1fa6029963 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -41,7 +42,8 @@ all() -> [decode_hello_handshake, decode_empty_server_sni_correctly, select_proper_tls_1_2_rsa_default_hashsign, ignore_hassign_extension_pre_tls_1_2, - unorded_chain]. + unorded_chain, + encode_decode_srp]. %%-------------------------------------------------------------------- init_per_suite(Config) -> @@ -192,6 +194,31 @@ unorded_chain(Config) when is_list(Config) -> {ok, _, OrderedChain} = ssl_certificate:certificate_chain(PeerCert, ets:new(foo, []), ExtractedCerts, UnordedChain). +encode_decode_srp(_Config) -> + Exts = #hello_extensions{ + srp = #srp{username = <<"foo">>}, + sni = #sni{hostname = "bar"}, + renegotiation_info = undefined, + signature_algs = undefined, + alpn = undefined, + next_protocol_negotiation = undefined, + ec_point_formats = undefined, + elliptic_curves = undefined + }, + EncodedExts = <<0,20, % Length + 0,0, % SNI extension + 0,8, % Length + 0,6, % ServerNameLength + 0, % NameType (host_name) + 0,3, % HostNameLength + 98,97,114, % hostname = "bar" + 0,12, % SRP extension + 0,4, % Length + 3, % srp_I length + 102,111,111>>, % username = "foo" + EncodedExts = ssl_handshake:encode_hello_extensions(Exts), + Exts = ssl_handshake:decode_hello_extensions({client, EncodedExts}). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 1c7d6b5f9f..878e983bb9 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -64,13 +64,12 @@ next_protocol_not_supported() -> npn_not_supported_server ]. -init_per_suite(Config) -> +init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> ssl_test_lib:clean_start(), - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), - proplists:get_value(priv_dir, Config)), + Config = ssl_test_lib:make_rsa_cert(Config0), ssl_test_lib:cert_options(Config) catch _:_ -> {skip, "Crypto did not start"} @@ -196,10 +195,10 @@ client_negotiate_server_does_not_support(Config) when is_list(Config) -> renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> Data = "hello world", - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, ExpectedProtocol = {ok, <<"http/1.0">>}, @@ -221,7 +220,7 @@ renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- npn_not_supported_client(Config) when is_list(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), PrefProtocols = {client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}, ClientOpts = [PrefProtocols] ++ ClientOpts0, @@ -236,7 +235,7 @@ npn_not_supported_client(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- npn_not_supported_server(Config) when is_list(Config)-> - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}, ServerOpts = [AdvProtocols] ++ ServerOpts0, @@ -244,63 +243,24 @@ npn_not_supported_server(Config) when is_list(Config)-> %-------------------------------------------------------------------------------- npn_handshake_session_reused(Config) when is_list(Config)-> - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts =[{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result_msg, []}}, - {options, ClientOpts}]), - - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:fail(Other) - end, + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> Data = "hello world", - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = ClientExtraOpts ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = ServerExtraOpts ++ ServerOpts0, {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 9af1ae0e3f..6d26b2df33 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -2122,26 +2122,13 @@ active_once_packet(Socket, Data, N) -> active_once_packet(Socket, Data, N-1). active_raw(Socket, Data, N) -> - active_raw(Socket, Data, N, []). - -active_raw(_Socket, _, 0, _) -> + active_raw(Socket, (length(Data) * N)). +active_raw(_Socket, 0) -> ok; -active_raw(Socket, Data, N, Acc) -> +active_raw(Socket, N) -> receive - {ssl, Socket, Byte} when length(Byte) == 1 -> - receive - {ssl, Socket, _} -> - active_raw(Socket, Data, N -1) - end; - {ssl, Socket, Data} -> - active_raw(Socket, Data, N-1, []); - {ssl, Socket, Other} -> - case Acc ++ Other of - Data -> - active_raw(Socket, Data, N-1, []); - NewAcc -> - active_raw(Socket, Data, NewAcc) - end + {ssl, Socket, Bytes} -> + active_raw(Socket, N-length(Bytes)) end. active_packet(Socket, _, 0) -> diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 1f9b6a5772..27b9c258a0 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -64,7 +64,8 @@ payload_tests() -> server_echos_active_huge, client_echos_passive_huge, client_echos_active_once_huge, - client_echos_active_huge]. + client_echos_active_huge, + client_active_once_server_close]. init_per_suite(Config) -> catch crypto:stop(), @@ -397,6 +398,23 @@ client_echos_active_huge(Config) when is_list(Config) -> client_echos_active( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + +%%-------------------------------------------------------------------- +client_active_once_server_close() -> + [{doc, "Server sends 500000 bytes and immediately after closes the connection" + "Make sure client recives all data if possible"}]. + +client_active_once_server_close(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 50000), + client_active_once_server_close( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- @@ -541,42 +559,57 @@ client_echos_active( ssl_test_lib:close(Server), ssl_test_lib:close(Client). +client_active_once_server_close( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> + Length = byte_size(Data), + Server = + ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_close, [Data]}}, + {options, [{active, once}, {mode, binary} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, active_once_recv, [Length]}}, + {options,[{active, once}, {mode, binary} | ClientOpts]}]), + %% + ssl_test_lib:check_result(Server, ok, Client, ok). + +send(_Socket, _Data, 0, _) -> + ok; +send(Socket, Data, Count, RecvEcho) -> + ok = ssl:send(Socket, Data), + RecvEcho(), + send(Socket, Data, Count - 1, RecvEcho). -send(Socket, Data, Count, Verify) -> - send(Socket, Data, Count, <<>>, Verify). -%% -send(_Socket, _Data, 0, Acc, _Verify) -> - Acc; -send(Socket, Data, Count, Acc, Verify) -> +send_close(Socket, Data) -> ok = ssl:send(Socket, Data), - NewAcc = Verify(Acc), - send(Socket, Data, Count - 1, NewAcc, Verify). + ssl:close(Socket). - sender(Socket, Data) -> ct:log("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_recv(Socket, Data, Acc) end), - ok. + send(Socket, Data, 100, + fun() -> + ssl_test_lib:recv_disregard(Socket, byte_size(Data)) + end). sender_active_once(Socket, Data) -> ct:log("Sender active once: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_active_once(Socket, Data, Acc) end), - ok. + send(Socket, Data, 100, + fun() -> + ssl_test_lib:active_once_disregard(Socket, byte_size(Data)) + end). sender_active(Socket, Data) -> ct:log("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_active(Socket, Data, Acc) end), - ok. - + send(Socket, Data, 100, + fun() -> + ssl_test_lib:active_disregard(Socket, byte_size(Data)) + end). echoer(Socket, Size) -> ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), @@ -592,99 +625,32 @@ echoer_active(Socket, Size) -> %% Receive Size bytes +echo_recv(_Socket, 0) -> + ok; echo_recv(Socket, Size) -> {ok, Data} = ssl:recv(Socket, 0), ok = ssl:send(Socket, Data), - NewSize = Size - byte_size(Data), - if - 0 < NewSize -> - echo_recv(Socket, NewSize); - 0 == NewSize -> - ok - end. - -%% Verify that received data is SentData, return any superflous data -verify_recv(Socket, SentData, Acc) -> - {ok, NewData} = ssl:recv(Socket, 0), - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, NewData/binary>>, - NewSize = byte_size(NewAcc), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_recv(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end. + echo_recv(Socket, Size - byte_size(Data)). %% Receive Size bytes +echo_active_once(_Socket, 0) -> + ok; echo_active_once(Socket, Size) -> receive {ssl, Socket, Data} -> ok = ssl:send(Socket, Data), NewSize = Size - byte_size(Data), ssl:setopts(Socket, [{active, once}]), - if - 0 < NewSize -> - echo_active_once(Socket, NewSize); - 0 == NewSize -> - ok - end + echo_active_once(Socket, NewSize) end. -%% Verify that received data is SentData, return any superflous data -verify_active_once(Socket, SentData, Acc) -> - receive - {ssl, Socket, Data} -> - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, Data/binary>>, - NewSize = byte_size(NewAcc), - ssl:setopts(Socket, [{active, once}]), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_active_once(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end - end. - - %% Receive Size bytes +echo_active(_Socket, 0) -> + ok; echo_active(Socket, Size) -> receive {ssl, Socket, Data} -> ok = ssl:send(Socket, Data), - NewSize = Size - byte_size(Data), - if - 0 < NewSize -> - echo_active(Socket, NewSize); - 0 == NewSize -> - ok - end - end. - -%% Verify that received data is SentData, return any superflous data -verify_active(Socket, SentData, Acc) -> - receive - {ssl, Socket, Data} -> - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, Data/binary>>, - NewSize = byte_size(NewAcc), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_active(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end - end. + echo_active(Socket, Size - byte_size(Data)) + end. + diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index 25d2cb300d..6f11e2bbe8 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -44,11 +44,8 @@ init_per_suite(Config0) -> try crypto:start() of ok -> ssl_test_lib:clean_start(), - %% make rsa certs using oppenssl - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), - proplists:get_value(priv_dir, Config0)), - Config1 = ssl_test_lib:make_dsa_cert(Config0), - ssl_test_lib:cert_options(Config1) + %% make rsa certs + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -86,8 +83,8 @@ pem_cleanup() -> [{doc, "Test pem cache invalidate mechanism"}]. pem_cleanup(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = @@ -118,8 +115,8 @@ invalid_insert() -> invalid_insert(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), BadClientOpts = [{cacertfile, "tmp/does_not_exist.pem"} | proplists:delete(cacertfile, ClientOpts)], Server = diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index a0fab58b9d..7f33fe3204 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -48,7 +48,8 @@ all() -> session_cache_process_list, session_cache_process_mnesia, client_unique_session, - max_table_size + max_table_size, + save_specific_session ]. groups() -> @@ -60,10 +61,7 @@ init_per_suite(Config0) -> ok -> ssl_test_lib:clean_start(), %% make rsa certs using - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), - proplists:get_value(priv_dir, Config0)), - Config = ssl_test_lib:make_dsa_cert(Config0), - ssl_test_lib:cert_options(Config) + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -97,7 +95,10 @@ init_per_testcase(session_cleanup, Config) -> init_per_testcase(client_unique_session, Config) -> ct:timetrap({seconds, 40}), Config; - +init_per_testcase(save_specific_session, Config) -> + ssl_test_lib:clean_start(), + ct:timetrap({seconds, 5}), + Config; init_per_testcase(max_table_size, Config) -> ssl:stop(), application:load(ssl), @@ -141,7 +142,7 @@ end_per_testcase(max_table_size, Config) -> end_per_testcase(default_action, Config); end_per_testcase(Case, Config) when Case == session_cache_process_list; Case == session_cache_process_mnesia -> - ets:delete(ssl_test), + catch ets:delete(ssl_test), Config; end_per_testcase(_, Config) -> Config. @@ -154,8 +155,8 @@ client_unique_session() -> "sets up many connections"}]. client_unique_session(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_opts, Config), - ServerOpts = proplists:get_value(server_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -164,8 +165,7 @@ client_unique_session(Config) when is_list(Config) -> {tcp_options, [{active, false}]}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, client_unique_session, 20), + LastClient = clients_start(Server, ClientNode, Hostname, Port, ClientOpts, 20), receive {LastClient, {ok, _}} -> ok @@ -185,8 +185,8 @@ session_cleanup() -> "does not grow and grow ..."}]. session_cleanup(Config) when is_list(Config) -> process_flag(trap_exit, true), - 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_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = @@ -254,13 +254,75 @@ session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). %%-------------------------------------------------------------------- +save_specific_session() -> + [{doc, "Test that we can save a specific client session" + }]. +save_specific_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + Server ! listen, + + Client2 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + SessionID1 = + receive + {Client1, S1} -> + S1 + end, + + SessionID2 = + receive + {Client2, S2} -> + S2 + end, + + true = SessionID1 =/= SessionID2, + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + ClientCache = element(2, State), + 2 = ssl_session_cache:size(ClientCache), + + Server ! listen, + + Client3 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_session, SessionID2} | ClientOpts]}]), + receive + {Client3, SessionID2} -> + ok; + {Client3, SessionID3}-> + ct:fail({got, SessionID3, expected, SessionID2}); + Other -> + ct:fail({got,Other}) + end. + +%%-------------------------------------------------------------------- max_table_size() -> [{doc,"Test max limit on session table"}]. max_table_size(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -270,7 +332,7 @@ max_table_size(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, max_table_size, 20), + ClientNode, Hostname, Port, ClientOpts, 20), receive {LastClient, {ok, _}} -> ok @@ -426,25 +488,27 @@ session_loop(Sess) -> %%-------------------------------------------------------------------- session_cache_process(_Type,Config) when is_list(Config) -> - ssl_basic_SUITE:reuse_session(Config). + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). -clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, Test, 0) -> +clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, 0) -> %% Make sure session is registered ct:sleep(?SLEEP * 2), ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {?MODULE, connection_info_result, []}}, - {from, self()}, {options, test_copts(Test, 0, ClientOpts)}]); -clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N) -> + {from, self()}, {options, ClientOpts}]); +clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N) -> spawn_link(ssl_test_lib, start_client, [[{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, test_copts(Test, N, ClientOpts)}]]), + {from, self()}, {options, ClientOpts}]]), Server ! listen, wait_for_server(), - clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N-1). + clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N-1). connection_info_result(Socket) -> ssl:connection_information(Socket, [protocol, cipher_suite]). @@ -481,21 +545,3 @@ get_delay_timers() -> wait_for_server() -> ct:sleep(100). - - -test_copts(_, 0, ClientOpts) -> - ClientOpts; -test_copts(max_table_size, N, ClientOpts) -> - Version = tls_record:highest_protocol_version([]), - CipherSuites = %%lists:map(fun(X) -> ssl_cipher_format:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), -[ Y|| Y = {Alg,_, _, _} <- lists:map(fun(X) -> ssl_cipher_format:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), Alg =/= ecdhe_ecdsa, Alg =/= ecdh_ecdsa, Alg =/= ecdh_rsa, Alg =/= ecdhe_rsa, Alg =/= dhe_dss, Alg =/= dss], - case length(CipherSuites) of - M when M >= N -> - Cipher = lists:nth(N, CipherSuites), - ct:pal("~p",[Cipher]), - [{ciphers, [Cipher]} | ClientOpts]; - _ -> - ClientOpts - end; -test_copts(_, _, ClientOpts) -> - ClientOpts. diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index 251b6a2639..7629d75100 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -236,8 +236,8 @@ dns_name_reuse(Config) -> {mfa, {ssl_test_lib, session_info_result, []}}, {from, self()}, {options, [{verify, verify_peer} | ClientConf]}]), - ssl_test_lib:check_result(Client1, {error, {tls_alert, "handshake failure"}}), - ssl_test_lib:close(Client0). + ssl_test_lib:check_client_alert(Client1, handshake_failure). + %%-------------------------------------------------------------------- %% Internal Functions ------------------------------------------------ %%-------------------------------------------------------------------- @@ -370,8 +370,8 @@ unsuccessfull_connect(ServerOptions, ClientOptions, Hostname0, Config) -> {from, self()}, {options, ClientOptions}]), - ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}, - Client, {error, {tls_alert, "handshake failure"}}). + ssl_test_lib:check_server_alert(Server, Client, handshake_failure). + host_name(undefined, Hostname) -> Hostname; host_name(Hostname, _) -> diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index a8d62d6c4e..c6a4a45dce 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -30,6 +30,7 @@ -record(sslsocket, { fd = nil, pid = nil}). -define(SLEEP, 1000). +-define(DEFAULT_CURVE, secp256r1). %% For now always run locally run_where(_) -> @@ -437,6 +438,37 @@ check_result(Pid, Msg) -> {got, Unexpected}}, ct:fail(Reason) end. +check_server_alert(Pid, Alert) -> + receive + {Pid, {error, {tls_alert, {Alert, _}}}} -> + ok + end. +check_server_alert(Server, Client, Alert) -> + receive + {Server, {error, {tls_alert, {Alert, _}}}} -> + receive + {Client, {error, {tls_alert, {Alert, _}}}} -> + ok; + {Client, {error, closed}} -> + ok + end + end. +check_client_alert(Pid, Alert) -> + receive + {Pid, {error, {tls_alert, {Alert, _}}}} -> + ok + end. +check_client_alert(Server, Client, Alert) -> + receive + {Client, {error, {tls_alert, {Alert, _}}}} -> + receive + {Server, {error, {tls_alert, {Alert, _}}}} -> + ok; + {Server, {error, closed}} -> + ok + end + end. + wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive @@ -523,7 +555,7 @@ cert_options(Config) -> {client_verification_opts, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, - {ssl_imp, new}]}, + {verify, verify_peer}]}, {client_verification_opts_digital_signature_only, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFileDigitalSignatureOnly}, {keyfile, ClientKeyFile}, @@ -618,9 +650,12 @@ make_rsa_cert_chains(UserConf, Config, Suffix) -> }. make_ec_cert_chains(UserConf, ClientChainType, ServerChainType, Config) -> + make_ec_cert_chains(UserConf, ClientChainType, ServerChainType, Config, ?DEFAULT_CURVE). +%% +make_ec_cert_chains(UserConf, ClientChainType, ServerChainType, Config, Curve) -> ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()), ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()), - CertChainConf = gen_conf(ClientChainType, ServerChainType, ClientChain, ServerChain), + CertChainConf = gen_conf(ClientChainType, ServerChainType, ClientChain, ServerChain, Curve), ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(ClientChainType)]), ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(ServerChainType)]), GenCertData = public_key:pkix_test_data(CertChainConf), @@ -635,7 +670,11 @@ default_cert_chain_conf() -> %% Use only default options [[],[],[]]. -gen_conf(mix, mix, UserClient, UserServer) -> + +gen_conf(ClientChainType, ServerChainType, UserClient, UserServer) -> + gen_conf(ClientChainType, ServerChainType, UserClient, UserServer, ?DEFAULT_CURVE). +%% +gen_conf(mix, mix, UserClient, UserServer, _) -> ClientTag = conf_tag("client"), ServerTag = conf_tag("server"), @@ -646,12 +685,12 @@ gen_conf(mix, mix, UserClient, UserServer) -> ServerConf = merge_chain_spec(UserServer, DefaultServer, []), new_format([{ClientTag, ClientConf}, {ServerTag, ServerConf}]); -gen_conf(ClientChainType, ServerChainType, UserClient, UserServer) -> +gen_conf(ClientChainType, ServerChainType, UserClient, UserServer, Curve) -> ClientTag = conf_tag("client"), ServerTag = conf_tag("server"), - DefaultClient = chain_spec(client, ClientChainType), - DefaultServer = chain_spec(server, ServerChainType), + DefaultClient = chain_spec(client, ClientChainType, Curve), + DefaultServer = chain_spec(server, ServerChainType, Curve), ClientConf = merge_chain_spec(UserClient, DefaultClient, []), ServerConf = merge_chain_spec(UserServer, DefaultServer, []), @@ -673,43 +712,43 @@ proplist_to_map([Head | Rest]) -> conf_tag(Role) -> list_to_atom(Role ++ "_chain"). -chain_spec(_Role, ecdh_rsa) -> +chain_spec(_Role, ecdh_rsa, Curve) -> Digest = {digest, appropriate_sha(crypto:supports())}, - CurveOid = hd(tls_v1:ecc_curves(0)), + CurveOid = pubkey_cert_records:namedCurves(Curve), [[Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, hardcode_rsa_key(1)}], [Digest, {key, {namedCurve, CurveOid}}]]; -chain_spec(_Role, ecdhe_ecdsa) -> +chain_spec(_Role, ecdhe_ecdsa, Curve) -> Digest = {digest, appropriate_sha(crypto:supports())}, - CurveOid = hd(tls_v1:ecc_curves(0)), + CurveOid = pubkey_cert_records:namedCurves(Curve), [[Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, {namedCurve, CurveOid}}]]; -chain_spec(_Role, ecdh_ecdsa) -> +chain_spec(_Role, ecdh_ecdsa, Curve) -> Digest = {digest, appropriate_sha(crypto:supports())}, - CurveOid = hd(tls_v1:ecc_curves(0)), + CurveOid = pubkey_cert_records:namedCurves(Curve), [[Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, {namedCurve, CurveOid}}]]; -chain_spec(_Role, ecdhe_rsa) -> +chain_spec(_Role, ecdhe_rsa, _) -> Digest = {digest, appropriate_sha(crypto:supports())}, [[Digest, {key, hardcode_rsa_key(1)}], [Digest, {key, hardcode_rsa_key(2)}], [Digest, {key, hardcode_rsa_key(3)}]]; -chain_spec(_Role, ecdsa) -> +chain_spec(_Role, ecdsa, Curve) -> Digest = {digest, appropriate_sha(crypto:supports())}, - CurveOid = hd(tls_v1:ecc_curves(0)), + CurveOid = pubkey_cert_records:namedCurves(Curve), [[Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, {namedCurve, CurveOid}}], [Digest, {key, {namedCurve, CurveOid}}]]; -chain_spec(_Role, rsa) -> +chain_spec(_Role, rsa, _) -> Digest = {digest, appropriate_sha(crypto:supports())}, [[Digest, {key, hardcode_rsa_key(1)}], [Digest, {key, hardcode_rsa_key(2)}], [Digest, {key, hardcode_rsa_key(3)}]]; -chain_spec(_Role, dsa) -> +chain_spec(_Role, dsa, _) -> Digest = {digest, appropriate_sha(crypto:supports())}, [[Digest, {key, hardcode_dsa_key(1)}], [Digest, {key, hardcode_dsa_key(2)}], @@ -742,7 +781,7 @@ merge_spec(User, Default, [Conf | Rest], Acc) -> make_mix_cert(Config) -> Ext = x509_test:extensions([{key_usage, [digitalSignature]}]), Digest = {digest, appropriate_sha(crypto:supports())}, - CurveOid = hd(tls_v1:ecc_curves(0)), + CurveOid = pubkey_cert_records:namedCurves(?DEFAULT_CURVE), Mix = proplists:get_value(mix, Config, peer_ecc), ClientChainType =ServerChainType = mix, {ClientChain, ServerChain} = mix(Mix, Digest, CurveOid, Ext), @@ -825,7 +864,8 @@ make_rsa_cert(Config) -> Config end. appropriate_sha(CryptoSupport) -> - case proplists:get_bool(sha256, CryptoSupport) of + Hashes = proplists:get_value(hashs, CryptoSupport), + case lists:member(sha256, Hashes) of true -> sha256; false -> @@ -1064,8 +1104,7 @@ ecc_test(Expect, COpts, SOpts, CECCOpts, SECCOpts, Config) -> ecc_test_error(COpts, SOpts, CECCOpts, SECCOpts, Config) -> {Server, Port} = start_server_ecc_error(erlang, SOpts, SECCOpts, Config), Client = start_client_ecc_error(erlang, Port, COpts, CECCOpts, Config), - Error = {error, {tls_alert, "insufficient security"}}, - check_result(Server, Error, Client, Error). + check_server_alert(Server, Client, insufficient_security). start_client(openssl, Port, ClientOpts, Config) -> Cert = proplists:get_value(certfile, ClientOpts), @@ -1073,11 +1112,11 @@ start_client(openssl, Port, ClientOpts, Config) -> CA = proplists:get_value(cacertfile, ClientOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", - Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port), + Args0 = ["s_client", "-verify", "2", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version), "-cert", Cert, "-CAfile", CA, "-key", Key, "-host","localhost", "-msg", "-debug"], - + Args = maybe_force_ipv4(Args0), OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), OpenSslPort; @@ -1091,6 +1130,18 @@ start_client(erlang, Port, ClientOpts, Config) -> {mfa, {ssl_test_lib, check_key_exchange_send_active, [KeyEx]}}, {options, [{verify, verify_peer} | ClientOpts]}]). +%% Workaround for running tests on machines where openssl +%% s_client would use an IPv6 address with localhost. As +%% this test suite and the ssl application is not prepared +%% for that we have to force s_client to use IPv4 if +%% OpenSSL supports IPv6. +maybe_force_ipv4(Args0) -> + case is_ipv6_supported() of + true -> + Args0 ++ ["-4"]; + false -> + Args0 + end. start_client_ecc(erlang, Port, ClientOpts, Expect, ECCOpts, Config) -> {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1461,19 +1512,10 @@ cipher_result(Socket, Result) -> %% Importante to send two packets here %% to properly test "cipher state" handling ssl:send(Socket, "Hello\n"), - receive - {ssl, Socket, "H"} -> - ssl:send(Socket, " world\n"), - receive_rizzo_duong_beast(); - {ssl, Socket, "Hello\n"} -> - ssl:send(Socket, " world\n"), - receive - {ssl, Socket, " world\n"} -> - ok - end; - Other -> - {unexpected, Other} - end. + "Hello\n" = active_recv(Socket, length( "Hello\n")), + ssl:send(Socket, " world\n"), + " world\n" = active_recv(Socket, length(" world\n")), + ok. session_info_result(Socket) -> {ok, Info} = ssl:connection_information(Socket, [session_id, cipher_suite]), @@ -1592,34 +1634,81 @@ v_1_2_check(ecdh_rsa, ecdh_ecdsa) -> v_1_2_check(_, _) -> false. -send_recv_result_active(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. - send_recv_result(Socket) -> - ssl:send(Socket, "Hello world"), - {ok,"Hello world"} = ssl:recv(Socket, 11), + Data = "Hello world", + ssl:send(Socket, Data), + {ok, Data} = ssl:recv(Socket, length(Data)), + ok. + +send_recv_result_active(Socket) -> + Data = "Hello world", + ssl:send(Socket, Data), + Data = active_recv(Socket, length(Data)), ok. send_recv_result_active_once(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - ssl:setopts(Socket, [{active, once}]), - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok + Data = "Hello world", + ssl:send(Socket, Data), + active_once_recv_list(Socket, length(Data)). + +active_recv(Socket, N) -> + active_recv(Socket, N, []). + +active_recv(_Socket, 0, Acc) -> + Acc; +active_recv(Socket, N, Acc) -> + receive + {ssl, Socket, Bytes} -> + active_recv(Socket, N-length(Bytes), Acc ++ Bytes) + end. + +active_once_recv(_Socket, 0) -> + ok; +active_once_recv(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_recv(Socket, N-byte_size(Bytes)) + end. + +active_once_recv_list(_Socket, 0) -> + ok; +active_once_recv_list(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_recv_list(Socket, N-length(Bytes)) + end. +recv_disregard(_Socket, 0) -> + ok; +recv_disregard(Socket, N) -> + {ok, Bytes} = ssl:recv(Socket, 0), + recv_disregard(Socket, N-byte_size(Bytes)). + +active_disregard(_Socket, 0) -> + ok; +active_disregard(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + active_disregard(Socket, N-byte_size(Bytes)) + end. +active_once_disregard(_Socket, 0) -> + ok; +active_once_disregard(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_disregard(Socket, N-byte_size(Bytes)) + end. + +is_ipv6_supported() -> + case os:cmd("openssl version") of + "OpenSSL 0.9.8" ++ _ -> % Does not support IPv6 + false; + "OpenSSL 1.0" ++ _ -> % Does not support IPv6 + false; + _ -> + true end. is_sane_ecc(openssl) -> @@ -2159,3 +2248,98 @@ server_msg(Server, ServerMsg) -> Unexpected -> ct:fail(Unexpected) end. + +session_id(Socket) -> + {ok, [{session_id, ID}]} = ssl:connection_information(Socket, [session_id]), + ID. + +reuse_session(ClientOpts, ServerOpts, Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port0 = ssl_test_lib:inet_port(Server0), + + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + Server0 ! listen, + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + SID = receive + {Client0, Id0} -> + Id0 + end, + + receive + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) + end, + + Server0 ! listen, + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, false} + | ClientOpts]}]), + receive + {Client2, SID} -> + ct:fail(session_reused_when_session_reuse_disabled_by_client); + {Client2, _} -> + ok + end, + + ssl_test_lib:close(Server0), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, [{reuse_sessions, false} |ServerOpts]}]), + Port1 = ssl_test_lib:inet_port(Server1), + + Client3 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + SID1 = receive + {Client3, Id3} -> + Id3 + end, + + Server1 ! listen, + + Client4 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client4, SID1} -> + ct:fail(session_reused_when_session_reuse_disabled_by_server); + {Client4, _} -> + ok + end, + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client3), + ssl_test_lib:close(Client4). + diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 5a38f5f9c1..df84411b6d 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -91,6 +91,7 @@ all_versions_tests() -> erlang_server_openssl_client_anon_with_cert, erlang_server_openssl_client_reuse_session, erlang_client_openssl_server_renegotiate, + erlang_client_openssl_server_renegotiate_after_client_data, erlang_client_openssl_server_nowrap_seqnum, erlang_server_openssl_client_nowrap_seqnum, erlang_client_openssl_server_no_server_ca_cert, @@ -259,8 +260,9 @@ special_init(TestCase, Config) when Config; special_init(TestCase, Config) when TestCase == erlang_client_openssl_server_renegotiate; - TestCase == erlang_client_openssl_server_nowrap_seqnum; - TestCase == erlang_server_openssl_client_nowrap_seqnum + TestCase == erlang_client_openssl_server_nowrap_seqnum; + TestCase == erlang_server_openssl_client_nowrap_seqnum; + TestCase == erlang_client_openssl_server_renegotiate_after_client_data -> {ok, Version} = application:get_env(ssl, protocol_version), check_sane_openssl_renegotaite(Config, Version); @@ -760,8 +762,8 @@ erlang_client_openssl_server_renegotiate() -> [{doc,"Test erlang client when openssl server issuses a renegotiate"}]. erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -770,12 +772,14 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile, "-msg"], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), @@ -800,6 +804,53 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. +%%-------------------------------------------------------------------- +erlang_client_openssl_server_renegotiate_after_client_data() -> + [{doc,"Test erlang client when openssl server issuses a renegotiate after reading client data"}]. +erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + ErlData = "From erlang to openssl", + OpenSslData = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile, "-msg"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_wait_send, [[ErlData, OpenSslData]]}}, + {options, ClientOpts}]), + + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, OpenSslData), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. %%-------------------------------------------------------------------- @@ -810,7 +861,7 @@ erlang_client_openssl_server_nowrap_seqnum() -> " to lower treashold substantially."}]. erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -819,12 +870,14 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> N = 10, Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile, "-msg"], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), @@ -853,7 +906,7 @@ erlang_server_openssl_client_nowrap_seqnum() -> " to lower treashold substantially."}]. erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1196,7 +1249,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]), ssl_test_lib:consume_port_exit(OpenSslPort), - ssl_test_lib:check_result(Server, {error, {tls_alert, "bad record mac"}}), + ssl_test_lib:check_server_alert(Server, bad_record_mac), process_flag(trap_exit, false). %%-------------------------------------------------------------------- @@ -1602,8 +1655,8 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = ErlangClientOpts ++ ClientOpts0, {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1611,6 +1664,7 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), @@ -1620,10 +1674,12 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens [] -> ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile,"-key", KeyFile]; [Opt, Value] -> ["s_server", Opt, Value, "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile,"-key", KeyFile] end, @@ -1648,8 +1704,8 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) -> process_flag(trap_exit, true), - ServerOpts = proplists:get_value(server_rsa_opts, Config), - ClientOpts0 = proplists:get_value(client_rsa_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), + ClientOpts0 = proplists:get_value(client_rsa_verify_opts, Config), ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0], {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1657,12 +1713,14 @@ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callba Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), @@ -1780,8 +1838,8 @@ start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Ca start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0], {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1789,6 +1847,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), @@ -1796,6 +1855,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac Exe = "openssl", Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), @@ -1886,6 +1946,11 @@ erlang_ssl_receive(Socket, Data) -> ct:log("Connection info: ~p~n", [ssl:connection_information(Socket)]), receive + {ssl, Socket, "R\n"} -> + %% Swallow s_client renegotiation command. + %% openssl s_client connected commands can appear on + %% server side with some openssl versions. + erlang_ssl_receive(Socket,Data); {ssl, Socket, Data} -> io:format("Received ~p~n",[Data]), %% open_ssl server sometimes hangs waiting in blocking read @@ -1924,6 +1989,12 @@ server_sent_garbage(Socket) -> {error, closed} == ssl:send(Socket, "data") end. + +send_wait_send(Socket, [ErlData, OpenSslData]) -> + ssl:send(Socket, ErlData), + ct:sleep(?SLEEP), + ssl:send(Socket, ErlData), + erlang_ssl_receive(Socket, OpenSslData). check_openssl_sni_support(Config) -> HelpText = os:cmd("openssl s_client --help"), diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 75d959accf..3527062a8a 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 9.1 +SSL_VSN = 9.1.2 diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml index db0ab42372..aa1577a067 100644 --- a/lib/stdlib/doc/src/array.xml +++ b/lib/stdlib/doc/src/array.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>array.xml</file> </header> - <module>array</module> + <module since="">array</module> <modulesummary>Functional, extendible arrays.</modulesummary> <description> <p>Functional, extendible arrays. Arrays can have fixed size, or can grow @@ -137,7 +137,7 @@ A3 = array:fix(A2).</pre> <funcs> <func> - <name name="default" arity="1"/> + <name name="default" arity="1" since=""/> <fsummary>Get the value used for uninitialized entries.</fsummary> <desc><marker id="default-1"/> <p>Gets the value used for uninitialized entries.</p> @@ -146,7 +146,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="fix" arity="1"/> + <name name="fix" arity="1" since=""/> <fsummary>Fix the array size.</fsummary> <desc><marker id="fix-1"/> <p>Fixes the array size. This prevents it from growing automatically @@ -157,7 +157,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="foldl" arity="3"/> + <name name="foldl" arity="3" since=""/> <fsummary>Fold the array elements using the specified function and initial accumulator value.</fsummary> <desc><marker id="foldl-3"/> @@ -172,7 +172,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="foldr" arity="3"/> + <name name="foldr" arity="3" since=""/> <fsummary>Fold the array elements right-to-left using the specified function and initial accumulator value.</fsummary> <desc><marker id="foldr-3"/> @@ -186,7 +186,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Equivalent to <c>from_list(List, undefined)</c>.</fsummary> <desc><marker id="from_list-1"/> <p>Equivalent to @@ -195,7 +195,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="from_list" arity="2"/> + <name name="from_list" arity="2" since=""/> <fsummary>Convert a list to an extendible array.</fsummary> <desc><marker id="from_list-2"/> <p>Converts a list to an extendible array. <c><anno>Default</anno></c> @@ -208,7 +208,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="from_orddict" arity="1"/> + <name name="from_orddict" arity="1" since=""/> <fsummary>Equivalent to <c>from_orddict(Orddict, undefined)</c>. </fsummary> <desc><marker id="from_orddict-1"/> @@ -218,7 +218,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="from_orddict" arity="2"/> + <name name="from_orddict" arity="2" since=""/> <fsummary>Convert an ordered list of pairs <c>{Index, Value}</c> to a corresponding extendible array.</fsummary> <desc><marker id="from_orddict-2"/> @@ -234,7 +234,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since=""/> <fsummary>Get the value of entry <c>I</c>.</fsummary> <desc><marker id="get-2"/> <p>Gets the value of entry <c><anno>I</anno></c>. If @@ -249,7 +249,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="is_array" arity="1"/> + <name name="is_array" arity="1" since=""/> <fsummary>Returns <c>true</c> if <c>X</c> is an array, otherwise <c>false</c>.</fsummary> <desc><marker id="is_array-1"/> @@ -261,7 +261,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="is_fix" arity="1"/> + <name name="is_fix" arity="1" since=""/> <fsummary>Check if the array has fixed size.</fsummary> <desc><marker id="is_fix-1"/> <p>Checks if the array has fixed size. Returns <c>true</c> if the array @@ -271,7 +271,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="map" arity="2"/> + <name name="map" arity="2" since=""/> <fsummary>Map the specified function onto each array element.</fsummary> <desc><marker id="map-2"/> <p>Maps the specified function onto each array element. The elements are @@ -285,7 +285,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Create a new, extendible array with initial size zero. </fsummary> <desc><marker id="new-0"/> @@ -296,7 +296,7 @@ A3 = array:fix(A2).</pre> </func> <func> - <name name="new" arity="1"/> + <name name="new" arity="1" since=""/> <fsummary>Create a new array according to the specified options. </fsummary> <desc><marker id="new-1"/> @@ -346,7 +346,7 @@ array:new([{size,10},{fixed,false},{default,-1}])</pre> </func> <func> - <name name="new" arity="2"/> + <name name="new" arity="2" since=""/> <fsummary>Create a new array according to the specified size and options. </fsummary> <desc><marker id="new-2"/> @@ -370,7 +370,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="relax" arity="1"/> + <name name="relax" arity="1" since=""/> <fsummary>Make the array resizable.</fsummary> <desc><marker id="relax-1"/> <p>Makes the array resizable. (Reverses the effects of @@ -380,7 +380,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="reset" arity="2"/> + <name name="reset" arity="2" since=""/> <fsummary>Reset entry <c>I</c> to the default value for the array. </fsummary> <desc><marker id="reset-2"/> @@ -399,7 +399,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="resize" arity="1"/> + <name name="resize" arity="1" since=""/> <fsummary>Change the array size to that reported by <c>sparse_size/1</c>. </fsummary> <desc><marker id="resize-1"/> @@ -413,7 +413,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="resize" arity="2"/> + <name name="resize" arity="2" since=""/> <fsummary>Change the array size.</fsummary> <desc><marker id="resize-2"/> <p>Change the array size. If <c><anno>Size</anno></c> is not a @@ -424,7 +424,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="set" arity="3"/> + <name name="set" arity="3" since=""/> <fsummary>Set entry <c>I</c> of the array to <c>Value</c>.</fsummary> <desc><marker id="set-3"/> <p>Sets entry <c><anno>I</anno></c> of the array to @@ -441,7 +441,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Get the number of entries in the array.</fsummary> <desc><marker id="size-1"/> <p>Gets the number of entries in the array. Entries are numbered from @@ -454,7 +454,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="sparse_foldl" arity="3"/> + <name name="sparse_foldl" arity="3" since=""/> <fsummary>Fold the array elements using the specified function and initial accumulator value, skipping default-valued entries.</fsummary> <desc><marker id="sparse_foldl-3"/> @@ -469,7 +469,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="sparse_foldr" arity="3"/> + <name name="sparse_foldr" arity="3" since=""/> <fsummary>Fold the array elements right-to-left using the specified function and initial accumulator value, skipping default-valued entries.</fsummary> @@ -485,7 +485,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="sparse_map" arity="2"/> + <name name="sparse_map" arity="2" since=""/> <fsummary>Map the specified function onto each array element, skipping default-valued entries.</fsummary> <desc><marker id="sparse_map-2"/> @@ -498,7 +498,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="sparse_size" arity="1"/> + <name name="sparse_size" arity="1" since=""/> <fsummary>Get the number of entries in the array up until the last non-default-valued entry.</fsummary> <desc><marker id="sparse_size-1"/> @@ -512,7 +512,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="sparse_to_list" arity="1"/> + <name name="sparse_to_list" arity="1" since=""/> <fsummary>Convert the array to a list, skipping default-valued entries. </fsummary> <desc><marker id="sparse_to_list-1"/> @@ -522,7 +522,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="sparse_to_orddict" arity="1"/> + <name name="sparse_to_orddict" arity="1" since=""/> <fsummary>Convert the array to an ordered list of pairs <c>{Index, Value}</c>, skipping default-valued entries.</fsummary> <desc><marker id="sparse_to_orddict-1"/> @@ -534,7 +534,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert the array to a list.</fsummary> <desc><marker id="to_list-1"/> <p>Converts the array to a list.</p> @@ -545,7 +545,7 @@ array:new(100, {default,0})</pre> </func> <func> - <name name="to_orddict" arity="1"/> + <name name="to_orddict" arity="1" since=""/> <fsummary>Convert the array to an ordered list of pairs <c>{Index, Value}</c>.</fsummary> <desc><marker id="to_orddict-1"/> diff --git a/lib/stdlib/doc/src/base64.xml b/lib/stdlib/doc/src/base64.xml index cfa1ecc006..479072ba4f 100644 --- a/lib/stdlib/doc/src/base64.xml +++ b/lib/stdlib/doc/src/base64.xml @@ -29,7 +29,7 @@ <rev></rev> <file>base64.xml</file> </header> - <module>base64</module> + <module since="">base64</module> <modulesummary>Provides base64 encode and decode, see RFC 2045.</modulesummary> <description> @@ -51,10 +51,10 @@ <funcs> <func> - <name name="decode" arity="1"/> - <name name="decode_to_string" arity="1"/> - <name name="mime_decode" arity="1"/> - <name name="mime_decode_to_string" arity="1"/> + <name name="decode" arity="1" since=""/> + <name name="decode_to_string" arity="1" since=""/> + <name name="mime_decode" arity="1" since=""/> + <name name="mime_decode_to_string" arity="1" since=""/> <fsummary>Decode a base64 encoded string to data.</fsummary> <type variable="Base64" name_i="1"/> <type variable="Data" name_i="1"/> @@ -69,8 +69,8 @@ </func> <func> - <name name="encode" arity="1"/> - <name name="encode_to_string" arity="1"/> + <name name="encode" arity="1" since=""/> + <name name="encode_to_string" arity="1" since=""/> <fsummary>Encode data into base64.</fsummary> <type variable="Data"/> <type variable="Base64" name_i="1"/> diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml index 213170df7f..8bb4cf9101 100644 --- a/lib/stdlib/doc/src/beam_lib.xml +++ b/lib/stdlib/doc/src/beam_lib.xml @@ -28,7 +28,7 @@ <date>1999-10-30</date> <rev>PA1</rev> </header> - <module>beam_lib</module> + <module since="">beam_lib</module> <modulesummary>An interface to the BEAM file format.</modulesummary> <description> <p>This module provides an interface to files created by @@ -267,7 +267,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> <funcs> <func> - <name name="all_chunks" arity="1"/> + <name name="all_chunks" arity="1" since="OTP 18.2"/> <fsummary>Read all chunks from a BEAM file or binary</fsummary> <desc> <p>Reads chunk data for all chunks.</p> @@ -275,7 +275,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="build_module" arity="1"/> + <name name="build_module" arity="1" since="OTP 18.2"/> <fsummary>Create a BEAM module from a list of chunks.</fsummary> <desc> <p>Builds a BEAM module (as a binary) from a list of chunks.</p> @@ -283,7 +283,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="chunks" arity="2"/> + <name name="chunks" arity="2" since=""/> <fsummary>Read selected chunks from a BEAM file or binary.</fsummary> <desc> <p>Reads chunk data for selected chunks references. The order of @@ -293,7 +293,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="chunks" arity="3"/> + <name name="chunks" arity="3" since=""/> <fsummary>Read selected chunks from a BEAM file or binary.</fsummary> <desc> <p>Reads chunk data for selected chunks references. The order of @@ -312,7 +312,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="clear_crypto_key_fun" arity="0"/> + <name name="clear_crypto_key_fun" arity="0" since=""/> <fsummary>Unregister the current crypto key fun.</fsummary> <desc> <p>Unregisters the crypto key fun and terminates the process @@ -327,7 +327,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="cmp" arity="2"/> + <name name="cmp" arity="2" since=""/> <fsummary>Compare two BEAM files.</fsummary> <type name="cmp_rsn"/> <desc> @@ -341,7 +341,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="cmp_dirs" arity="2"/> + <name name="cmp_dirs" arity="2" since=""/> <fsummary>Compare the BEAM files in two directories.</fsummary> <desc> <p>Compares the BEAM files in @@ -359,7 +359,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code> </func> <func> - <name name="crypto_key_fun" arity="1"/> + <name name="crypto_key_fun" arity="1" since=""/> <fsummary>Register a fun that provides a crypto key.</fsummary> <type name="crypto_fun"/> <type name="crypto_fun_arg"/> @@ -398,7 +398,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="diff_dirs" arity="2"/> + <name name="diff_dirs" arity="2" since=""/> <fsummary>Compare the BEAM files in two directories.</fsummary> <desc> <p>Compares the BEAM files in two directories as @@ -409,7 +409,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Return an English description of a BEAM read error reply. </fsummary> <desc> @@ -422,7 +422,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since=""/> <fsummary>Information about a BEAM file.</fsummary> <desc> <p>Returns a list containing some information about a BEAM file @@ -449,7 +449,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="md5" arity="1"/> + <name name="md5" arity="1" since=""/> <fsummary>Read the module version of the BEAM file.</fsummary> <desc> <p>Calculates an MD5 redundancy check for the code of the module @@ -458,7 +458,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="strip" arity="1"/> + <name name="strip" arity="1" since=""/> <fsummary>Remove chunks not needed by the loader from a BEAM file. </fsummary> <desc> @@ -470,7 +470,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="strip_files" arity="1"/> + <name name="strip_files" arity="1" since=""/> <fsummary>Removes chunks not needed by the loader from BEAM files. </fsummary> <desc> @@ -483,7 +483,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="strip_release" arity="1"/> + <name name="strip_release" arity="1" since=""/> <fsummary>Remove chunks not needed by the loader from all BEAM files of a release.</fsummary> <desc> @@ -497,7 +497,7 @@ CryptoKeyFun(clear) -> term()</code> </func> <func> - <name name="version" arity="1"/> + <name name="version" arity="1" since=""/> <fsummary>Read the module version of the BEAM file.</fsummary> <desc> <p>Returns the module version or versions. A version is defined by diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index 6a86d6c7ba..f3d4edd30f 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>binary.xml</file> </header> - <module>binary</module> + <module since="OTP R14B">binary</module> <modulesummary>Library for handling binary data.</modulesummary> <description> @@ -79,7 +79,7 @@ <funcs> <func> - <name name="at" arity="2"/> + <name name="at" arity="2" since="OTP R14B"/> <fsummary>Return the byte at a specific position in a binary.</fsummary> <desc> <p>Returns the byte at position <c><anno>Pos</anno></c> (zero-based) in @@ -90,7 +90,7 @@ </func> <func> - <name name="bin_to_list" arity="1"/> + <name name="bin_to_list" arity="1" since="OTP R14B"/> <fsummary>Convert a binary to a list of integers.</fsummary> <desc> <p>Same as <c>bin_to_list(<anno>Subject</anno>, {0,byte_size(<anno>Subject</anno>)})</c>.</p> @@ -98,7 +98,7 @@ </func> <func> - <name name="bin_to_list" arity="2"/> + <name name="bin_to_list" arity="2" since="OTP R14B"/> <fsummary>Convert a binary to a list of integers.</fsummary> <desc> <p>Converts <c><anno>Subject</anno></c> to a list of <c>byte()</c>s, each @@ -118,7 +118,7 @@ </func> <func> - <name name="bin_to_list" arity="3"/> + <name name="bin_to_list" arity="3" since="OTP R14B"/> <fsummary>Convert a binary to a list of integers.</fsummary> <desc> <p>Same as<c> bin_to_list(<anno>Subject</anno>, {<anno>Pos</anno>, <anno>Len</anno>})</c>.</p> @@ -126,7 +126,7 @@ </func> <func> - <name name="compile_pattern" arity="1"/> + <name name="compile_pattern" arity="1" since="OTP R14B"/> <fsummary>Precompile a binary search pattern.</fsummary> <desc> <p>Builds an internal structure representing a compilation of a @@ -158,7 +158,7 @@ </func> <func> - <name name="copy" arity="1"/> + <name name="copy" arity="1" since="OTP R14B"/> <fsummary>Create a duplicate of a binary.</fsummary> <desc> <p>Same as <c>copy(<anno>Subject</anno>, 1)</c>.</p> @@ -166,7 +166,7 @@ </func> <func> - <name name="copy" arity="2"/> + <name name="copy" arity="2" since="OTP R14B"/> <fsummary>Duplicate a binary <c>N</c> times and create a new.</fsummary> <desc> <p>Creates a binary with the content of <c><anno>Subject</anno></c> @@ -193,7 +193,7 @@ </func> <func> - <name name="decode_unsigned" arity="1"/> + <name name="decode_unsigned" arity="1" since="OTP R14B"/> <fsummary>Decode a whole binary into an integer of arbitrary size. </fsummary> <desc> @@ -202,7 +202,7 @@ </func> <func> - <name name="decode_unsigned" arity="2"/> + <name name="decode_unsigned" arity="2" since="OTP R14B"/> <fsummary>Decode a whole binary into an integer of arbitrary size. </fsummary> <desc> @@ -219,7 +219,7 @@ </func> <func> - <name name="encode_unsigned" arity="1"/> + <name name="encode_unsigned" arity="1" since="OTP R14B"/> <fsummary>Encode an unsigned integer into the minimal binary.</fsummary> <desc> <p>Same as <c>encode_unsigned(<anno>Unsigned</anno>, big)</c>.</p> @@ -227,7 +227,7 @@ </func> <func> - <name name="encode_unsigned" arity="2"/> + <name name="encode_unsigned" arity="2" since="OTP R14B"/> <fsummary>Encode an unsigned integer into the minimal binary.</fsummary> <desc> <p>Converts a positive integer to the smallest possible @@ -243,7 +243,7 @@ </func> <func> - <name name="first" arity="1"/> + <name name="first" arity="1" since="OTP R14B"/> <fsummary>Return the first byte of a binary.</fsummary> <desc> <p>Returns the first byte of binary <c><anno>Subject</anno></c> as an @@ -253,7 +253,7 @@ </func> <func> - <name name="last" arity="1"/> + <name name="last" arity="1" since="OTP R14B"/> <fsummary>Return the last byte of a binary.</fsummary> <desc> <p>Returns the last byte of binary <c><anno>Subject</anno></c> as an @@ -263,7 +263,7 @@ </func> <func> - <name name="list_to_bin" arity="1"/> + <name name="list_to_bin" arity="1" since="OTP R14B"/> <fsummary>Convert a list of integers and binaries to a binary.</fsummary> <desc> <p>Works exactly as @@ -273,7 +273,7 @@ </func> <func> - <name name="longest_common_prefix" arity="1"/> + <name name="longest_common_prefix" arity="1" since="OTP R14B"/> <fsummary>Return length of longest common prefix for a set of binaries. </fsummary> <desc> @@ -294,7 +294,7 @@ </func> <func> - <name name="longest_common_suffix" arity="1"/> + <name name="longest_common_suffix" arity="1" since="OTP R14B"/> <fsummary>Return length of longest common suffix for a set of binaries. </fsummary> <desc> @@ -315,7 +315,7 @@ </func> <func> - <name name="match" arity="2"/> + <name name="match" arity="2" since="OTP R14B"/> <fsummary>Search for the first match of a pattern in a binary.</fsummary> <desc> <p>Same as <c>match(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>. @@ -324,7 +324,7 @@ </func> <func> - <name name="match" arity="3"/> + <name name="match" arity="3" since="OTP R14B"/> <fsummary>Search for the first match of a pattern in a binary.</fsummary> <type name="part"/> <desc> @@ -372,7 +372,7 @@ </func> <func> - <name name="matches" arity="2"/> + <name name="matches" arity="2" since="OTP R14B"/> <fsummary>Search for all matches of a pattern in a binary.</fsummary> <desc> <p>Same as <c>matches(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>. @@ -381,7 +381,7 @@ </func> <func> - <name name="matches" arity="3"/> + <name name="matches" arity="3" since="OTP R14B"/> <fsummary>Search for all matches of a pattern in a binary.</fsummary> <type name="part"/> <desc> @@ -425,7 +425,7 @@ </func> <func> - <name name="part" arity="2"/> + <name name="part" arity="2" since="OTP R14B"/> <fsummary>Extract a part of a binary.</fsummary> <desc> <p>Extracts the part of binary <c><anno>Subject</anno></c> described by @@ -453,7 +453,7 @@ </func> <func> - <name name="part" arity="3"/> + <name name="part" arity="3" since="OTP R14B"/> <fsummary>Extract a part of a binary.</fsummary> <desc> <p>Same as <c>part(<anno>Subject</anno>, {<anno>Pos</anno>, @@ -462,7 +462,7 @@ </func> <func> - <name name="referenced_byte_size" arity="1"/> + <name name="referenced_byte_size" arity="1" since="OTP R14B"/> <fsummary>Determine the size of the binary pointed out by a subbinary. </fsummary> <desc> @@ -525,7 +525,7 @@ store(Binary, GBSet) -> </func> <func> - <name name="replace" arity="3"/> + <name name="replace" arity="3" since="OTP R14B"/> <fsummary>Replace bytes in a binary according to a pattern.</fsummary> <desc> <p>Same as <c>replace(<anno>Subject</anno>, <anno>Pattern</anno>, <anno>Replacement</anno>,[])</c>.</p> @@ -533,7 +533,7 @@ store(Binary, GBSet) -> </func> <func> - <name name="replace" arity="4"/> + <name name="replace" arity="4" since="OTP R14B"/> <fsummary>Replace bytes in a binary according to a pattern.</fsummary> <type_desc variable="OnePos">An integer() =< byte_size(<anno>Replacement</anno>) </type_desc> @@ -575,7 +575,7 @@ store(Binary, GBSet) -> </func> <func> - <name name="split" arity="2"/> + <name name="split" arity="2" since="OTP R14B"/> <fsummary>Split a binary according to a pattern.</fsummary> <desc> <p>Same as <c>split(<anno>Subject</anno>, <anno>Pattern</anno>, @@ -584,7 +584,7 @@ store(Binary, GBSet) -> </func> <func> - <name name="split" arity="3"/> + <name name="split" arity="3" since="OTP R14B"/> <fsummary>Split a binary according to a pattern.</fsummary> <desc> <p>Splits <c><anno>Subject</anno></c> into a list of binaries based on diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml index b6cb6f5aae..29edc373c7 100644 --- a/lib/stdlib/doc/src/c.xml +++ b/lib/stdlib/doc/src/c.xml @@ -28,7 +28,7 @@ <date>1996-10-30</date> <rev>B</rev> </header> - <module>c</module> + <module since="">c</module> <modulesummary>Command interface module.</modulesummary> <description> <p>This module enables users to enter the short form of @@ -41,7 +41,7 @@ <funcs> <func> - <name name="bt" arity="1"/> + <name name="bt" arity="1" since=""/> <fsummary>Stack backtrace for a process.</fsummary> <desc> <p>Stack backtrace for a process. Equivalent to @@ -50,9 +50,9 @@ </func> <func> - <name name="c" arity="1"/> - <name name="c" arity="2"/> - <name name="c" arity="3"/> + <name name="c" arity="1" since=""/> + <name name="c" arity="2" since=""/> + <name name="c" arity="3" since="OTP 20.0"/> <fsummary>Compile and load a file or module.</fsummary> <desc> <p>Compiles and then purges and loads the code for a module. @@ -80,7 +80,7 @@ </func> <func> - <name name="cd" arity="1"/> + <name name="cd" arity="1" since=""/> <fsummary>Change working directory.</fsummary> <desc> <p>Changes working directory to <c><anno>Dir</anno></c>, which can be a @@ -94,7 +94,7 @@ </func> <func> - <name name="erlangrc" arity="1"/> + <name name="erlangrc" arity="1" since="OTP 21.0"/> <fsummary>Load an erlang resource file.</fsummary> <desc> <p>Search <c>PathList</c> and load <c>.erlang</c> resource file if @@ -103,7 +103,7 @@ </func> <func> - <name name="flush" arity="0"/> + <name name="flush" arity="0" since=""/> <fsummary>Flush any messages sent to the shell.</fsummary> <desc> <p>Flushes any messages sent to the shell.</p> @@ -111,7 +111,7 @@ </func> <func> - <name name="help" arity="0"/> + <name name="help" arity="0" since=""/> <fsummary>Help information.</fsummary> <desc> <p>Displays help information: all valid shell internal commands, @@ -120,8 +120,8 @@ </func> <func> - <name name="i" arity="0"/> - <name name="ni" arity="0"/> + <name name="i" arity="0" since=""/> + <name name="ni" arity="0" since=""/> <fsummary>System information.</fsummary> <desc> <p><c>i/0</c> displays system information, listing @@ -131,7 +131,7 @@ </func> <func> - <name name="i" arity="3"/> + <name name="i" arity="3" since=""/> <fsummary>Information about pid <X.Y.Z>.</fsummary> <desc> <p>Displays information about a process, Equivalent to @@ -141,7 +141,7 @@ </func> <func> - <name name="l" arity="1"/> + <name name="l" arity="1" since=""/> <fsummary>Load or reload a module.</fsummary> <desc> <p>Purges and loads, or reloads, a module by calling @@ -154,7 +154,7 @@ </func> <func> - <name>lc(Files) -> ok</name> + <name since="">lc(Files) -> ok</name> <fsummary>Compile a list of files.</fsummary> <type> <v>Files = [File]</v> @@ -171,7 +171,7 @@ </func> <func> - <name name="lm" arity="0"/> + <name name="lm" arity="0" since="OTP 20.0"/> <fsummary>Loads all modified modules.</fsummary> <desc> <p>Reloads all currently loaded modules that have changed on disk (see <c>mm()</c>). @@ -180,7 +180,7 @@ </func> <func> - <name name="ls" arity="0"/> + <name name="ls" arity="0" since=""/> <fsummary>List files in the current directory.</fsummary> <desc> <p>Lists files in the current directory.</p> @@ -188,7 +188,7 @@ </func> <func> - <name name="ls" arity="1"/> + <name name="ls" arity="1" since=""/> <fsummary>List files in a directory or a single file.</fsummary> <desc> <p>Lists files in directory <c><anno>Dir</anno></c> or, if <c>Dir</c> @@ -197,7 +197,7 @@ </func> <func> - <name name="m" arity="0"/> + <name name="m" arity="0" since=""/> <fsummary>Which modules are loaded.</fsummary> <desc> <p>Displays information about the loaded modules, including @@ -206,7 +206,7 @@ </func> <func> - <name name="m" arity="1"/> + <name name="m" arity="1" since=""/> <fsummary>Information about a module.</fsummary> <desc> <p>Displays information about <c><anno>Module</anno></c>.</p> @@ -214,7 +214,7 @@ </func> <func> - <name name="mm" arity="0"/> + <name name="mm" arity="0" since="OTP 20.0"/> <fsummary>Lists all modified modules.</fsummary> <desc> <p>Lists all modified modules. Shorthand for @@ -223,7 +223,7 @@ </func> <func> - <name name="memory" arity="0"/> + <name name="memory" arity="0" since=""/> <fsummary>Memory allocation information.</fsummary> <desc> <p>Memory allocation information. Equivalent to @@ -232,8 +232,8 @@ </func> <func> - <name name="memory" arity="1" clause_i="1"/> - <name name="memory" arity="1" clause_i="2"/> + <name name="memory" arity="1" clause_i="1" since=""/> + <name name="memory" arity="1" clause_i="2" since=""/> <fsummary>Memory allocation information.</fsummary> <desc> <p>Memory allocation information. Equivalent to @@ -242,8 +242,8 @@ </func> <func> - <name name="nc" arity="1"/> - <name name="nc" arity="2"/> + <name name="nc" arity="1" since=""/> + <name name="nc" arity="2" since=""/> <fsummary>Compile and load code in a file on all nodes.</fsummary> <desc> <p>Compiles and then loads the code for a file on all nodes. @@ -255,7 +255,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name name="nl" arity="1"/> + <name name="nl" arity="1" since=""/> <fsummary>Load module on all nodes.</fsummary> <desc> <p>Loads <c><anno>Module</anno></c> on all nodes.</p> @@ -263,7 +263,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name name="pid" arity="3"/> + <name name="pid" arity="3" since=""/> <fsummary>Convert <c>X,Y,Z</c> to a pid.</fsummary> <desc> <p>Converts <c><anno>X</anno></c>, <c><anno>Y</anno></c>, @@ -273,7 +273,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name name="pwd" arity="0"/> + <name name="pwd" arity="0" since=""/> <fsummary>Print working directory.</fsummary> <desc> <p>Prints the name of the working directory.</p> @@ -281,7 +281,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name name="q" arity="0"/> + <name name="q" arity="0" since=""/> <fsummary>Quit - shorthand for <c>init:stop()</c>.</fsummary> <desc> <p>This function is shorthand for <c>init:stop()</c>, that is, @@ -290,8 +290,8 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name name="regs" arity="0"/> - <name name="nregs" arity="0"/> + <name name="regs" arity="0" since=""/> + <name name="nregs" arity="0" since=""/> <fsummary>Information about registered processes.</fsummary> <desc> <p><c>regs/0</c> displays information about all registered @@ -301,7 +301,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name name="uptime" arity="0"/> + <name name="uptime" arity="0" since="OTP 18.0"/> <fsummary>Print node uptime.</fsummary> <desc> <p>Prints the node uptime (as specified by @@ -310,7 +310,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name>xm(ModSpec) -> void()</name> + <name since="">xm(ModSpec) -> void()</name> <fsummary>Cross-reference check a module.</fsummary> <type> <v>ModSpec = Module | Filename</v> @@ -325,7 +325,7 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w </func> <func> - <name>y(File) -> YeccRet</name> + <name since="">y(File) -> YeccRet</name> <fsummary>Generate an LALR-1 parser.</fsummary> <type> <v>File = name()</v> @@ -344,7 +344,7 @@ yecc:file(File)</code> </func> <func> - <name>y(File, Options) -> YeccRet</name> + <name since="">y(File, Options) -> YeccRet</name> <fsummary>Generate an LALR-1 parser.</fsummary> <type> <v>File = name()</v> diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml index 5aee635c38..518a085c89 100644 --- a/lib/stdlib/doc/src/calendar.xml +++ b/lib/stdlib/doc/src/calendar.xml @@ -28,7 +28,7 @@ <date>1996-11-05</date> <rev>B</rev> </header> - <module>calendar</module> + <module since="">calendar</module> <modulesummary>Local and universal time, day of the week, date and time conversions.</modulesummary> <description> @@ -128,8 +128,8 @@ <funcs> <func> - <name name="date_to_gregorian_days" arity="1"/> - <name name="date_to_gregorian_days" arity="3"/> + <name name="date_to_gregorian_days" arity="1" since=""/> + <name name="date_to_gregorian_days" arity="3" since=""/> <fsummary>Compute the number of days from year 0 up to the specified date.</fsummary> <type variable="Date" name_i="1"/> @@ -143,7 +143,7 @@ </func> <func> - <name name="datetime_to_gregorian_seconds" arity="1"/> + <name name="datetime_to_gregorian_seconds" arity="1" since=""/> <fsummary>Compute the number of seconds from year 0 up to the specified date and time.</fsummary> <desc> @@ -153,8 +153,8 @@ </func> <func> - <name name="day_of_the_week" arity="1"/> - <name name="day_of_the_week" arity="3"/> + <name name="day_of_the_week" arity="1" since=""/> + <name name="day_of_the_week" arity="3" since=""/> <fsummary>Compute the day of the week.</fsummary> <type variable="Date" name_i="1"/> <type variable="Year"/> @@ -169,7 +169,7 @@ </func> <func> - <name name="gregorian_days_to_date" arity="1"/> + <name name="gregorian_days_to_date" arity="1" since=""/> <fsummary>Compute the date from the number of gregorian days.</fsummary> <desc> <p>Computes the date from the specified number of gregorian days.</p> @@ -177,7 +177,7 @@ </func> <func> - <name name="gregorian_seconds_to_datetime" arity="1"/> + <name name="gregorian_seconds_to_datetime" arity="1" since=""/> <fsummary>Compute the date and time from the number of gregorian seconds. </fsummary> <desc> @@ -187,7 +187,7 @@ </func> <func> - <name name="is_leap_year" arity="1"/> + <name name="is_leap_year" arity="1" since=""/> <fsummary>Check if the year is a leap year.</fsummary> <desc> <p>Checks if the specified year is a leap year.</p> @@ -195,7 +195,7 @@ </func> <func> - <name name="iso_week_number" arity="0"/> + <name name="iso_week_number" arity="0" since="OTP R14B02"/> <fsummary>Compute the ISO week number for the actual date.</fsummary> <desc> <p>Returns tuple <c>{Year, WeekNum}</c> representing @@ -206,7 +206,7 @@ </func> <func> - <name name="iso_week_number" arity="1"/> + <name name="iso_week_number" arity="1" since="OTP R14B02"/> <fsummary>Compute the ISO week number for the specified date.</fsummary> <desc> <p>Returns tuple <c>{Year, WeekNum}</c> representing @@ -215,7 +215,7 @@ </func> <func> - <name name="last_day_of_the_month" arity="2"/> + <name name="last_day_of_the_month" arity="2" since=""/> <fsummary>Compute the number of days in a month.</fsummary> <desc> <p>Computes the number of days in a month.</p> @@ -223,7 +223,7 @@ </func> <func> - <name name="local_time" arity="0"/> + <name name="local_time" arity="0" since=""/> <fsummary>Compute local time.</fsummary> <desc> <p>Returns the local time reported by @@ -232,7 +232,7 @@ </func> <func> - <name name="local_time_to_universal_time" arity="1"/> + <name name="local_time_to_universal_time" arity="1" since=""/> <fsummary>Convert from local time to universal time (deprecated). </fsummary> <desc> @@ -253,7 +253,7 @@ </func> <func> - <name name="local_time_to_universal_time_dst" arity="1"/> + <name name="local_time_to_universal_time_dst" arity="1" since=""/> <fsummary>Convert from local time to universal time(s).</fsummary> <desc> <p>Converts from local time to Universal Coordinated Time (UTC). @@ -285,7 +285,7 @@ </func> <func> - <name name="now_to_datetime" arity="1"/> + <name name="now_to_datetime" arity="1" since=""/> <fsummary>Convert now to date and time.</fsummary> <desc> <p>Returns Universal Coordinated Time (UTC) @@ -296,7 +296,7 @@ </func> <func> - <name name="now_to_local_time" arity="1"/> + <name name="now_to_local_time" arity="1" since=""/> <fsummary>Convert now to local date and time.</fsummary> <desc> <p>Returns local date and time converted from the return value from @@ -306,7 +306,7 @@ </func> <func> - <name name="now_to_universal_time" arity="1"/> + <name name="now_to_universal_time" arity="1" since=""/> <fsummary>Convert now to date and time.</fsummary> <desc> <p>Returns Universal Coordinated Time (UTC) @@ -317,8 +317,8 @@ </func> <func> - <name name="rfc3339_to_system_time" arity="1"/> - <name name="rfc3339_to_system_time" arity="2"/> + <name name="rfc3339_to_system_time" arity="1" since="OTP 21.0"/> + <name name="rfc3339_to_system_time" arity="2" since="OTP 21.0"/> <fsummary>Convert from RFC 3339 timestamp to system time.</fsummary> <type name="rfc3339_string"/> <type name="rfc3339_time_unit"/> @@ -343,7 +343,7 @@ </func> <func> - <name name="seconds_to_daystime" arity="1"/> + <name name="seconds_to_daystime" arity="1" since=""/> <fsummary>Compute days and time from seconds.</fsummary> <desc> <p>Converts a specified number of seconds into days, hours, minutes, @@ -354,7 +354,7 @@ </func> <func> - <name name="seconds_to_time" arity="1"/> + <name name="seconds_to_time" arity="1" since=""/> <fsummary>Compute time from seconds.</fsummary> <type name="secs_per_day"/> <desc> @@ -365,7 +365,7 @@ </func> <func> - <name name="system_time_to_local_time" arity="2"/> + <name name="system_time_to_local_time" arity="2" since="OTP 21.0"/> <fsummary>Convert system time to local date and time.</fsummary> <desc> <p>Converts a specified system time into local date and time.</p> @@ -373,8 +373,8 @@ </func> <func> - <name name="system_time_to_rfc3339" arity="1"/> - <name name="system_time_to_rfc3339" arity="2"/> + <name name="system_time_to_rfc3339" arity="1" since="OTP 21.0"/> + <name name="system_time_to_rfc3339" arity="2" since="OTP 21.0"/> <fsummary>Convert from system to RFC 3339 timestamp.</fsummary> <type name="offset"/> <type name="rfc3339_string"/> @@ -426,7 +426,7 @@ </func> <func> - <name name="system_time_to_universal_time" arity="2"/> + <name name="system_time_to_universal_time" arity="2" since="OTP 21.0"/> <fsummary>Convert system time to universal date and time.</fsummary> <desc> <p>Converts a specified system time into universal date and time.</p> @@ -434,7 +434,7 @@ </func> <func> - <name name="time_difference" arity="2"/> + <name name="time_difference" arity="2" since=""/> <fsummary>Compute the difference between two times (deprecated). </fsummary> <desc> @@ -449,7 +449,7 @@ </func> <func> - <name name="time_to_seconds" arity="1"/> + <name name="time_to_seconds" arity="1" since=""/> <fsummary>Compute the number of seconds since midnight up to the specified time.</fsummary> <type name="secs_per_day"/> @@ -460,7 +460,7 @@ </func> <func> - <name name="universal_time" arity="0"/> + <name name="universal_time" arity="0" since=""/> <fsummary>Compute universal time.</fsummary> <desc> <p>Returns the Universal Coordinated Time (UTC) @@ -470,7 +470,7 @@ </func> <func> - <name name="universal_time_to_local_time" arity="1"/> + <name name="universal_time_to_local_time" arity="1" since=""/> <fsummary>Convert from universal time to local time.</fsummary> <desc> <p>Converts from Universal Coordinated Time (UTC) to local time. @@ -480,8 +480,8 @@ </func> <func> - <name name="valid_date" arity="1"/> - <name name="valid_date" arity="3"/> + <name name="valid_date" arity="1" since=""/> + <name name="valid_date" arity="3" since=""/> <fsummary>Check if a date is valid</fsummary> <type variable="Date" name_i="1"/> <type variable="Year"/> diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml index eb6e32aecf..8e4e002000 100644 --- a/lib/stdlib/doc/src/dets.xml +++ b/lib/stdlib/doc/src/dets.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>dets.xml</file> </header> - <module>dets</module> + <module since="">dets</module> <modulesummary>A disk-based term storage.</modulesummary> <description> <p>This module provides a term storage on file. The @@ -188,7 +188,7 @@ <funcs> <func> - <name name="all" arity="0"/> + <name name="all" arity="0" since=""/> <fsummary>Return a list of the names of all open Dets tables on this node.</fsummary> <desc> @@ -197,7 +197,7 @@ </func> <func> - <name name="bchunk" arity="2"/> + <name name="bchunk" arity="2" since=""/> <fsummary>Return a chunk of objects stored in a Dets table. </fsummary> <desc> @@ -227,7 +227,7 @@ </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a Dets table.</fsummary> <desc> <p>Closes a table. Only processes that have opened a table are @@ -239,7 +239,7 @@ </func> <func> - <name name="delete" arity="2"/> + <name name="delete" arity="2" since=""/> <fsummary>Delete all objects with a specified key from a Dets table.</fsummary> <desc> @@ -249,7 +249,7 @@ </func> <func> - <name name="delete_all_objects" arity="1"/> + <name name="delete_all_objects" arity="1" since=""/> <fsummary>Delete all objects from a Dets table.</fsummary> <desc> <p>Deletes all objects from a table in almost constant time. @@ -259,7 +259,7 @@ </func> <func> - <name name="delete_object" arity="2"/> + <name name="delete_object" arity="2" since=""/> <fsummary>Delete a specified object from a Dets table.</fsummary> <desc> <p>Deletes all instances of a specified object from a table. If a @@ -270,7 +270,7 @@ </func> <func> - <name name="first" arity="1"/> + <name name="first" arity="1" since=""/> <fsummary>Return the first key stored in a Dets table.</fsummary> <desc> <p>Returns the first key stored in table <c><anno>Name</anno></c> @@ -295,8 +295,8 @@ </func> <func> - <name name="foldl" arity="3"/> - <name name="foldr" arity="3"/> + <name name="foldl" arity="3" since=""/> + <name name="foldr" arity="3" since=""/> <fsummary>Fold a function over a Dets table.</fsummary> <desc> <p>Calls <c><anno>Function</anno></c> on successive elements of @@ -309,7 +309,7 @@ </func> <func> - <name name="from_ets" arity="2"/> + <name name="from_ets" arity="2" since=""/> <fsummary>Replace the objects of a Dets table with the objects of an ETS table.</fsummary> <desc> @@ -322,7 +322,7 @@ </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since=""/> <fsummary>Return information about a Dets table.</fsummary> <desc> <p>Returns information about table <c><anno>Name</anno></c> @@ -354,7 +354,7 @@ </func> <func> - <name name="info" arity="2"/> + <name name="info" arity="2" since=""/> <fsummary>Return the information associated with a specified item for a Dets table.</fsummary> <desc> @@ -455,8 +455,8 @@ </func> <func> - <name name="init_table" arity="2"/> - <name name="init_table" arity="3"/> + <name name="init_table" arity="2" since=""/> + <name name="init_table" arity="3" since=""/> <fsummary>Replace all objects of a Dets table.</fsummary> <desc> <p>Replaces the existing objects of table <c><anno>Name</anno></c> @@ -516,7 +516,7 @@ </func> <func> - <name name="insert" arity="2"/> + <name name="insert" arity="2" since=""/> <fsummary>Insert one or more objects into a Dets table.</fsummary> <desc> <p>Inserts one or more objects into the table <c><anno>Name</anno></c>. @@ -527,7 +527,7 @@ </func> <func> - <name name="insert_new" arity="2"/> + <name name="insert_new" arity="2" since=""/> <fsummary>Insert one or more objects into a Dets table.</fsummary> <desc> <p>Inserts one or more objects into table <c><anno>Name</anno></c>. @@ -539,7 +539,7 @@ </func> <func> - <name name="is_compatible_bchunk_format" arity="2"/> + <name name="is_compatible_bchunk_format" arity="2" since=""/> <fsummary>Test compatibility of chunk data of a table.</fsummary> <desc> <p>Returns <c>true</c> if it would be possible to initialize @@ -554,7 +554,7 @@ </func> <func> - <name name="is_dets_file" arity="1"/> + <name name="is_dets_file" arity="1" since=""/> <fsummary>Test for a Dets table.</fsummary> <desc> <p>Returns <c>true</c> if file <c><anno>Filename</anno></c> @@ -563,7 +563,7 @@ </func> <func> - <name name="lookup" arity="2"/> + <name name="lookup" arity="2" since=""/> <fsummary>Return all objects with a specified key stored in a Dets table.</fsummary> <desc> @@ -590,7 +590,7 @@ ok </func> <func> - <name name="match" arity="1"/> + <name name="match" arity="1" since=""/> <fsummary>Match a chunk of objects stored in a Dets table and return a list of variable bindings.</fsummary> <desc> @@ -606,7 +606,7 @@ ok </func> <func> - <name name="match" arity="2"/> + <name name="match" arity="2" since=""/> <fsummary>Match the objects stored in a Dets table and return a list of variable bindings.</fsummary> <desc> @@ -622,7 +622,7 @@ ok </func> <func> - <name name="match" arity="3"/> + <name name="match" arity="3" since=""/> <fsummary>Match the first chunk of objects stored in a Dets table and return a list of variable bindings.</fsummary> <desc> @@ -654,7 +654,7 @@ ok </func> <func> - <name name="match_delete" arity="2"/> + <name name="match_delete" arity="2" since=""/> <fsummary>Delete all objects that match a given pattern from a Dets table.</fsummary> <desc> @@ -667,7 +667,7 @@ ok </func> <func> - <name name="match_object" arity="1"/> + <name name="match_object" arity="1" since=""/> <fsummary>Match a chunk of objects stored in a Dets table and return a list of objects.</fsummary> <desc> @@ -683,7 +683,7 @@ ok </func> <func> - <name name="match_object" arity="2"/> + <name name="match_object" arity="2" since=""/> <fsummary>Match the objects stored in a Dets table and return a list of objects.</fsummary> <desc> @@ -702,7 +702,7 @@ ok </func> <func> - <name name="match_object" arity="3"/> + <name name="match_object" arity="3" since=""/> <fsummary>Match the first chunk of objects stored in a Dets table and return a list of objects.</fsummary> <desc> @@ -735,7 +735,7 @@ ok </func> <func> - <name name="member" arity="2"/> + <name name="member" arity="2" since=""/> <fsummary>Test for occurrence of a key in a Dets table.</fsummary> <desc> <p>Works like <seealso marker="#lookup/2"><c>lookup/2</c></seealso>, @@ -746,7 +746,7 @@ ok </func> <func> - <name name="next" arity="2"/> + <name name="next" arity="2" since=""/> <fsummary>Return the next key in a Dets table.</fsummary> <desc> <p>Returns either the key following <c><anno>Key1</anno></c> in table @@ -760,7 +760,7 @@ ok </func> <func> - <name name="open_file" arity="1"/> + <name name="open_file" arity="1" since=""/> <fsummary>Open an existing Dets table.</fsummary> <desc> <p>Opens an existing table. If the table is not properly closed, @@ -770,7 +770,7 @@ ok </func> <func> - <name name="open_file" arity="2"/> + <name name="open_file" arity="2" since=""/> <fsummary>Open a Dets table.</fsummary> <desc> <p>Opens a table. An empty Dets table is created if no file @@ -872,7 +872,7 @@ ok </func> <func> - <name name="pid2name" arity="1"/> + <name name="pid2name" arity="1" since=""/> <fsummary>Return the name of the Dets table handled by a pid.</fsummary> <desc> <p>Returns the table name given the pid of a process @@ -883,7 +883,7 @@ ok </func> <func> - <name name="repair_continuation" arity="2"/> + <name name="repair_continuation" arity="2" since=""/> <fsummary>Repair a continuation from <c>select/1</c> or <c>select/3</c>. </fsummary> <desc> @@ -917,7 +917,7 @@ ok </func> <func> - <name name="safe_fixtable" arity="2"/> + <name name="safe_fixtable" arity="2" since=""/> <fsummary>Fix a Dets table for safe traversal.</fsummary> <desc> <p>If <c><anno>Fix</anno></c> is <c>true</c>, table @@ -945,7 +945,7 @@ ok </func> <func> - <name name="select" arity="1"/> + <name name="select" arity="1" since=""/> <fsummary>Apply a match specification to some objects stored in a Dets table.</fsummary> <desc> @@ -962,7 +962,7 @@ ok </func> <func> - <name name="select" arity="2"/> + <name name="select" arity="2" since=""/> <fsummary>Apply a match specification to all objects stored in a Dets table.</fsummary> <desc> @@ -984,7 +984,7 @@ ok </func> <func> - <name name="select" arity="3"/> + <name name="select" arity="3" since=""/> <fsummary>Apply a match specification to the first chunk of objects stored in a Dets table.</fsummary> <desc> @@ -1019,7 +1019,7 @@ ok </func> <func> - <name name="select_delete" arity="2"/> + <name name="select_delete" arity="2" since=""/> <fsummary>Delete all objects that match a given pattern from a Dets table.</fsummary> <desc> @@ -1036,7 +1036,7 @@ ok </func> <func> - <name name="slot" arity="2"/> + <name name="slot" arity="2" since=""/> <fsummary>Return the list of objects associated with a slot of a Dets table.</fsummary> <desc> @@ -1049,7 +1049,7 @@ ok </func> <func> - <name name="sync" arity="1"/> + <name name="sync" arity="1" since=""/> <fsummary>Ensure that all updates made to a Dets table are written to disk.</fsummary> <desc> @@ -1064,8 +1064,8 @@ ok </func> <func> - <name name="table" arity="1"/> - <name name="table" arity="2"/> + <name name="table" arity="1" since=""/> + <name name="table" arity="2" since=""/> <fsummary>Return a QLC query handle.</fsummary> <desc> <p>Returns a Query List @@ -1140,7 +1140,7 @@ true</pre> </func> <func> - <name name="to_ets" arity="2"/> + <name name="to_ets" arity="2" since=""/> <fsummary>Insert all objects of a Dets table into an ETS table.</fsummary> <desc> @@ -1153,7 +1153,7 @@ true</pre> </func> <func> - <name name="traverse" arity="2"/> + <name name="traverse" arity="2" since=""/> <fsummary>Apply a function to all or some objects stored in a Dets table.</fsummary> <desc> @@ -1192,7 +1192,7 @@ fun(X) -> {continue, X} end.</pre> </func> <func> - <name name="update_counter" arity="3"/> + <name name="update_counter" arity="3" since=""/> <fsummary>Update a counter object stored in a Dets table. </fsummary> <desc> diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml index c229a18721..95a98cef12 100644 --- a/lib/stdlib/doc/src/dict.xml +++ b/lib/stdlib/doc/src/dict.xml @@ -28,7 +28,7 @@ <date>1997-01-15</date> <rev>B</rev> </header> - <module>dict</module> + <module since="">dict</module> <modulesummary>Key-value dictionary.</modulesummary> <description> <p>This module provides a <c>Key</c>-<c>Value</c> dictionary. @@ -55,7 +55,7 @@ <funcs> <func> - <name name="append" arity="3"/> + <name name="append" arity="3" since=""/> <fsummary>Append a value to keys in a dictionary.</fsummary> <desc> <p>Appends a new <c><anno>Value</anno></c> to the current list @@ -65,7 +65,7 @@ </func> <func> - <name name="append_list" arity="3"/> + <name name="append_list" arity="3" since=""/> <fsummary>Append new values to keys in a dictionary.</fsummary> <desc> <p>Appends a list of values <c><anno>ValList</anno></c> to @@ -77,7 +77,7 @@ </func> <func> - <name name="erase" arity="2"/> + <name name="erase" arity="2" since=""/> <fsummary>Erase a key from a dictionary.</fsummary> <desc> <p>Erases all items with a given key from a dictionary.</p> @@ -85,7 +85,7 @@ </func> <func> - <name name="fetch" arity="2"/> + <name name="fetch" arity="2" since=""/> <fsummary>Look up values in a dictionary.</fsummary> <desc> <p>Returns the value associated with <c><anno>Key</anno></c> @@ -98,7 +98,7 @@ </func> <func> - <name name="fetch_keys" arity="1"/> + <name name="fetch_keys" arity="1" since=""/> <fsummary>Return all keys in a dictionary.</fsummary> <desc> <p>Returns a list of all keys in dictionary <c>Dict</c>.</p> @@ -106,7 +106,7 @@ </func> <func> - <name name="take" arity="2"/> + <name name="take" arity="2" since="OTP 20.0"/> <fsummary>Return value and new dictionary without element with this value.</fsummary> <desc> <p>This function returns value from dictionary and a @@ -116,7 +116,7 @@ </func> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Select elements that satisfy a predicate.</fsummary> <desc> <p><c><anno>Dict2</anno></c> is a dictionary of all keys and values in @@ -127,7 +127,7 @@ </func> <func> - <name name="find" arity="2"/> + <name name="find" arity="2" since=""/> <fsummary>Search for a key in a dictionary.</fsummary> <desc> <p>Searches for a key in dictionary <c>Dict</c>. Returns @@ -139,7 +139,7 @@ </func> <func> - <name name="fold" arity="3"/> + <name name="fold" arity="3" since=""/> <fsummary>Fold a function over a dictionary.</fsummary> <desc> <p>Calls <c><anno>Fun</anno></c> on successive keys and values of @@ -153,7 +153,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Convert a list of pairs to a dictionary.</fsummary> <desc> <p>Converts the <c><anno>Key</anno></c>-<c><anno>Value</anno></c> list @@ -162,7 +162,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since="OTP 17.0"/> <fsummary>Return <c>true</c> if the dictionary is empty.</fsummary> <desc> <p>Returns <c>true</c> if dictionary <c><anno>Dict</anno></c> has no @@ -171,7 +171,7 @@ </func> <func> - <name name="is_key" arity="2"/> + <name name="is_key" arity="2" since=""/> <fsummary>Test if a key is in a dictionary.</fsummary> <desc> <p>Tests if <c><anno>Key</anno></c> is contained in @@ -180,7 +180,7 @@ </func> <func> - <name name="map" arity="2"/> + <name name="map" arity="2" since=""/> <fsummary>Map a function over a dictionary.</fsummary> <desc> <p>Calls <c><anno>Fun</anno></c> on successive keys and values @@ -190,7 +190,7 @@ </func> <func> - <name name="merge" arity="3"/> + <name name="merge" arity="3" since=""/> <fsummary>Merge two dictionaries.</fsummary> <desc> <p>Merges two dictionaries, <c><anno>Dict1</anno></c> and @@ -209,7 +209,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Create a dictionary.</fsummary> <desc> <p>Creates a new dictionary.</p> @@ -217,7 +217,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Return the number of elements in a dictionary.</fsummary> <desc> <p>Returns the number of elements in dictionary @@ -226,7 +226,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="store" arity="3"/> + <name name="store" arity="3" since=""/> <fsummary>Store a value in a dictionary.</fsummary> <desc> <p>Stores a <c><anno>Key</anno></c>-<c><anno>Value</anno></c> pair in @@ -237,7 +237,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert a dictionary to a list of pairs.</fsummary> <desc> <p>Converts dictionary <c>Dict</c> to a list representation.</p> @@ -245,7 +245,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="update" arity="3"/> + <name name="update" arity="3" since=""/> <fsummary>Update a value in a dictionary.</fsummary> <desc> <p>Updates a value in a dictionary by calling <c><anno>Fun</anno></c> on @@ -255,7 +255,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="update" arity="4"/> + <name name="update" arity="4" since=""/> <fsummary>Update a value in a dictionary.</fsummary> <desc> <p>Updates a value in a dictionary by calling <c><anno>Fun</anno></c> on @@ -269,7 +269,7 @@ append(Key, Val, D) -> </func> <func> - <name name="update_counter" arity="3"/> + <name name="update_counter" arity="3" since=""/> <fsummary>Increment a value in a dictionary.</fsummary> <desc> <p>Adds <c><anno>Increment</anno></c> to the value associated with diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml index a5252b443b..cf2c0844c9 100644 --- a/lib/stdlib/doc/src/digraph.xml +++ b/lib/stdlib/doc/src/digraph.xml @@ -32,7 +32,7 @@ <rev>C</rev> <file>digraph.xml</file> </header> - <module>digraph</module> + <module since="">digraph</module> <modulesummary>Directed graphs.</modulesummary> <description> <p>This module provides a version of labeled @@ -144,9 +144,9 @@ <funcs> <func> - <name name="add_edge" arity="3"/> - <name name="add_edge" arity="4"/> - <name name="add_edge" arity="5"/> + <name name="add_edge" arity="3" since=""/> + <name name="add_edge" arity="4" since=""/> + <name name="add_edge" arity="5" since=""/> <fsummary>Add an edge to a digraph.</fsummary> <type name="add_edge_err_rsn"/> <desc> @@ -183,9 +183,9 @@ </func> <func> - <name name="add_vertex" arity="1"/> - <name name="add_vertex" arity="2"/> - <name name="add_vertex" arity="3"/> + <name name="add_vertex" arity="1" since=""/> + <name name="add_vertex" arity="2" since=""/> + <name name="add_vertex" arity="3" since=""/> <fsummary>Add or modify a vertex of a digraph.</fsummary> <desc> <p><c>add_vertex/3</c> creates (or modifies) vertex @@ -204,7 +204,7 @@ </func> <func> - <name name="del_edge" arity="2"/> + <name name="del_edge" arity="2" since=""/> <fsummary>Delete an edge from a digraph.</fsummary> <desc> <p>Deletes edge <c><anno>E</anno></c> from digraph @@ -213,7 +213,7 @@ </func> <func> - <name name="del_edges" arity="2"/> + <name name="del_edges" arity="2" since=""/> <fsummary>Delete edges from a digraph.</fsummary> <desc> <p>Deletes the edges in list <c><anno>Edges</anno></c> from digraph @@ -222,7 +222,7 @@ </func> <func> - <name name="del_path" arity="3"/> + <name name="del_path" arity="3" since=""/> <fsummary>Delete paths from a digraph.</fsummary> <desc> <p>Deletes edges from digraph <c><anno>G</anno></c> until there are no @@ -252,7 +252,7 @@ </func> <func> - <name name="del_vertex" arity="2"/> + <name name="del_vertex" arity="2" since=""/> <fsummary>Delete a vertex from a digraph.</fsummary> <desc> <p>Deletes vertex <c><anno>V</anno></c> from digraph @@ -265,7 +265,7 @@ </func> <func> - <name name="del_vertices" arity="2"/> + <name name="del_vertices" arity="2" since=""/> <fsummary>Delete vertices from a digraph.</fsummary> <desc> <p>Deletes the vertices in list <c><anno>Vertices</anno></c> from @@ -274,7 +274,7 @@ </func> <func> - <name name="delete" arity="1"/> + <name name="delete" arity="1" since=""/> <fsummary>Delete a digraph.</fsummary> <desc> <p>Deletes digraph <c><anno>G</anno></c>. This call is important @@ -285,7 +285,7 @@ </func> <func> - <name name="edge" arity="2"/> + <name name="edge" arity="2" since=""/> <fsummary>Return the vertices and the label of an edge of a digraph. </fsummary> <desc> @@ -303,7 +303,7 @@ </func> <func> - <name name="edges" arity="1"/> + <name name="edges" arity="1" since=""/> <fsummary>Return all edges of a digraph.</fsummary> <desc> <p>Returns a list of all edges of digraph <c><anno>G</anno></c>, in @@ -312,7 +312,7 @@ </func> <func> - <name name="edges" arity="2"/> + <name name="edges" arity="2" since=""/> <fsummary>Return the edges emanating from or incident on a vertex of a digraph.</fsummary> <desc> @@ -324,7 +324,7 @@ </func> <func> - <name name="get_cycle" arity="2"/> + <name name="get_cycle" arity="2" since=""/> <fsummary>Find one cycle in a digraph.</fsummary> <desc> <p>If a <seealso marker="#simple_cycle">simple cycle</seealso> of @@ -341,7 +341,7 @@ </func> <func> - <name name="get_path" arity="3"/> + <name name="get_path" arity="3" since=""/> <fsummary>Find one path in a digraph.</fsummary> <desc> <p>Tries to find @@ -357,7 +357,7 @@ </func> <func> - <name name="get_short_cycle" arity="2"/> + <name name="get_short_cycle" arity="2" since=""/> <fsummary>Find one short cycle in a digraph.</fsummary> <desc> <p>Tries to find an as short as possible @@ -375,7 +375,7 @@ </func> <func> - <name name="get_short_path" arity="3"/> + <name name="get_short_path" arity="3" since=""/> <fsummary>Find one short path in a digraph.</fsummary> <desc> <p>Tries to find an as short as possible @@ -392,7 +392,7 @@ </func> <func> - <name name="in_degree" arity="2"/> + <name name="in_degree" arity="2" since=""/> <fsummary>Return the in-degree of a vertex of a digraph.</fsummary> <desc> <p>Returns the <seealso marker="#in_degree">in-degree</seealso> of @@ -401,7 +401,7 @@ </func> <func> - <name name="in_edges" arity="2"/> + <name name="in_edges" arity="2" since=""/> <fsummary>Return all edges incident on a vertex of a digraph.</fsummary> <desc> <p>Returns a list of all @@ -412,7 +412,7 @@ </func> <func> - <name name="in_neighbours" arity="2"/> + <name name="in_neighbours" arity="2" since=""/> <fsummary>Return all in-neighbors of a vertex of a digraph.</fsummary> <desc> <p>Returns a list of @@ -423,7 +423,7 @@ </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since=""/> <fsummary>Return information about a digraph.</fsummary> <type name="d_cyclicity"/> <type name="d_protection"/> @@ -453,7 +453,7 @@ </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Return a protected empty digraph, where cycles are allowed. </fsummary> <desc> @@ -462,7 +462,7 @@ </func> <func> - <name name="new" arity="1"/> + <name name="new" arity="1" since=""/> <fsummary>Create a new empty digraph.</fsummary> <type variable="Type"/> <type name="d_type"/> @@ -492,7 +492,7 @@ </func> <func> - <name name="no_edges" arity="1"/> + <name name="no_edges" arity="1" since=""/> <fsummary>Return the number of edges of a digraph.</fsummary> <desc> <p>Returns the number of edges of digraph <c><anno>G</anno></c>.</p> @@ -500,7 +500,7 @@ </func> <func> - <name name="no_vertices" arity="1"/> + <name name="no_vertices" arity="1" since=""/> <fsummary>Return the number of vertices of a digraph.</fsummary> <desc> <p>Returns the number of vertices of digraph <c><anno>G</anno></c>.</p> @@ -508,7 +508,7 @@ </func> <func> - <name name="out_degree" arity="2"/> + <name name="out_degree" arity="2" since=""/> <fsummary>Return the out-degree of a vertex of a digraph.</fsummary> <desc> <p>Returns the <seealso marker="#out_degree">out-degree</seealso> of @@ -517,7 +517,7 @@ </func> <func> - <name name="out_edges" arity="2"/> + <name name="out_edges" arity="2" since=""/> <fsummary>Return all edges emanating from a vertex of a digraph. </fsummary> <desc> @@ -529,7 +529,7 @@ </func> <func> - <name name="out_neighbours" arity="2"/> + <name name="out_neighbours" arity="2" since=""/> <fsummary>Return all out-neighbors of a vertex of a digraph.</fsummary> <desc> <p>Returns a list of @@ -540,7 +540,7 @@ </func> <func> - <name name="vertex" arity="2"/> + <name name="vertex" arity="2" since=""/> <fsummary>Return the label of a vertex of a digraph.</fsummary> <desc> <p>Returns <c>{<anno>V</anno>, <anno>Label</anno>}</c>, @@ -553,7 +553,7 @@ </func> <func> - <name name="vertices" arity="1"/> + <name name="vertices" arity="1" since=""/> <fsummary>Return all vertices of a digraph.</fsummary> <desc> <p>Returns a list of all vertices of digraph <c><anno>G</anno></c>, in diff --git a/lib/stdlib/doc/src/digraph_utils.xml b/lib/stdlib/doc/src/digraph_utils.xml index cb316e5b93..13b0aaad9e 100644 --- a/lib/stdlib/doc/src/digraph_utils.xml +++ b/lib/stdlib/doc/src/digraph_utils.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>digraph_utils.xml</file> </header> - <module>digraph_utils</module> + <module since="">digraph_utils</module> <modulesummary>Algorithms for directed graphs.</modulesummary> <description> <p>This module provides algorithms based on depth-first traversal of @@ -154,7 +154,7 @@ <funcs> <func> - <name name="arborescence_root" arity="1"/> + <name name="arborescence_root" arity="1" since=""/> <fsummary>Check if a digraph is an arborescence.</fsummary> <desc> <p>Returns <c>{yes, <anno>Root</anno>}</c> if <c><anno>Root</anno></c> @@ -164,7 +164,7 @@ </func> <func> - <name name="components" arity="1"/> + <name name="components" arity="1" since=""/> <fsummary>Return the components of a digraph.</fsummary> <desc> <p>Returns a list @@ -177,7 +177,7 @@ </func> <func> - <name name="condensation" arity="1"/> + <name name="condensation" arity="1" since=""/> <fsummary>Return a condensed graph of a digraph.</fsummary> <desc> <p>Creates a digraph where the vertices are @@ -202,7 +202,7 @@ </func> <func> - <name name="cyclic_strong_components" arity="1"/> + <name name="cyclic_strong_components" arity="1" since=""/> <fsummary>Return the cyclic strong components of a digraph.</fsummary> <desc> <p>Returns a list of <seealso marker="#strong_components">strongly @@ -218,7 +218,7 @@ </func> <func> - <name name="is_acyclic" arity="1"/> + <name name="is_acyclic" arity="1" since=""/> <fsummary>Check if a digraph is acyclic.</fsummary> <desc> <p>Returns <c>true</c> if and only if digraph @@ -228,7 +228,7 @@ </func> <func> - <name name="is_arborescence" arity="1"/> + <name name="is_arborescence" arity="1" since=""/> <fsummary>Check if a digraph is an arborescence.</fsummary> <desc> <p>Returns <c>true</c> if and only if digraph @@ -238,7 +238,7 @@ </func> <func> - <name name="is_tree" arity="1"/> + <name name="is_tree" arity="1" since=""/> <fsummary>Check if a digraph is a tree.</fsummary> <desc> <p>Returns <c>true</c> if and only if digraph @@ -248,7 +248,7 @@ </func> <func> - <name name="loop_vertices" arity="1"/> + <name name="loop_vertices" arity="1" since=""/> <fsummary>Return the vertices of a digraph included in some loop. </fsummary> <desc> @@ -258,7 +258,7 @@ </func> <func> - <name name="postorder" arity="1"/> + <name name="postorder" arity="1" since=""/> <fsummary>Return the vertices of a digraph in postorder.</fsummary> <desc> <p>Returns all vertices of digraph <c><anno>Digraph</anno></c>. @@ -273,7 +273,7 @@ </func> <func> - <name name="preorder" arity="1"/> + <name name="preorder" arity="1" since=""/> <fsummary>Return the vertices of a digraph in preorder.</fsummary> <desc> <p>Returns all vertices of digraph <c><anno>Digraph</anno></c>. @@ -285,7 +285,7 @@ </func> <func> - <name name="reachable" arity="2"/> + <name name="reachable" arity="2" since=""/> <fsummary>Return the vertices reachable from some vertices of a digraph. </fsummary> <desc> @@ -300,7 +300,7 @@ </func> <func> - <name name="reachable_neighbours" arity="2"/> + <name name="reachable_neighbours" arity="2" since=""/> <fsummary>Return the neighbors reachable from some vertices of a digraph.</fsummary> <desc> @@ -316,7 +316,7 @@ </func> <func> - <name name="reaching" arity="2"/> + <name name="reaching" arity="2" since=""/> <fsummary>Return the vertices that reach some vertices of a digraph. </fsummary> <desc> @@ -330,7 +330,7 @@ </func> <func> - <name name="reaching_neighbours" arity="2"/> + <name name="reaching_neighbours" arity="2" since=""/> <fsummary>Return the neighbors that reach some vertices of a digraph. </fsummary> <desc> @@ -345,7 +345,7 @@ </func> <func> - <name name="strong_components" arity="1"/> + <name name="strong_components" arity="1" since=""/> <fsummary>Return the strong components of a digraph.</fsummary> <desc> <p>Returns a list of <seealso marker="#strong_components">strongly @@ -359,8 +359,8 @@ </func> <func> - <name name="subgraph" arity="2"/> - <name name="subgraph" arity="3"/> + <name name="subgraph" arity="2" since=""/> + <name name="subgraph" arity="3" since=""/> <fsummary>Return a subgraph of a digraph.</fsummary> <desc> <p>Creates a maximal <seealso marker="#subgraph">subgraph</seealso> @@ -387,7 +387,7 @@ </func> <func> - <name name="topsort" arity="1"/> + <name name="topsort" arity="1" since=""/> <fsummary>Return a topological sorting of the vertices of a digraph. </fsummary> <desc> diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml index d803d259aa..110c1cea2c 100644 --- a/lib/stdlib/doc/src/epp.xml +++ b/lib/stdlib/doc/src/epp.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>epp.xml</file> </header> - <module>epp</module> + <module since="">epp</module> <modulesummary>An Erlang code preprocessor.</modulesummary> <description> <p>The Erlang code preprocessor includes functions that are used by the @@ -76,7 +76,7 @@ <funcs> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close the preprocessing of the file associated with <c>Epp</c>. </fsummary> <desc> @@ -85,7 +85,7 @@ </func> <func> - <name name="default_encoding" arity="0"/> + <name name="default_encoding" arity="0" since="OTP R16B"/> <fsummary>Return the default encoding of Erlang source files.</fsummary> <desc> <p>Returns the default encoding of Erlang source files.</p> @@ -93,7 +93,7 @@ </func> <func> - <name name="encoding_to_string" arity="1"/> + <name name="encoding_to_string" arity="1" since="OTP R16B"/> <fsummary>Return a string representation of an encoding.</fsummary> <desc> <p>Returns a string representation of an encoding. The string @@ -107,7 +107,7 @@ </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since="OTP R14B03"/> <fsummary>Format an error descriptor.</fsummary> <desc> <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns @@ -120,7 +120,7 @@ </func> <func> - <name name="open" arity="1"/> + <name name="open" arity="1" since="OTP 17.0"/> <fsummary>Open a file for preprocessing.</fsummary> <desc> <p>Opens a file for preprocessing.</p> @@ -136,7 +136,7 @@ </func> <func> - <name name="open" arity="2"/> + <name name="open" arity="2" since=""/> <fsummary>Open a file for preprocessing.</fsummary> <desc> <p>Equivalent to @@ -145,7 +145,7 @@ </func> <func> - <name name="open" arity="3"/> + <name name="open" arity="3" since=""/> <fsummary>Open a file for preprocessing.</fsummary> <desc> <p>Equivalent to <c>epp:open([{name, FileName}, {includes, IncludePath}, @@ -154,7 +154,7 @@ </func> <func> - <name name="parse_erl_form" arity="1"/> + <name name="parse_erl_form" arity="1" since=""/> <fsummary>Return the next Erlang form from the opened Erlang source file. </fsummary> <type name="warning_info"/> @@ -167,7 +167,7 @@ </func> <func> - <name name="parse_file" arity="2"/> + <name name="parse_file" arity="2" since="OTP 17.0"/> <fsummary>Preprocess and parse an Erlang source file.</fsummary> <desc> <p>Preprocesses and parses an Erlang source file. @@ -185,7 +185,7 @@ </func> <func> - <name name="parse_file" arity="3"/> + <name name="parse_file" arity="3" since=""/> <fsummary>Preprocess and parse an Erlang source file.</fsummary> <desc> <p>Equivalent to <c>epp:parse_file(FileName, [{includes, IncludePath}, @@ -194,8 +194,8 @@ </func> <func> - <name name="read_encoding" arity="1"/> - <name name="read_encoding" arity="2"/> + <name name="read_encoding" arity="1" since="OTP R16B"/> + <name name="read_encoding" arity="2" since="OTP R16B"/> <fsummary>Read the encoding from a file.</fsummary> <desc> <p>Read the <seealso marker="#encoding">encoding</seealso> from @@ -209,8 +209,8 @@ </func> <func> - <name name="read_encoding_from_binary" arity="1"/> - <name name="read_encoding_from_binary" arity="2"/> + <name name="read_encoding_from_binary" arity="1" since="OTP R16B"/> + <name name="read_encoding_from_binary" arity="2" since="OTP R16B"/> <fsummary>Read the encoding from a binary.</fsummary> <desc> <p>Read the <seealso marker="#encoding">encoding</seealso> from @@ -224,7 +224,7 @@ </func> <func> - <name name="set_encoding" arity="1"/> + <name name="set_encoding" arity="1" since="OTP R16B"/> <fsummary>Read and set the encoding of an I/O device.</fsummary> <desc> <p>Reads the <seealso marker="#encoding">encoding</seealso> from @@ -239,7 +239,7 @@ </func> <func> - <name name="set_encoding" arity="2"/> + <name name="set_encoding" arity="2" since="OTP 17.0"/> <fsummary>Read and set the encoding of an I/O device.</fsummary> <desc> <p>Reads the <seealso marker="#encoding">encoding</seealso> from diff --git a/lib/stdlib/doc/src/erl_anno.xml b/lib/stdlib/doc/src/erl_anno.xml index f316f63d98..dff93619ab 100644 --- a/lib/stdlib/doc/src/erl_anno.xml +++ b/lib/stdlib/doc/src/erl_anno.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>erl_anno.xml</file> </header> - <module>erl_anno</module> + <module since="OTP 18.0">erl_anno</module> <modulesummary>Abstract datatype for the annotations of the Erlang Compiler. </modulesummary> @@ -135,7 +135,7 @@ <funcs> <func> - <name name="column" arity="1"/> + <name name="column" arity="1" since="OTP 18.0"/> <fsummary>Return the column.</fsummary> <type name="column"></type> <desc> @@ -144,7 +144,7 @@ </func> <func> - <name name="end_location" arity="1"/> + <name name="end_location" arity="1" since="OTP 18.0"/> <fsummary>Return the end location of the text.</fsummary> <type name="location"></type> <desc> @@ -155,7 +155,7 @@ </func> <func> - <name name="file" arity="1"/> + <name name="file" arity="1" since="OTP 18.0"/> <fsummary>Return the filename.</fsummary> <type name="filename"></type> <desc> @@ -165,7 +165,7 @@ </func> <func> - <name name="from_term" arity="1"/> + <name name="from_term" arity="1" since="OTP 18.0"/> <fsummary>Return annotations given a term.</fsummary> <desc> <p>Returns annotations with representation <anno>Term</anno>.</p> @@ -174,7 +174,7 @@ </func> <func> - <name name="generated" arity="1"/> + <name name="generated" arity="1" since="OTP 18.0"/> <fsummary>Return the generated Boolean.</fsummary> <type name="generated"></type> <desc> @@ -185,7 +185,7 @@ </func> <func> - <name name="is_anno" arity="1"/> + <name name="is_anno" arity="1" since="OTP 18.0"/> <fsummary>Test for a collection of annotations.</fsummary> <desc> <p>Returns <c>true</c> if <anno>Term</anno> is a collection of @@ -194,7 +194,7 @@ </func> <func> - <name name="line" arity="1"/> + <name name="line" arity="1" since="OTP 18.0"/> <fsummary>Return the line.</fsummary> <type name="line"></type> <desc> @@ -203,7 +203,7 @@ </func> <func> - <name name="location" arity="1"/> + <name name="location" arity="1" since="OTP 18.0"/> <fsummary>Return the location.</fsummary> <type name="location"></type> <desc> @@ -212,7 +212,7 @@ </func> <func> - <name name="new" arity="1"/> + <name name="new" arity="1" since="OTP 18.0"/> <fsummary>Create a new collection of annotations.</fsummary> <type name="location"></type> <desc> @@ -221,7 +221,7 @@ </func> <func> - <name name="set_file" arity="2"/> + <name name="set_file" arity="2" since="OTP 18.0"/> <fsummary>Modify the filename.</fsummary> <type name="filename"></type> <desc> @@ -230,7 +230,7 @@ </func> <func> - <name name="set_generated" arity="2"/> + <name name="set_generated" arity="2" since="OTP 18.0"/> <fsummary>Modify the generated marker.</fsummary> <type name="generated"></type> <desc> @@ -240,7 +240,7 @@ </func> <func> - <name name="set_line" arity="2"/> + <name name="set_line" arity="2" since="OTP 18.0"/> <fsummary>Modify the line.</fsummary> <type name="line"></type> <desc> @@ -249,7 +249,7 @@ </func> <func> - <name name="set_location" arity="2"/> + <name name="set_location" arity="2" since="OTP 18.0"/> <fsummary>Modify the location.</fsummary> <type name="location"></type> <desc> @@ -258,7 +258,7 @@ </func> <func> - <name name="set_record" arity="2"/> + <name name="set_record" arity="2" since="OTP 18.0"/> <fsummary>Modify the record marker.</fsummary> <type name="record"></type> <desc> @@ -267,7 +267,7 @@ </func> <func> - <name name="set_text" arity="2"/> + <name name="set_text" arity="2" since="OTP 18.0"/> <fsummary>Modify the text.</fsummary> <type name="text"></type> <desc> @@ -276,7 +276,7 @@ </func> <func> - <name name="text" arity="1"/> + <name name="text" arity="1" since="OTP 18.0"/> <fsummary>Return the text.</fsummary> <type name="text"></type> <desc> @@ -286,7 +286,7 @@ </func> <func> - <name name="to_term" arity="1"/> + <name name="to_term" arity="1" since="OTP 18.0"/> <fsummary>Return the term representing a collection of annotations. </fsummary> <desc> diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml index 1c0f7f062f..813cbecd89 100644 --- a/lib/stdlib/doc/src/erl_eval.xml +++ b/lib/stdlib/doc/src/erl_eval.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>erl_eval.xml</file> </header> - <module>erl_eval</module> + <module since="">erl_eval</module> <modulesummary>The Erlang meta interpreter.</modulesummary> <description> <p>This module provides an interpreter for Erlang expressions. The @@ -96,7 +96,7 @@ <funcs> <func> - <name name="add_binding" arity="3"/> + <name name="add_binding" arity="3" since=""/> <fsummary>Add a binding.</fsummary> <desc> <p>Adds binding <c><anno>Name</anno>=<anno>Value</anno></c> @@ -106,7 +106,7 @@ </func> <func> - <name name="binding" arity="2"/> + <name name="binding" arity="2" since=""/> <fsummary>Return bindings.</fsummary> <desc> <p>Returns the binding of <c><anno>Name</anno></c> @@ -115,7 +115,7 @@ </func> <func> - <name name="bindings" arity="1"/> + <name name="bindings" arity="1" since=""/> <fsummary>Return bindings.</fsummary> <desc> <p>Returns the list of bindings contained in the binding @@ -124,7 +124,7 @@ </func> <func> - <name name="del_binding" arity="2"/> + <name name="del_binding" arity="2" since=""/> <fsummary>Delete a binding.</fsummary> <desc> <p>Removes the binding of <c><anno>Name</anno></c> @@ -134,10 +134,10 @@ </func> <func> - <name name="expr" arity="2"/> - <name name="expr" arity="3"/> - <name name="expr" arity="4"/> - <name name="expr" arity="5"/> + <name name="expr" arity="2" since=""/> + <name name="expr" arity="3" since=""/> + <name name="expr" arity="4" since=""/> + <name name="expr" arity="5" since=""/> <fsummary>Evaluate expression.</fsummary> <desc> <p>Evaluates <c><anno>Expression</anno></c> with the set of bindings @@ -157,9 +157,9 @@ </func> <func> - <name name="expr_list" arity="2"/> - <name name="expr_list" arity="3"/> - <name name="expr_list" arity="4"/> + <name name="expr_list" arity="2" since=""/> + <name name="expr_list" arity="3" since=""/> + <name name="expr_list" arity="4" since=""/> <fsummary>Evaluate a list of expressions.</fsummary> <desc> <p>Evaluates a list of expressions in parallel, using the same @@ -174,9 +174,9 @@ </func> <func> - <name name="exprs" arity="2"/> - <name name="exprs" arity="3"/> - <name name="exprs" arity="4"/> + <name name="exprs" arity="2" since=""/> + <name name="exprs" arity="3" since=""/> + <name name="exprs" arity="4" since=""/> <fsummary>Evaluate expressions.</fsummary> <desc> <p>Evaluates <c><anno>Expressions</anno></c> with the set of bindings @@ -197,7 +197,7 @@ </func> <func> - <name name="new_bindings" arity="0"/> + <name name="new_bindings" arity="0" since=""/> <fsummary>Return a bindings structure.</fsummary> <desc> <p>Returns an empty binding structure.</p> diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml index b6aa75ed03..20e5f1960b 100644 --- a/lib/stdlib/doc/src/erl_expand_records.xml +++ b/lib/stdlib/doc/src/erl_expand_records.xml @@ -34,7 +34,7 @@ <rev>PA1</rev> <file>erl_expand_records.xml</file> </header> - <module>erl_expand_records</module> + <module since="">erl_expand_records</module> <modulesummary>Expands records in a module.</modulesummary> <description> <p>This module expands records in a module.</p> @@ -42,7 +42,7 @@ <funcs> <func> - <name name="module" arity="2"/> + <name name="module" arity="2" since=""/> <fsummary>Expand all records in a module.</fsummary> <desc> <p>Expands all records in a module to use explicit tuple diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml index 16952a9582..ec66842ac0 100644 --- a/lib/stdlib/doc/src/erl_id_trans.xml +++ b/lib/stdlib/doc/src/erl_id_trans.xml @@ -34,7 +34,7 @@ <rev>B</rev> <file>erl_id_trans.xml</file> </header> - <module>erl_id_trans</module> + <module since="">erl_id_trans</module> <modulesummary>An identity parse transform.</modulesummary> <description> <p>This module performs an identity parse transformation of Erlang code. @@ -46,7 +46,7 @@ <funcs> <func> - <name>parse_transform(Forms, Options) -> Forms</name> + <name since="">parse_transform(Forms, Options) -> Forms</name> <fsummary>Transform Erlang forms.</fsummary> <type> <v>Forms = [<seealso marker="erl_parse#type-abstract_form">erl_parse:abstract_form()</seealso> diff --git a/lib/stdlib/doc/src/erl_internal.xml b/lib/stdlib/doc/src/erl_internal.xml index 17cd0fb240..77551ffed7 100644 --- a/lib/stdlib/doc/src/erl_internal.xml +++ b/lib/stdlib/doc/src/erl_internal.xml @@ -34,7 +34,7 @@ <rev>B</rev> <file>erl_internal.xml</file> </header> - <module>erl_internal</module> + <module since="">erl_internal</module> <modulesummary>Internal Erlang definitions.</modulesummary> <description> <p>This module defines Erlang BIFs, guard tests, and operators. @@ -44,7 +44,7 @@ <funcs> <func> - <name name="add_predefined_functions" arity="1"/> + <name name="add_predefined_functions" arity="1" since="OTP 20.0"/> <fsummary>Add code for pre-defined functions.</fsummary> <desc> <p>Adds to <c><anno>Forms</anno></c> the code for the standard @@ -54,7 +54,7 @@ </func> <func> - <name name="arith_op" arity="2"/> + <name name="arith_op" arity="2" since=""/> <fsummary>Test for an arithmetic operator.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> @@ -63,7 +63,7 @@ </func> <func> - <name name="bif" arity="2"/> + <name name="bif" arity="2" since=""/> <fsummary>Test for an Erlang BIF.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> @@ -73,7 +73,7 @@ </func> <func> - <name name="bool_op" arity="2"/> + <name name="bool_op" arity="2" since=""/> <fsummary>Test for a Boolean operator.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> @@ -82,7 +82,7 @@ </func> <func> - <name name="comp_op" arity="2"/> + <name name="comp_op" arity="2" since=""/> <fsummary>Test for a comparison operator.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> @@ -91,7 +91,7 @@ </func> <func> - <name name="guard_bif" arity="2"/> + <name name="guard_bif" arity="2" since=""/> <fsummary>Test for an Erlang BIF allowed in guards.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is @@ -100,7 +100,7 @@ </func> <func> - <name name="list_op" arity="2"/> + <name name="list_op" arity="2" since=""/> <fsummary>Test for a list operator.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> @@ -109,7 +109,7 @@ </func> <func> - <name name="op_type" arity="2"/> + <name name="op_type" arity="2" since=""/> <fsummary>Return operator type.</fsummary> <desc> <p>Returns the <c><anno>Type</anno></c> of operator that @@ -120,7 +120,7 @@ </func> <func> - <name name="send_op" arity="2"/> + <name name="send_op" arity="2" since=""/> <fsummary>Test for a send operator.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> @@ -129,7 +129,7 @@ </func> <func> - <name name="type_test" arity="2"/> + <name name="type_test" arity="2" since=""/> <fsummary>Test for a valid type test.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml index 77cb7a9916..12eaafc3a8 100644 --- a/lib/stdlib/doc/src/erl_lint.xml +++ b/lib/stdlib/doc/src/erl_lint.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>erl_lint.xml</file> </header> - <module>erl_lint</module> + <module since="">erl_lint</module> <modulesummary>The Erlang code linter.</modulesummary> <description> <p>This module is used to check Erlang code for illegal syntax and @@ -78,7 +78,7 @@ <funcs> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Format an error descriptor.</fsummary> <desc> <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns a string @@ -90,7 +90,7 @@ </func> <func> - <name name="is_guard_test" arity="1"/> + <name name="is_guard_test" arity="1" since=""/> <fsummary>Test for a guard test.</fsummary> <desc> <p>Tests if <c><anno>Expr</anno></c> is a legal guard test. @@ -102,9 +102,9 @@ </func> <func> - <name name="module" arity="1"/> - <name name="module" arity="2"/> - <name name="module" arity="3"/> + <name name="module" arity="1" since=""/> + <name name="module" arity="2" since=""/> + <name name="module" arity="3" since=""/> <fsummary>Check a module for errors.</fsummary> <desc> <p>Checks all the forms in a module for errors. It returns:</p> diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml index 647f36883c..8142e5c0aa 100644 --- a/lib/stdlib/doc/src/erl_parse.xml +++ b/lib/stdlib/doc/src/erl_parse.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>erl_parse.xml</file> </header> - <module>erl_parse</module> + <module since="">erl_parse</module> <modulesummary>The Erlang parser.</modulesummary> <description> <p>This module is the basic Erlang parser that converts tokens into @@ -89,7 +89,7 @@ <funcs> <func> - <name name="abstract" arity="1"/> + <name name="abstract" arity="1" since=""/> <fsummary>Convert an Erlang term into an abstract form.</fsummary> <desc> <p>Converts the Erlang data structure <c><anno>Data</anno></c> into an @@ -102,7 +102,7 @@ </func> <func> - <name name="abstract" arity="2"/> + <name name="abstract" arity="2" since="OTP R16B01"/> <fsummary>Convert an Erlang term into an abstract form.</fsummary> <type name="encoding_func"/> <desc> @@ -124,7 +124,7 @@ </func> <func> - <name name="anno_from_term" arity="1"/> + <name name="anno_from_term" arity="1" since="OTP 18.0"/> <fsummary>Return annotations as terms.</fsummary> <desc> <p>Assumes that <c><anno>Term</anno></c> is a term with the same @@ -140,7 +140,7 @@ </func> <func> - <name name="anno_to_term" arity="1"/> + <name name="anno_to_term" arity="1" since="OTP 18.0"/> <fsummary>Return the representation of annotations.</fsummary> <desc> <p>Returns a term where each collection of annotations @@ -154,7 +154,7 @@ </func> <func> - <name name="fold_anno" arity="3"/> + <name name="fold_anno" arity="3" since="OTP 18.0"/> <fsummary>Fold a function over the annotations of an <c>erl_parse</c> tree. </fsummary> <desc> @@ -171,7 +171,7 @@ </func> <func> - <name>format_error(ErrorDescriptor) -> Chars</name> + <name since="">format_error(ErrorDescriptor) -> Chars</name> <fsummary>Format an error descriptor.</fsummary> <type> <v>ErrorDescriptor = <seealso @@ -188,7 +188,7 @@ </func> <func> - <name name="map_anno" arity="2"/> + <name name="map_anno" arity="2" since="OTP 18.0"/> <fsummary>Map a function over the annotations of an <c>erl_parse</c> tree. </fsummary> <desc> @@ -201,7 +201,7 @@ </func> <func> - <name name="mapfold_anno" arity="3"/> + <name name="mapfold_anno" arity="3" since="OTP 18.0"/> <fsummary>Map and fold a function over the annotations of an <c>erl_parse</c> tree.</fsummary> <desc> @@ -220,7 +220,7 @@ </func> <func> - <name name="new_anno" arity="1"/> + <name name="new_anno" arity="1" since="OTP 18.0"/> <fsummary>Create new annotations.</fsummary> <desc> <p>Assumes that <c><anno>Term</anno></c> is a term with the same @@ -236,7 +236,7 @@ </func> <func> - <name name="normalise" arity="1"/> + <name name="normalise" arity="1" since=""/> <fsummary>Convert abstract form to an Erlang term.</fsummary> <desc> <p>Converts the abstract form <c><anno>AbsTerm</anno></c> of a @@ -247,7 +247,7 @@ </func> <func> - <name name="parse_exprs" arity="1"/> + <name name="parse_exprs" arity="1" since=""/> <fsummary>Parse Erlang expressions.</fsummary> <desc> <p>Parses <c><anno>Tokens</anno></c> as if it was a list of expressions. @@ -267,7 +267,7 @@ </func> <func> - <name name="parse_form" arity="1"/> + <name name="parse_form" arity="1" since=""/> <fsummary>Parse an Erlang form.</fsummary> <desc> <p>Parses <c><anno>Tokens</anno></c> as if it was a form. Returns one @@ -287,7 +287,7 @@ </func> <func> - <name name="parse_term" arity="1"/> + <name name="parse_term" arity="1" since=""/> <fsummary>Parse an Erlang term.</fsummary> <desc> <p>Parses <c><anno>Tokens</anno></c> as if it was a term. Returns @@ -307,8 +307,8 @@ </func> <func> - <name name="tokens" arity="1"/> - <name name="tokens" arity="2"/> + <name name="tokens" arity="1" since=""/> + <name name="tokens" arity="2" since=""/> <fsummary>Generate a list of tokens for an expression.</fsummary> <desc> <p>Generates a list of tokens representing the abstract diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index 77a7f1e8d1..f1c3aa5a41 100644 --- a/lib/stdlib/doc/src/erl_pp.xml +++ b/lib/stdlib/doc/src/erl_pp.xml @@ -34,7 +34,7 @@ <rev>B</rev> <file>erl_pp.xml</file> </header> - <module>erl_pp</module> + <module since="">erl_pp</module> <modulesummary>The Erlang pretty printer.</modulesummary> <description> <p>The functions in this module are used to generate @@ -73,8 +73,8 @@ <funcs> <func> - <name name="attribute" arity="1"/> - <name name="attribute" arity="2"/> + <name name="attribute" arity="1" since=""/> + <name name="attribute" arity="2" since=""/> <fsummary>Pretty print an attribute.</fsummary> <desc> <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>, @@ -83,10 +83,10 @@ </func> <func> - <name name="expr" arity="1"/> - <name name="expr" arity="2"/> - <name name="expr" arity="3"/> - <name name="expr" arity="4"/> + <name name="expr" arity="1" since=""/> + <name name="expr" arity="2" since=""/> + <name name="expr" arity="3" since=""/> + <name name="expr" arity="4" since=""/> <fsummary>Pretty print one <c>Expression</c>.</fsummary> <desc> <p>Prints one expression. It is useful for implementing hooks (see @@ -96,9 +96,9 @@ </func> <func> - <name name="exprs" arity="1"/> - <name name="exprs" arity="2"/> - <name name="exprs" arity="3"/> + <name name="exprs" arity="1" since=""/> + <name name="exprs" arity="2" since=""/> + <name name="exprs" arity="3" since=""/> <fsummary>Pretty print <c>Expressions</c>.</fsummary> <desc> <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>, @@ -108,8 +108,8 @@ </func> <func> - <name name="form" arity="1"/> - <name name="form" arity="2"/> + <name name="form" arity="1" since=""/> + <name name="form" arity="2" since=""/> <fsummary>Pretty print a form.</fsummary> <desc> <p>Pretty prints a @@ -120,8 +120,8 @@ </func> <func> - <name name="function" arity="1"/> - <name name="function" arity="2"/> + <name name="function" arity="1" since=""/> + <name name="function" arity="2" since=""/> <fsummary>Pretty print a function.</fsummary> <desc> <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>, @@ -130,8 +130,8 @@ </func> <func> - <name name="guard" arity="1"/> - <name name="guard" arity="2"/> + <name name="guard" arity="1" since=""/> + <name name="guard" arity="2" since=""/> <fsummary>Pretty print a guard.</fsummary> <desc> <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>, diff --git a/lib/stdlib/doc/src/erl_scan.xml b/lib/stdlib/doc/src/erl_scan.xml index 137ccd3416..38111f73bc 100644 --- a/lib/stdlib/doc/src/erl_scan.xml +++ b/lib/stdlib/doc/src/erl_scan.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>erl_scan.xml</file> </header> - <module>erl_scan</module> + <module since="">erl_scan</module> <modulesummary>The Erlang token scanner.</modulesummary> <description> <p>This module contains functions for tokenizing (scanning) characters into @@ -74,7 +74,7 @@ <funcs> <func> - <name name="category" arity="1"/> + <name name="category" arity="1" since="OTP 18.0"/> <fsummary>Return the category.</fsummary> <desc> <p>Returns the category of <c><anno>Token</anno></c>.</p> @@ -82,7 +82,7 @@ </func> <func> - <name name="column" arity="1"/> + <name name="column" arity="1" since="OTP 18.0"/> <fsummary>Return the column.</fsummary> <desc> <p>Returns the column of <c><anno>Token</anno></c>'s @@ -91,7 +91,7 @@ </func> <func> - <name name="end_location" arity="1"/> + <name name="end_location" arity="1" since="OTP 18.0"/> <fsummary>Return the end location of the text.</fsummary> <desc> <p>Returns the end location of the text of @@ -101,7 +101,7 @@ </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Format an error descriptor.</fsummary> <desc> <p>Uses an <c><anno>ErrorDescriptor</anno></c> and returns a string @@ -113,7 +113,7 @@ </func> <func> - <name name="line" arity="1"/> + <name name="line" arity="1" since="OTP 18.0"/> <fsummary>Return the line.</fsummary> <desc> <p>Returns the line of <c><anno>Token</anno></c>'s collection @@ -122,7 +122,7 @@ </func> <func> - <name name="location" arity="1"/> + <name name="location" arity="1" since="OTP 18.0"/> <fsummary>Return the location.</fsummary> <desc> <p>Returns the location of <c><anno>Token</anno></c>'s @@ -131,7 +131,7 @@ </func> <func> - <name name="reserved_word" arity="1"/> + <name name="reserved_word" arity="1" since=""/> <fsummary>Test for a reserved word.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Atom</anno></c> is an @@ -140,9 +140,9 @@ </func> <func> - <name name="string" arity="1"/> - <name name="string" arity="2"/> - <name name="string" arity="3"/> + <name name="string" arity="1" since=""/> + <name name="string" arity="2" since=""/> + <name name="string" arity="3" since=""/> <fsummary>Scan a string and return the Erlang tokens.</fsummary> <desc> <p>Takes the list of characters <c><anno>String</anno></c> and tries to @@ -229,7 +229,7 @@ </func> <func> - <name name="symbol" arity="1"/> + <name name="symbol" arity="1" since="OTP 18.0"/> <fsummary>Return the symbol.</fsummary> <desc> <p>Returns the symbol of <c><anno>Token</anno></c>.</p> @@ -237,7 +237,7 @@ </func> <func> - <name name="text" arity="1"/> + <name name="text" arity="1" since="OTP 18.0"/> <fsummary>Return the text.</fsummary> <desc> <p>Returns the text of <c><anno>Token</anno></c>'s collection @@ -247,8 +247,8 @@ </func> <func> - <name name="tokens" arity="3"/> - <name name="tokens" arity="4"/> + <name name="tokens" arity="3" since=""/> + <name name="tokens" arity="4" since=""/> <fsummary>Re-entrant scanner.</fsummary> <type name="char_spec"/> <type name="return_cont"/> diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml index 68fa071090..ea8173748a 100644 --- a/lib/stdlib/doc/src/erl_tar.xml +++ b/lib/stdlib/doc/src/erl_tar.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>erl_tar.xml</file> </header> - <module>erl_tar</module> + <module since="">erl_tar</module> <modulesummary>Unix 'tar' utility for reading and writing tar archives. </modulesummary> <description> @@ -127,7 +127,7 @@ <funcs> <func> - <name>add(TarDescriptor, Filename, Options) -> RetValue</name> + <name since="">add(TarDescriptor, Filename, Options) -> RetValue</name> <fsummary>Add a file to an open tar file.</fsummary> <type> <v>TarDescriptor = term()</v> @@ -211,7 +211,7 @@ </func> <func> - <name>add(TarDescriptor, FilenameOrBin, NameInArchive, Options) -> + <name since="">add(TarDescriptor, FilenameOrBin, NameInArchive, Options) -> RetValue </name> <fsummary>Add a file to an open tar file.</fsummary> <type> @@ -233,7 +233,7 @@ </func> <func> - <name>close(TarDescriptor)</name> + <name since="">close(TarDescriptor)</name> <fsummary>Close an open tar file.</fsummary> <type> <v>TarDescriptor = term()</v> @@ -245,7 +245,7 @@ </func> <func> - <name>create(Name, FileList) ->RetValue </name> + <name since="">create(Name, FileList) ->RetValue </name> <fsummary>Create a tar archive.</fsummary> <type> <v>Name = filename()</v> @@ -264,7 +264,7 @@ </func> <func> - <name>create(Name, FileList, OptionList)</name> + <name since="">create(Name, FileList, OptionList)</name> <fsummary>Create a tar archive with options.</fsummary> <type> <v>Name = filename()</v> @@ -315,7 +315,7 @@ </func> <func> - <name>extract(Name) -> RetValue</name> + <name since="">extract(Name) -> RetValue</name> <fsummary>Extract all files from a tar file.</fsummary> <type> <v>Name = filename() | {binary,binary()} | {file,Fd}</v> @@ -339,7 +339,7 @@ </func> <func> - <name>extract(Name, OptionList)</name> + <name since="">extract(Name, OptionList)</name> <fsummary>Extract files from a tar file.</fsummary> <type> <v>Name = filename() | {binary,binary()} | {file,Fd}</v> @@ -411,7 +411,7 @@ </func> <func> - <name>format_error(Reason) -> string()</name> + <name since="">format_error(Reason) -> string()</name> <fsummary>Convert error term to a readable string.</fsummary> <type> <v>Reason = term()</v> @@ -423,7 +423,7 @@ </func> <func> - <name>init(UserPrivate, AccessMode, Fun) -> + <name since="OTP 17.4">init(UserPrivate, AccessMode, Fun) -> {ok,TarDescriptor} | {error,Reason}</name> <fsummary>Create a <c>TarDescriptor</c> used in subsequent tar operations when defining own low-level storage access functions.</fsummary> @@ -518,7 +518,7 @@ erl_tar:close(TarDesc)</code> </func> <func> - <name>open(Name, OpenModeList) -> RetValue</name> + <name since="">open(Name, OpenModeList) -> RetValue</name> <fsummary>Open a tar file for writing.</fsummary> <type> <v>Name = filename()</v> @@ -565,7 +565,7 @@ erl_tar:close(TarDesc)</code> </func> <func> - <name>table(Name) -> RetValue</name> + <name since="">table(Name) -> RetValue</name> <fsummary>Retrieve the name of all files in a tar file.</fsummary> <type> <v>Name = filename()|{binary,binary()}|{file,file_descriptor()}</v> @@ -578,7 +578,7 @@ erl_tar:close(TarDesc)</code> </func> <func> - <name>table(Name, Options)</name> + <name since="">table(Name, Options)</name> <fsummary>Retrieve name and information of all files in a tar file. </fsummary> <type> @@ -590,7 +590,7 @@ erl_tar:close(TarDesc)</code> </func> <func> - <name>t(Name)</name> + <name since="">t(Name)</name> <fsummary>Print the name of each file in a tar file.</fsummary> <type> <v>Name = filename()|{binary,binary()}|{file,file_descriptor()}</v> @@ -602,7 +602,7 @@ erl_tar:close(TarDesc)</code> </func> <func> - <name>tt(Name)</name> + <name since="">tt(Name)</name> <fsummary>Print name and information for each file in a tar file. </fsummary> <type> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index ad006f9a2b..b9ceaa1d69 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>ets</module> + <module since="">ets</module> <modulesummary>Built-in term storage.</modulesummary> <description> <p>This module is an interface to the Erlang built-in term storage @@ -138,23 +138,71 @@ operation. In database terms the isolation level can be seen as "serializable", as if all isolated operations are carried out serially, one after the other in a strict order.</p> + </section> - <p>No other support is available within this module that would guarantee - consistency between objects. However, function - <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso> - can be used to guarantee that a sequence of - <seealso marker="#first/1"><c>first/1</c></seealso> and - <seealso marker="#next/2"><c>next/2</c></seealso> calls traverse the - table without errors and that each existing object in the table is - visited exactly once, even if another (or the same) process - simultaneously deletes or inserts objects into the table. - Nothing else is guaranteed; in particular objects that are inserted - or deleted during such a traversal can be visited once or not at all. - Functions that internally traverse over a table, like - <seealso marker="#select/1"><c>select</c></seealso> and - <seealso marker="#match/1"><c>match</c></seealso>, - give the same guarantee as - <seealso marker="#safe_fixtable/2"><c>safe_fixtable</c></seealso>.</p> + <section><marker id="traversal"></marker> + <title>Table traversal</title> + <p>There are different ways to traverse through the objects of a table.</p> + <list type="bulleted"> + <item><p><em>Single-step</em> traversal one key at at time, using + <seealso marker="#first/1"><c>first/1</c></seealso>, + <seealso marker="#next/2"><c>next/2</c></seealso>, + <seealso marker="#last/1"><c>last/1</c></seealso> and + <seealso marker="#prev/2"><c>prev/2</c></seealso>.</p> + </item> + <item><p>Search with simple <em>match patterns</em>, using + <seealso marker="#match/1"><c>match/1/2/3</c></seealso>, + <seealso marker="#match_delete/2"><c>match_delete/2</c></seealso> and + <seealso marker="#match_object/1"><c>match_object/1/2/3</c></seealso>.</p> + </item> + <item><p>Search with more powerful <em>match specifications</em>, using + <seealso marker="#select/1"><c>select/1/2/3</c></seealso>, + <seealso marker="#select_count/2"><c>select_count/2</c></seealso>, + <seealso marker="#select_delete/2"><c>select_delete/2</c></seealso>, + <seealso marker="#select_replace/2"><c>select_replace/2</c></seealso> and + <seealso marker="#select_reverse/1"><c>select_reverse/1/2/3</c></seealso>.</p> + </item> + <item><p><em>Table conversions</em>, using + <seealso marker="#tab2file/2"><c>tab2file/2/3</c></seealso> and + <seealso marker="#tab2list/1"><c>tab2list/1</c></seealso>.</p> + </item> + </list> + <p>None of these ways of table traversal will guarantee a consistent table snapshot + if the table is also updated during the traversal. Moreover, traversals not + done in a <em>safe</em> way, on tables where keys are inserted or deleted + during the traversal, may yield the following undesired effects:</p> + <list type="bulleted"> + <item><p>Any key may be missed.</p></item> + <item><p>Any key may be found more than once.</p></item> + <item><p>The traversal may fail with <c>badarg</c> exception if keys are deleted.</p> + </item> + </list> + <p>A table traversal is <em>safe</em> if either</p> + <list type="bulleted"> + <item><p>the table is of type <c>ordered_set</c>.</p> + </item> + <item><p>the entire table traversal is done within one ETS function + call.</p> + </item> + <item><p>function <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso> + is used to keep the table fixated during the entire traversal.</p> + </item> + </list> + <p>Traversals using <c>match</c> and <c>select</c> functions may not need to + scan the entire table depending on how the key is specified. A match + pattern with a <em>fully bound key</em> (without any match variables) will + optimize the operation to a single key lookup without any table traversal + at all. For <c>ordered_set</c> a <em>partially bound key</em> will limit the + traversal to only scan a subset of the table based on term order. A + partially bound key is either a list or a tuple with a prefix that is fully + bound. Example:</p> +<pre> +1> <input>T = ets:new(t,[ordered_set]), ets:insert(T, {"555-1234", "John Smith"}).</input> +true +2> <input>%% Efficient search of all with area code 555</input> +2> <input>ets:match(T,{[$5,$5,$5,$- |'$1'],'$2'}).</input> +[["1234","John Smith"]] +</pre> </section> <section> @@ -207,7 +255,7 @@ <funcs> <func> - <name name="all" arity="0"/> + <name name="all" arity="0" since=""/> <fsummary>Return a list of all ETS tables.</fsummary> <desc> <p>Returns a list of all tables at the node. Named tables are @@ -222,7 +270,7 @@ </func> <func> - <name name="delete" arity="1"/> + <name name="delete" arity="1" since=""/> <fsummary>Delete an entire ETS table.</fsummary> <desc> <p>Deletes the entire table <c><anno>Tab</anno></c>.</p> @@ -230,7 +278,7 @@ </func> <func> - <name name="delete" arity="2"/> + <name name="delete" arity="2" since=""/> <fsummary>Delete all objects with a specified key from an ETS table.</fsummary> <desc> @@ -240,7 +288,7 @@ </func> <func> - <name name="delete_all_objects" arity="1"/> + <name name="delete_all_objects" arity="1" since=""/> <fsummary>Delete all objects in an ETS table.</fsummary> <desc> <p>Delete all objects in the ETS table <c><anno>Tab</anno></c>. @@ -250,7 +298,7 @@ </func> <func> - <name name="delete_object" arity="2"/> + <name name="delete_object" arity="2" since=""/> <fsummary>Deletes a specific from an ETS table.</fsummary> <desc> <p>Delete the exact object <c><anno>Object</anno></c> from the @@ -262,7 +310,7 @@ </func> <func> - <name name="file2tab" arity="1"/> + <name name="file2tab" arity="1" since=""/> <fsummary>Read an ETS table from a file.</fsummary> <desc> <p>Reads a file produced by <seealso marker="#tab2file/2"> @@ -274,7 +322,7 @@ </func> <func> - <name name="file2tab" arity="2"/> + <name name="file2tab" arity="2" since=""/> <fsummary>Read an ETS table from a file.</fsummary> <desc> <p>Reads a file produced by <seealso marker="#tab2file/2"> @@ -306,7 +354,7 @@ </func> <func> - <name name="first" arity="1"/> + <name name="first" arity="1" since=""/> <fsummary>Return the first key in an ETS table.</fsummary> <desc> <p>Returns the first key <c><anno>Key</anno></c> in table @@ -321,7 +369,7 @@ </func> <func> - <name name="foldl" arity="3"/> + <name name="foldl" arity="3" since=""/> <fsummary>Fold a function over an ETS table.</fsummary> <desc> <p><c><anno>Acc0</anno></c> is returned if the table is empty. @@ -337,7 +385,7 @@ </func> <func> - <name name="foldr" arity="3"/> + <name name="foldr" arity="3" since=""/> <fsummary>Fold a function over an ETS table.</fsummary> <desc> <p><c><anno>Acc0</anno></c> is returned if the table is empty. @@ -353,7 +401,7 @@ </func> <func> - <name name="from_dets" arity="2"/> + <name name="from_dets" arity="2" since=""/> <fsummary>Fill an ETS table with objects from a Dets table.</fsummary> <desc> @@ -367,7 +415,7 @@ </func> <func> - <name name="fun2ms" arity="1"/> + <name name="fun2ms" arity="1" since=""/> <fsummary>Pseudo function that transforms fun syntax to a match specification.</fsummary> <desc> @@ -436,7 +484,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="give_away" arity="3"/> + <name name="give_away" arity="3" since=""/> <fsummary>Change owner of a table.</fsummary> <desc> <p>Make process <c><anno>Pid</anno></c> the new owner of table @@ -454,7 +502,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="i" arity="0"/> + <name name="i" arity="0" since=""/> <fsummary>Display information about all ETS tables on a terminal. </fsummary> <desc> @@ -463,7 +511,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="i" arity="1"/> + <name name="i" arity="1" since=""/> <fsummary>Browse an ETS table on a terminal.</fsummary> <desc> <p>Browses table <c><anno>Tab</anno></c> on a terminal.</p> @@ -471,7 +519,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since=""/> <fsummary>Return information about an <c>table</c>.</fsummary> <desc> <p>Returns information about table <c><anno>Tab</anno></c> as a list of @@ -547,7 +595,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="info" arity="2"/> + <name name="info" arity="2" since=""/> <fsummary>Return the information associated with the specified item for an ETS table.</fsummary> <desc> @@ -619,7 +667,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="init_table" arity="2"/> + <name name="init_table" arity="2" since=""/> <fsummary>Replace all objects of an ETS table.</fsummary> <desc> <p>Replaces the existing objects of table <c><anno>Tab</anno></c> with @@ -649,7 +697,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="insert" arity="2"/> + <name name="insert" arity="2" since=""/> <fsummary>Insert an object into an ETS table.</fsummary> <desc> <p>Inserts the object or all of the objects in list @@ -681,7 +729,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="insert_new" arity="2"/> + <name name="insert_new" arity="2" since=""/> <fsummary>Insert an object into an ETS table if the key is not already present.</fsummary> <desc> @@ -700,7 +748,7 @@ Error: fun containing local Erlang function calls </func> <func> - <name name="is_compiled_ms" arity="1"/> + <name name="is_compiled_ms" arity="1" since=""/> <fsummary>Check if an Erlang term is the result of <c>match_spec_compile</c>.</fsummary> <desc> @@ -732,7 +780,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="last" arity="1"/> + <name name="last" arity="1" since=""/> <fsummary>Return the last key in an ETS table of type <c>ordered_set</c>.</fsummary> <desc> @@ -747,7 +795,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="lookup" arity="2"/> + <name name="lookup" arity="2" since=""/> <fsummary>Return all objects with a specified key in an ETS table. </fsummary> <desc> @@ -787,7 +835,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="lookup_element" arity="3"/> + <name name="lookup_element" arity="3" since=""/> <fsummary>Return the <c>Pos</c>:th element of all objects with a specified key in an ETS table.</fsummary> <desc> @@ -810,7 +858,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match" arity="1"/> + <name name="match" arity="1" since=""/> <fsummary>Continues matching objects in an ETS table.</fsummary> <desc> <p>Continues a match started with @@ -824,7 +872,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match" arity="2"/> + <name name="match" arity="2" since=""/> <fsummary>Match the objects in an ETS table against a pattern. </fsummary> <desc> @@ -856,7 +904,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match" arity="3"/> + <name name="match" arity="3" since=""/> <fsummary>Match the objects in an ETS table against a pattern and return part of the answers.</fsummary> <desc> @@ -871,11 +919,14 @@ ets:is_compiled_ms(Broken).</code> <seealso marker="#first/1"><c>first/1</c></seealso> and <seealso marker="#next/2"><c>next/2</c></seealso>.</p> <p>If the table is empty, <c>'$end_of_table'</c> is returned.</p> + <p>Use <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso> + to guarantee <seealso marker="#traversal">safe traversal</seealso> + for subsequent calls to <seealso marker="#match/1"><c>match/1</c></seealso>.</p> </desc> </func> <func> - <name name="match_delete" arity="2"/> + <name name="match_delete" arity="2" since=""/> <fsummary>Delete all objects that match a specified pattern from an ETS table.</fsummary> <desc> @@ -886,7 +937,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match_object" arity="1"/> + <name name="match_object" arity="1" since=""/> <fsummary>Continues matching objects in an ETS table.</fsummary> <desc> <p>Continues a match started with @@ -901,7 +952,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match_object" arity="2"/> + <name name="match_object" arity="2" since=""/> <fsummary>Match the objects in an ETS table against a pattern. </fsummary> <desc> @@ -920,7 +971,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match_object" arity="3"/> + <name name="match_object" arity="3" since=""/> <fsummary>Match the objects in an ETS table against a pattern and return part of the answers.</fsummary> <desc> @@ -936,11 +987,15 @@ ets:is_compiled_ms(Broken).</code> <seealso marker="#first/1"><c>first/1</c></seealso> and <seealso marker="#next/2"><c>next/2</c></seealso>.</p> <p>If the table is empty, <c>'$end_of_table'</c> is returned.</p> + <p>Use <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso> + to guarantee <seealso marker="#traversal">safe traversal</seealso> + for subsequent calls to <seealso marker="#match_object/1"> + <c>match_object/1</c></seealso>.</p> </desc> </func> <func> - <name name="match_spec_compile" arity="1"/> + <name name="match_spec_compile" arity="1" since=""/> <fsummary>Compile a match specification into its internal representation. </fsummary> <desc> @@ -968,7 +1023,7 @@ ets:is_compiled_ms(Broken).</code> </func> <func> - <name name="match_spec_run" arity="2"/> + <name name="match_spec_run" arity="2" since=""/> <fsummary>Perform matching, using a compiled match specification on a list of terms.</fsummary> <desc> @@ -1005,7 +1060,7 @@ ets:select(Table, MatchSpec),</code> </func> <func> - <name name="member" arity="2"/> + <name name="member" arity="2" since=""/> <fsummary>Tests for occurrence of a key in an ETS table.</fsummary> <desc> <p>Works like <seealso marker="#lookup/2"><c>lookup/2</c></seealso>, @@ -1016,7 +1071,7 @@ ets:select(Table, MatchSpec),</code> </func> <func> - <name name="new" arity="2"/> + <name name="new" arity="2" since=""/> <fsummary>Create a new ETS table.</fsummary> <desc> <p>Creates a new table and returns a table identifier that can @@ -1180,7 +1235,7 @@ ets:select(Table, MatchSpec),</code> </func> <func> - <name name="next" arity="2"/> + <name name="next" arity="2" since=""/> <fsummary>Return the next key in an ETS table.</fsummary> <desc> <p>Returns the next key <c><anno>Key2</anno></c>, following key @@ -1192,17 +1247,18 @@ ets:select(Table, MatchSpec),</code> <p>To find the first key in the table, use <seealso marker="#first/1"><c>first/1</c></seealso>.</p> <p>Unless a table of type <c>set</c>, <c>bag</c>, or - <c>duplicate_bag</c> is protected using + <c>duplicate_bag</c> is fixated using <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>, - a traversal can fail if - concurrent updates are made to the table. For table - type <c>ordered_set</c>, the function returns the next key in - order, even if the object does no longer exist.</p> + a call to <c>next/2</c> will fail if <c><anno>Key1</anno></c> no longer + exists in the table. For table type <c>ordered_set</c>, the function + always returns the next key after <c><anno>Key1</anno></c> in term + order, regardless whether <c><anno>Key1</anno></c> ever existed in the + table.</p> </desc> </func> <func> - <name name="prev" arity="2"/> + <name name="prev" arity="2" since=""/> <fsummary>Return the previous key in an ETS table of type <c>ordered_set</c>.</fsummary> <desc> @@ -1212,13 +1268,13 @@ ets:select(Table, MatchSpec),</code> table types, the function is synonymous to <seealso marker="#next/2"><c>next/2</c></seealso>. If no previous key exists, <c>'$end_of_table'</c> is returned.</p> - <p>To find the last key in the table, use + <p>To find the last key in an <c>ordered_set</c> table, use <seealso marker="#last/1"><c>last/1</c></seealso>.</p> </desc> </func> <func> - <name name="rename" arity="2"/> + <name name="rename" arity="2" since=""/> <fsummary>Rename a named ETS table.</fsummary> <desc> <p>Renames the named table <c><anno>Tab</anno></c> to the new name @@ -1228,7 +1284,7 @@ ets:select(Table, MatchSpec),</code> </func> <func> - <name name="repair_continuation" arity="2"/> + <name name="repair_continuation" arity="2" since=""/> <fsummary>Repair a continuation from <c>ets:select/1 or ets:select/3</c> that has passed through external representation.</fsummary> <desc> @@ -1283,11 +1339,20 @@ ets:select(ets:repair_continuation(Broken,MS)).</code> </func> <func> - <name name="safe_fixtable" arity="2"/> + <name name="safe_fixtable" arity="2" since=""/> <fsummary>Fix an ETS table for safe traversal.</fsummary> <desc> <p>Fixes a table of type <c>set</c>, <c>bag</c>, or - <c>duplicate_bag</c> for safe traversal.</p> + <c>duplicate_bag</c> for <seealso marker="#traversal"> + safe traversal</seealso> using + <seealso marker="#first/1"><c>first/1</c></seealso> & + <seealso marker="#next/2"><c>next/2</c></seealso>, + <seealso marker="#match/3"><c>match/3</c></seealso> & + <seealso marker="#match/1"><c>match/1</c></seealso>, + <seealso marker="#match_object/3"><c>match_object/3</c></seealso> & + <seealso marker="#match_object/1"><c>match_object/1</c></seealso>, or + <seealso marker="#select/3"><c>select/3</c></seealso> & + <seealso marker="#select/1"><c>select/1</c></seealso>.</p> <p>A process fixes a table by calling <c>safe_fixtable(<anno>Tab</anno>, true)</c>. The table remains fixed until the process releases it by calling @@ -1300,11 +1365,11 @@ ets:select(ets:repair_continuation(Broken,MS)).</code> <p>When a table is fixed, a sequence of <seealso marker="#first/1"><c>first/1</c></seealso> and <seealso marker="#next/2"><c>next/2</c></seealso> calls are - guaranteed to succeed, and each object in - the table is returned only once, even if objects - are removed or inserted during the traversal. The keys for new - objects inserted during the traversal <em>can</em> be returned by - <c>next/2</c> (it depends on the internal ordering of the keys).</p> + guaranteed to succeed even if keys are removed during the + traversal. The keys for objects inserted or deleted during a + traversal may or may not be returned by <c>next/2</c> depending on + the ordering of keys within the table and if the key exists at the time + <c>next/2</c> is called.</p> <p><em>Example:</em></p> <code type="none"> clean_all_with_value(Tab,X) -> @@ -1322,7 +1387,7 @@ clean_all_with_value(Tab,X,Key) -> true end, clean_all_with_value(Tab,X,ets:next(Tab,Key)).</code> - <p>Notice that no deleted objects are removed from a + <p>Notice that deleted objects are not freed from a fixed table until it has been released. If a process fixes a table but never releases it, the memory used by the deleted objects is never freed. The performance of operations on @@ -1332,14 +1397,14 @@ clean_all_with_value(Tab,X,Key) -> <c>info(Tab, safe_fixed_monotonic_time)</c></seealso>. A system with many processes fixing tables can need a monitor that sends alarms when tables have been fixed for too long.</p> - <p>Notice that for table type <c>ordered_set</c>, - <c>safe_fixtable/2</c> is not necessary, as calls to - <c>first/1</c> and <c>next/2</c> always succeed.</p> + <p>Notice that <c>safe_fixtable/2</c> is not necessary for table type + <c>ordered_set</c> and for traversals done by a single ETS function call, + like <seealso marker="#select/2"><c>select/2</c></seealso>.</p> </desc> </func> <func> - <name name="select" arity="1"/> + <name name="select" arity="1" since=""/> <fsummary>Continue matching objects in an ETS table.</fsummary> <desc> <p>Continues a match started with @@ -1353,7 +1418,7 @@ clean_all_with_value(Tab,X,Key) -> </func> <func> - <name name="select" arity="2"/> + <name name="select" arity="2" since=""/> <fsummary>Match the objects in an ETS table against a match specification.</fsummary> <desc> @@ -1448,7 +1513,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </func> <func> - <name name="select" arity="3"/> + <name name="select" arity="3" since=""/> <fsummary>Match the objects in an ETS table against a match specification and return part of the answers.</fsummary> <desc> @@ -1462,12 +1527,15 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> table, which is still faster than traversing the table object by object using <seealso marker="#first/1"><c>first/1</c></seealso> and <seealso marker="#next/2"><c>next/2</c></seealso>.</p> - <p>If the table is empty, <c>'$end_of_table'</c> is returned.</p> + <p>If the table is empty, <c>'$end_of_table'</c> is returned.</p> + <p>Use <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso> + to guarantee <seealso marker="#traversal">safe traversal</seealso> + for subsequent calls to <seealso marker="#select/1"><c>select/1</c></seealso>.</p> </desc> </func> <func> - <name name="select_count" arity="2"/> + <name name="select_count" arity="2" since=""/> <fsummary>Match the objects in an ETS table against a match specification and return the number of objects for which the match specification returned <c>true</c>.</fsummary> @@ -1486,7 +1554,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </func> <func> - <name name="select_delete" arity="2"/> + <name name="select_delete" arity="2" since=""/> <fsummary>Match the objects in an ETS table against a match specification and delete objects where the match specification returns <c>true</c>.</fsummary> @@ -1510,7 +1578,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </func> <func> - <name name="select_replace" arity="2"/> + <name name="select_replace" arity="2" since="OTP 20.0"/> <fsummary>Match and replace objects atomically in an ETS table</fsummary> <desc> <p>Matches the objects in the table <c><anno>Tab</anno></c> using a @@ -1519,7 +1587,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> the match specification result.</p> <p>The match-and-replace operation for each individual object is guaranteed to be <seealso marker="#concurrency">atomic and isolated</seealso>. The - <c>select_replace</c> table iteration as a whole, like all other select functions, + <c>select_replace</c> table traversal as a whole, like all other select functions, does not give such guarantees.</p> <p>The match specifiction must be guaranteed to <em>retain the key</em> of any matched object. If not, <c>select_replace</c> will fail with <c>badarg</c> @@ -1549,7 +1617,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="select_reverse" arity="1"/> + <name name="select_reverse" arity="1" since="OTP R14B"/> <fsummary>Continue matching objects in an ETS table.</fsummary> <desc> <p>Continues a match started with <seealso marker="#select_reverse/3"> @@ -1582,7 +1650,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="select_reverse" arity="2"/> + <name name="select_reverse" arity="2" since="OTP R14B"/> <fsummary>Match the objects in an ETS table against a match specification.</fsummary> <desc> @@ -1594,7 +1662,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="select_reverse" arity="3"/> + <name name="select_reverse" arity="3" since="OTP R14B"/> <fsummary>Match the objects in an ETS table against a match specification and return part of the answers.</fsummary> <desc> @@ -1612,7 +1680,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="setopts" arity="2"/> + <name name="setopts" arity="2" since=""/> <fsummary>Set table options.</fsummary> <desc> <p>Sets table options. The only allowed option to be set after the @@ -1623,7 +1691,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="slot" arity="2"/> + <name name="slot" arity="2" since=""/> <fsummary>Return all objects in a specified slot of an ETS table. </fsummary> <desc> @@ -1648,7 +1716,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="tab2file" arity="2"/> + <name name="tab2file" arity="2" since=""/> <fsummary>Dump an ETS table to a file.</fsummary> <desc> <p>Dumps table <c><anno>Tab</anno></c> to file @@ -1659,7 +1727,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="tab2file" arity="3"/> + <name name="tab2file" arity="3" since=""/> <fsummary>Dump an ETS table to a file.</fsummary> <desc> <p>Dumps table <c><anno>Tab</anno></c> to file @@ -1706,7 +1774,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="tab2list" arity="1"/> + <name name="tab2list" arity="1" since=""/> <fsummary>Return a list of all objects in an ETS table.</fsummary> <desc> <p>Returns a list of all objects in table <c><anno>Tab</anno></c>.</p> @@ -1714,7 +1782,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="tabfile_info" arity="1"/> + <name name="tabfile_info" arity="1" since=""/> <fsummary>Return a list of all objects in an ETS table.</fsummary> <desc> <p>Returns information about the table dumped to file by @@ -1792,8 +1860,8 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), </func> <func> - <name name="table" arity="1"/> - <name name="table" arity="2"/> + <name name="table" arity="1" since=""/> + <name name="table" arity="2" since=""/> <fsummary>Return a QLC query handle.</fsummary> <desc> <p>Returns a Query List @@ -1869,7 +1937,7 @@ true</pre> </func> <func> - <name name="take" arity="2"/> + <name name="take" arity="2" since="OTP 18.0"/> <fsummary>Return and remove all objects with a specified key from an ETS table.</fsummary> <desc> @@ -1884,7 +1952,7 @@ true</pre> </desc> </func> <func> - <name name="test_ms" arity="2"/> + <name name="test_ms" arity="2" since=""/> <fsummary>Test a match specification for use in <c>select/2</c>. </fsummary> <desc> @@ -1911,7 +1979,7 @@ true</pre> </func> <func> - <name name="to_dets" arity="2"/> + <name name="to_dets" arity="2" since=""/> <fsummary>Fill a Dets table with objects from an ETS table. </fsummary> <desc> @@ -1922,12 +1990,12 @@ true</pre> </func> <func> - <name name="update_counter" arity="3" clause_i="1"/> - <name name="update_counter" arity="4" clause_i="1"/> - <name name="update_counter" arity="3" clause_i="2"/> - <name name="update_counter" arity="4" clause_i="2"/> - <name name="update_counter" arity="3" clause_i="3"/> - <name name="update_counter" arity="4" clause_i="3"/> + <name name="update_counter" arity="3" clause_i="1" since=""/> + <name name="update_counter" arity="4" clause_i="1" since="OTP 18.0"/> + <name name="update_counter" arity="3" clause_i="2" since=""/> + <name name="update_counter" arity="4" clause_i="2" since="OTP 18.0"/> + <name name="update_counter" arity="3" clause_i="3" since=""/> + <name name="update_counter" arity="4" clause_i="3" since="OTP 18.0"/> <fsummary>Update a counter object in an ETS table.</fsummary> <type variable="Tab"/> <type variable="Key"/> @@ -1940,9 +2008,8 @@ true</pre> <p>This function provides an efficient way to update one or more counters, without the trouble of having to look up an object, update the object by incrementing an element, and insert the resulting - object into the table again. (The update is done atomically, - that is, no process - can access the ETS table in the middle of the operation.)</p> + object into the table again. The operation is guaranteed to be + <seealso marker="#concurrency">atomic and isolated</seealso>.</p> <p>This function destructively update the object with key <c><anno>Key</anno></c> in table <c><anno>Tab</anno></c> by adding <c><anno>Incr</anno></c> to the element at position @@ -2006,8 +2073,8 @@ true</pre> </func> <func> - <name name="update_element" arity="3" clause_i="1"/> - <name name="update_element" arity="3" clause_i="2"/> + <name name="update_element" arity="3" clause_i="1" since=""/> + <name name="update_element" arity="3" clause_i="2" since=""/> <fsummary>Update the <c>Pos</c>:th element of the object with a specified key in an ETS table.</fsummary> <type variable="Tab"/> @@ -2049,7 +2116,7 @@ true</pre> </func> <func> - <name name="whereis" arity="1"/> + <name name="whereis" arity="1" since="OTP 21.0"/> <fsummary>Retrieves the tid() of a named table.</fsummary> <desc> <p>This function returns the diff --git a/lib/stdlib/doc/src/file_sorter.xml b/lib/stdlib/doc/src/file_sorter.xml index e988d58c2f..942d98fe61 100644 --- a/lib/stdlib/doc/src/file_sorter.xml +++ b/lib/stdlib/doc/src/file_sorter.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>file_sorter.xml</file> </header> - <module>file_sorter</module> + <module since="">file_sorter</module> <modulesummary>File sorter.</modulesummary> <description> <p>This module contains functions for sorting terms on files, merging @@ -334,8 +334,8 @@ output(L) -> <funcs> <func> - <name name="check" arity="1"/> - <name name="check" arity="2"/> + <name name="check" arity="1" since=""/> + <name name="check" arity="2" since=""/> <fsummary>Check whether terms on files are sorted.</fsummary> <desc> <p>Checks files for sortedness. If a file is not sorted, the @@ -347,8 +347,8 @@ output(L) -> </func> <func> - <name name="keycheck" arity="2"/> - <name name="keycheck" arity="3"/> + <name name="keycheck" arity="2" since=""/> + <name name="keycheck" arity="3" since=""/> <fsummary>Check whether terms on files are sorted by key.</fsummary> <desc> <p>Checks files for sortedness. If a file is not sorted, the @@ -360,8 +360,8 @@ output(L) -> </func> <func> - <name name="keymerge" arity="3"/> - <name name="keymerge" arity="4"/> + <name name="keymerge" arity="3" since=""/> + <name name="keymerge" arity="4" since=""/> <fsummary>Merge terms on files by key.</fsummary> <desc> <p>Merges tuples on files. Each input file is assumed to be @@ -372,7 +372,7 @@ output(L) -> </func> <func> - <name name="keysort" arity="2"/> + <name name="keysort" arity="2" since=""/> <fsummary>Sort terms on files by key.</fsummary> <desc> <p>Sorts tuples on files.</p> @@ -382,8 +382,8 @@ output(L) -> </func> <func> - <name name="keysort" arity="3"/> - <name name="keysort" arity="4"/> + <name name="keysort" arity="3" since=""/> + <name name="keysort" arity="4" since=""/> <fsummary>Sort terms on files by key.</fsummary> <desc> <p>Sorts tuples on files. The sort is performed on the @@ -397,8 +397,8 @@ output(L) -> </func> <func> - <name name="merge" arity="2"/> - <name name="merge" arity="3"/> + <name name="merge" arity="2" since=""/> + <name name="merge" arity="3" since=""/> <fsummary>Merge terms on files.</fsummary> <desc> <p>Merges terms on files. Each input file is assumed to be @@ -409,7 +409,7 @@ output(L) -> </func> <func> - <name name="sort" arity="1"/> + <name name="sort" arity="1" since=""/> <fsummary>Sort terms on files.</fsummary> <desc> <p>Sorts terms on files.</p> @@ -419,8 +419,8 @@ output(L) -> </func> <func> - <name name="sort" arity="2"/> - <name name="sort" arity="3"/> + <name name="sort" arity="2" since=""/> + <name name="sort" arity="3" since=""/> <fsummary>Sort terms on files.</fsummary> <desc> <p>Sorts terms on files.</p> diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index 3b5be75bc0..5df415834f 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>filelib.xml</file> </header> - <module>filelib</module> + <module since="">filelib</module> <modulesummary>File utilities, such as wildcard matching of filenames. </modulesummary> <description> @@ -94,7 +94,7 @@ <funcs> <func> - <name name="ensure_dir" arity="1"/> + <name name="ensure_dir" arity="1" since=""/> <fsummary>Ensure that all parent directories for a file or directory exist.</fsummary> <desc> @@ -108,7 +108,7 @@ </func> <func> - <name name="file_size" arity="1"/> + <name name="file_size" arity="1" since=""/> <fsummary>Return the size in bytes of a file.</fsummary> <desc> <p>Returns the size of the specified file.</p> @@ -116,7 +116,7 @@ </func> <func> - <name name="fold_files" arity="5"/> + <name name="fold_files" arity="5" since=""/> <fsummary>Fold over all files matching a regular expression.</fsummary> <desc> <p>Folds function <c><anno>Fun</anno></c> over all (regular) files @@ -142,7 +142,7 @@ </func> <func> - <name name="is_dir" arity="1"/> + <name name="is_dir" arity="1" since=""/> <fsummary>Test whether <c>Name</c> refers to a directory.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Name</anno></c> @@ -151,7 +151,7 @@ </func> <func> - <name name="is_file" arity="1"/> + <name name="is_file" arity="1" since=""/> <fsummary>Test whether <c>Name</c> refers to a file or directory. </fsummary> <desc> @@ -161,7 +161,7 @@ </func> <func> - <name name="is_regular" arity="1"/> + <name name="is_regular" arity="1" since=""/> <fsummary>Test whether <c>Name</c> refers to a (regular) file.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Name</anno></c> @@ -170,7 +170,7 @@ </func> <func> - <name name="last_modified" arity="1"/> + <name name="last_modified" arity="1" since=""/> <fsummary>Return the local date and time when a file was last modified. </fsummary> <desc> @@ -180,7 +180,7 @@ </func> <func> - <name name="wildcard" arity="1"/> + <name name="wildcard" arity="1" since=""/> <fsummary>Match filenames using Unix-style wildcards.</fsummary> <desc> <p>Returns a list of all files that match Unix-style wildcard string @@ -252,7 +252,7 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code> </func> <func> - <name name="wildcard" arity="2"/> + <name name="wildcard" arity="2" since=""/> <fsummary>Match filenames using Unix-style wildcards starting at a specified directory.</fsummary> <desc> @@ -263,8 +263,8 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code> </func> <func> - <name name="find_file" arity="2"/> - <name name="find_file" arity="3"/> + <name name="find_file" arity="2" since="OTP 20.0"/> + <name name="find_file" arity="3" since="OTP 20.0"/> <fsummary>Find a file relative to a given directory.</fsummary> <desc> <p>Looks for a file of the given name by applying suffix rules to @@ -278,7 +278,7 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code> </desc> </func> <func> - <name name="find_source" arity="1"/> + <name name="find_source" arity="1" since="OTP 20.0"/> <fsummary>Find the source file for a given object file.</fsummary> <desc> <p>Equivalent to <c>find_source(Base, Dir)</c>, where <c>Dir</c> is @@ -287,8 +287,8 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code> </desc> </func> <func> - <name name="find_source" arity="2"/> - <name name="find_source" arity="3"/> + <name name="find_source" arity="2" since="OTP 20.0"/> + <name name="find_source" arity="3" since="OTP 20.0"/> <fsummary>Find a source file relative to a given directory.</fsummary> <desc> <p>Applies file extension specific rules to find the source file for diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index 36254c2d00..ae42846c6b 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -28,7 +28,7 @@ <date>1997-11-13</date> <rev>B</rev> </header> - <module>filename</module> + <module since="">filename</module> <modulesummary>Filename manipulation functions.</modulesummary> <description> <p>This module provides functions @@ -87,7 +87,7 @@ <funcs> <func> - <name name="absname" arity="1"/> + <name name="absname" arity="1" since=""/> <fsummary>Convert a filename to an absolute name, relative the working directory.</fsummary> <desc> @@ -119,7 +119,7 @@ </func> <func> - <name name="absname" arity="2"/> + <name name="absname" arity="2" since=""/> <fsummary>Convert a filename to an absolute name, relative a specified directory.</fsummary> <desc> @@ -130,7 +130,7 @@ </func> <func> - <name name="absname_join" arity="2"/> + <name name="absname_join" arity="2" since=""/> <fsummary>Join an absolute directory with a relative filename.</fsummary> <desc> <p>Joins an absolute directory with a relative filename. Similar to @@ -144,8 +144,8 @@ </func> <func> - <name name="basedir" arity="2" clause_i="1"/> - <name name="basedir" arity="2" clause_i="2"/> + <name name="basedir" arity="2" clause_i="1" since="OTP 19.0"/> + <name name="basedir" arity="2" clause_i="2" since="OTP 19.0"/> <fsummary>Equivalent to <c>basedir(<anno>PathType</anno>, <anno>Application</anno>,#{})</c> or <c>basedir(<anno>PathsType</anno>, <anno>Application</anno>,#{})</c>. @@ -165,8 +165,8 @@ basedir(<anno>PathsType</anno>, <anno>Application</anno>, #{})</seealso>. </desc> </func> <func> - <name name="basedir" arity="3" clause_i="1" anchor="basedir_3_1"/> - <name name="basedir" arity="3" clause_i="2" anchor="basedir_3_2"/> + <name name="basedir" arity="3" clause_i="1" anchor="basedir_3_1" since="OTP 19.0"/> + <name name="basedir" arity="3" clause_i="2" anchor="basedir_3_2" since="OTP 19.0"/> <fsummary></fsummary> <type variable="PathType" name_i="1"/> <type name="basedir_path_type"/> @@ -314,7 +314,7 @@ true </desc> </func> <func> - <name name="basename" arity="1"/> + <name name="basename" arity="1" since=""/> <fsummary>Return the last component of a filename.</fsummary> <desc> <p>Returns the last component of <c><anno>Filename</anno></c>, or @@ -332,7 +332,7 @@ true </func> <func> - <name name="basename" arity="2"/> + <name name="basename" arity="2" since=""/> <fsummary>Return the last component of a filename, stripped of the specified extension.</fsummary> <desc> @@ -357,7 +357,7 @@ true </func> <func> - <name name="dirname" arity="1"/> + <name name="dirname" arity="1" since=""/> <fsummary>Return the directory part of a path name.</fsummary> <desc> <p>Returns the directory part of <c><anno>Filename</anno></c>.</p> @@ -374,7 +374,7 @@ true </func> <func> - <name name="extension" arity="1"/> + <name name="extension" arity="1" since=""/> <fsummary>Return the file extension.</fsummary> <desc> <p>Returns the file extension of <c><anno>Filename</anno></c>, @@ -390,8 +390,8 @@ true </func> <func> - <name name="find_src" arity="1"/> - <name name="find_src" arity="2"/> + <name name="find_src" arity="1" since=""/> + <name name="find_src" arity="2" since=""/> <fsummary>Find the filename and compiler options for a module.</fsummary> <desc> <p>Finds the source filename and compiler options for a module. @@ -438,7 +438,7 @@ true </func> <func> - <name name="flatten" arity="1"/> + <name name="flatten" arity="1" since=""/> <fsummary>Convert a filename to a flat string.</fsummary> <desc> <p>Converts a possibly deep list filename consisting of @@ -448,7 +448,7 @@ true </func> <func> - <name name="join" arity="1"/> + <name name="join" arity="1" since=""/> <fsummary>Join a list of filename components with directory separators. </fsummary> <desc> @@ -476,7 +476,7 @@ true </func> <func> - <name name="join" arity="2"/> + <name name="join" arity="2" since=""/> <fsummary>Join two filename components with directory separators. </fsummary> <desc> @@ -487,7 +487,7 @@ true </func> <func> - <name name="nativename" arity="1"/> + <name name="nativename" arity="1" since=""/> <fsummary>Return the native form of a file path.</fsummary> <desc> <p>Converts <c><anno>Path</anno></c> to a form accepted by the command @@ -506,7 +506,7 @@ true </func> <func> - <name name="pathtype" arity="1"/> + <name name="pathtype" arity="1" since=""/> <fsummary>Return the path type.</fsummary> <desc> <p>Returns the path type, which is one of the following:</p> @@ -536,8 +536,8 @@ true </func> <func> - <name name="rootname" arity="1"/> - <name name="rootname" arity="2"/> + <name name="rootname" arity="1" since=""/> + <name name="rootname" arity="2" since=""/> <fsummary>Remove a filename extension.</fsummary> <desc> <p>Removes a filename extension. <c>rootname/2</c> works as @@ -557,7 +557,7 @@ true </func> <func> - <name name="safe_relative_path" arity="1"/> + <name name="safe_relative_path" arity="1" since="OTP 19.3"/> <fsummary>Sanitize a relative path to avoid directory traversal attacks.</fsummary> <desc> <p>Sanitizes the relative path by eliminating ".." and "." @@ -584,7 +584,7 @@ unsafe</pre> </func> <func> - <name name="split" arity="1"/> + <name name="split" arity="1" since=""/> <fsummary>Split a filename into its path components.</fsummary> <desc> <p>Returns a list whose elements are the path components of diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml index 03397b4503..a9596c6e4d 100644 --- a/lib/stdlib/doc/src/gb_sets.xml +++ b/lib/stdlib/doc/src/gb_sets.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>gb_sets</module> + <module since="">gb_sets</module> <modulesummary>General balanced trees.</modulesummary> <description> <p>This module provides ordered sets using Prof. Arne Andersson's @@ -123,8 +123,8 @@ <funcs> <func> - <name name="add" arity="2"/> - <name name="add_element" arity="2"/> + <name name="add" arity="2" since=""/> + <name name="add_element" arity="2" since=""/> <fsummary>Add a (possibly existing) element to a set.</fsummary> <desc> <p>Returns a new set formed from <c><anno>Set1</anno></c> with @@ -135,7 +135,7 @@ </func> <func> - <name name="balance" arity="1"/> + <name name="balance" arity="1" since=""/> <fsummary>Rebalance tree representation of a set.</fsummary> <desc> <p>Rebalances the tree representation of <c><anno>Set1</anno></c>. @@ -149,7 +149,7 @@ </func> <func> - <name name="del_element" arity="2"/> + <name name="del_element" arity="2" since=""/> <fsummary>Remove a (possibly non-existing) element from a set.</fsummary> <desc> <p>Returns a new set formed from <c><anno>Set1</anno></c> with @@ -160,7 +160,7 @@ </func> <func> - <name name="delete" arity="2"/> + <name name="delete" arity="2" since=""/> <fsummary>Remove an element from a set.</fsummary> <desc> <p>Returns a new set formed from <c><anno>Set1</anno></c> with @@ -171,7 +171,7 @@ </func> <func> - <name name="delete_any" arity="2"/> + <name name="delete_any" arity="2" since=""/> <fsummary>Remove a (possibly non-existing) element from a set.</fsummary> <desc> <p>Returns a new set formed from <c><anno>Set1</anno></c> with @@ -182,7 +182,7 @@ </func> <func> - <name name="difference" arity="2"/> + <name name="difference" arity="2" since=""/> <fsummary>Return the difference of two sets.</fsummary> <desc> <p>Returns only the elements of <c><anno>Set1</anno></c> that are not @@ -191,7 +191,7 @@ </func> <func> - <name name="empty" arity="0"/> + <name name="empty" arity="0" since=""/> <fsummary>Return an empty set.</fsummary> <desc> <p>Returns a new empty set.</p> @@ -199,7 +199,7 @@ </func> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Filter set elements.</fsummary> <desc> <p>Filters elements in <c><anno>Set1</anno></c> using predicate function @@ -208,7 +208,7 @@ </func> <func> - <name name="fold" arity="3"/> + <name name="fold" arity="3" since=""/> <fsummary>Fold over set elements.</fsummary> <desc> <p>Folds <c><anno>Function</anno></c> over every element in @@ -218,7 +218,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Convert a list into a set.</fsummary> <desc> <p>Returns a set of the elements in <c><anno>List</anno></c>, where @@ -227,7 +227,7 @@ </func> <func> - <name name="from_ordset" arity="1"/> + <name name="from_ordset" arity="1" since=""/> <fsummary>Make a set from an ordset list.</fsummary> <desc> <p>Turns an ordered-set list <c><anno>List</anno></c> into a set. @@ -236,7 +236,7 @@ </func> <func> - <name name="insert" arity="2"/> + <name name="insert" arity="2" since=""/> <fsummary>Add a new element to a set.</fsummary> <desc> <p>Returns a new set formed from <c><anno>Set1</anno></c> with @@ -247,7 +247,7 @@ </func> <func> - <name name="intersection" arity="1"/> + <name name="intersection" arity="1" since=""/> <fsummary>Return the intersection of a list of sets.</fsummary> <desc> <p>Returns the intersection of the non-empty list of sets.</p> @@ -255,7 +255,7 @@ </func> <func> - <name name="intersection" arity="2"/> + <name name="intersection" arity="2" since=""/> <fsummary>Return the intersection of two sets.</fsummary> <desc> <p>Returns the intersection of <c><anno>Set1</anno></c> and @@ -264,7 +264,7 @@ </func> <func> - <name name="is_disjoint" arity="2"/> + <name name="is_disjoint" arity="2" since=""/> <fsummary>Check whether two sets are disjoint.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and @@ -274,7 +274,7 @@ </func> <func> - <name name="is_element" arity="2"/> + <name name="is_element" arity="2" since=""/> <fsummary>Test for membership of a set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of @@ -283,7 +283,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since=""/> <fsummary>Test for empty set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set</anno></c> is an empty set, @@ -292,7 +292,7 @@ </func> <func> - <name name="is_member" arity="2"/> + <name name="is_member" arity="2" since=""/> <fsummary>Test for membership of a set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of @@ -301,7 +301,7 @@ </func> <func> - <name name="is_set" arity="1"/> + <name name="is_set" arity="1" since=""/> <fsummary>Test for a set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a set, @@ -310,7 +310,7 @@ </func> <func> - <name name="is_subset" arity="2"/> + <name name="is_subset" arity="2" since=""/> <fsummary>Test for subset.</fsummary> <desc> <p>Returns <c>true</c> when every element of <c><anno>Set1</anno></c> is @@ -319,7 +319,7 @@ </func> <func> - <name name="iterator" arity="1"/> + <name name="iterator" arity="1" since=""/> <fsummary>Return an iterator for a set.</fsummary> <desc> <p>Returns an iterator that can be used for traversing the entries of @@ -336,7 +336,7 @@ </func> <func> - <name name="iterator_from" arity="2"/> + <name name="iterator_from" arity="2" since="OTP 18.0"/> <fsummary>Return an iterator for a set starting from a specified element. </fsummary> <desc> @@ -351,7 +351,7 @@ </func> <func> - <name name="largest" arity="1"/> + <name name="largest" arity="1" since=""/> <fsummary>Return largest element.</fsummary> <desc> <p>Returns the largest element in <c><anno>Set</anno></c>. Assumes that @@ -360,7 +360,7 @@ </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Return an empty set.</fsummary> <desc> <p>Returns a new empty set.</p> @@ -368,7 +368,7 @@ </func> <func> - <name name="next" arity="1"/> + <name name="next" arity="1" since=""/> <fsummary>Traverse a set with an iterator.</fsummary> <desc> <p>Returns <c>{<anno>Element</anno>, <anno>Iter2</anno>}</c>, where @@ -381,7 +381,7 @@ </func> <func> - <name name="singleton" arity="1"/> + <name name="singleton" arity="1" since=""/> <fsummary>Return a set with one element.</fsummary> <desc> <p>Returns a set containing only element <c><anno>Element</anno></c>. @@ -390,7 +390,7 @@ </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Return the number of elements in a set.</fsummary> <desc> <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> @@ -398,7 +398,7 @@ </func> <func> - <name name="smallest" arity="1"/> + <name name="smallest" arity="1" since=""/> <fsummary>Return smallest element.</fsummary> <desc> <p>Returns the smallest element in <c><anno>Set</anno></c>. Assumes that @@ -407,7 +407,7 @@ </func> <func> - <name name="subtract" arity="2"/> + <name name="subtract" arity="2" since=""/> <fsummary>Return the difference of two sets.</fsummary> <desc> <p>Returns only the elements of <c><anno>Set1</anno></c> that are not @@ -416,7 +416,7 @@ </func> <func> - <name name="take_largest" arity="1"/> + <name name="take_largest" arity="1" since=""/> <fsummary>Extract largest element.</fsummary> <desc> <p>Returns <c>{<anno>Element</anno>, <anno>Set2</anno>}</c>, where @@ -428,7 +428,7 @@ </func> <func> - <name name="take_smallest" arity="1"/> + <name name="take_smallest" arity="1" since=""/> <fsummary>Extract smallest element.</fsummary> <desc> <p>Returns <c>{<anno>Element</anno>, <anno>Set2</anno>}</c>, where @@ -440,7 +440,7 @@ </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert a set into a list.</fsummary> <desc> <p>Returns the elements of <c><anno>Set</anno></c> as a list.</p> @@ -448,7 +448,7 @@ </func> <func> - <name name="union" arity="1"/> + <name name="union" arity="1" since=""/> <fsummary>Return the union of a list of sets.</fsummary> <desc> <p>Returns the merged (union) set of the list of sets.</p> @@ -456,7 +456,7 @@ </func> <func> - <name name="union" arity="2"/> + <name name="union" arity="2" since=""/> <fsummary>Return the union of two sets.</fsummary> <desc> <p>Returns the merged (union) set of <c><anno>Set1</anno></c> and diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml index 5cfff021c1..570c9c7cb6 100644 --- a/lib/stdlib/doc/src/gb_trees.xml +++ b/lib/stdlib/doc/src/gb_trees.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>gb_trees</module> + <module since="">gb_trees</module> <modulesummary>General balanced trees.</modulesummary> <description> <p>This module provides Prof. Arne Andersson's General @@ -75,7 +75,7 @@ <funcs> <func> - <name name="balance" arity="1"/> + <name name="balance" arity="1" since=""/> <fsummary>Rebalance a tree.</fsummary> <desc> <p>Rebalances <c><anno>Tree1</anno></c>. Notice that this is @@ -88,7 +88,7 @@ </func> <func> - <name name="delete" arity="2"/> + <name name="delete" arity="2" since=""/> <fsummary>Remove a node from a tree.</fsummary> <desc> <p>Removes the node with key <c><anno>Key</anno></c> from @@ -98,7 +98,7 @@ </func> <func> - <name name="delete_any" arity="2"/> + <name name="delete_any" arity="2" since=""/> <fsummary>Remove a (possibly non-existing) node from a tree.</fsummary> <desc> <p>Removes the node with key <c><anno>Key</anno></c> from @@ -109,7 +109,7 @@ </func> <func> - <name name="take" arity="2"/> + <name name="take" arity="2" since="OTP 20.0"/> <fsummary>Returns a value and new tree without node with key <c>Key</c>.</fsummary> <desc> <p>Returns a value <c><anno>Value</anno></c> from node with key <c><anno>Key</anno></c> @@ -120,7 +120,7 @@ </func> <func> - <name name="take_any" arity="2"/> + <name name="take_any" arity="2" since="OTP 20.0"/> <fsummary>Returns a value and new tree without node with key <c>Key</c>.</fsummary> <desc> <p>Returns a value <c><anno>Value</anno></c> from node with key <c><anno>Key</anno></c> @@ -131,7 +131,7 @@ </func> <func> - <name name="empty" arity="0"/> + <name name="empty" arity="0" since=""/> <fsummary>Return an empty tree.</fsummary> <desc> <p>Returns a new empty tree.</p> @@ -139,7 +139,7 @@ </func> <func> - <name name="enter" arity="3"/> + <name name="enter" arity="3" since=""/> <fsummary>Insert or update key with value in a tree.</fsummary> <desc> <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> @@ -151,7 +151,7 @@ </func> <func> - <name name="from_orddict" arity="1"/> + <name name="from_orddict" arity="1" since=""/> <fsummary>Make a tree from an orddict.</fsummary> <desc> <p>Turns an ordered list <c><anno>List</anno></c> of key-value tuples @@ -160,7 +160,7 @@ </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since=""/> <fsummary>Look up a key in a tree, if present.</fsummary> <desc> <p>Retrieves the value stored with <c><anno>Key</anno></c> in @@ -171,7 +171,7 @@ </func> <func> - <name name="insert" arity="3"/> + <name name="insert" arity="3" since=""/> <fsummary>Insert a new key and value in a tree.</fsummary> <desc> <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> @@ -182,7 +182,7 @@ </func> <func> - <name name="is_defined" arity="2"/> + <name name="is_defined" arity="2" since=""/> <fsummary>Test for membership of a tree.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Key</anno></c> is present in @@ -191,7 +191,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since=""/> <fsummary>Test for empty tree.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Tree</anno></c> is an empty tree, @@ -200,7 +200,7 @@ </func> <func> - <name name="iterator" arity="1"/> + <name name="iterator" arity="1" since=""/> <fsummary>Return an iterator for a tree.</fsummary> <desc> <p>Returns an iterator that can be used for traversing the @@ -218,7 +218,7 @@ </func> <func> - <name name="iterator_from" arity="2"/> + <name name="iterator_from" arity="2" since="OTP 18.0"/> <fsummary>Return an iterator for a tree starting from a specified key. </fsummary> <desc> @@ -233,7 +233,7 @@ </func> <func> - <name name="keys" arity="1"/> + <name name="keys" arity="1" since=""/> <fsummary>Return a list of the keys in a tree.</fsummary> <desc> <p>Returns the keys in <c><anno>Tree</anno></c> as an ordered list.</p> @@ -241,7 +241,7 @@ </func> <func> - <name name="largest" arity="1"/> + <name name="largest" arity="1" since=""/> <fsummary>Return largest key and value.</fsummary> <desc> <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>}</c>, where @@ -253,7 +253,7 @@ </func> <func> - <name name="lookup" arity="2"/> + <name name="lookup" arity="2" since=""/> <fsummary>Look up a key in a tree.</fsummary> <desc> <p>Looks up <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>. @@ -263,7 +263,7 @@ </func> <func> - <name name="map" arity="2"/> + <name name="map" arity="2" since=""/> <fsummary>Return largest key and value.</fsummary> <desc> <p>Maps function F(<anno>K</anno>, <anno>V1</anno>) -> <anno>V2</anno> @@ -275,7 +275,7 @@ </func> <func> - <name name="next" arity="1"/> + <name name="next" arity="1" since=""/> <fsummary>Traverse a tree with an iterator.</fsummary> <desc> <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, @@ -288,7 +288,7 @@ </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Return the number of nodes in a tree.</fsummary> <desc> <p>Returns the number of nodes in <c><anno>Tree</anno></c>.</p> @@ -296,7 +296,7 @@ </func> <func> - <name name="smallest" arity="1"/> + <name name="smallest" arity="1" since=""/> <fsummary>Return smallest key and value.</fsummary> <desc> <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>}</c>, where @@ -308,7 +308,7 @@ </func> <func> - <name name="take_largest" arity="1"/> + <name name="take_largest" arity="1" since=""/> <fsummary>Extract largest key and value.</fsummary> <desc> <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, @@ -321,7 +321,7 @@ </func> <func> - <name name="take_smallest" arity="1"/> + <name name="take_smallest" arity="1" since=""/> <fsummary>Extract smallest key and value.</fsummary> <desc> <p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>, @@ -334,7 +334,7 @@ </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert a tree into a list.</fsummary> <desc> <p>Converts a tree into an ordered list of key-value tuples.</p> @@ -342,7 +342,7 @@ </func> <func> - <name name="update" arity="3"/> + <name name="update" arity="3" since=""/> <fsummary>Update a key to new value in a tree.</fsummary> <desc> <p>Updates <c><anno>Key</anno></c> to value <c><anno>Value</anno></c> @@ -352,7 +352,7 @@ </func> <func> - <name name="values" arity="1"/> + <name name="values" arity="1" since=""/> <fsummary>Return a list of the values in a tree.</fsummary> <desc> <p>Returns the values in <c><anno>Tree</anno></c> as an ordered list, diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index fc34e51216..2915c4f507 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>gen_event</module> + <module since="">gen_event</module> <modulesummary>Generic event handling behavior.</modulesummary> <description> <p>This behavior module provides event handling functionality. It @@ -130,7 +130,7 @@ gen_event:stop -----> Module:terminate/2 <funcs> <func> - <name>add_handler(EventMgrRef, Handler, Args) -> Result</name> + <name since="">add_handler(EventMgrRef, Handler, Args) -> Result</name> <fsummary>Add an event handler to a generic event manager.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -178,7 +178,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>add_sup_handler(EventMgrRef, Handler, Args) -> Result</name> + <name since="">add_sup_handler(EventMgrRef, Handler, Args) -> Result</name> <fsummary>Add a supervised event handler to a generic event manager. </fsummary> <type> @@ -241,8 +241,8 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>call(EventMgrRef, Handler, Request) -> Result</name> - <name>call(EventMgrRef, Handler, Request, Timeout) -> Result</name> + <name since="">call(EventMgrRef, Handler, Request) -> Result</name> + <name since="">call(EventMgrRef, Handler, Request, Timeout) -> Result</name> <fsummary>Make a synchronous call to a generic event manager.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -285,7 +285,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>delete_handler(EventMgrRef, Handler, Args) -> Result</name> + <name since="">delete_handler(EventMgrRef, Handler, Args) -> Result</name> <fsummary>Delete an event handler from a generic event manager.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -318,8 +318,8 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>notify(EventMgrRef, Event) -> ok</name> - <name>sync_notify(EventMgrRef, Event) -> ok</name> + <name since="">notify(EventMgrRef, Event) -> ok</name> + <name since="">sync_notify(EventMgrRef, Event) -> ok</name> <fsummary>Notify an event manager about an event.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -349,9 +349,9 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>start() -> Result</name> - <name>start(EventMgrName | Options) -> Result</name> - <name>start(EventMgrName, Options) -> Result</name> + <name since="">start() -> Result</name> + <name since="">start(EventMgrName | Options) -> Result</name> + <name since="OTP 20.0">start(EventMgrName, Options) -> Result</name> <fsummary>Create a stand-alone event manager process.</fsummary> <type> <v>EventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}</v> @@ -375,9 +375,9 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>start_link() -> Result</name> - <name>start_link(EventMgrName | Options) -> Result</name> - <name>start_link(EventMgrName, Options) -> Result</name> + <name since="">start_link() -> Result</name> + <name since="">start_link(EventMgrName | Options) -> Result</name> + <name since="OTP 20.0">start_link(EventMgrName, Options) -> Result</name> <fsummary>Create a generic event manager process in a supervision tree. </fsummary> <type> @@ -436,8 +436,8 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>stop(EventMgrRef) -> ok</name> - <name>stop(EventMgrRef, Reason, Timeout) -> ok</name> + <name since="">stop(EventMgrRef) -> ok</name> + <name since="OTP 18.0">stop(EventMgrRef, Reason, Timeout) -> ok</name> <fsummary>Terminate a generic event manager.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -474,7 +474,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>swap_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name> + <name since="">swap_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name> <fsummary>Replace an event handler in a generic event manager.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -521,7 +521,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>swap_sup_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name> + <name since="">swap_sup_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name> <fsummary>Replace an event handler in a generic event manager.</fsummary> <type> <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} @@ -546,7 +546,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>which_handlers(EventMgrRef) -> [Handler]</name> + <name since="">which_handlers(EventMgrRef) -> [Handler]</name> <fsummary>Return all event handlers installed in a generic event manager. </fsummary> <type> @@ -575,7 +575,7 @@ gen_event:stop -----> Module:terminate/2 <funcs> <func> - <name>Module:code_change(OldVsn, State, Extra) -> {ok, NewState}</name> + <name since="">Module:code_change(OldVsn, State, Extra) -> {ok, NewState}</name> <fsummary>Update the internal state during upgrade/downgrade.</fsummary> <type> <v>OldVsn = Vsn | {down, Vsn}</v> @@ -611,7 +611,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>Module:format_status(Opt, [PDict, State]) -> Status</name> + <name since="OTP R14B">Module:format_status(Opt, [PDict, State]) -> Status</name> <fsummary>Optional function for providing a term describing the current event handler state.</fsummary> <type> @@ -667,7 +667,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>Module:handle_call(Request, State) -> Result</name> + <name since="">Module:handle_call(Request, State) -> Result</name> <fsummary>Handle a synchronous request.</fsummary> <type> <v>Request = term()</v> @@ -698,7 +698,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>Module:handle_event(Event, State) -> Result</name> + <name since="">Module:handle_event(Event, State) -> Result</name> <fsummary>Handle an event.</fsummary> <type> <v>Event = term()</v> @@ -756,7 +756,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>Module:handle_info(Info, State) -> Result</name> + <name since="">Module:handle_info(Info, State) -> Result</name> <fsummary>Handle an incoming message.</fsummary> <type> <v>Info = term()</v> @@ -788,7 +788,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>Module:init(InitArgs) -> {ok,State} | {ok,State,hibernate} | {error,Reason}</name> + <name since="">Module:init(InitArgs) -> {ok,State} | {ok,State,hibernate} | {error,Reason}</name> <fsummary>Initialize an event handler.</fsummary> <type> <v>InitArgs = Args | {Args,Term}</v> @@ -825,7 +825,7 @@ gen_event:stop -----> Module:terminate/2 </func> <func> - <name>Module:terminate(Arg, State) -> term()</name> + <name since="">Module:terminate(Arg, State) -> term()</name> <fsummary>Clean up before deletion.</fsummary> <type> <v>Arg = Args | {stop,Reason} | stop | remove_handler</v> diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index 106bda85f5..a4554d7657 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>gen_server</module> + <module since="">gen_server</module> <modulesummary>Generic server behavior.</modulesummary> <description> <p>This behavior module provides the server of a client-server @@ -101,8 +101,8 @@ gen_server:abcast -----> Module:handle_cast/2 <funcs> <func> - <name>abcast(Name, Request) -> abcast</name> - <name>abcast(Nodes, Name, Request) -> abcast</name> + <name since="">abcast(Name, Request) -> abcast</name> + <name since="">abcast(Nodes, Name, Request) -> abcast</name> <fsummary>Send an asynchronous request to many generic servers.</fsummary> <type> <v>Nodes = [Node]</v> @@ -124,8 +124,8 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>call(ServerRef, Request) -> Reply</name> - <name>call(ServerRef, Request, Timeout) -> Reply</name> + <name since="">call(ServerRef, Request) -> Reply</name> + <name since="">call(ServerRef, Request, Timeout) -> Reply</name> <fsummary>Make a synchronous call to a generic server.</fsummary> <type> <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v> @@ -175,7 +175,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>cast(ServerRef, Request) -> ok</name> + <name since="">cast(ServerRef, Request) -> ok</name> <fsummary>Send an asynchronous request to a generic server.</fsummary> <type> <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v> @@ -200,10 +200,10 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>enter_loop(Module, Options, State)</name> - <name>enter_loop(Module, Options, State, ServerName)</name> - <name>enter_loop(Module, Options, State, Timeout)</name> - <name>enter_loop(Module, Options, State, ServerName, Timeout)</name> + <name since="">enter_loop(Module, Options, State)</name> + <name since="">enter_loop(Module, Options, State, ServerName)</name> + <name since="">enter_loop(Module, Options, State, Timeout)</name> + <name since="">enter_loop(Module, Options, State, ServerName, Timeout)</name> <fsummary>Enter the <c>gen_server</c> receive loop.</fsummary> <type> <v>Module = atom()</v> @@ -248,9 +248,9 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>multi_call(Name, Request) -> Result</name> - <name>multi_call(Nodes, Name, Request) -> Result</name> - <name>multi_call(Nodes, Name, Request, Timeout) -> Result</name> + <name since="">multi_call(Name, Request) -> Result</name> + <name since="">multi_call(Nodes, Name, Request) -> Result</name> + <name since="">multi_call(Nodes, Name, Request, Timeout) -> Result</name> <fsummary>Make a synchronous call to many generic servers.</fsummary> <type> <v>Nodes = [Node]</v> @@ -307,7 +307,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>reply(Client, Reply) -> Result</name> + <name since="">reply(Client, Reply) -> Result</name> <fsummary>Send a reply to a client.</fsummary> <type> <v>Client - see below</v> @@ -332,8 +332,8 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>start(Module, Args, Options) -> Result</name> - <name>start(ServerName, Module, Args, Options) -> Result</name> + <name since="">start(Module, Args, Options) -> Result</name> + <name since="">start(ServerName, Module, Args, Options) -> Result</name> <fsummary>Create a standalone <c>gen_server</c> process.</fsummary> <type> <v>ServerName = {local,Name} | {global,GlobalName}</v> @@ -361,8 +361,8 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>start_link(Module, Args, Options) -> Result</name> - <name>start_link(ServerName, Module, Args, Options) -> Result</name> + <name since="">start_link(Module, Args, Options) -> Result</name> + <name since="">start_link(ServerName, Module, Args, Options) -> Result</name> <fsummary>Create a <c>gen_server</c> process in a supervision tree. </fsummary> <type> @@ -466,8 +466,8 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>stop(ServerRef) -> ok</name> - <name>stop(ServerRef, Reason, Timeout) -> ok</name> + <name since="OTP 18.0">stop(ServerRef) -> ok</name> + <name since="OTP 18.0">stop(ServerRef, Reason, Timeout) -> ok</name> <fsummary>Synchronously stop a generic server.</fsummary> <type> <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v> @@ -508,7 +508,7 @@ gen_server:abcast -----> Module:handle_cast/2 <funcs> <func> - <name>Module:code_change(OldVsn, State, Extra) -> {ok, NewState} | {error, Reason}</name> + <name since="">Module:code_change(OldVsn, State, Extra) -> {ok, NewState} | {error, Reason}</name> <fsummary>Update the internal state during upgrade/downgrade.</fsummary> <type> <v>OldVsn = Vsn | {down, Vsn}</v> @@ -550,7 +550,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:format_status(Opt, [PDict, State]) -> Status</name> + <name since="OTP R13B04">Module:format_status(Opt, [PDict, State]) -> Status</name> <fsummary>Optional function for providing a term describing the current <c>gen_server</c> status.</fsummary> <type> @@ -610,7 +610,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:handle_call(Request, From, State) -> Result</name> + <name since="">Module:handle_call(Request, From, State) -> Result</name> <fsummary>Handle a synchronous request.</fsummary> <type> <v>Request = term()</v> @@ -677,7 +677,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:handle_cast(Request, State) -> Result</name> + <name since="">Module:handle_cast(Request, State) -> Result</name> <fsummary>Handle an asynchronous request.</fsummary> <type> <v>Request = term()</v> @@ -703,7 +703,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:handle_continue(Continue, State) -> Result</name> + <name since="OTP 21.0">Module:handle_continue(Continue, State) -> Result</name> <fsummary>Handle a continue instruction.</fsummary> <type> <v>Continue = term()</v> @@ -738,7 +738,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:handle_info(Info, State) -> Result</name> + <name since="">Module:handle_info(Info, State) -> Result</name> <fsummary>Handle an incoming message.</fsummary> <type> <v>Info = timeout | term()</v> @@ -770,7 +770,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:init(Args) -> Result</name> + <name since="">Module:init(Args) -> Result</name> <fsummary>Initialize process and internal state.</fsummary> <type> <v>Args = term()</v> @@ -811,7 +811,7 @@ gen_server:abcast -----> Module:handle_cast/2 </func> <func> - <name>Module:terminate(Reason, State)</name> + <name since="">Module:terminate(Reason, State)</name> <fsummary>Clean up before termination.</fsummary> <type> <v>Reason = normal | shutdown | {shutdown,term()} | term()</v> diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index a808d3af55..aaa26df18d 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>gen_statem</module> + <module since="OTP 19.0">gen_statem</module> <modulesummary>Generic state machine behavior.</modulesummary> <description> <p> @@ -1398,8 +1398,8 @@ handle_event(_, _, State, Data) -> <funcs> <func> - <name name="call" arity="2"/> - <name name="call" arity="3"/> + <name name="call" arity="2" since="OTP 19.0"/> + <name name="call" arity="3" since="OTP 19.0"/> <fsummary>Make a synchronous call to a <c>gen_statem</c>.</fsummary> <desc> <p> @@ -1474,7 +1474,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="cast" arity="2"/> + <name name="cast" arity="2" since="OTP 19.0"/> <fsummary>Send an asynchronous event to a <c>gen_statem</c>.</fsummary> <desc> <p> @@ -1493,7 +1493,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="enter_loop" arity="4"/> + <name name="enter_loop" arity="4" since="OTP 19.1"/> <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary> <desc> <p> @@ -1507,7 +1507,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="enter_loop" arity="5"/> + <name name="enter_loop" arity="5" since="OTP 19.0"/> <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary> <desc> <p> @@ -1531,7 +1531,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="enter_loop" arity="6"/> + <name name="enter_loop" arity="6" since="OTP 19.0"/> <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary> <desc> <p> @@ -1588,8 +1588,8 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="reply" arity="1"/> - <name name="reply" arity="2"/> + <name name="reply" arity="1" since="OTP 19.0"/> + <name name="reply" arity="2" since="OTP 19.0"/> <fsummary>Reply to a caller.</fsummary> <desc> <p> @@ -1621,8 +1621,8 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="start" arity="3"/> - <name name="start" arity="4"/> + <name name="start" arity="3" since="OTP 19.0"/> + <name name="start" arity="4" since="OTP 19.0"/> <fsummary>Create a standalone <c>gen_statem</c> process.</fsummary> <desc> <p> @@ -1642,8 +1642,8 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="start_link" arity="3"/> - <name name="start_link" arity="4"/> + <name name="start_link" arity="3" since="OTP 19.0"/> + <name name="start_link" arity="4" since="OTP 19.0"/> <fsummary>Create a linked <c>gen_statem</c> process.</fsummary> <desc> <p> @@ -1750,7 +1750,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="stop" arity="1"/> + <name name="stop" arity="1" since="OTP 19.0"/> <fsummary>Synchronously stop a generic server.</fsummary> <desc> <p> @@ -1761,7 +1761,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name name="stop" arity="3"/> + <name name="stop" arity="3" since="OTP 19.0"/> <fsummary>Synchronously stop a generic server.</fsummary> <desc> <p> @@ -1807,7 +1807,7 @@ handle_event(_, _, State, Data) -> <funcs> <func> - <name>Module:callback_mode() -> CallbackMode</name> + <name since="OTP 19.1">Module:callback_mode() -> CallbackMode</name> <fsummary>Update the internal state during upgrade/downgrade.</fsummary> <type> <v> @@ -1858,7 +1858,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name>Module:code_change(OldVsn, OldState, OldData, Extra) -> + <name since="OTP 19.0">Module:code_change(OldVsn, OldState, OldData, Extra) -> Result </name> <fsummary>Update the internal state during upgrade/downgrade.</fsummary> @@ -1947,7 +1947,7 @@ handle_event(_, _, State, Data) -> </func> <func> - <name>Module:init(Args) -> Result(StateType)</name> + <name since="OTP 19.0">Module:init(Args) -> Result(StateType)</name> <fsummary> Initializing process and internal state. </fsummary> @@ -1989,7 +1989,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre> </func> <func> - <name>Module:format_status(Opt, [PDict,State,Data]) -> + <name since="OTP 19.0">Module:format_status(Opt, [PDict,State,Data]) -> Status </name> <fsummary>Optional function for providing a term describing the @@ -2088,16 +2088,16 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre> </func> <func> - <name>Module:StateName(enter, OldState, Data) -> + <name since="OTP 19.0">Module:StateName(enter, OldState, Data) -> StateEnterResult(StateName) </name> - <name>Module:StateName(EventType, EventContent, Data) -> + <name since="OTP 19.0">Module:StateName(EventType, EventContent, Data) -> StateFunctionResult </name> - <name>Module:handle_event(enter, OldState, State, Data) -> + <name since="OTP 19.0">Module:handle_event(enter, OldState, State, Data) -> StateEnterResult(State) </name> - <name>Module:handle_event(EventType, EventContent, State, Data) -> + <name since="OTP 19.0">Module:handle_event(EventType, EventContent, State, Data) -> HandleEventResult </name> <fsummary>Handle an event.</fsummary> @@ -2216,7 +2216,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre> </func> <func> - <name>Module:terminate(Reason, State, Data) -> Ignored</name> + <name since="OTP 19.0">Module:terminate(Reason, State, Data) -> Ignored</name> <fsummary>Clean up before termination.</fsummary> <type> <v>Reason = normal | shutdown | {shutdown,term()} | term()</v> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index d4a2713840..d69e808586 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>io</module> + <module since="">io</module> <modulesummary>Standard I/O server interface functions.</modulesummary> <description> <p>This module provides an interface to standard Erlang I/O servers. @@ -104,8 +104,8 @@ <funcs> <func> - <name name="columns" arity="0"/> - <name name="columns" arity="1"/> + <name name="columns" arity="0" since=""/> + <name name="columns" arity="1" since=""/> <fsummary>Get the number of columns of an I/O device.</fsummary> <desc> <p>Retrieves the number of columns of the @@ -116,12 +116,12 @@ </func> <func> - <name name="format" arity="1"/> - <name name="format" arity="2"/> - <name name="format" arity="3"/> - <name name="fwrite" arity="1"/> - <name name="fwrite" arity="2"/> - <name name="fwrite" arity="3"/> + <name name="format" arity="1" since=""/> + <name name="format" arity="2" since=""/> + <name name="format" arity="3" since=""/> + <name name="fwrite" arity="1" since=""/> + <name name="fwrite" arity="2" since=""/> + <name name="fwrite" arity="3" since=""/> <fsummary>Write formatted output.</fsummary> <desc> <p>Writes the items in <c><anno>Data</anno></c> (<c>[]</c>) on the @@ -523,8 +523,8 @@ ok </func> <func> - <name name="fread" arity="2"/> - <name name="fread" arity="3"/> + <name name="fread" arity="2" since=""/> + <name name="fread" arity="3" since=""/> <fsummary>Read formatted input.</fsummary> <type name="server_no_data"/> <desc> @@ -690,8 +690,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </func> <func> - <name name="get_chars" arity="2"/> - <name name="get_chars" arity="3"/> + <name name="get_chars" arity="2" since=""/> + <name name="get_chars" arity="3" since=""/> <fsummary>Read a specified number of characters.</fsummary> <type name="server_no_data"/> <desc> @@ -722,8 +722,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </func> <func> - <name name="get_line" arity="1"/> - <name name="get_line" arity="2"/> + <name name="get_line" arity="1" since=""/> + <name name="get_line" arity="2" since=""/> <fsummary>Read a line.</fsummary> <type name="server_no_data"/> <desc> @@ -754,8 +754,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </func> <func> - <name name="getopts" arity="0"/> - <name name="getopts" arity="1"/> + <name name="getopts" arity="0" since=""/> + <name name="getopts" arity="1" since=""/> <fsummary>Get the supported options and values from an I/O server. </fsummary> <desc> @@ -781,8 +781,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </func> <func> - <name name="nl" arity="0"/> - <name name="nl" arity="1"/> + <name name="nl" arity="0" since=""/> + <name name="nl" arity="1" since=""/> <fsummary>Write a newline.</fsummary> <desc> <p>Writes new line to the standard output @@ -791,10 +791,10 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </func> <func> - <name name="parse_erl_exprs" arity="1"/> - <name name="parse_erl_exprs" arity="2"/> - <name name="parse_erl_exprs" arity="3"/> - <name name="parse_erl_exprs" arity="4"/> + <name name="parse_erl_exprs" arity="1" since=""/> + <name name="parse_erl_exprs" arity="2" since=""/> + <name name="parse_erl_exprs" arity="3" since=""/> + <name name="parse_erl_exprs" arity="4" since="OTP R16B"/> <fsummary>Read, tokenize, and parse Erlang expressions.</fsummary> <type name="parse_ret"/> <type name="server_no_data"/> @@ -844,10 +844,10 @@ enter><input>abc("hey".</input> </func> <func> - <name name="parse_erl_form" arity="1"/> - <name name="parse_erl_form" arity="2"/> - <name name="parse_erl_form" arity="3"/> - <name name="parse_erl_form" arity="4"/> + <name name="parse_erl_form" arity="1" since=""/> + <name name="parse_erl_form" arity="2" since=""/> + <name name="parse_erl_form" arity="3" since=""/> + <name name="parse_erl_form" arity="4" since="OTP R16B"/> <fsummary>Read, tokenize, and parse an Erlang form.</fsummary> <type name="parse_form_ret"/> <type name="server_no_data"/> @@ -888,7 +888,7 @@ enter><input>abc("hey".</input> </func> <func> - <name name="printable_range" arity="0"/> + <name name="printable_range" arity="0" since="OTP R16B"/> <fsummary>Get user-requested printable character range.</fsummary> <desc> <p>Returns the user-requested range of printable Unicode characters.</p> @@ -918,8 +918,8 @@ enter><input>abc("hey".</input> </func> <func> - <name name="put_chars" arity="1"/> - <name name="put_chars" arity="2"/> + <name name="put_chars" arity="1" since=""/> + <name name="put_chars" arity="2" since=""/> <fsummary>Write a list of characters.</fsummary> <desc> <p>Writes the characters of <c><anno>CharData</anno></c> to the I/O @@ -928,8 +928,8 @@ enter><input>abc("hey".</input> </func> <func> - <name name="read" arity="1"/> - <name name="read" arity="2"/> + <name name="read" arity="1" since=""/> + <name name="read" arity="2" since=""/> <fsummary>Read a term.</fsummary> <type name="server_no_data"/> <desc> @@ -960,8 +960,8 @@ enter><input>abc("hey".</input> </func> <func> - <name name="read" arity="3"/> - <name name="read" arity="4"/> + <name name="read" arity="3" since=""/> + <name name="read" arity="4" since="OTP R16B"/> <fsummary>Read a term.</fsummary> <type name="server_no_data"/> <desc> @@ -997,8 +997,8 @@ enter><input>abc("hey".</input> </func> <func> - <name name="rows" arity="0"/> - <name name="rows" arity="1"/> + <name name="rows" arity="0" since=""/> + <name name="rows" arity="1" since=""/> <fsummary>Get the number of rows of an I/O device.</fsummary> <desc> <p>Retrieves the number of rows of <c><anno>IoDevice</anno></c> @@ -1009,10 +1009,10 @@ enter><input>abc("hey".</input> </func> <func> - <name name="scan_erl_exprs" arity="1"/> - <name name="scan_erl_exprs" arity="2"/> - <name name="scan_erl_exprs" arity="3"/> - <name name="scan_erl_exprs" arity="4"/> + <name name="scan_erl_exprs" arity="1" since=""/> + <name name="scan_erl_exprs" arity="2" since=""/> + <name name="scan_erl_exprs" arity="3" since=""/> + <name name="scan_erl_exprs" arity="4" since="OTP R16B"/> <fsummary>Read and tokenize Erlang expressions.</fsummary> <type name="server_no_data"/> <desc> @@ -1060,10 +1060,10 @@ enter><input>1.0er.</input> </func> <func> - <name name="scan_erl_form" arity="1"/> - <name name="scan_erl_form" arity="2"/> - <name name="scan_erl_form" arity="3"/> - <name name="scan_erl_form" arity="4"/> + <name name="scan_erl_form" arity="1" since=""/> + <name name="scan_erl_form" arity="2" since=""/> + <name name="scan_erl_form" arity="3" since=""/> + <name name="scan_erl_form" arity="4" since="OTP R16B"/> <fsummary>Read and tokenize an Erlang form.</fsummary> <type name="server_no_data"/> <desc> @@ -1083,8 +1083,8 @@ enter><input>1.0er.</input> </func> <func> - <name name="setopts" arity="1"/> - <name name="setopts" arity="2"/> + <name name="setopts" arity="1" since=""/> + <name name="setopts" arity="2" since=""/> <fsummary>Set options.</fsummary> <desc> <p>Set options for the standard I/O device @@ -1198,8 +1198,8 @@ fun("") -> {yes, "quit", []}; </func> <func> - <name name="write" arity="1"/> - <name name="write" arity="2"/> + <name name="write" arity="1" since=""/> + <name name="write" arity="2" since=""/> <fsummary>Write a term.</fsummary> <desc> <p>Writes term <c><anno>Term</anno></c> to the standard output diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index a3df2897ac..4d527f8ed3 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>io_lib</module> + <module since="">io_lib</module> <modulesummary>I/O library functions.</modulesummary> <description> <p>This module contains functions for converting to and from @@ -99,7 +99,7 @@ <funcs> <func> - <name name="build_text" arity="1"/> + <name name="build_text" arity="1" since="OTP 18.0"/> <fsummary>Build the output text for a preparsed format list.</fsummary> <desc> <p>For details, see @@ -108,7 +108,7 @@ </func> <func> - <name name="char_list" arity="1"/> + <name name="char_list" arity="1" since=""/> <fsummary>Test for a list of characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of @@ -117,7 +117,7 @@ </func> <func> - <name name="deep_char_list" arity="1"/> + <name name="deep_char_list" arity="1" since=""/> <fsummary>Test for a deep list of characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, @@ -126,7 +126,7 @@ </func> <func> - <name name="deep_latin1_char_list" arity="1"/> + <name name="deep_latin1_char_list" arity="1" since="OTP R16B"/> <fsummary>Test for a deep list of characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, @@ -136,8 +136,8 @@ </func> <func> - <name name="format" arity="2"/> - <name name="fwrite" arity="2"/> + <name name="format" arity="2" since=""/> + <name name="fwrite" arity="2" since=""/> <fsummary>Write formatted output.</fsummary> <desc> <p>Returns a character list that represents <c><anno>Data</anno></c> @@ -156,8 +156,8 @@ </func> <func> - <name name="format" arity="3"/> - <name name="fwrite" arity="3"/> + <name name="format" arity="3" since="OTP 21.0"/> + <name name="fwrite" arity="3" since="OTP 21.0"/> <fsummary>Write formatted output.</fsummary> <desc> <p>Returns a character list that represents <c><anno>Data</anno></c> @@ -181,7 +181,7 @@ </func> <func> - <name name="fread" arity="2"/> + <name name="fread" arity="2" since=""/> <fsummary>Read formatted input.</fsummary> <desc> <p>Tries to read <c><anno>String</anno></c> in accordance with the @@ -222,7 +222,7 @@ </func> <func> - <name name="fread" arity="3"/> + <name name="fread" arity="3" since=""/> <fsummary>Re-entrant formatted reader</fsummary> <desc> <p>This is the re-entrant formatted reader. The continuation of @@ -268,7 +268,7 @@ </func> <func> - <name name="indentation" arity="2"/> + <name name="indentation" arity="2" since=""/> <fsummary>Indentation after printing string.</fsummary> <desc> <p>Returns the indentation if <c><anno>String</anno></c> has been @@ -277,7 +277,7 @@ </func> <func> - <name name="latin1_char_list" arity="1"/> + <name name="latin1_char_list" arity="1" since="OTP R16B"/> <fsummary>Test for a list of ISO Latin-1 characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of @@ -286,7 +286,7 @@ </func> <func> - <name name="nl" arity="0"/> + <name name="nl" arity="0" since=""/> <fsummary>Write a newline.</fsummary> <desc> <p>Returns a character list that represents a new line character.</p> @@ -294,8 +294,8 @@ </func> <func> - <name name="print" arity="1"/> - <name name="print" arity="4"/> + <name name="print" arity="1" since=""/> + <name name="print" arity="4" since=""/> <fsummary>Pretty print a term.</fsummary> <desc> <p>Returns a list of characters that represents @@ -315,7 +315,7 @@ </func> <func> - <name name="printable_latin1_list" arity="1"/> + <name name="printable_latin1_list" arity="1" since="OTP R16B"/> <fsummary>Test for a list of printable ISO Latin-1 characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of @@ -324,7 +324,7 @@ </func> <func> - <name name="printable_list" arity="1"/> + <name name="printable_list" arity="1" since=""/> <fsummary>Test for a list of printable characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of @@ -338,7 +338,7 @@ </func> <func> - <name name="printable_unicode_list" arity="1"/> + <name name="printable_unicode_list" arity="1" since="OTP R16B"/> <fsummary>Test for a list of printable Unicode characters.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of @@ -347,7 +347,7 @@ </func> <func> - <name name="scan_format" arity="2"/> + <name name="scan_format" arity="2" since="OTP 18.0"/> <fsummary>Parse all control sequences in the format string.</fsummary> <desc> <p>Returns a list corresponding to the specified format string, @@ -373,7 +373,7 @@ </func> <func> - <name name="unscan_format" arity="1"/> + <name name="unscan_format" arity="1" since="OTP 18.0"/> <fsummary>Revert a preparsed format list to a plain character list and a list of arguments.</fsummary> <desc> @@ -383,9 +383,9 @@ </func> <func> - <name name="write" arity="1"/> - <name name="write" arity="2" clause_i="1"/> - <name name="write" arity="2" clause_i="2"/> + <name name="write" arity="1" since=""/> + <name name="write" arity="2" clause_i="1" since=""/> + <name name="write" arity="2" clause_i="2" since="OTP 20.0"/> <fsummary>Write a term.</fsummary> <desc> <p>Returns a character list that represents <c><anno>Term</anno></c>. @@ -411,7 +411,7 @@ </func> <func> - <name name="write_atom" arity="1"/> + <name name="write_atom" arity="1" since=""/> <fsummary>Write an atom.</fsummary> <desc> <p>Returns the list of characters needed to print atom @@ -420,7 +420,7 @@ </func> <func> - <name name="write_atom_as_latin1" arity="1"/> + <name name="write_atom_as_latin1" arity="1" since="OTP 20.0"/> <fsummary>Write an atom.</fsummary> <desc> <p>Returns the list of characters needed to print atom @@ -430,7 +430,7 @@ </func> <func> - <name name="write_char" arity="1"/> + <name name="write_char" arity="1" since=""/> <fsummary>Write a character.</fsummary> <desc> <p>Returns the list of characters needed to print a character @@ -439,7 +439,7 @@ </func> <func> - <name name="write_char_as_latin1" arity="1"/> + <name name="write_char_as_latin1" arity="1" since="OTP R16B"/> <fsummary>Write a character.</fsummary> <desc> <p>Returns the list of characters needed to print a character @@ -449,7 +449,7 @@ </func> <func> - <name name="write_latin1_char" arity="1"/> + <name name="write_latin1_char" arity="1" since="OTP R16B"/> <fsummary>Write an ISO Latin-1 character.</fsummary> <desc> <p>Returns the list of characters needed to print a character @@ -458,7 +458,7 @@ </func> <func> - <name name="write_latin1_string" arity="1"/> + <name name="write_latin1_string" arity="1" since="OTP R16B"/> <fsummary>Write an ISO Latin-1 string.</fsummary> <desc> <p>Returns the list of characters needed to print @@ -467,7 +467,7 @@ </func> <func> - <name name="write_string" arity="1"/> + <name name="write_string" arity="1" since=""/> <fsummary>Write a string.</fsummary> <desc> <p>Returns the list of characters needed to print @@ -476,7 +476,7 @@ </func> <func> - <name name="write_string_as_latin1" arity="1"/> + <name name="write_string_as_latin1" arity="1" since="OTP R16B"/> <fsummary>Write a string.</fsummary> <desc> <p>Returns the list of characters needed to print diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index e4215a5336..2755fb3dce 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2018</year> + <year>1996</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,7 +28,7 @@ <date>1996-09-28</date> <rev>A</rev> </header> - <module>lists</module> + <module since="">lists</module> <modulesummary>List processing functions.</modulesummary> <description> <p>This module contains functions for list processing.</p> @@ -63,7 +63,7 @@ <funcs> <func> - <name name="all" arity="2"/> + <name name="all" arity="2" since=""/> <fsummary>Return <c>true</c> if all elements in a list satisfy <c>Pred</c>.</fsummary> <desc> @@ -74,7 +74,7 @@ </func> <func> - <name name="any" arity="2"/> + <name name="any" arity="2" since=""/> <fsummary>Return <c>true</c> if any of the elements in a list satisfies <c>Pred</c>.</fsummary> <desc> @@ -85,7 +85,7 @@ </func> <func> - <name name="append" arity="1"/> + <name name="append" arity="1" since=""/> <fsummary>Append a list of lists.</fsummary> <desc> <p>Returns a list in which all the sublists of @@ -98,7 +98,7 @@ </func> <func> - <name name="append" arity="2"/> + <name name="append" arity="2" since=""/> <fsummary>Append two lists.</fsummary> <desc> <p>Returns a new list <c><anno>List3</anno></c>, which is made from @@ -113,7 +113,7 @@ </func> <func> - <name name="concat" arity="1"/> + <name name="concat" arity="1" since=""/> <fsummary>Concatenate a list of atoms.</fsummary> <desc> <p>Concatenates the text representation of the elements of @@ -127,7 +127,7 @@ </func> <func> - <name name="delete" arity="2"/> + <name name="delete" arity="2" since=""/> <fsummary>Delete an element from a list.</fsummary> <desc> <p>Returns a copy of <c><anno>List1</anno></c> where the first element @@ -137,7 +137,7 @@ </func> <func> - <name name="droplast" arity="1"/> + <name name="droplast" arity="1" since="OTP 17.0"/> <fsummary>Drop the last element of a list.</fsummary> <desc> <p>Drops the last element of a <c><anno>List</anno></c>. The list is to @@ -147,7 +147,7 @@ </func> <func> - <name name="dropwhile" arity="2"/> + <name name="dropwhile" arity="2" since=""/> <fsummary>Drop elements from a list while a predicate is <c>true</c>. </fsummary> <desc> @@ -159,7 +159,7 @@ </func> <func> - <name name="duplicate" arity="2"/> + <name name="duplicate" arity="2" since=""/> <fsummary>Make <c>N</c> copies of element.</fsummary> <desc> <p>Returns a list containing <c><anno>N</anno></c> copies of term @@ -172,7 +172,7 @@ </func> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Select elements that satisfy a predicate.</fsummary> <desc> <p><c><anno>List2</anno></c> is a list of all elements @@ -182,7 +182,7 @@ </func> <func> - <name name="filtermap" arity="2"/> + <name name="filtermap" arity="2" since="OTP R16B01"/> <fsummary>Filter and map elements that satisfy a function.</fsummary> <desc> <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>)</c> on successive @@ -211,7 +211,7 @@ filtermap(Fun, List1) -> </func> <func> - <name name="flatlength" arity="1"/> + <name name="flatlength" arity="1" since=""/> <fsummary>Length of flattened deep list.</fsummary> <desc> <p>Equivalent to <c>length(flatten(<anno>DeepList</anno>))</c>, but @@ -220,7 +220,7 @@ filtermap(Fun, List1) -> </func> <func> - <name name="flatmap" arity="2"/> + <name name="flatmap" arity="2" since=""/> <fsummary>Map and flatten in one pass.</fsummary> <desc> <p>Takes a function from <c><anno>A</anno></c>s to lists of @@ -241,7 +241,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="flatten" arity="1"/> + <name name="flatten" arity="1" since=""/> <fsummary>Flatten a deep list.</fsummary> <desc> <p>Returns a flattened version of <c><anno>DeepList</anno></c>.</p> @@ -249,7 +249,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="flatten" arity="2"/> + <name name="flatten" arity="2" since=""/> <fsummary>Flatten a deep list.</fsummary> <desc> <p>Returns a flattened version of <c><anno>DeepList</anno></c> with tail @@ -258,7 +258,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="foldl" arity="3"/> + <name name="foldl" arity="3" since=""/> <fsummary>Fold a function over a list.</fsummary> <desc> <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>, <anno>AccIn</anno>)</c> @@ -278,7 +278,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="foldr" arity="3"/> + <name name="foldr" arity="3" since=""/> <fsummary>Fold a function over a list.</fsummary> <desc> <p>Like <seealso marker="#foldl/3"><c>foldl/3</c></seealso>, but the @@ -297,7 +297,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="join" arity="2"/> + <name name="join" arity="2" since="OTP 19.0"/> <fsummary>Insert an element between elements in a list</fsummary> <desc> <p>Inserts <c><anno>Sep</anno></c> between each element in <c><anno>List1</anno></c>. Has no @@ -312,7 +312,7 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name name="foreach" arity="2"/> + <name name="foreach" arity="2" since=""/> <fsummary>Apply a function to each element of a list.</fsummary> <desc> <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>)</c> for each element @@ -324,7 +324,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keydelete" arity="3"/> + <name name="keydelete" arity="3" since=""/> <fsummary>Delete an element from a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -336,7 +336,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keyfind" arity="3"/> + <name name="keyfind" arity="3" since=""/> <fsummary>Search for an element in a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -349,7 +349,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keymap" arity="3"/> + <name name="keymap" arity="3" since=""/> <fsummary>Map a function over a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -368,7 +368,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keymember" arity="3"/> + <name name="keymember" arity="3" since=""/> <fsummary>Test for membership of a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -379,7 +379,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keymerge" arity="3"/> + <name name="keymerge" arity="3" since=""/> <fsummary>Merge two key-sorted lists of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -395,7 +395,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keyreplace" arity="4"/> + <name name="keyreplace" arity="4" since=""/> <fsummary>Replace an element in a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -407,7 +407,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keysearch" arity="3"/> + <name name="keysearch" arity="3" since=""/> <fsummary>Search for an element in a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -425,7 +425,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keysort" arity="2"/> + <name name="keysort" arity="2" since=""/> <fsummary>Sort a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -436,7 +436,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keystore" arity="4"/> + <name name="keystore" arity="4" since=""/> <fsummary>Store an element in a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -452,7 +452,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="keytake" arity="3"/> + <name name="keytake" arity="3" since=""/> <fsummary>Extract an element from a list of tuples.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -467,7 +467,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="last" arity="1"/> + <name name="last" arity="1" since=""/> <fsummary>Return last element in a list.</fsummary> <desc> <p>Returns the last element in <c><anno>List</anno></c>.</p> @@ -475,7 +475,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="map" arity="2"/> + <name name="map" arity="2" since=""/> <fsummary>Map a function over a list.</fsummary> <desc> <p>Takes a function from <c><anno>A</anno></c>s to @@ -488,7 +488,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="mapfoldl" arity="3"/> + <name name="mapfoldl" arity="3" since=""/> <fsummary>Map and fold in one pass.</fsummary> <desc> <p>Combines the operations of @@ -504,7 +504,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="mapfoldr" arity="3"/> + <name name="mapfoldr" arity="3" since=""/> <fsummary>Map and fold in one pass.</fsummary> <desc> <p>Combines the operations of @@ -514,7 +514,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="max" arity="1"/> + <name name="max" arity="1" since=""/> <fsummary>Return maximum element of a list.</fsummary> <desc> <p>Returns the first element of <c><anno>List</anno></c> that compares @@ -524,7 +524,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="member" arity="2"/> + <name name="member" arity="2" since=""/> <fsummary>Test for membership of a list.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Elem</anno></c> matches some element @@ -533,7 +533,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="merge" arity="1"/> + <name name="merge" arity="1" since=""/> <fsummary>Merge a list of sorted lists.</fsummary> <desc> <p>Returns the sorted list formed by merging all the sublists of @@ -546,7 +546,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="merge" arity="2"/> + <name name="merge" arity="2" since=""/> <fsummary>Merge two sorted lists.</fsummary> <desc> <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> @@ -559,7 +559,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="merge" arity="3"/> + <name name="merge" arity="3" since=""/> <fsummary>Merge two sorted list.</fsummary> <desc> <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> @@ -577,7 +577,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="merge3" arity="3"/> + <name name="merge3" arity="3" since=""/> <fsummary>Merge three sorted lists.</fsummary> <desc> <p>Returns the sorted list formed by merging <c><anno>List1</anno></c>, @@ -593,7 +593,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="min" arity="1"/> + <name name="min" arity="1" since=""/> <fsummary>Return minimum element of a list.</fsummary> <desc> <p>Returns the first element of <c><anno>List</anno></c> that compares @@ -603,7 +603,7 @@ flatmap(Fun, List1) -> </func> <func> - <name name="nth" arity="2"/> + <name name="nth" arity="2" since=""/> <fsummary>Return the <c>N</c>th element of a list.</fsummary> <type_desc variable="N">1..length(<anno>List</anno>)</type_desc> <desc> @@ -617,7 +617,7 @@ c</pre> </func> <func> - <name name="nthtail" arity="2"/> + <name name="nthtail" arity="2" since=""/> <fsummary>Return the <c>N</c>th tail of a list.</fsummary> <type_desc variable="N">0..length(<anno>List</anno>)</type_desc> <desc> @@ -638,7 +638,7 @@ c</pre> </func> <func> - <name name="partition" arity="2"/> + <name name="partition" arity="2" since=""/> <fsummary>Partition a list into two lists based on a predicate.</fsummary> <desc> <p>Partitions <c><anno>List</anno></c> into two lists, where the first @@ -658,7 +658,7 @@ c</pre> </func> <func> - <name name="prefix" arity="2"/> + <name name="prefix" arity="2" since=""/> <fsummary>Test for list prefix.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>List1</anno></c> is a prefix of @@ -667,7 +667,7 @@ c</pre> </func> <func> - <name name="reverse" arity="1"/> + <name name="reverse" arity="1" since=""/> <fsummary>Reverse a list.</fsummary> <desc> <p>Returns a list with the elements in <c><anno>List1</anno></c> @@ -676,7 +676,7 @@ c</pre> </func> <func> - <name name="reverse" arity="2"/> + <name name="reverse" arity="2" since=""/> <fsummary>Reverse a list appending a tail.</fsummary> <desc> <p>Returns a list with the elements in <c><anno>List1</anno></c> @@ -689,8 +689,20 @@ c</pre> </func> <func> - <name name="seq" arity="2"/> - <name name="seq" arity="3"/> + <name name="search" arity="2" since="OTP 21.0"/> + <fsummary>Find the first element that satisfies a predicate.</fsummary> + <desc> + <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c> + such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns + <c>true</c>, returns <c>{value, <anno>Value</anno>}</c> + for the first such <c><anno>Value</anno></c>, + otherwise returns <c>false</c>.</p> + </desc> + </func> + + <func> + <name name="seq" arity="2" since=""/> + <name name="seq" arity="3" since=""/> <fsummary>Generate a sequence of integers.</fsummary> <desc> <p>Returns a sequence of integers that starts with @@ -736,7 +748,7 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code> </func> <func> - <name name="sort" arity="1"/> + <name name="sort" arity="1" since=""/> <fsummary>Sort a list.</fsummary> <desc> <p>Returns a list containing the sorted elements of @@ -745,7 +757,7 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code> </func> <func> - <name name="sort" arity="2"/> + <name name="sort" arity="2" since=""/> <fsummary>Sort a list.</fsummary> <desc> <p>Returns a list containing the sorted elements of @@ -759,7 +771,7 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code> </func> <func> - <name name="split" arity="2"/> + <name name="split" arity="2" since=""/> <fsummary>Split a list into two lists.</fsummary> <type_desc variable="N">0..length(<anno>List1</anno>)</type_desc> <desc> @@ -771,19 +783,7 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code> </func> <func> - <name name="search" arity="2"/> - <fsummary>Find the first element that satisfies a predicate.</fsummary> - <desc> - <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c> - such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns - <c>true</c>, returns <c>{value, <anno>Value</anno>}</c> - for the first such <c><anno>Value</anno></c>, - otherwise returns <c>false</c>.</p> - </desc> - </func> - - <func> - <name name="splitwith" arity="2"/> + <name name="splitwith" arity="2" since=""/> <fsummary>Split a list into two lists based on a predicate.</fsummary> <desc> <p>Partitions <c><anno>List</anno></c> into two lists according to @@ -804,7 +804,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="sublist" arity="2"/> + <name name="sublist" arity="2" since=""/> <fsummary>Return a sublist of a certain length, starting at the first position.</fsummary> <desc> @@ -816,7 +816,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="sublist" arity="3"/> + <name name="sublist" arity="3" since=""/> <fsummary>Return a sublist starting at a specified position and with a specified number of elements.</fsummary> <type_desc variable="Start">1..(length(<anno>List1</anno>)+1)</type_desc> @@ -838,7 +838,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="subtract" arity="2"/> + <name name="subtract" arity="2" since=""/> <fsummary>Subtract the element in one list from another list.</fsummary> <desc> <p>Returns a new list <c><anno>List3</anno></c> that is a copy of @@ -854,7 +854,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="suffix" arity="2"/> + <name name="suffix" arity="2" since=""/> <fsummary>Test for list suffix.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>List1</anno></c> is a suffix of @@ -863,7 +863,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="sum" arity="1"/> + <name name="sum" arity="1" since=""/> <fsummary>Return the sum of elements in a list.</fsummary> <desc> <p>Returns the sum of the elements in <c><anno>List</anno></c>.</p> @@ -871,7 +871,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="takewhile" arity="2"/> + <name name="takewhile" arity="2" since=""/> <fsummary>Take elements from a list while a predicate is <c>true</c>. </fsummary> <desc> @@ -884,7 +884,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="ukeymerge" arity="3"/> + <name name="ukeymerge" arity="3" since=""/> <fsummary>Merge two key-sorted lists of tuples, removing duplicates. </fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> @@ -902,7 +902,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="ukeysort" arity="2"/> + <name name="ukeysort" arity="2" since=""/> <fsummary>Sort a list of tuples, removing duplicates.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -914,7 +914,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="umerge" arity="1"/> + <name name="umerge" arity="1" since=""/> <fsummary>Merge a list of sorted lists, removing duplicates.</fsummary> <desc> <p>Returns the sorted list formed by merging all the sublists @@ -927,7 +927,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="umerge" arity="2"/> + <name name="umerge" arity="2" since=""/> <fsummary>Merge two sorted lists, removing duplicates.</fsummary> <desc> <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> @@ -941,7 +941,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="umerge" arity="3"/> + <name name="umerge" arity="3" since=""/> <fsummary>Merge two sorted lists, removing duplicates.</fsummary> <desc> <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> @@ -958,7 +958,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="umerge3" arity="3"/> + <name name="umerge3" arity="3" since=""/> <fsummary>Merge three sorted lists, removing duplicates.</fsummary> <desc> <p>Returns the sorted list formed by merging <c><anno>List1</anno></c>, @@ -973,7 +973,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="unzip" arity="1"/> + <name name="unzip" arity="1" since=""/> <fsummary>Unzip a list of two-tuples into two lists.</fsummary> <desc> <p>"Unzips" a list of two-tuples into two lists, where the first @@ -983,7 +983,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="unzip3" arity="1"/> + <name name="unzip3" arity="1" since=""/> <fsummary>Unzip a list of three-tuples into three lists.</fsummary> <desc> <p>"Unzips" a list of three-tuples into three lists, where @@ -994,7 +994,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="usort" arity="1"/> + <name name="usort" arity="1" since=""/> <fsummary>Sort a list, removing duplicates.</fsummary> <desc> <p>Returns a list containing the sorted elements of @@ -1004,7 +1004,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="usort" arity="2"/> + <name name="usort" arity="2" since=""/> <fsummary>Sort a list, removing duplicates.</fsummary> <desc> <p>Returns a list containing the sorted elements of @@ -1019,7 +1019,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="zip" arity="2"/> + <name name="zip" arity="2" since=""/> <fsummary>Zip two lists into a list of two-tuples.</fsummary> <desc> <p>"Zips" two lists of equal length into one list of two-tuples, @@ -1030,7 +1030,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="zip3" arity="3"/> + <name name="zip3" arity="3" since=""/> <fsummary>Zip three lists into a list of three-tuples.</fsummary> <desc> <p>"Zips" three lists of equal length into one list of @@ -1042,7 +1042,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="zipwith" arity="3"/> + <name name="zipwith" arity="3" since=""/> <fsummary>Zip two lists into one list according to a fun.</fsummary> <desc> <p>Combines the elements of two lists of equal length into one list. @@ -1059,7 +1059,7 @@ splitwith(Pred, List) -> </func> <func> - <name name="zipwith3" arity="4"/> + <name name="zipwith3" arity="4" since=""/> <fsummary>Zip three lists into one list according to a fun.</fsummary> <desc> <p>Combines the elements of three lists of equal length into one diff --git a/lib/stdlib/doc/src/log_mf_h.xml b/lib/stdlib/doc/src/log_mf_h.xml index edc3d31025..b922006cc0 100644 --- a/lib/stdlib/doc/src/log_mf_h.xml +++ b/lib/stdlib/doc/src/log_mf_h.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>log_mf_h.xml</file> </header> - <module>log_mf_h</module> + <module since="">log_mf_h</module> <modulesummary>An event handler that logs events to disk.</modulesummary> <description> <p>This module is a <c>gen_event</c> handler module that can be installed @@ -60,8 +60,8 @@ <funcs> <func> - <name name="init" arity="3"/> - <name name="init" arity="4"/> + <name name="init" arity="3" since=""/> + <name name="init" arity="4" since=""/> <fsummary>Initiate the event handler.</fsummary> <desc> <p>Initiates the event handler. Returns <c><anno>Args</anno></c>, which diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml index a225dea3b5..e2a62cb397 100644 --- a/lib/stdlib/doc/src/maps.xml +++ b/lib/stdlib/doc/src/maps.xml @@ -27,7 +27,7 @@ <date>2014-02-28</date> <rev>A</rev> </header> - <module>maps</module> + <module since="OTP 17.0">maps</module> <modulesummary>Maps processing functions.</modulesummary> <description> <p>This module contains functions for maps processing.</p> @@ -49,7 +49,7 @@ <funcs> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since="OTP 18.0"/> <fsummary>Select pairs that satisfy a predicate.</fsummary> <desc> <p>Returns a map <c><anno>Map</anno></c> for which predicate @@ -68,7 +68,7 @@ </func> <func> - <name name="find" arity="2"/> + <name name="find" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns a tuple <c>{ok, Value}</c>, where <c><anno>Value</anno></c> @@ -87,7 +87,7 @@ </func> <func> - <name name="fold" arity="3"/> + <name name="fold" arity="3" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Calls <c>F(K, V, AccIn)</c> for every <c><anno>K</anno></c> to value @@ -111,7 +111,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Takes a list of key-value tuples elements and builds a map. The @@ -128,7 +128,7 @@ </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns value <c><anno>Value</anno></c> associated with @@ -147,7 +147,7 @@ </func> <func> - <name name="get" arity="3"/> + <name name="get" arity="3" since="OTP 17.1"/> <fsummary></fsummary> <desc> <p>Returns value <c><anno>Value</anno></c> associated with @@ -168,7 +168,7 @@ val1 </func> <func> - <name name="is_key" arity="2"/> + <name name="is_key" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns <c>true</c> if map <c><anno>Map</anno></c> contains @@ -188,7 +188,7 @@ false</code> </func> <func> - <name name="iterator" arity="1"/> + <name name="iterator" arity="1" since="OTP 21.0"/> <fsummary>Create a map iterator.</fsummary> <desc> <p>Returns a map iterator <c><anno>Iterator</anno></c> that can @@ -202,19 +202,19 @@ false</code> <code type="none"> > M = #{ a => 1, b => 2 }. #{a => 1,b => 2} -> I = maps:iterator(M). -[{a,1},{b,2}] -> {K1, V1, I2} = maps:next(I). -{a,1,[{b,2}]} -> {K2, V2, I3} = maps:next(I2). -{b,2,[]} +> I = maps:iterator(M), ok. +ok +> {K1, V1, I2} = maps:next(I), {K1, V1}. +{a,1} +> {K2, V2, I3} = maps:next(I2),{K2, V2}. +{b,2} > maps:next(I3). none</code> </desc> </func> <func> - <name name="keys" arity="1"/> + <name name="keys" arity="1" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns a complete list of keys, in any order, which resides @@ -230,7 +230,7 @@ none</code> </func> <func> - <name name="map" arity="2"/> + <name name="map" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Produces a new map <c><anno>Map</anno></c> by calling function @@ -253,7 +253,7 @@ none</code> </func> <func> - <name name="merge" arity="2"/> + <name name="merge" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Merges two maps into a single map <c><anno>Map3</anno></c>. If two @@ -271,7 +271,7 @@ none</code> </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns a new empty map.</p> @@ -283,7 +283,7 @@ none</code> </func> <func> - <name name="next" arity="1"/> + <name name="next" arity="1" since="OTP 21.0"/> <fsummary>Get the next key and value from an iterator.</fsummary> <desc> <p>Returns the next key-value association in @@ -298,21 +298,21 @@ none</code> <code type="none"> > Map = #{a => 1, b => 2, c => 3}. #{a => 1,b => 2,c => 3} -> Iter = maps:iterator(Map). -[{a,1},{b,2},{c,3}] -> {_, _, Iter1} = maps:next(Iter). -{a,1,[{b,2},{c,3}]} -> {_, _, Iter2} = maps:next(Iter1). -{b,2,[{c,3}]} -> {_, _, Iter3} = maps:next(Iter2). -{c,3,[]} -> maps:next(Iter3). +> I = maps:iterator(Map), ok. +ok +> {K1, V1, I1} = maps:next(I), {K1, V1}. +{a,1} +> {K2, V2, I2} = maps:next(I1), {K2, V2}. +{b,2} +> {K3, V3, I3} = maps:next(I2), {K3, V3}. +{c,3} +> maps:next(I3). none</code> </desc> </func> <func> - <name name="put" arity="3"/> + <name name="put" arity="3" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Associates <c><anno>Key</anno></c> with value @@ -336,7 +336,7 @@ none</code> </func> <func> - <name name="remove" arity="2"/> + <name name="remove" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Removes the <c><anno>Key</anno></c>, if it exists, and its @@ -356,7 +356,7 @@ none</code> </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns the number of key-value associations in @@ -370,7 +370,7 @@ none</code> </func> <func> - <name name="take" arity="2"/> + <name name="take" arity="2" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>The function removes the <c><anno>Key</anno></c>, if it @@ -395,7 +395,7 @@ error</code> </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns a list of pairs representing the key-value associations of @@ -412,7 +412,7 @@ error</code> </func> <func> - <name name="update" arity="3"/> + <name name="update" arity="3" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>If <c><anno>Key</anno></c> exists in <c><anno>Map1</anno></c>, the @@ -432,7 +432,7 @@ error</code> </func> <func> - <name name="update_with" arity="3"/> + <name name="update_with" arity="3" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Update a value in a <c><anno>Map1</anno></c> associated @@ -451,7 +451,7 @@ error</code> </func> <func> - <name name="update_with" arity="4"/> + <name name="update_with" arity="4" since="OTP 19.0"/> <fsummary></fsummary> <desc> <p>Update a value in a <c><anno>Map1</anno></c> associated @@ -471,7 +471,7 @@ error</code> </func> <func> - <name name="values" arity="1"/> + <name name="values" arity="1" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns a complete list of values, in arbitrary order, contained in @@ -487,7 +487,7 @@ error</code> </func> <func> - <name name="with" arity="2"/> + <name name="with" arity="2" since="OTP 17.3"/> <fsummary></fsummary> <desc> <p>Returns a new map <c><anno>Map2</anno></c> with the keys <c>K1</c> @@ -504,7 +504,7 @@ error</code> </func> <func> - <name name="without" arity="2"/> + <name name="without" arity="2" since="OTP 17.0"/> <fsummary></fsummary> <desc> <p>Returns a new map <c><anno>Map2</anno></c> without keys <c>K1</c> diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml index b4f096217a..d89310e2c8 100644 --- a/lib/stdlib/doc/src/math.xml +++ b/lib/stdlib/doc/src/math.xml @@ -34,7 +34,7 @@ <rev>B</rev> <file>math.xml</file> </header> - <module>math</module> + <module since="">math</module> <modulesummary>Mathematical functions.</modulesummary> <description> <p>This module provides an interface to a number of mathematical @@ -50,28 +50,28 @@ <funcs> <func> - <name name="acos" arity="1"/> - <name name="acosh" arity="1"/> - <name name="asin" arity="1"/> - <name name="asinh" arity="1"/> - <name name="atan" arity="1"/> - <name name="atan2" arity="2"/> - <name name="atanh" arity="1"/> - <name name="ceil" arity="1"/> - <name name="cos" arity="1"/> - <name name="cosh" arity="1"/> - <name name="exp" arity="1"/> - <name name="floor" arity="1"/> - <name name="fmod" arity="2"/> - <name name="log" arity="1"/> - <name name="log10" arity="1"/> - <name name="log2" arity="1"/> - <name name="pow" arity="2"/> - <name name="sin" arity="1"/> - <name name="sinh" arity="1"/> - <name name="sqrt" arity="1"/> - <name name="tan" arity="1"/> - <name name="tanh" arity="1"/> + <name name="acos" arity="1" since=""/> + <name name="acosh" arity="1" since=""/> + <name name="asin" arity="1" since=""/> + <name name="asinh" arity="1" since=""/> + <name name="atan" arity="1" since=""/> + <name name="atan2" arity="2" since=""/> + <name name="atanh" arity="1" since=""/> + <name name="ceil" arity="1" since="OTP 20.0"/> + <name name="cos" arity="1" since=""/> + <name name="cosh" arity="1" since=""/> + <name name="exp" arity="1" since=""/> + <name name="floor" arity="1" since="OTP 20.0"/> + <name name="fmod" arity="2" since="OTP 20.0"/> + <name name="log" arity="1" since=""/> + <name name="log10" arity="1" since=""/> + <name name="log2" arity="1" since="OTP 18.0"/> + <name name="pow" arity="2" since=""/> + <name name="sin" arity="1" since=""/> + <name name="sinh" arity="1" since=""/> + <name name="sqrt" arity="1" since=""/> + <name name="tan" arity="1" since=""/> + <name name="tanh" arity="1" since=""/> <fsummary>Diverse math functions.</fsummary> <type variable="X" name_i="6"/> <type variable="Y" name_i="6"/> @@ -82,7 +82,7 @@ </func> <func> - <name name="erf" arity="1"/> + <name name="erf" arity="1" since=""/> <fsummary>Error function.</fsummary> <desc> <p>Returns the error function of <c><anno>X</anno></c>, where:</p> @@ -92,7 +92,7 @@ erf(X) = 2/sqrt(pi)*integral from 0 to X of exp(-t*t) dt.</pre> </func> <func> - <name name="erfc" arity="1"/> + <name name="erfc" arity="1" since=""/> <fsummary>Another error function.</fsummary> <desc> <p><c>erfc(X)</c> returns <c>1.0</c> - <c>erf(X)</c>, computed by @@ -101,7 +101,7 @@ erf(X) = 2/sqrt(pi)*integral from 0 to X of exp(-t*t) dt.</pre> </func> <func> - <name name="pi" arity="0"/> + <name name="pi" arity="0" since=""/> <fsummary>A useful number.</fsummary> <desc> <p>A useful number.</p> diff --git a/lib/stdlib/doc/src/ms_transform.xml b/lib/stdlib/doc/src/ms_transform.xml index 0a05fa37c5..65cc150507 100644 --- a/lib/stdlib/doc/src/ms_transform.xml +++ b/lib/stdlib/doc/src/ms_transform.xml @@ -32,7 +32,7 @@ <rev>C</rev> <file>ms_transform.xml</file> </header> - <module>ms_transform</module> + <module since="">ms_transform</module> <modulesummary>A parse transformation that translates fun syntax into match specifications.</modulesummary> <description> @@ -731,7 +731,7 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> <funcs> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Error formatting function as required by the parse transformation interface.</fsummary> <desc> <p>Takes an error code returned by one of the other functions @@ -741,7 +741,7 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> </func> <func> - <name name="parse_transform" arity="2"/> + <name name="parse_transform" arity="2" since=""/> <fsummary>Transforms Erlang abstract format containing calls to ets/dbg:fun2ms/1 into literal match specifications.</fsummary> <type_desc variable="Options">Option list, required but not used. @@ -762,7 +762,7 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> </func> <func> - <name name="transform_from_shell" arity="3"/> + <name name="transform_from_shell" arity="3" since=""/> <fsummary>Used when transforming funs created in the shell into match_specifications.</fsummary> <type_desc variable="BoundEnvironment">List of variable bindings in the diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 7ba19a98ea..993945b9c7 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 3.7.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Optimize pretty printing of terms. The slower + behaviour was introduced in Erlang/OTP 20. </p> + <p> + Own Id: OTP-15573 Aux Id: ERIERL-306 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml index 26bbf499c6..27ccccee7e 100644 --- a/lib/stdlib/doc/src/orddict.xml +++ b/lib/stdlib/doc/src/orddict.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>orddict.xml</file> </header> - <module>orddict</module> + <module since="">orddict</module> <modulesummary>Key-value dictionary as ordered list.</modulesummary> <description> <p>This module provides a <c>Key</c>-<c>Value</c> dictionary. @@ -61,7 +61,7 @@ <funcs> <func> - <name name="append" arity="3"/> + <name name="append" arity="3" since=""/> <fsummary>Append a value to keys in a dictionary.</fsummary> <desc> <p>Appends a new <c><anno>Value</anno></c> to the current list @@ -73,7 +73,7 @@ </func> <func> - <name name="append_list" arity="3"/> + <name name="append_list" arity="3" since=""/> <fsummary>Append new values to keys in a dictionary.</fsummary> <desc> <p>Appends a list of values <c><anno>ValList</anno></c> to @@ -85,7 +85,7 @@ </func> <func> - <name name="erase" arity="2"/> + <name name="erase" arity="2" since=""/> <fsummary>Erase a key from a dictionary.</fsummary> <desc> <p>Erases all items with a specified key from a dictionary.</p> @@ -93,7 +93,7 @@ </func> <func> - <name name="fetch" arity="2"/> + <name name="fetch" arity="2" since=""/> <fsummary>Look up values in a dictionary.</fsummary> <desc> <p>Returns the value associated with <c><anno>Key</anno></c> @@ -105,7 +105,7 @@ </func> <func> - <name name="fetch_keys" arity="1"/> + <name name="fetch_keys" arity="1" since=""/> <fsummary>Return all keys in a dictionary.</fsummary> <desc> <p>Returns a list of all keys in a dictionary.</p> @@ -113,7 +113,7 @@ </func> <func> - <name name="take" arity="2"/> + <name name="take" arity="2" since="OTP 20.0"/> <fsummary>Return value and new dictionary without element with this value.</fsummary> <desc> <p>This function returns value from dictionary and new dictionary without this value. @@ -122,7 +122,7 @@ </func> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Select elements that satisfy a predicate.</fsummary> <desc> <p><c><anno>Orddict2</anno></c> is a dictionary of all keys and values @@ -133,7 +133,7 @@ </func> <func> - <name name="find" arity="2"/> + <name name="find" arity="2" since=""/> <fsummary>Search for a key in a dictionary.</fsummary> <desc> <p>Searches for a key in a dictionary. Returns @@ -145,7 +145,7 @@ </func> <func> - <name name="fold" arity="3"/> + <name name="fold" arity="3" since=""/> <fsummary>Fold a function over a dictionary.</fsummary> <desc> <p>Calls <c><anno>Fun</anno></c> on successive keys and values of @@ -157,7 +157,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Convert a list of pairs to a dictionary.</fsummary> <desc> <p>Converts the <c><anno>Key</anno></c>-<c><anno>Value</anno></c> list @@ -166,7 +166,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since="OTP 17.0"/> <fsummary>Return true if the dictionary is empty.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Orddict</anno></c> has no elements, @@ -175,7 +175,7 @@ </func> <func> - <name name="is_key" arity="2"/> + <name name="is_key" arity="2" since=""/> <fsummary>Test if a key is in a dictionary.</fsummary> <desc> <p>Tests if <c><anno>Key</anno></c> is contained in @@ -184,7 +184,7 @@ </func> <func> - <name name="map" arity="2"/> + <name name="map" arity="2" since=""/> <fsummary>Map a function over a dictionary.</fsummary> <desc> <p>Calls <c><anno>Fun</anno></c> on successive keys and values of @@ -193,7 +193,7 @@ </func> <func> - <name name="merge" arity="3"/> + <name name="merge" arity="3" since=""/> <fsummary>Merge two dictionaries.</fsummary> <desc> <p>Merges two dictionaries, <c><anno>Orddict1</anno></c> and @@ -212,7 +212,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Create a dictionary.</fsummary> <desc> <p>Creates a new dictionary.</p> @@ -220,7 +220,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Return the number of elements in an ordered dictionary. </fsummary> <desc> @@ -229,7 +229,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="store" arity="3"/> + <name name="store" arity="3" since=""/> <fsummary>Store a value in a dictionary.</fsummary> <desc> <p>Stores a <c><anno>Key</anno></c>-<c><anno>Value</anno></c> pair in a @@ -240,7 +240,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert a dictionary to a list of pairs.</fsummary> <desc> <p>Converts a dictionary to a list representation.</p> @@ -248,7 +248,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="update" arity="3"/> + <name name="update" arity="3" since=""/> <fsummary>Update a value in a dictionary.</fsummary> <desc> <p>Updates a value in a dictionary by calling <c><anno>Fun</anno></c> @@ -258,7 +258,7 @@ merge(Fun, D1, D2) -> </func> <func> - <name name="update" arity="4"/> + <name name="update" arity="4" since=""/> <fsummary>Update a value in a dictionary.</fsummary> <desc> <p>Updates a value in a dictionary by calling <c><anno>Fun</anno></c> @@ -273,7 +273,7 @@ append(Key, Val, D) -> </func> <func> - <name name="update_counter" arity="3"/> + <name name="update_counter" arity="3" since=""/> <fsummary>Increment a value in a dictionary.</fsummary> <desc> <p>Adds <c><anno>Increment</anno></c> to the value associated with diff --git a/lib/stdlib/doc/src/ordsets.xml b/lib/stdlib/doc/src/ordsets.xml index 11f98c8fb7..fbe334c009 100644 --- a/lib/stdlib/doc/src/ordsets.xml +++ b/lib/stdlib/doc/src/ordsets.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>ordsets.xml</file> </header> - <module>ordsets</module> + <module since="">ordsets</module> <modulesummary>Functions for manipulating sets as ordered lists. </modulesummary> <description> @@ -60,7 +60,7 @@ <funcs> <func> - <name name="add_element" arity="2"/> + <name name="add_element" arity="2" since=""/> <fsummary>Add an element to an <c>Ordset</c>.</fsummary> <desc> <p>Returns a new ordered set formed from <c><anno>Ordset1</anno></c> @@ -69,7 +69,7 @@ </func> <func> - <name name="del_element" arity="2"/> + <name name="del_element" arity="2" since=""/> <fsummary>Remove an element from an <c>Ordset</c>.</fsummary> <desc> <p>Returns <c><anno>Ordset1</anno></c>, but with @@ -78,7 +78,7 @@ </func> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Filter set elements.</fsummary> <desc> <p>Filters elements in <c><anno>Ordset1</anno></c> with boolean function @@ -87,7 +87,7 @@ </func> <func> - <name name="fold" arity="3"/> + <name name="fold" arity="3" since=""/> <fsummary>Fold over set elements.</fsummary> <desc> <p>Folds <c><anno>Function</anno></c> over every element in @@ -97,7 +97,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Convert a list into an <c>Ordset</c>.</fsummary> <desc> <p>Returns an ordered set of the elements in <c><anno>List</anno></c>. @@ -106,7 +106,7 @@ </func> <func> - <name name="intersection" arity="1"/> + <name name="intersection" arity="1" since=""/> <fsummary>Return the intersection of a list of <c>Ordsets</c></fsummary> <desc> <p>Returns the intersection of the non-empty list of sets.</p> @@ -114,7 +114,7 @@ </func> <func> - <name name="intersection" arity="2"/> + <name name="intersection" arity="2" since=""/> <fsummary>Return the intersection of two <c>Ordsets</c>.</fsummary> <desc> <p>Returns the intersection of <c><anno>Ordset1</anno></c> and @@ -123,7 +123,7 @@ </func> <func> - <name name="is_disjoint" arity="2"/> + <name name="is_disjoint" arity="2" since=""/> <fsummary>Check whether two <c>Ordsets</c> are disjoint.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Ordset1</anno></c> and @@ -133,7 +133,7 @@ </func> <func> - <name name="is_element" arity="2"/> + <name name="is_element" arity="2" since=""/> <fsummary>Test for membership of an <c>Ordset</c>.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of @@ -142,7 +142,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since="OTP 21.0"/> <fsummary>Test for empty set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Ordset</anno></c> is an empty set, @@ -151,7 +151,7 @@ </func> <func> - <name name="is_set" arity="1"/> + <name name="is_set" arity="1" since=""/> <fsummary>Test for an <c>Ordset</c>.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Ordset</anno></c> is an ordered set @@ -160,7 +160,7 @@ </func> <func> - <name name="is_subset" arity="2"/> + <name name="is_subset" arity="2" since=""/> <fsummary>Test for subset.</fsummary> <desc> <p>Returns <c>true</c> when every element of <c><anno>Ordset1</anno></c> @@ -170,7 +170,7 @@ </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Return an empty set.</fsummary> <desc> <p>Returns a new empty ordered set.</p> @@ -178,7 +178,7 @@ </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Return the number of elements in a set.</fsummary> <desc> <p>Returns the number of elements in <c><anno>Ordset</anno></c>.</p> @@ -186,7 +186,7 @@ </func> <func> - <name name="subtract" arity="2"/> + <name name="subtract" arity="2" since=""/> <fsummary>Return the difference of two <c>Ordsets</c>.</fsummary> <desc> <p>Returns only the elements of <c><anno>Ordset1</anno></c> that are not @@ -195,7 +195,7 @@ </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert an <c>Ordset</c> into a list.</fsummary> <desc> <p>Returns the elements of <c><anno>Ordset</anno></c> as a list.</p> @@ -203,7 +203,7 @@ </func> <func> - <name name="union" arity="1"/> + <name name="union" arity="1" since=""/> <fsummary>Return the union of a list of <c>Ordsets</c>.</fsummary> <desc> <p>Returns the merged (union) set of the list of sets.</p> @@ -211,7 +211,7 @@ </func> <func> - <name name="union" arity="2"/> + <name name="union" arity="2" since=""/> <fsummary>Return the union of two <c>Ordsets</c>.</fsummary> <desc> <p>Returns the merged (union) set of <c><anno>Ordset1</anno></c> and diff --git a/lib/stdlib/doc/src/pool.xml b/lib/stdlib/doc/src/pool.xml index 05d12ade28..675ee08bfb 100644 --- a/lib/stdlib/doc/src/pool.xml +++ b/lib/stdlib/doc/src/pool.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>pool</module> + <module since="">pool</module> <modulesummary>Load distribution facility.</modulesummary> <description> <p>This module can be used to run a set of Erlang nodes as a pool @@ -54,7 +54,7 @@ <funcs> <func> - <name name="attach" arity="1"/> + <name name="attach" arity="1" since=""/> <fsummary>Ensure that a pool master is running.</fsummary> <desc> <p>Ensures that a pool master is running and includes @@ -63,7 +63,7 @@ </func> <func> - <name name="get_node" arity="0"/> + <name name="get_node" arity="0" since=""/> <fsummary>Return the node with the expected lowest future load.</fsummary> <desc> <p>Returns the node with the expected lowest future load.</p> @@ -71,7 +71,7 @@ </func> <func> - <name name="get_nodes" arity="0"/> + <name name="get_nodes" arity="0" since=""/> <fsummary>Return a list of the current member nodes of the pool. </fsummary> <desc> @@ -80,7 +80,7 @@ </func> <func> - <name name="pspawn" arity="3"/> + <name name="pspawn" arity="3" since=""/> <fsummary>Spawn a process on the pool node with expected lowest future load.</fsummary> <desc> @@ -90,7 +90,7 @@ </func> <func> - <name name="pspawn_link" arity="3"/> + <name name="pspawn_link" arity="3" since=""/> <fsummary>Spawn and link to a process on the pool node with expected lowest future load.</fsummary> <desc> @@ -100,8 +100,8 @@ </func> <func> - <name name="start" arity="1"/> - <name name="start" arity="2"/> + <name name="start" arity="1" since=""/> + <name name="start" arity="2" since=""/> <fsummary>>Start a new pool.</fsummary> <desc> <p>Starts a new pool. The file <c>.hosts.erlang</c> is read to @@ -122,7 +122,7 @@ </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary>Stop the pool and kill all the slave nodes.</fsummary> <desc> <p>Stops the pool and kills all the slave nodes.</p> diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index b85fab67d5..aeb9f48735 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>proc_lib</module> + <module since="">proc_lib</module> <modulesummary>Functions for asynchronous and synchronous start of processes adhering to the OTP design principles.</modulesummary> <description> @@ -102,7 +102,7 @@ <funcs> <func> - <name name="format" arity="1"/> + <name name="format" arity="1" since=""/> <fsummary>Format a crash report.</fsummary> <desc> <p>Equivalent to <seealso marker="#format/2"> @@ -111,7 +111,7 @@ </func> <func> - <name name="format" arity="2"/> + <name name="format" arity="2" since="OTP R16B"/> <fsummary>Format a crash report.</fsummary> <desc> <note> @@ -138,7 +138,7 @@ </func> <func> - <name name="format" arity="3"/> + <name name="format" arity="3" since="OTP 18.1"/> <fsummary>Format a crash report.</fsummary> <desc> <note> @@ -162,7 +162,7 @@ </func> <func> - <name name="hibernate" arity="3"/> + <name name="hibernate" arity="3" since=""/> <fsummary>Hibernate a process until a message is sent to it.</fsummary> <desc> <p>This function does the same as (and does call) the @@ -176,8 +176,8 @@ </func> <func> - <name name="init_ack" arity="1"/> - <name name="init_ack" arity="2"/> + <name name="init_ack" arity="1" since=""/> + <name name="init_ack" arity="2" since=""/> <fsummary>Used by a process when it has started.</fsummary> <desc> <p>This function must be used by a process that has been started by @@ -214,7 +214,7 @@ init(Parent) -> </func> <func> - <name name="initial_call" arity="1"/> + <name name="initial_call" arity="1" since=""/> <fsummary>Extract the initial call of a <c>proc_lib</c>spawned process. </fsummary> <desc> @@ -244,10 +244,10 @@ init(Parent) -> </func> <func> - <name name="spawn" arity="1"/> - <name name="spawn" arity="2"/> - <name name="spawn" arity="3"/> - <name name="spawn" arity="4"/> + <name name="spawn" arity="1" since=""/> + <name name="spawn" arity="2" since=""/> + <name name="spawn" arity="3" since=""/> + <name name="spawn" arity="4" since=""/> <fsummary>Spawn a new process.</fsummary> <type variable="Node"/> <type variable="Fun" name_i="1"/> @@ -262,10 +262,10 @@ init(Parent) -> </func> <func> - <name name="spawn_link" arity="1"/> - <name name="spawn_link" arity="2"/> - <name name="spawn_link" arity="3"/> - <name name="spawn_link" arity="4"/> + <name name="spawn_link" arity="1" since=""/> + <name name="spawn_link" arity="2" since=""/> + <name name="spawn_link" arity="3" since=""/> + <name name="spawn_link" arity="4" since=""/> <fsummary>Spawn and link to a new process.</fsummary> <type variable="Node"/> <type variable="Fun" name_i="1"/> @@ -281,10 +281,10 @@ init(Parent) -> </func> <func> - <name name="spawn_opt" arity="2"/> - <name name="spawn_opt" arity="3"/> - <name name="spawn_opt" arity="4"/> - <name name="spawn_opt" arity="5"/> + <name name="spawn_opt" arity="2" since=""/> + <name name="spawn_opt" arity="3" since=""/> + <name name="spawn_opt" arity="4" since=""/> + <name name="spawn_opt" arity="5" since=""/> <fsummary>Spawn a new process with specified options.</fsummary> <type variable="Node"/> <type variable="Fun" name_i="1"/> @@ -306,12 +306,12 @@ init(Parent) -> </func> <func> - <name name="start" arity="3"/> - <name name="start" arity="4"/> - <name name="start" arity="5"/> - <name name="start_link" arity="3"/> - <name name="start_link" arity="4"/> - <name name="start_link" arity="5"/> + <name name="start" arity="3" since=""/> + <name name="start" arity="4" since=""/> + <name name="start" arity="5" since=""/> + <name name="start_link" arity="3" since=""/> + <name name="start_link" arity="4" since=""/> + <name name="start_link" arity="5" since=""/> <fsummary>Start a new process synchronously.</fsummary> <desc> <p>Starts a new process synchronously. Spawns the process and @@ -341,7 +341,7 @@ init(Parent) -> </func> <func> - <name name="stop" arity="1"/> + <name name="stop" arity="1" since="OTP 18.0"/> <fsummary>Terminate a process synchronously.</fsummary> <type variable="Process"/> <desc> @@ -351,7 +351,7 @@ init(Parent) -> </func> <func> - <name name="stop" arity="3"/> + <name name="stop" arity="3" since="OTP 18.0"/> <fsummary>Terminate a process synchronously.</fsummary> <type variable="Process"/> <type variable="Reason"/> @@ -375,7 +375,7 @@ init(Parent) -> </func> <func> - <name name="translate_initial_call" arity="1"/> + <name name="translate_initial_call" arity="1" since=""/> <fsummary>Extract and translate the initial call of a <c>proc_lib</c>spawned process.</fsummary> <desc> diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml index f9a54bf804..4465103469 100644 --- a/lib/stdlib/doc/src/proplists.xml +++ b/lib/stdlib/doc/src/proplists.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>proplists.xml</file> </header> - <module>proplists</module> + <module since="">proplists</module> <modulesummary>Support functions for property lists.</modulesummary> <description> <p>Property lists are ordinary lists containing entries in the form @@ -57,11 +57,16 @@ <datatype> <name name="property"/> </datatype> + + <datatype> + <name name="proplist"/> + </datatype> + </datatypes> <funcs> <func> - <name name="append_values" arity="2"/> + <name name="append_values" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Similar to @@ -79,7 +84,7 @@ append_values(a, [{a, [1,2]}, {b, 0}, {a, 3}, {c, -1}, {a, [4]}])</code> </func> <func> - <name name="compact" arity="1"/> + <name name="compact" arity="1" since=""/> <fsummary></fsummary> <desc> <p>Minimizes the representation of all entries in the list. This is @@ -91,7 +96,7 @@ append_values(a, [{a, [1,2]}, {b, 0}, {a, 3}, {c, -1}, {a, [4]}])</code> </func> <func> - <name name="delete" arity="2"/> + <name name="delete" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Deletes all entries associated with <c><anno>Key</anno></c> from @@ -100,7 +105,7 @@ append_values(a, [{a, [1,2]}, {b, 0}, {a, 3}, {c, -1}, {a, [4]}])</code> </func> <func> - <name name="expand" arity="2"/> + <name name="expand" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Expands particular properties to corresponding sets of @@ -133,7 +138,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="get_all_values" arity="2"/> + <name name="get_all_values" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Similar to @@ -145,7 +150,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="get_bool" arity="2"/> + <name name="get_bool" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Returns the value of a boolean key/value option. If @@ -159,7 +164,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="get_keys" arity="1"/> + <name name="get_keys" arity="1" since=""/> <fsummary></fsummary> <desc> <p>Returns an unordered list of the keys used in @@ -168,7 +173,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="get_value" arity="2"/> + <name name="get_value" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Equivalent to @@ -177,7 +182,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="get_value" arity="3"/> + <name name="get_value" arity="3" since=""/> <fsummary></fsummary> <desc> <p>Returns the value of a simple key/value property in @@ -194,7 +199,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="is_defined" arity="2"/> + <name name="is_defined" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Returns <c>true</c> if <c><anno>List</anno></c> contains at least @@ -204,7 +209,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="lookup" arity="2"/> + <name name="lookup" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Returns the first entry associated with <c><anno>Key</anno></c> in @@ -219,7 +224,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="lookup_all" arity="2"/> + <name name="lookup_all" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Returns the list of all entries associated with @@ -231,7 +236,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="normalize" arity="2"/> + <name name="normalize" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Passes <c><anno>ListIn</anno></c> through a sequence of @@ -263,7 +268,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="property" arity="1"/> + <name name="property" arity="1" since=""/> <fsummary></fsummary> <desc> <p>Creates a normal form (minimal) representation of a property. If @@ -276,7 +281,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="property" arity="2"/> + <name name="property" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Creates a normal form (minimal) representation of a simple key/value @@ -289,7 +294,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code> </func> <func> - <name name="split" arity="2"/> + <name name="split" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Partitions <c><anno>List</anno></c> into a list of sublists and a @@ -310,7 +315,7 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code> </func> <func> - <name name="substitute_aliases" arity="2"/> + <name name="substitute_aliases" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Substitutes keys of properties. For each entry in @@ -332,7 +337,7 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code> </func> <func> - <name name="substitute_negations" arity="2"/> + <name name="substitute_negations" arity="2" since=""/> <fsummary></fsummary> <desc> <p>Substitutes keys of boolean-valued properties and @@ -360,7 +365,7 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code> </func> <func> - <name name="unfold" arity="1"/> + <name name="unfold" arity="1" since=""/> <fsummary></fsummary> <desc> <p>Unfolds all occurrences of atoms in <c><anno>ListIn</anno></c> to diff --git a/lib/stdlib/doc/src/qlc.xml b/lib/stdlib/doc/src/qlc.xml index fe14a6334c..fe60c2e9bb 100644 --- a/lib/stdlib/doc/src/qlc.xml +++ b/lib/stdlib/doc/src/qlc.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>qlc.xml</file> </header> - <module>qlc</module> + <module since="">qlc</module> <modulesummary>Query interface to Mnesia, ETS, Dets, and so on. </modulesummary> <description> @@ -720,7 +720,7 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), <funcs> <func> - <name name="append" arity="1"/> + <name name="append" arity="1" since=""/> <fsummary>Return a query handle.</fsummary> <desc> <p>Returns a query handle. When evaluating query handle @@ -731,7 +731,7 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), </func> <func> - <name name="append" arity="2"/> + <name name="append" arity="2" since=""/> <fsummary>Return a query handle.</fsummary> <desc> <p>Returns a query handle. When evaluating query handle @@ -744,8 +744,8 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), </func> <func> - <name name="cursor" arity="1"/> - <name name="cursor" arity="2"/> + <name name="cursor" arity="1" since=""/> + <name name="cursor" arity="2" since=""/> <fsummary>Create a query cursor.</fsummary> <desc> <p>Creates a query cursor and @@ -777,7 +777,7 @@ ok</pre> </func> <func> - <name name="delete_cursor" arity="1"/> + <name name="delete_cursor" arity="1" since=""/> <fsummary>Delete a query cursor.</fsummary> <desc> <p>Deletes a query cursor. Only the owner of the cursor can @@ -786,10 +786,10 @@ ok</pre> </func> <func> - <name name="e" arity="1"/> - <name name="e" arity="2"/> - <name name="eval" arity="1"/> - <name name="eval" arity="2"/> + <name name="e" arity="1" since=""/> + <name name="e" arity="2" since=""/> + <name name="eval" arity="1" since=""/> + <name name="eval" arity="2" since=""/> <fsummary>Return all answers to a query.</fsummary> <desc> <p>Evaluates a query handle in the @@ -805,8 +805,8 @@ ok</pre> </func> <func> - <name name="fold" arity="3"/> - <name name="fold" arity="4"/> + <name name="fold" arity="3" since=""/> + <name name="fold" arity="4" since=""/> <fsummary>Fold a function over the answers to a query.</fsummary> <desc> <p>Calls <c><anno>Function</anno></c> on successive answers to @@ -830,7 +830,7 @@ ok</pre> </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Return an English description of a an error tuple.</fsummary> <desc> <p>Returns a descriptive string in English of an error tuple @@ -841,8 +841,8 @@ ok</pre> </func> <func> - <name name="info" arity="1"/> - <name name="info" arity="2"/> + <name name="info" arity="1" since=""/> + <name name="info" arity="2" since=""/> <fsummary>Return code describing a query handle.</fsummary> <desc> <p>Returns information about a @@ -946,8 +946,8 @@ end</pre> </func> <func> - <name name="keysort" arity="2"/> - <name name="keysort" arity="3"/> + <name name="keysort" arity="2" since=""/> + <name name="keysort" arity="3" since=""/> <fsummary>Return a query handle.</fsummary> <desc> <p>Returns a query handle. When evaluating query handle @@ -967,8 +967,8 @@ end</pre> </func> <func> - <name name="next_answers" arity="1"/> - <name name="next_answers" arity="2"/> + <name name="next_answers" arity="1" since=""/> + <name name="next_answers" arity="2" since=""/> <fsummary>Return some or all answers to a query.</fsummary> <desc> <p>Returns some or all of the remaining answers to a query @@ -983,8 +983,8 @@ end</pre> </func> <func> - <name name="q" arity="1"/> - <name name="q" arity="2"/> + <name name="q" arity="1" since=""/> + <name name="q" arity="2" since=""/> <fsummary>Return a handle for a query list comprehension.</fsummary> <desc> <p>Returns a query handle for a QLC. @@ -1188,8 +1188,8 @@ ets:match_spec_run( </func> <func> - <name name="sort" arity="1"/> - <name name="sort" arity="2"/> + <name name="sort" arity="1" since=""/> + <name name="sort" arity="2" since=""/> <fsummary>Return a query handle.</fsummary> <desc> <p>Returns a query handle. When evaluating query handle @@ -1208,9 +1208,9 @@ ets:match_spec_run( </func> <func> - <name name="string_to_handle" arity="1"/> - <name name="string_to_handle" arity="2"/> - <name name="string_to_handle" arity="3"/> + <name name="string_to_handle" arity="1" since=""/> + <name name="string_to_handle" arity="2" since=""/> + <name name="string_to_handle" arity="3" since=""/> <fsummary>Return a handle for a query list comprehension.</fsummary> <desc> <p>A string version of <seealso marker="#q/1"><c>q/1,2</c></seealso>. @@ -1238,7 +1238,7 @@ ets:match_spec_run( </func> <func> - <name name="table" arity="2"/> + <name name="table" arity="2" since=""/> <fsummary>Return a query handle for a table.</fsummary> <desc> <p>Returns a query handle for a QLC table. diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index 9f3aff03a3..83a8afea81 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -32,7 +32,7 @@ <rev>B</rev> <file>queue.xml</file> </header> - <module>queue</module> + <module since="">queue</module> <modulesummary>Abstract data type for FIFO queues.</modulesummary> <description> <p>This module provides (double-ended) FIFO queues @@ -113,7 +113,7 @@ <funcs> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Filter a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of calling @@ -134,7 +134,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Convert a list to a queue.</fsummary> <desc> <p>Returns a queue containing the items in <c><anno>L</anno></c> in the @@ -144,7 +144,7 @@ </func> <func> - <name name="in" arity="2"/> + <name name="in" arity="2" since=""/> <fsummary>Insert an item at the rear of a queue.</fsummary> <desc> <p>Inserts <c><anno>Item</anno></c> at the rear of queue @@ -154,7 +154,7 @@ </func> <func> - <name name="in_r" arity="2"/> + <name name="in_r" arity="2" since=""/> <fsummary>Insert an item at the front of a queue.</fsummary> <desc> <p>Inserts <c><anno>Item</anno></c> at the front of queue @@ -164,7 +164,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since=""/> <fsummary>Test if a queue is empty.</fsummary> <desc> <p>Tests if <c><anno>Q</anno></c> is empty and returns <c>true</c> if @@ -173,7 +173,7 @@ </func> <func> - <name name="is_queue" arity="1"/> + <name name="is_queue" arity="1" since=""/> <fsummary>Test if a term is a queue.</fsummary> <desc> <p>Tests if <c><anno>Term</anno></c> is a queue and returns <c>true</c> @@ -182,7 +182,7 @@ </func> <func> - <name name="join" arity="2"/> + <name name="join" arity="2" since=""/> <fsummary>Join two queues.</fsummary> <desc> <p>Returns a queue <c><anno>Q3</anno></c> that is the result of joining @@ -192,7 +192,7 @@ </func> <func> - <name name="len" arity="1"/> + <name name="len" arity="1" since=""/> <fsummary>Get the length of a queue.</fsummary> <desc> <p>Calculates and returns the length of queue <c><anno>Q</anno></c>.</p> @@ -200,7 +200,7 @@ </func> <func> - <name name="member" arity="2"/> + <name name="member" arity="2" since=""/> <fsummary>Test if an item is in a queue.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Item</anno></c> matches some element @@ -209,7 +209,7 @@ </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Create an empty queue.</fsummary> <desc> <p>Returns an empty queue.</p> @@ -217,7 +217,7 @@ </func> <func> - <name name="out" arity="1"/> + <name name="out" arity="1" since=""/> <fsummary>Remove the front item from a queue.</fsummary> <desc> <p>Removes the item at the front of queue <c><anno>Q1</anno></c>. @@ -230,7 +230,7 @@ </func> <func> - <name name="out_r" arity="1"/> + <name name="out_r" arity="1" since=""/> <fsummary>Remove the rear item from a queue.</fsummary> <desc> <p>Removes the item at the rear of queue <c><anno>Q1</anno></c>. @@ -242,7 +242,7 @@ </func> <func> - <name name="reverse" arity="1"/> + <name name="reverse" arity="1" since=""/> <fsummary>Reverse a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> containing the items of @@ -251,7 +251,7 @@ </func> <func> - <name name="split" arity="2"/> + <name name="split" arity="2" since=""/> <fsummary>Split a queue in two.</fsummary> <desc> <p>Splits <c><anno>Q1</anno></c> in two. The <c><anno>N</anno></c> @@ -261,7 +261,7 @@ </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert a queue to a list.</fsummary> <desc> <p>Returns a list of the items in the queue in the same order; @@ -276,7 +276,7 @@ <funcs> <func> - <name name="drop" arity="1"/> + <name name="drop" arity="1" since=""/> <fsummary>Remove the front item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing @@ -286,7 +286,7 @@ </func> <func> - <name name="drop_r" arity="1"/> + <name name="drop_r" arity="1" since=""/> <fsummary>Remove the rear item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing @@ -296,7 +296,7 @@ </func> <func> - <name name="get" arity="1"/> + <name name="get" arity="1" since=""/> <fsummary>Return the front item of a queue.</fsummary> <desc> <p>Returns <c><anno>Item</anno></c> at the front of queue @@ -306,7 +306,7 @@ </func> <func> - <name name="get_r" arity="1"/> + <name name="get_r" arity="1" since=""/> <fsummary>Return the rear item of a queue.</fsummary> <desc> <p>Returns <c><anno>Item</anno></c> at the rear of queue @@ -316,7 +316,7 @@ </func> <func> - <name name="peek" arity="1"/> + <name name="peek" arity="1" since=""/> <fsummary>Return the front item of a queue.</fsummary> <desc> <p>Returns tuple <c>{value, <anno>Item</anno>}</c>, where @@ -326,7 +326,7 @@ </func> <func> - <name name="peek_r" arity="1"/> + <name name="peek_r" arity="1" since=""/> <fsummary>Return the rear item of a queue.</fsummary> <desc> <p>Returns tuple <c>{value, <anno>Item</anno>}</c>, where @@ -342,7 +342,7 @@ <funcs> <func> - <name name="cons" arity="2"/> + <name name="cons" arity="2" since=""/> <fsummary>Insert an item at the head of a queue.</fsummary> <desc> <p>Inserts <c><anno>Item</anno></c> at the head of queue @@ -352,7 +352,7 @@ </func> <func> - <name name="daeh" arity="1"/> + <name name="daeh" arity="1" since=""/> <fsummary>Return the tail item of a queue.</fsummary> <desc> <p>Returns the tail item of queue <c><anno>Q</anno></c>.</p> @@ -361,7 +361,7 @@ </func> <func> - <name name="head" arity="1"/> + <name name="head" arity="1" since=""/> <fsummary>Return the item at the head of a queue.</fsummary> <desc> <p>Returns <c><anno>Item</anno></c> from the head of queue @@ -371,7 +371,7 @@ </func> <func> - <name name="init" arity="1"/> + <name name="init" arity="1" since=""/> <fsummary>Remove the tail item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing @@ -381,7 +381,7 @@ </func> <func> - <name name="lait" arity="1"/> + <name name="lait" arity="1" since=""/> <fsummary>Remove the tail item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing @@ -392,7 +392,7 @@ </func> <func> - <name name="last" arity="1"/> + <name name="last" arity="1" since=""/> <fsummary>Return the tail item of a queue.</fsummary> <desc> <p>Returns the tail item of queue <c><anno>Q</anno></c>.</p> @@ -401,7 +401,7 @@ </func> <func> - <name name="liat" arity="1"/> + <name name="liat" arity="1" since=""/> <fsummary>Remove the tail item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing @@ -411,7 +411,7 @@ </func> <func> - <name name="snoc" arity="2"/> + <name name="snoc" arity="2" since=""/> <fsummary>Insert an item at the tail of a queue.</fsummary> <desc> <p>Inserts <c><anno>Item</anno></c> as the tail item of queue @@ -421,7 +421,7 @@ </func> <func> - <name name="tail" arity="1"/> + <name name="tail" arity="1" since=""/> <fsummary>Remove the head item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml index 21f680a0ee..27d2d99f3c 100644 --- a/lib/stdlib/doc/src/rand.xml +++ b/lib/stdlib/doc/src/rand.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>rand.xml</file> </header> - <module>rand</module> + <module since="OTP 18.0">rand</module> <modulesummary>Pseudo random number generation.</modulesummary> <description> <p> @@ -273,7 +273,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> <funcs> <func> - <name name="export_seed" arity="0"/> + <name name="export_seed" arity="0" since="OTP 18.0"/> <fsummary>Export the random number generation state.</fsummary> <desc><marker id="export_seed-0"/> <p>Returns the random number state in an external format. @@ -282,7 +282,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="export_seed_s" arity="1"/> + <name name="export_seed_s" arity="1" since="OTP 18.0"/> <fsummary>Export the random number generation state.</fsummary> <desc><marker id="export_seed_s-1"/> <p>Returns the random number generator state in an external format. @@ -291,7 +291,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="jump" arity="0"/> + <name name="jump" arity="0" since="OTP 20.0"/> <fsummary>Return the seed after performing jump calculation to the state in the process dictionary.</fsummary> <desc><marker id="jump-0" /> @@ -306,7 +306,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="jump" arity="1"/> + <name name="jump" arity="1" since="OTP 20.0"/> <fsummary>Return the seed after performing jump calculation.</fsummary> <desc><marker id="jump-1" /> <p>Returns the state after performing jump calculation @@ -318,7 +318,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="normal" arity="0"/> + <name name="normal" arity="0" since="OTP 18.0"/> <fsummary>Return a standard normal distributed random float.</fsummary> <desc> <p>Returns a standard normal deviate float (that is, the mean @@ -328,7 +328,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="normal" arity="2"/> + <name name="normal" arity="2" since="OTP 20.0"/> <fsummary>Return a normal distributed random float.</fsummary> <desc> <p>Returns a normal N(Mean, Variance) deviate float @@ -337,7 +337,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="normal_s" arity="1"/> + <name name="normal_s" arity="1" since="OTP 18.0"/> <fsummary>Return a standard normal distributed random float.</fsummary> <desc> <p>Returns, for a specified state, a standard normal @@ -347,7 +347,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="normal_s" arity="3"/> + <name name="normal_s" arity="3" since="OTP 20.0"/> <fsummary>Return a normal distributed random float.</fsummary> <desc> <p>Returns, for a specified state, a normal N(Mean, Variance) @@ -356,7 +356,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="seed" arity="1"/> + <name name="seed" arity="1" since="OTP 18.0"/> <fsummary>Seed random number generator.</fsummary> <desc> <marker id="seed-1"/> @@ -372,7 +372,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="seed" arity="2"/> + <name name="seed" arity="2" since="OTP 18.0"/> <fsummary>Seed the random number generation.</fsummary> <desc> <p>Seeds random number generation with the specified algorithm and @@ -381,7 +381,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="seed_s" arity="1"/> + <name name="seed_s" arity="1" since="OTP 18.0"/> <fsummary>Seed random number generator.</fsummary> <desc> <p> @@ -396,7 +396,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="seed_s" arity="2"/> + <name name="seed_s" arity="2" since="OTP 18.0"/> <fsummary>Seed the random number generation.</fsummary> <desc> <p>Seeds random number generation with the specified algorithm and @@ -405,7 +405,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre> </func> <func> - <name name="uniform" arity="0"/> + <name name="uniform" arity="0" since="OTP 18.0"/> <fsummary>Return a random float.</fsummary> <desc><marker id="uniform-0"/> <p> @@ -441,7 +441,7 @@ end.</pre> </func> <func> - <name name="uniform_real" arity="0"/> + <name name="uniform_real" arity="0" since="OTP 21.0"/> <fsummary>Return a random float.</fsummary> <desc><marker id="uniform_real-0"/> <p> @@ -477,7 +477,7 @@ end.</pre> </func> <func> - <name name="uniform" arity="1"/> + <name name="uniform" arity="1" since="OTP 18.0"/> <fsummary>Return a random integer.</fsummary> <desc><marker id="uniform-1"/> <p>Returns, for a specified integer <c><anno>N</anno> >= 1</c>, @@ -488,7 +488,7 @@ end.</pre> </func> <func> - <name name="uniform_s" arity="1"/> + <name name="uniform_s" arity="1" since="OTP 18.0"/> <fsummary>Return a random float.</fsummary> <desc> <p> @@ -524,7 +524,7 @@ end.</pre> </func> <func> - <name name="uniform_real_s" arity="1"/> + <name name="uniform_real_s" arity="1" since="OTP 21.0"/> <fsummary>Return a random float.</fsummary> <desc> <p> @@ -586,7 +586,7 @@ end.</pre> </func> <func> - <name name="uniform_s" arity="2"/> + <name name="uniform_s" arity="2" since="OTP 18.0"/> <fsummary>Return a random integer.</fsummary> <desc> <p>Returns, for a specified integer <c><anno>N</anno> >= 1</c> diff --git a/lib/stdlib/doc/src/random.xml b/lib/stdlib/doc/src/random.xml index 8d090d20b3..f0261ed009 100644 --- a/lib/stdlib/doc/src/random.xml +++ b/lib/stdlib/doc/src/random.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>random.xml</file> </header> - <module>random</module> + <module since="">random</module> <modulesummary>Pseudo-random number generation.</modulesummary> <description> <p>This module provides a random number generator. The method is attributed @@ -73,7 +73,7 @@ <funcs> <func> - <name name="seed" arity="0"/> + <name name="seed" arity="0" since=""/> <fsummary>Seed random number generation with default values.</fsummary> <desc> <p>Seeds random number generation with default (fixed) values @@ -82,7 +82,7 @@ </func> <func> - <name name="seed" arity="1"/> + <name name="seed" arity="1" since=""/> <fsummary>Seed random number generator.</fsummary> <desc> <p><c>seed({<anno>A1</anno>, <anno>A2</anno>, <anno>A3</anno>})</c> @@ -92,7 +92,7 @@ </func> <func> - <name name="seed" arity="3"/> + <name name="seed" arity="3" since=""/> <fsummary>Seed random number generator.</fsummary> <desc> <p>Seeds random number generation with integer values in the process @@ -116,7 +116,7 @@ random:seed(erlang:phash2([node()]), </func> <func> - <name name="seed0" arity="0"/> + <name name="seed0" arity="0" since=""/> <fsummary>Return default state for random number generation.</fsummary> <desc> <p>Returns the default state.</p> @@ -124,7 +124,7 @@ random:seed(erlang:phash2([node()]), </func> <func> - <name name="uniform" arity="0"/> + <name name="uniform" arity="0" since=""/> <fsummary>Return a random float.</fsummary> <desc> <p>Returns a random float uniformly distributed between <c>0.0</c> @@ -133,7 +133,7 @@ random:seed(erlang:phash2([node()]), </func> <func> - <name name="uniform" arity="1"/> + <name name="uniform" arity="1" since=""/> <fsummary>Return a random integer.</fsummary> <desc> <p>Returns, for a specified integer <c><anno>N</anno> >= 1</c>, @@ -144,7 +144,7 @@ random:seed(erlang:phash2([node()]), </func> <func> - <name name="uniform_s" arity="1"/> + <name name="uniform_s" arity="1" since=""/> <fsummary>Return a random float.</fsummary> <desc> <p>Returns, for a specified state, a random float uniformly @@ -153,7 +153,7 @@ random:seed(erlang:phash2([node()]), </func> <func> - <name name="uniform_s" arity="2"/> + <name name="uniform_s" arity="2" since=""/> <fsummary>Return a random integer.</fsummary> <desc> <p>Returns, for a specified integer <c><anno>N</anno> >= 1</c> and a diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 078ca0e38c..b04434492d 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -34,7 +34,7 @@ <rev>A</rev> <file>re.xml</file> </header> - <module>re</module> + <module since="">re</module> <modulesummary>Perl-like regular expressions for Erlang.</modulesummary> <description> <p>This module contains regular expression matching functions for @@ -79,7 +79,7 @@ <funcs> <func> - <name name="version" arity="0"/> + <name name="version" arity="0" since="OTP 20.0"/> <fsummary>Gives the PCRE version of the system in a string format</fsummary> <desc> <p>The return of this function is a string with the PCRE version of the system that was used in the Erlang/OTP compilation.</p> @@ -87,7 +87,7 @@ </func> <func> - <name name="compile" arity="1"/> + <name name="compile" arity="1" since=""/> <fsummary>Compile a regular expression into a match program</fsummary> <desc> <p>The same as <c>compile(<anno>Regexp</anno>,[])</c></p> @@ -95,7 +95,7 @@ </func> <func> - <name name="compile" arity="2"/> + <name name="compile" arity="2" since=""/> <fsummary>Compile a regular expression into a match program.</fsummary> <desc> <p>Compiles a regular expression, with the syntax @@ -304,7 +304,7 @@ </func> <func> - <name name="inspect" arity="2"/> + <name name="inspect" arity="2" since="OTP 17.0"/> <fsummary>Inspects a compiled regular expression.</fsummary> <desc> <p>Takes a compiled regular expression and an item, and returns the @@ -348,7 +348,7 @@ </func> <func> - <name name="replace" arity="3"/> + <name name="replace" arity="3" since=""/> <fsummary>Match a subject against regular expression and replace matching elements with Replacement.</fsummary> <desc> @@ -358,7 +358,7 @@ </func> <func> - <name name="replace" arity="4"/> + <name name="replace" arity="4" since=""/> <fsummary>Match a subject against regular expression and replace matching elements with Replacement.</fsummary> <desc> @@ -408,7 +408,7 @@ re:replace("abcd","c","[\\&]",[{return,list}]).</code> </func> <func> - <name name="run" arity="2"/> + <name name="run" arity="2" since=""/> <fsummary>Match a subject against regular expression and capture subpatterns.</fsummary> <desc> @@ -417,7 +417,7 @@ re:replace("abcd","c","[\\&]",[{return,list}]).</code> </func> <func> - <name name="run" arity="3"/> + <name name="run" arity="3" since=""/> <fsummary>Match a subject against regular expression and capture subpatterns.</fsummary> <type_desc variable="CompileOpt">See <seealso marker="#compile_options"> @@ -992,7 +992,7 @@ re:run("cacb","c(a|b)",[global,{capture,[1],list}]).</code> </func> <func> - <name name="split" arity="2"/> + <name name="split" arity="2" since=""/> <fsummary>Split a string by tokens specified as a regular expression. </fsummary> <desc> @@ -1001,7 +1001,7 @@ re:run("cacb","c(a|b)",[global,{capture,[1],list}]).</code> </func> <func> - <name name="split" arity="3"/> + <name name="split" arity="3" since=""/> <fsummary>Split a string by tokens specified as a regular expression</fsummary> <type_desc variable="CompileOpt">See <seealso marker="#compile_options"> <c>compile/2</c></seealso>.</type_desc> diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml index 8db3e1e623..07ce41b7a7 100644 --- a/lib/stdlib/doc/src/sets.xml +++ b/lib/stdlib/doc/src/sets.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>sets.xml</file> </header> - <module>sets</module> + <module since="">sets</module> <modulesummary>Functions for set manipulation.</modulesummary> <description> <p>Sets are collections of elements with no duplicate elements. @@ -59,7 +59,7 @@ <funcs> <func> - <name name="add_element" arity="2"/> + <name name="add_element" arity="2" since=""/> <fsummary>Add an element to a <c>Set</c>.</fsummary> <desc> <p>Returns a new set formed from <c><anno>Set1</anno></c> with @@ -68,7 +68,7 @@ </func> <func> - <name name="del_element" arity="2"/> + <name name="del_element" arity="2" since=""/> <fsummary>Remove an element from a <c>Set</c>.</fsummary> <desc> <p>Returns <c><anno>Set1</anno></c>, but with @@ -77,7 +77,7 @@ </func> <func> - <name name="filter" arity="2"/> + <name name="filter" arity="2" since=""/> <fsummary>Filter set elements.</fsummary> <desc> <p>Filters elements in <c><anno>Set1</anno></c> with boolean function @@ -86,7 +86,7 @@ </func> <func> - <name name="fold" arity="3"/> + <name name="fold" arity="3" since=""/> <fsummary>Fold over set elements.</fsummary> <desc> <p>Folds <c><anno>Function</anno></c> over every element in @@ -96,7 +96,7 @@ </func> <func> - <name name="from_list" arity="1"/> + <name name="from_list" arity="1" since=""/> <fsummary>Convert a list into a <c>Set</c>.</fsummary> <desc> <p>Returns a set of the elements in <c><anno>List</anno></c>.</p> @@ -104,7 +104,7 @@ </func> <func> - <name name="intersection" arity="1"/> + <name name="intersection" arity="1" since=""/> <fsummary>Return the intersection of a list of <c>Sets</c>.</fsummary> <desc> <p>Returns the intersection of the non-empty list of sets.</p> @@ -112,7 +112,7 @@ </func> <func> - <name name="intersection" arity="2"/> + <name name="intersection" arity="2" since=""/> <fsummary>Return the intersection of two <c>Sets</c>.</fsummary> <desc> <p>Returns the intersection of <c><anno>Set1</anno></c> and @@ -121,7 +121,7 @@ </func> <func> - <name name="is_disjoint" arity="2"/> + <name name="is_disjoint" arity="2" since=""/> <fsummary>Check whether two <c>Sets</c> are disjoint.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and @@ -131,7 +131,7 @@ </func> <func> - <name name="is_element" arity="2"/> + <name name="is_element" arity="2" since=""/> <fsummary>Test for membership of a <c>Set</c>.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of @@ -140,7 +140,7 @@ </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since="OTP 21.0"/> <fsummary>Test for empty set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set</anno></c> is an empty set, @@ -149,7 +149,7 @@ </func> <func> - <name name="is_set" arity="1"/> + <name name="is_set" arity="1" since=""/> <fsummary>Test for a <c>Set</c>.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set</anno></c> is a set of @@ -158,7 +158,7 @@ </func> <func> - <name name="is_subset" arity="2"/> + <name name="is_subset" arity="2" since=""/> <fsummary>Test for subset.</fsummary> <desc> <p>Returns <c>true</c> when every element of <c><anno>Set1</anno></c> is @@ -167,7 +167,7 @@ </func> <func> - <name name="new" arity="0"/> + <name name="new" arity="0" since=""/> <fsummary>Return an empty set.</fsummary> <desc> <p>Returns a new empty set.</p> @@ -175,7 +175,7 @@ </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Return the number of elements in a set.</fsummary> <desc> <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> @@ -183,7 +183,7 @@ </func> <func> - <name name="subtract" arity="2"/> + <name name="subtract" arity="2" since=""/> <fsummary>Return the difference of two <c>Sets</c>.</fsummary> <desc> <p>Returns only the elements of <c><anno>Set1</anno></c> that are not @@ -192,7 +192,7 @@ </func> <func> - <name name="to_list" arity="1"/> + <name name="to_list" arity="1" since=""/> <fsummary>Convert a <c>Set</c>into a list.</fsummary> <desc> <p>Returns the elements of <c><anno>Set</anno></c> as a list. @@ -201,7 +201,7 @@ </func> <func> - <name name="union" arity="1"/> + <name name="union" arity="1" since=""/> <fsummary>Return the union of a list of <c>Sets</c>.</fsummary> <desc> <p>Returns the merged (union) set of the list of sets.</p> @@ -209,7 +209,7 @@ </func> <func> - <name name="union" arity="2"/> + <name name="union" arity="2" since=""/> <fsummary>Return the union of two <c>Sets</c>.</fsummary> <desc> <p>Returns the merged (union) set of <c><anno>Set1</anno></c> and diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index 2593d3690b..50a0968531 100644 --- a/lib/stdlib/doc/src/shell.xml +++ b/lib/stdlib/doc/src/shell.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>shell.xml</file> </header> - <module>shell</module> + <module since="">shell</module> <modulesummary>The Erlang shell.</modulesummary> <description> <p>This module provides an Erlang shell.</p> @@ -874,7 +874,7 @@ q - quit erlang <funcs> <func> - <name>catch_exception(Bool) -> boolean()</name> + <name since="">catch_exception(Bool) -> boolean()</name> <fsummary>Set the exception handling of the shell.</fsummary> <type> <v>Bool = boolean()</v> @@ -892,7 +892,7 @@ q - quit erlang </func> <func> - <name name="history" arity="1"/> + <name name="history" arity="1" since=""/> <fsummary>Set the number of previous commands to keep.</fsummary> <desc> <p>Sets the number of previous commands to keep in the @@ -902,7 +902,7 @@ q - quit erlang </func> <func> - <name name="prompt_func" arity="1"/> + <name name="prompt_func" arity="1" since="OTP R13B04"/> <fsummary>Set the shell prompt.</fsummary> <desc> <p>Sets the shell prompt function to <c><anno>PromptFunc</anno></c>. @@ -911,7 +911,7 @@ q - quit erlang </func> <func> - <name name="results" arity="1"/> + <name name="results" arity="1" since=""/> <fsummary>Set the number of previous results to keep.</fsummary> <desc> <p>Sets the number of results from previous commands to keep in @@ -921,7 +921,7 @@ q - quit erlang </func> <func> - <name name="start_restricted" arity="1"/> + <name name="start_restricted" arity="1" since=""/> <fsummary>Exit a normal shell and starts a restricted shell.</fsummary> <desc> <p>Exits a normal shell and starts a restricted shell. @@ -936,7 +936,7 @@ q - quit erlang </func> <func> - <name name="stop_restricted" arity="0"/> + <name name="stop_restricted" arity="0" since=""/> <fsummary>Exit a restricted shell and starts a normal shell.</fsummary> <desc> <p>Exits a restricted shell and starts a normal shell. The function @@ -945,7 +945,7 @@ q - quit erlang </func> <func> - <name name="strings" arity="1"/> + <name name="strings" arity="1" since="OTP R16B"/> <fsummary>Set the shell's string recognition flag.</fsummary> <desc> <p>Sets pretty printing of lists to <c><anno>Strings</anno></c>. diff --git a/lib/stdlib/doc/src/slave.xml b/lib/stdlib/doc/src/slave.xml index e53ec8231b..80fb28b548 100644 --- a/lib/stdlib/doc/src/slave.xml +++ b/lib/stdlib/doc/src/slave.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>slave</module> + <module since="">slave</module> <modulesummary>Functions for starting and controlling slave nodes. </modulesummary> <description> @@ -68,7 +68,7 @@ <funcs> <func> - <name>pseudo([Master | ServerList]) -> ok</name> + <name since="">pseudo([Master | ServerList]) -> ok</name> <fsummary>Start a number of pseudo servers.</fsummary> <type> <v>Master = node()</v> @@ -84,7 +84,7 @@ </func> <func> - <name name="pseudo" arity="2"/> + <name name="pseudo" arity="2" since=""/> <fsummary>Start a number of pseudo servers.</fsummary> <desc> <p>Starts a number of pseudo servers. A pseudo server is a @@ -102,7 +102,7 @@ rpc:call(N, slave, pseudo, [node(), [pxw_server]]).</code> </func> <func> - <name name="relay" arity="1"/> + <name name="relay" arity="1" since=""/> <fsummary>Run a pseudo server.</fsummary> <desc> <p>Runs a pseudo server. This function never returns any value @@ -113,9 +113,9 @@ rpc:call(N, slave, pseudo, [node(), [pxw_server]]).</code> </func> <func> - <name name="start" arity="1"/> - <name name="start" arity="2"/> - <name name="start" arity="3"/> + <name name="start" arity="1" since=""/> + <name name="start" arity="2" since=""/> + <name name="start" arity="3" since=""/> <fsummary>Start a slave node on a host.</fsummary> <desc> <p>Starts a slave node on host <c><anno>Host</anno></c>. Host names @@ -178,9 +178,9 @@ slave:start(H, Name, Arg).</code> </func> <func> - <name name="start_link" arity="1"/> - <name name="start_link" arity="2"/> - <name name="start_link" arity="3"/> + <name name="start_link" arity="1" since=""/> + <name name="start_link" arity="2" since=""/> + <name name="start_link" arity="3" since=""/> <fsummary>Start and link to a slave node on a host.</fsummary> <desc> <p>Starts a slave node in the same way as <c>start/1,2,3</c>, @@ -193,7 +193,7 @@ slave:start(H, Name, Arg).</code> </func> <func> - <name name="stop" arity="1"/> + <name name="stop" arity="1" since=""/> <fsummary>Stop (kill) a node.</fsummary> <desc> <p>Stops (kills) a node.</p> diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml index 4cf1984d46..a0759d2f52 100644 --- a/lib/stdlib/doc/src/sofs.xml +++ b/lib/stdlib/doc/src/sofs.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>sofs.xml</file> </header> - <module>sofs</module> + <module since="">sofs</module> <modulesummary>Functions for manipulating sets of sets.</modulesummary> <description> <p>This module provides operations on finite sets and @@ -456,8 +456,8 @@ fun(S) -> sofs:partition(1, S) end <funcs> <func> - <name name="a_function" arity="1"/> - <name name="a_function" arity="2"/> + <name name="a_function" arity="1" since=""/> + <name name="a_function" arity="2" since=""/> <fsummary>Create a function.</fsummary> <desc> <p>Creates a <seealso marker="#function">function</seealso>. @@ -470,7 +470,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="canonical_relation" arity="1"/> + <name name="canonical_relation" arity="1" since=""/> <fsummary>Return the canonical map.</fsummary> <desc> <p>Returns the binary relation containing the elements @@ -490,7 +490,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="composite" arity="2"/> + <name name="composite" arity="2" since=""/> <fsummary>Return the composite of two functions.</fsummary> <desc> <p>Returns the <seealso marker="#composite">composite</seealso> of @@ -506,7 +506,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="constant_function" arity="2"/> + <name name="constant_function" arity="2" since=""/> <fsummary>Create the function that maps each element of a set onto another set.</fsummary> <desc> @@ -522,7 +522,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="converse" arity="1"/> + <name name="converse" arity="1" since=""/> <fsummary>Return the converse of a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#converse">converse</seealso> @@ -536,7 +536,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="difference" arity="2"/> + <name name="difference" arity="2" since=""/> <fsummary>Return the difference of two sets.</fsummary> <desc> <p>Returns the <seealso marker="#difference">difference</seealso> of @@ -545,8 +545,8 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="digraph_to_family" arity="1"/> - <name name="digraph_to_family" arity="2"/> + <name name="digraph_to_family" arity="1" since=""/> + <name name="digraph_to_family" arity="2" since=""/> <fsummary>Create a family from a directed graph.</fsummary> <desc> <p>Creates a <seealso marker="#family">family</seealso> from @@ -565,7 +565,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="domain" arity="1"/> + <name name="domain" arity="1" since=""/> <fsummary>Return the domain of a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#domain">domain</seealso> of @@ -579,7 +579,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="drestriction" arity="2"/> + <name name="drestriction" arity="2" since=""/> <fsummary>Return a restriction of a binary relation.</fsummary> <desc> <p>Returns the difference between the binary relation @@ -598,7 +598,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="drestriction" arity="3"/> + <name name="drestriction" arity="3" since=""/> <fsummary>Return a restriction of a relation.</fsummary> <desc> <p>Returns a subset of <c><anno>Set1</anno></c> containing those @@ -618,7 +618,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="empty_set" arity="0"/> + <name name="empty_set" arity="0" since=""/> <fsummary>Return the untyped empty set.</fsummary> <desc> <p>Returns the <seealso marker="#sets_definition">untyped empty @@ -628,7 +628,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="extension" arity="3"/> + <name name="extension" arity="3" since=""/> <fsummary>Extend the domain of a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#extension">extension</seealso> of @@ -648,8 +648,8 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family" arity="1"/> - <name name="family" arity="2"/> + <name name="family" arity="1" since=""/> + <name name="family" arity="2" since=""/> <fsummary>Create a family of subsets.</fsummary> <desc> <p>Creates a <seealso marker="#family">family of subsets</seealso>. @@ -662,7 +662,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_difference" arity="2"/> + <name name="family_difference" arity="2" since=""/> <fsummary>Return the difference of two families.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> and <c><anno>Family2</anno></c> @@ -683,7 +683,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_domain" arity="1"/> + <name name="family_domain" arity="1" since=""/> <fsummary>Return a family of domains.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> is @@ -704,7 +704,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_field" arity="1"/> + <name name="family_field" arity="1" since=""/> <fsummary>Return a family of fields.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> is @@ -728,7 +728,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_intersection" arity="1"/> + <name name="family_intersection" arity="1" since=""/> <fsummary>Return the intersection of a family of sets of sets.</fsummary> <desc> @@ -752,7 +752,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_intersection" arity="2"/> + <name name="family_intersection" arity="2" since=""/> <fsummary>Return the intersection of two families.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> and <c><anno>Family2</anno></c> @@ -772,7 +772,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_projection" arity="2"/> + <name name="family_projection" arity="2" since=""/> <fsummary>Return a family of modified subsets.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> is @@ -791,7 +791,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_range" arity="1"/> + <name name="family_range" arity="1" since=""/> <fsummary>Return a family of ranges.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> is @@ -812,7 +812,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_specification" arity="2"/> + <name name="family_specification" arity="2" since=""/> <fsummary>Select a subset of a family using a predicate.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> is @@ -837,8 +837,8 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_to_digraph" arity="1"/> - <name name="family_to_digraph" arity="2"/> + <name name="family_to_digraph" arity="1" since=""/> + <name name="family_to_digraph" arity="2" since=""/> <fsummary>Create a directed graph from a family.</fsummary> <desc> <p>Creates a directed graph from @@ -863,7 +863,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_to_relation" arity="1"/> + <name name="family_to_relation" arity="1" since=""/> <fsummary>Create a binary relation from a family.</fsummary> <desc> <p>If <c><anno>Family</anno></c> is @@ -881,7 +881,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_union" arity="1"/> + <name name="family_union" arity="1" since=""/> <fsummary>Return the union of a family of sets of sets.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> is @@ -904,7 +904,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="family_union" arity="2"/> + <name name="family_union" arity="2" since=""/> <fsummary>Return the union of two families.</fsummary> <desc> <p>If <c><anno>Family1</anno></c> and <c><anno>Family2</anno></c> @@ -926,7 +926,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="field" arity="1"/> + <name name="field" arity="1" since=""/> <fsummary>Return the field of a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#field">field</seealso> of the @@ -942,7 +942,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="from_external" arity="2"/> + <name name="from_external" arity="2" since=""/> <fsummary>Create a set.</fsummary> <desc> <p>Creates a set from the <seealso marker="#external_set">external @@ -955,7 +955,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="from_sets" arity="1" clause_i="1"/> + <name name="from_sets" arity="1" clause_i="1" since=""/> <fsummary>Create a set out of a list of sets.</fsummary> <desc> <p>Returns the <seealso marker="#sets_definition">unordered @@ -971,7 +971,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="from_sets" arity="1" clause_i="2"/> + <name name="from_sets" arity="1" clause_i="2" since=""/> <fsummary>Create an ordered set out of a tuple of sets.</fsummary> <desc> <p>Returns the <seealso marker="#sets_definition">ordered @@ -981,8 +981,8 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="from_term" arity="1"/> - <name name="from_term" arity="2"/> + <name name="from_term" arity="1" since=""/> + <name name="from_term" arity="2" since=""/> <fsummary>Create a set.</fsummary> <desc> <p><marker id="from_term"></marker>Creates an element @@ -1031,7 +1031,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="image" arity="2"/> + <name name="image" arity="2" since=""/> <fsummary>Return the image of a set under a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#image">image</seealso> of @@ -1047,7 +1047,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="intersection" arity="1"/> + <name name="intersection" arity="1" since=""/> <fsummary>Return the intersection of a set of sets.</fsummary> <desc> <p>Returns @@ -1059,7 +1059,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="intersection" arity="2"/> + <name name="intersection" arity="2" since=""/> <fsummary>Return the intersection of two sets.</fsummary> <desc> <p>Returns @@ -1069,7 +1069,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="intersection_of_family" arity="1"/> + <name name="intersection_of_family" arity="1" since=""/> <fsummary>Return the intersection of a family.</fsummary> <desc> <p>Returns the intersection of @@ -1086,7 +1086,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="inverse" arity="1"/> + <name name="inverse" arity="1" since=""/> <fsummary>Return the inverse of a function.</fsummary> <desc> <p>Returns the <seealso marker="#inverse">inverse</seealso> @@ -1100,7 +1100,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="inverse_image" arity="2"/> + <name name="inverse_image" arity="2" since=""/> <fsummary>Return the inverse image of a set under a binary relation.</fsummary> <desc> @@ -1117,7 +1117,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="is_a_function" arity="1"/> + <name name="is_a_function" arity="1" since=""/> <fsummary>Test for a function.</fsummary> <desc> <p>Returns <c>true</c> if the binary relation <c><anno>BinRel</anno></c> @@ -1127,7 +1127,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="is_disjoint" arity="2"/> + <name name="is_disjoint" arity="2" since=""/> <fsummary>Test for disjoint sets.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set1</anno></c> @@ -1138,7 +1138,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="is_empty_set" arity="1"/> + <name name="is_empty_set" arity="1" since=""/> <fsummary>Test for an empty set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>AnySet</anno></c> is an empty @@ -1147,7 +1147,7 @@ fun(S) -> sofs:partition(1, S) end </func> <func> - <name name="is_equal" arity="2"/> + <name name="is_equal" arity="2" since=""/> <fsummary>Test two sets for equality.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>AnySet1</anno></c> @@ -1164,7 +1164,7 @@ true</pre> </func> <func> - <name name="is_set" arity="1"/> + <name name="is_set" arity="1" since=""/> <fsummary>Test for an unordered set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>AnySet</anno></c> is @@ -1175,7 +1175,7 @@ true</pre> </func> <func> - <name name="is_sofs_set" arity="1"/> + <name name="is_sofs_set" arity="1" since=""/> <fsummary>Test for an unordered set.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is @@ -1185,7 +1185,7 @@ true</pre> </func> <func> - <name name="is_subset" arity="2"/> + <name name="is_subset" arity="2" since=""/> <fsummary>Test two sets for subset.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Set1</anno></c> is @@ -1195,7 +1195,7 @@ true</pre> </func> <func> - <name name="is_type" arity="1"/> + <name name="is_type" arity="1" since=""/> <fsummary>Test for a type.</fsummary> <desc> <p>Returns <c>true</c> if term <c><anno>Term</anno></c> is @@ -1204,7 +1204,7 @@ true</pre> </func> <func> - <name name="join" arity="4"/> + <name name="join" arity="4" since=""/> <fsummary>Return the join of two relations.</fsummary> <desc> <p>Returns the <seealso marker="#natural_join">natural @@ -1221,7 +1221,7 @@ true</pre> </func> <func> - <name name="multiple_relative_product" arity="2"/> + <name name="multiple_relative_product" arity="2" since=""/> <fsummary>Return the multiple relative product of a tuple of binary relations and a relation.</fsummary> <desc> @@ -1242,7 +1242,7 @@ true</pre> </func> <func> - <name name="no_elements" arity="1"/> + <name name="no_elements" arity="1" since=""/> <fsummary>Return the number of elements of a set.</fsummary> <desc> <p>Returns the number of elements of the ordered or unordered @@ -1251,7 +1251,7 @@ true</pre> </func> <func> - <name name="partition" arity="1"/> + <name name="partition" arity="1" since=""/> <fsummary>Return the coarsest partition given a set of sets.</fsummary> <desc> <p>Returns the <seealso marker="#partition">partition</seealso> of @@ -1268,7 +1268,7 @@ true</pre> </func> <func> - <name name="partition" arity="2"/> + <name name="partition" arity="2" since=""/> <fsummary>Return a partition of a set.</fsummary> <desc> <p>Returns the <seealso marker="#partition">partition</seealso> of @@ -1284,7 +1284,7 @@ true</pre> </func> <func> - <name name="partition" arity="3"/> + <name name="partition" arity="3" since=""/> <fsummary>Return a partition of a set.</fsummary> <desc> <p>Returns a pair of sets that, regarded as constituting a @@ -1307,7 +1307,7 @@ true</pre> </func> <func> - <name name="partition_family" arity="2"/> + <name name="partition_family" arity="2" since=""/> <fsummary>Return a family indexing a partition.</fsummary> <desc> <p>Returns <seealso marker="#family">family</seealso> @@ -1328,7 +1328,7 @@ true</pre> </func> <func> - <name name="product" arity="1"/> + <name name="product" arity="1" since=""/> <fsummary>Return the Cartesian product of a tuple of sets.</fsummary> <desc> <p>Returns the <seealso marker="#Cartesian_product_tuple">Cartesian @@ -1347,7 +1347,7 @@ true</pre> </func> <func> - <name name="product" arity="2"/> + <name name="product" arity="2" since=""/> <fsummary>Return the Cartesian product of two sets.</fsummary> <desc> <p>Returns the <seealso marker="#Cartesian_product">Cartesian @@ -1365,7 +1365,7 @@ true</pre> </func> <func> - <name name="projection" arity="2"/> + <name name="projection" arity="2" since=""/> <fsummary>Return a set of substituted elements.</fsummary> <desc> <p>Returns the set created by substituting each element of @@ -1384,7 +1384,7 @@ true</pre> </func> <func> - <name name="range" arity="1"/> + <name name="range" arity="1" since=""/> <fsummary>Return the range of a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#range">range</seealso> of the @@ -1398,8 +1398,8 @@ true</pre> </func> <func> - <name name="relation" arity="1"/> - <name name="relation" arity="2"/> + <name name="relation" arity="1" since=""/> + <name name="relation" arity="2" since=""/> <fsummary>Create a relation.</fsummary> <desc> <p>Creates a <seealso marker="#relation">relation</seealso>. @@ -1417,7 +1417,7 @@ true</pre> </func> <func> - <name name="relation_to_family" arity="1"/> + <name name="relation_to_family" arity="1" since=""/> <fsummary>Create a family from a binary relation.</fsummary> <desc> <p>Returns <seealso marker="#family">family</seealso> @@ -1435,8 +1435,8 @@ true</pre> </func> <func> - <name name="relative_product" arity="1"/> - <name name="relative_product" arity="2" clause_i="1"/> + <name name="relative_product" arity="1" since=""/> + <name name="relative_product" arity="2" clause_i="1" since=""/> <fsummary>Return the relative product of a list of binary relations and a binary relation.</fsummary> <desc> @@ -1466,7 +1466,7 @@ true</pre> </func> <func> - <name name="relative_product" arity="2" clause_i="2"/> + <name name="relative_product" arity="2" clause_i="2" since=""/> <fsummary>Return the relative product of two binary relations.</fsummary> <desc> @@ -1477,7 +1477,7 @@ true</pre> </func> <func> - <name name="relative_product1" arity="2"/> + <name name="relative_product1" arity="2" since=""/> <fsummary>Return the relative_product of two binary relations.</fsummary> <desc> @@ -1498,7 +1498,7 @@ true</pre> </func> <func> - <name name="restriction" arity="2"/> + <name name="restriction" arity="2" since=""/> <fsummary>Return a restriction of a binary relation.</fsummary> <desc> <p>Returns the <seealso marker="#restriction">restriction</seealso> of @@ -1514,7 +1514,7 @@ true</pre> </func> <func> - <name name="restriction" arity="3"/> + <name name="restriction" arity="3" since=""/> <fsummary>Return a restriction of a set.</fsummary> <desc> <p>Returns a subset of <c><anno>Set1</anno></c> containing those @@ -1530,8 +1530,8 @@ true</pre> </func> <func> - <name name="set" arity="1"/> - <name name="set" arity="2"/> + <name name="set" arity="1" since=""/> + <name name="set" arity="2" since=""/> <fsummary>Create a set of atoms or any type of sets.</fsummary> <desc> <p>Creates an <seealso marker="#sets_definition">unordered @@ -1543,7 +1543,7 @@ true</pre> </func> <func> - <name name="specification" arity="2"/> + <name name="specification" arity="2" since=""/> <fsummary>Select a subset using a predicate.</fsummary> <desc> <p>Returns the set containing every element @@ -1564,7 +1564,7 @@ true</pre> </func> <func> - <name name="strict_relation" arity="1"/> + <name name="strict_relation" arity="1" since=""/> <fsummary>Return the strict relation corresponding to a given relation.</fsummary> <desc> @@ -1580,7 +1580,7 @@ true</pre> </func> <func> - <name name="substitution" arity="2"/> + <name name="substitution" arity="2" since=""/> <fsummary>Return a function with a given set as domain.</fsummary> <desc> <p>Returns a function, the domain of which @@ -1629,7 +1629,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="symdiff" arity="2"/> + <name name="symdiff" arity="2" since=""/> <fsummary>Return the symmetric difference of two sets.</fsummary> <desc> <p>Returns the <seealso marker="#symmetric_difference">symmetric @@ -1645,7 +1645,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="symmetric_partition" arity="2"/> + <name name="symmetric_partition" arity="2" since=""/> <fsummary>Return a partition of two sets.</fsummary> <desc> <p>Returns a triple of sets:</p> @@ -1666,7 +1666,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="to_external" arity="1"/> + <name name="to_external" arity="1" since=""/> <fsummary>Return the elements of a set.</fsummary> <desc> <p>Returns the <seealso marker="#external_set">external @@ -1675,7 +1675,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="to_sets" arity="1"/> + <name name="to_sets" arity="1" since=""/> <fsummary>Return a list or a tuple of the elements of a set.</fsummary> <desc> <p>Returns the elements of the ordered set <c><anno>ASet</anno></c> @@ -1686,7 +1686,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="type" arity="1"/> + <name name="type" arity="1" since=""/> <fsummary>Return the type of a set.</fsummary> <desc> <p>Returns the <seealso marker="#type">type</seealso> of an @@ -1695,7 +1695,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="union" arity="1"/> + <name name="union" arity="1" since=""/> <fsummary>Return the union of a set of sets.</fsummary> <desc> <p>Returns the <seealso marker="#union_n">union</seealso> of the @@ -1704,7 +1704,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="union" arity="2"/> + <name name="union" arity="2" since=""/> <fsummary>Return the union of two sets.</fsummary> <desc> <p>Returns the <seealso marker="#union">union</seealso> of @@ -1713,7 +1713,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="union_of_family" arity="1"/> + <name name="union_of_family" arity="1" since=""/> <fsummary>Return the union of a family.</fsummary> <desc> <p>Returns the union of <seealso marker="#family">family</seealso> @@ -1727,7 +1727,7 @@ images2(SetOfSets, BinRel) -> </func> <func> - <name name="weak_relation" arity="1"/> + <name name="weak_relation" arity="1" since=""/> <fsummary>Return the weak relation corresponding to a given relation.</fsummary> <desc> diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml index 3348464eba..d102191a57 100644 --- a/lib/stdlib/doc/src/string.xml +++ b/lib/stdlib/doc/src/string.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>string.xml</file> </header> - <module>string</module> + <module since="">string</module> <modulesummary>String processing functions.</modulesummary> <description> <p>This module provides functions for string processing.</p> @@ -130,7 +130,7 @@ <funcs> <func> - <name name="casefold" arity="1"/> + <name name="casefold" arity="1" since="OTP 20.0"/> <fsummary>Convert a string to a comparable string.</fsummary> <desc> <p> @@ -147,7 +147,7 @@ </func> <func> - <name name="chomp" arity="1"/> + <name name="chomp" arity="1" since="OTP 20.0"/> <fsummary>Remove trailing end of line control characters.</fsummary> <desc> <p> @@ -164,9 +164,9 @@ </func> <func> - <name name="equal" arity="2"/> - <name name="equal" arity="3"/> - <name name="equal" arity="4"/> + <name name="equal" arity="2" since=""/> + <name name="equal" arity="3" since="OTP 20.0"/> + <name name="equal" arity="4" since="OTP 20.0"/> <fsummary>Test string equality.</fsummary> <desc> <p> @@ -201,8 +201,8 @@ true</pre> </func> <func> - <name name="find" arity="2"/> - <name name="find" arity="3"/> + <name name="find" arity="2" since="OTP 20.0"/> + <name name="find" arity="3" since="OTP 20.0"/> <fsummary>Find start of substring.</fsummary> <desc> <p> @@ -230,7 +230,7 @@ nomatch</pre> </func> <func> - <name name="is_empty" arity="1"/> + <name name="is_empty" arity="1" since="OTP 20.0"/> <fsummary>Check if the string is empty.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>String</anno></c> is the @@ -245,7 +245,7 @@ true</pre> </func> <func> - <name name="length" arity="1"/> + <name name="length" arity="1" since="OTP 20.0"/> <fsummary>Calculate length of the string.</fsummary> <desc> <p> @@ -261,7 +261,7 @@ true</pre> </func> <func> - <name name="lexemes" arity="2"/> + <name name="lexemes" arity="2" since="OTP 20.0"/> <fsummary>Split string into lexemes.</fsummary> <desc> <p> @@ -287,7 +287,7 @@ true</pre> </func> <func> - <name name="lowercase" arity="1"/> + <name name="lowercase" arity="1" since="OTP 20.0"/> <fsummary>Convert a string to lowercase</fsummary> <desc> <p> @@ -306,7 +306,7 @@ true</pre> </func> <func> - <name name="next_codepoint" arity="1"/> + <name name="next_codepoint" arity="1" since="OTP 20.0"/> <fsummary>Pick the first codepoint.</fsummary> <desc> <p> @@ -323,7 +323,7 @@ true</pre> </func> <func> - <name name="next_grapheme" arity="1"/> + <name name="next_grapheme" arity="1" since="OTP 20.0"/> <fsummary>Pick the first grapheme cluster.</fsummary> <desc> <p> @@ -340,7 +340,7 @@ true</pre> </func> <func> - <name name="nth_lexeme" arity="3"/> + <name name="nth_lexeme" arity="3" since="OTP 20.0"/> <fsummary>Pick the nth lexeme.</fsummary> <desc> <p>Returns lexeme number <c><anno>N</anno></c> in @@ -355,9 +355,9 @@ true</pre> </func> <func> - <name name="pad" arity="2"/> - <name name="pad" arity="3"/> - <name name="pad" arity="4"/> + <name name="pad" arity="2" since="OTP 20.0"/> + <name name="pad" arity="3" since="OTP 20.0"/> + <name name="pad" arity="4" since="OTP 20.0"/> <fsummary>Pad a string to given length.</fsummary> <desc> <p> @@ -381,7 +381,7 @@ true</pre> </func> <func> - <name name="prefix" arity="2"/> + <name name="prefix" arity="2" since="OTP 20.0"/> <fsummary>Remove prefix from string.</fsummary> <desc> <p> @@ -400,8 +400,8 @@ nomatch</pre> </func> <func> - <name name="replace" arity="3"/> - <name name="replace" arity="4"/> + <name name="replace" arity="3" since="OTP 20.0"/> + <name name="replace" arity="4" since="OTP 20.0"/> <fsummary>Replace a pattern in string.</fsummary> <desc> <p> @@ -423,7 +423,7 @@ nomatch</pre> </func> <func> - <name name="reverse" arity="1"/> + <name name="reverse" arity="1" since="OTP 20.0"/> <fsummary>Reverses a string</fsummary> <desc> <p> @@ -439,8 +439,8 @@ ÖÄÅ</pre> </func> <func> - <name name="slice" arity="2"/> - <name name="slice" arity="3"/> + <name name="slice" arity="2" since="OTP 20.0"/> + <name name="slice" arity="3" since="OTP 20.0"/> <fsummary>Extract a part of string</fsummary> <desc> <p>Returns a substring of <c><anno>String</anno></c> of @@ -459,8 +459,8 @@ ÖÄÅ</pre> </func> <func> - <name name="split" arity="2"/> - <name name="split" arity="3"/> + <name name="split" arity="2" since="OTP 20.0"/> + <name name="split" arity="3" since="OTP 20.0"/> <fsummary>Split a string into substrings.</fsummary> <desc> <p> @@ -482,9 +482,9 @@ ÖÄÅ</pre> </func> <func> - <name name="take" arity="2"/> - <name name="take" arity="3"/> - <name name="take" arity="4"/> + <name name="take" arity="2" since="OTP 20.0"/> + <name name="take" arity="3" since="OTP 20.0"/> + <name name="take" arity="4" since="OTP 20.0"/> <fsummary>Take leading or trailing parts.</fsummary> <desc> <p>Takes characters from <c><anno>String</anno></c> as long as @@ -508,7 +508,7 @@ ÖÄÅ</pre> </func> <func> - <name name="titlecase" arity="1"/> + <name name="titlecase" arity="1" since="OTP 20.0"/> <fsummary>Convert a string to titlecase.</fsummary> <desc> <p> @@ -522,7 +522,7 @@ ÖÄÅ</pre> </func> <func> - <name name="to_float" arity="1"/> + <name name="to_float" arity="1" since=""/> <fsummary>Return a float whose text representation is the integers (ASCII values) of a string.</fsummary> <desc> @@ -544,7 +544,7 @@ ÖÄÅ</pre> </func> <func> - <name name="to_integer" arity="1"/> + <name name="to_integer" arity="1" since=""/> <fsummary>Return an integer whose text representation is the integers (ASCII values) of a string.</fsummary> <desc> @@ -566,7 +566,7 @@ ÖÄÅ</pre> </func> <func> - <name name="to_graphemes" arity="1"/> + <name name="to_graphemes" arity="1" since="OTP 20.0"/> <fsummary>Convert a string to a list of grapheme clusters.</fsummary> <desc> <p> @@ -582,9 +582,9 @@ ÖÄÅ</pre> </func> <func> - <name name="trim" arity="1"/> - <name name="trim" arity="2"/> - <name name="trim" arity="3"/> + <name name="trim" arity="1" since="OTP 20.0"/> + <name name="trim" arity="2" since="OTP 20.0"/> + <name name="trim" arity="3" since="OTP 20.0"/> <fsummary>Trim leading or trailing, or both, characters.</fsummary> <desc> <p> @@ -616,7 +616,7 @@ ÖÄÅ</pre> </func> <func> - <name name="uppercase" arity="1"/> + <name name="uppercase" arity="1" since="OTP 20.0"/> <fsummary>Convert a string to uppercase.</fsummary> <desc> <p> @@ -649,8 +649,8 @@ ÖÄÅ</pre> <funcs> <func> - <name name="centre" arity="2"/> - <name name="centre" arity="3"/> + <name name="centre" arity="2" since=""/> + <name name="centre" arity="3" since=""/> <fsummary>Center a string.</fsummary> <desc> <p>Returns a string, where <c><anno>String</anno></c> is centered in the @@ -664,8 +664,8 @@ ÖÄÅ</pre> </func> <func> - <name name="chars" arity="2"/> - <name name="chars" arity="3"/> + <name name="chars" arity="2" since=""/> + <name name="chars" arity="3" since=""/> <fsummary>Return a string consisting of numbers of characters.</fsummary> <desc> <p>Returns a string consisting of <c><anno>Number</anno></c> characters @@ -678,7 +678,7 @@ ÖÄÅ</pre> </func> <func> - <name name="chr" arity="2"/> + <name name="chr" arity="2" since=""/> <fsummary>Return the index of the first occurrence of a character in a string.</fsummary> <desc> @@ -692,7 +692,7 @@ ÖÄÅ</pre> </func> <func> - <name name="concat" arity="2"/> + <name name="concat" arity="2" since=""/> <fsummary>Concatenate two strings.</fsummary> <desc> <p>Concatenates <c><anno>String1</anno></c> and @@ -712,7 +712,7 @@ ÖÄÅ</pre> </func> <func> - <name name="copies" arity="2"/> + <name name="copies" arity="2" since=""/> <fsummary>Copy a string.</fsummary> <desc> <p>Returns a string containing <c><anno>String</anno></c> repeated @@ -724,7 +724,7 @@ ÖÄÅ</pre> </func> <func> - <name name="cspan" arity="2"/> + <name name="cspan" arity="2" since=""/> <fsummary>Span characters at start of a string.</fsummary> <desc> <p>Returns the length of the maximum initial segment of @@ -741,7 +741,7 @@ ÖÄÅ</pre> </func> <func> - <name name="join" arity="2"/> + <name name="join" arity="2" since=""/> <fsummary>Join a list of strings with separator.</fsummary> <desc> <p>Returns a string with the elements of <c><anno>StringList</anno></c> @@ -757,8 +757,8 @@ ÖÄÅ</pre> </func> <func> - <name name="left" arity="2"/> - <name name="left" arity="3"/> + <name name="left" arity="2" since=""/> + <name name="left" arity="3" since=""/> <fsummary>Adjust left end of a string.</fsummary> <desc> <p>Returns <c><anno>String</anno></c> with the length adjusted in @@ -778,7 +778,7 @@ ÖÄÅ</pre> </func> <func> - <name name="len" arity="1"/> + <name name="len" arity="1" since=""/> <fsummary>Return the length of a string.</fsummary> <desc> <p>Returns the number of characters in <c><anno>String</anno></c>.</p> @@ -789,7 +789,7 @@ ÖÄÅ</pre> </func> <func> - <name name="rchr" arity="2"/> + <name name="rchr" arity="2" since=""/> <fsummary>Return the index of the last occurrence of a character in a string.</fsummary> <desc> @@ -803,8 +803,8 @@ ÖÄÅ</pre> </func> <func> - <name name="right" arity="2"/> - <name name="right" arity="3"/> + <name name="right" arity="2" since=""/> + <name name="right" arity="3" since=""/> <fsummary>Adjust right end of a string.</fsummary> <desc> <p>Returns <c><anno>String</anno></c> with the length adjusted in @@ -823,7 +823,7 @@ ÖÄÅ</pre> </func> <func> - <name name="rstr" arity="2"/> + <name name="rstr" arity="2" since=""/> <fsummary>Find the index of a substring.</fsummary> <desc> <p>Returns the position where the last occurrence of @@ -841,7 +841,7 @@ ÖÄÅ</pre> </func> <func> - <name name="span" arity="2"/> + <name name="span" arity="2" since=""/> <fsummary>Span characters at start of a string.</fsummary> <desc> <p>Returns the length of the maximum initial segment of @@ -858,7 +858,7 @@ ÖÄÅ</pre> </func> <func> - <name name="str" arity="2"/> + <name name="str" arity="2" since=""/> <fsummary>Find the index of a substring.</fsummary> <desc> <p>Returns the position where the first occurrence of @@ -876,9 +876,9 @@ ÖÄÅ</pre> </func> <func> - <name name="strip" arity="1"/> - <name name="strip" arity="2"/> - <name name="strip" arity="3"/> + <name name="strip" arity="1" since=""/> + <name name="strip" arity="2" since=""/> + <name name="strip" arity="3" since=""/> <fsummary>Strip leading or trailing characters.</fsummary> <desc> <p>Returns a string, where leading or trailing, or both, blanks or a @@ -898,8 +898,8 @@ ÖÄÅ</pre> </func> <func> - <name name="sub_string" arity="2"/> - <name name="sub_string" arity="3"/> + <name name="sub_string" arity="2" since=""/> + <name name="sub_string" arity="3" since=""/> <fsummary>Extract a substring.</fsummary> <desc> <p>Returns a substring of <c><anno>String</anno></c>, starting at @@ -916,8 +916,8 @@ sub_string("Hello World", 4, 8). </func> <func> - <name name="substr" arity="2"/> - <name name="substr" arity="3"/> + <name name="substr" arity="2" since=""/> + <name name="substr" arity="3" since=""/> <fsummary>Return a substring of a string.</fsummary> <desc> <p>Returns a substring of <c><anno>String</anno></c>, starting at @@ -934,8 +934,8 @@ sub_string("Hello World", 4, 8). </func> <func> - <name name="sub_word" arity="2"/> - <name name="sub_word" arity="3"/> + <name name="sub_word" arity="2" since=""/> + <name name="sub_word" arity="3" since=""/> <fsummary>Extract subword.</fsummary> <desc> <p>Returns the word in position <c><anno>Number</anno></c> of @@ -952,10 +952,10 @@ sub_string("Hello World", 4, 8). </func> <func> - <name name="to_lower" arity="1" clause_i="1"/> - <name name="to_lower" arity="1" clause_i="2"/> - <name name="to_upper" arity="1" clause_i="1"/> - <name name="to_upper" arity="1" clause_i="2"/> + <name name="to_lower" arity="1" clause_i="1" since=""/> + <name name="to_lower" arity="1" clause_i="2" since=""/> + <name name="to_upper" arity="1" clause_i="1" since=""/> + <name name="to_upper" arity="1" clause_i="2" since=""/> <fsummary>Convert case of string (ISO/IEC 8859-1).</fsummary> <type variable="String" name_i="1"/> <type variable="Result" name_i="1"/> @@ -974,7 +974,7 @@ sub_string("Hello World", 4, 8). </func> <func> - <name name="tokens" arity="2"/> + <name name="tokens" arity="2" since=""/> <fsummary>Split string into tokens.</fsummary> <desc> <p>Returns a list of tokens in <c><anno>String</anno></c>, separated @@ -994,8 +994,8 @@ sub_string("Hello World", 4, 8). </func> <func> - <name name="words" arity="1"/> - <name name="words" arity="2"/> + <name name="words" arity="1" since=""/> + <name name="words" arity="2" since=""/> <fsummary>Count blank separated words.</fsummary> <desc> <p>Returns the number of words in <c><anno>String</anno></c>, separated diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index 5fd5760499..f15b1a2dd3 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>supervisor</module> + <module since="">supervisor</module> <modulesummary>Generic supervisor behavior.</modulesummary> <description> <p>This behavior module provides a supervisor, a process that @@ -318,7 +318,7 @@ child_spec() = #{id => child_id(), % mandatory <funcs> <func> - <name name="check_childspecs" arity="1"/> + <name name="check_childspecs" arity="1" since=""/> <fsummary>Check if children specifications are syntactically correct. </fsummary> <desc> @@ -329,7 +329,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="count_children" arity="1"/> + <name name="count_children" arity="1" since="OTP R13B04"/> <fsummary>Return counts for the number of child specifications, active children, supervisors, and workers.</fsummary> <desc> @@ -366,7 +366,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="delete_child" arity="2"/> + <name name="delete_child" arity="2" since=""/> <fsummary>Delete a child specification from a supervisor.</fsummary> <desc> <p>Tells supervisor <c><anno>SupRef</anno></c> to delete the child @@ -387,7 +387,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="get_childspec" arity="2"/> + <name name="get_childspec" arity="2" since="OTP 18.0"/> <fsummary>Return the child specification map for the specified child.</fsummary> <desc> @@ -400,7 +400,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="restart_child" arity="2"/> + <name name="restart_child" arity="2" since=""/> <fsummary>Restart a terminated child process belonging to a supervisor. </fsummary> <desc> @@ -436,7 +436,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="start_child" arity="2"/> + <name name="start_child" arity="2" since=""/> <fsummary>Dynamically add a child process to a supervisor.</fsummary> <type name="startchild_ret"/> <type name="startchild_err"/> @@ -503,8 +503,8 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="start_link" arity="2"/> - <name name="start_link" arity="3"/> + <name name="start_link" arity="2" since=""/> + <name name="start_link" arity="3" since=""/> <fsummary>Create a supervisor process.</fsummary> <type name="startlink_ret"/> <type name="startlink_err"/> @@ -584,7 +584,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="terminate_child" arity="2"/> + <name name="terminate_child" arity="2" since=""/> <fsummary>Terminate a child process belonging to a supervisor.</fsummary> <desc> <p>Tells supervisor <c><anno>SupRef</anno></c> to terminate the @@ -621,7 +621,7 @@ child_spec() = #{id => child_id(), % mandatory </func> <func> - <name name="which_children" arity="1"/> + <name name="which_children" arity="1" since=""/> <fsummary>Return information about all children specifications and child processes belonging to a supervisor.</fsummary> <desc> @@ -666,7 +666,7 @@ child_spec() = #{id => child_id(), % mandatory <funcs> <func> - <name>Module:init(Args) -> Result</name> + <name since="">Module:init(Args) -> Result</name> <fsummary>Return a supervisor specification.</fsummary> <type> <v>Args = term()</v> diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml index c4c1b37548..ee5d97fea1 100644 --- a/lib/stdlib/doc/src/supervisor_bridge.xml +++ b/lib/stdlib/doc/src/supervisor_bridge.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>supervisor_bridge</module> + <module since="">supervisor_bridge</module> <modulesummary>Generic supervisor bridge behavior.</modulesummary> <description> <p>This behavior module provides a supervisor bridge, a process @@ -57,8 +57,8 @@ <funcs> <func> - <name name="start_link" arity="2"/> - <name name="start_link" arity="3"/> + <name name="start_link" arity="2" since=""/> + <name name="start_link" arity="3" since=""/> <fsummary>Create a supervisor bridge process.</fsummary> <desc> <p>Creates a supervisor bridge process, linked to the calling process, @@ -133,7 +133,7 @@ <funcs> <func> - <name>Module:init(Args) -> Result</name> + <name since="">Module:init(Args) -> Result</name> <fsummary>Initialize process and start subsystem.</fsummary> <type> <v>Args = term()</v> @@ -164,7 +164,7 @@ </func> <func> - <name>Module:terminate(Reason, State)</name> + <name since="">Module:terminate(Reason, State)</name> <fsummary>Clean up and stop subsystem.</fsummary> <type> <v>Reason = shutdown | term()</v> diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index 9fe816e33a..6fc508b853 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -32,7 +32,7 @@ <rev></rev> <file>sys.xml</file> </header> - <module>sys</module> + <module since="">sys</module> <modulesummary>A functional interface to system messages.</modulesummary> <description> <p>This module contains functions for sending system messages used by @@ -129,8 +129,8 @@ <funcs> <func> - <name name="change_code" arity="4"/> - <name name="change_code" arity="5"/> + <name name="change_code" arity="4" since=""/> + <name name="change_code" arity="5" since=""/> <fsummary>Send the code change system message to the process.</fsummary> <desc> <p>Tells the process to change code. The process must be @@ -143,8 +143,8 @@ </func> <func> - <name name="get_state" arity="1"/> - <name name="get_state" arity="2"/> + <name name="get_state" arity="1" since="OTP R16B01"/> + <name name="get_state" arity="2" since="OTP R16B01"/> <fsummary>Get the state of the process.</fsummary> <desc> <p>Gets the state of the process.</p> @@ -227,8 +227,8 @@ </func> <func> - <name name="get_status" arity="1"/> - <name name="get_status" arity="2"/> + <name name="get_status" arity="1" since=""/> + <name name="get_status" arity="2" since=""/> <fsummary>Get the status of the process.</fsummary> <desc> <p>Gets the status of the process.</p> @@ -265,8 +265,8 @@ </func> <func> - <name name="install" arity="2"/> - <name name="install" arity="3"/> + <name name="install" arity="2" since=""/> + <name name="install" arity="3" since=""/> <fsummary>Install a debug function in the process.</fsummary> <desc> <p>Enables installation of alternative debug functions. An example of @@ -283,8 +283,8 @@ </func> <func> - <name name="log" arity="2"/> - <name name="log" arity="3"/> + <name name="log" arity="2" since=""/> + <name name="log" arity="3" since=""/> <fsummary>Log system events in memory.</fsummary> <desc> <p>Turns the logging of system events on or off. If on, a @@ -302,8 +302,8 @@ </func> <func> - <name name="log_to_file" arity="2"/> - <name name="log_to_file" arity="3"/> + <name name="log_to_file" arity="2" since=""/> + <name name="log_to_file" arity="3" since=""/> <fsummary>Log system events to the specified file.</fsummary> <desc> <p>Enables or disables the logging of all system events in text @@ -315,8 +315,8 @@ </func> <func> - <name name="no_debug" arity="1"/> - <name name="no_debug" arity="2"/> + <name name="no_debug" arity="1" since=""/> + <name name="no_debug" arity="2" since=""/> <fsummary>Turn off debugging.</fsummary> <desc> <p>Turns off all debugging for the process. This includes @@ -327,8 +327,8 @@ </func> <func> - <name name="remove" arity="2"/> - <name name="remove" arity="3"/> + <name name="remove" arity="2" since=""/> + <name name="remove" arity="3" since=""/> <fsummary>Remove a debug function from the process.</fsummary> <desc> <p>Removes an installed debug function from the @@ -338,8 +338,8 @@ </func> <func> - <name name="replace_state" arity="2"/> - <name name="replace_state" arity="3"/> + <name name="replace_state" arity="2" since="OTP R16B01"/> + <name name="replace_state" arity="3" since="OTP R16B01"/> <fsummary>Replace the state of the process.</fsummary> <desc> <p>Replaces the state of the process, and returns the new state.</p> @@ -451,8 +451,8 @@ </func> <func> - <name name="resume" arity="1"/> - <name name="resume" arity="2"/> + <name name="resume" arity="1" since=""/> + <name name="resume" arity="2" since=""/> <fsummary>Resume a suspended process.</fsummary> <desc> <p>Resumes a suspended process.</p> @@ -460,8 +460,8 @@ </func> <func> - <name name="statistics" arity="2"/> - <name name="statistics" arity="3"/> + <name name="statistics" arity="2" since=""/> + <name name="statistics" arity="3" since=""/> <fsummary>Enable or disable the collections of statistics.</fsummary> <desc> <p>Enables or disables the collection of statistics. If @@ -471,8 +471,8 @@ </func> <func> - <name name="suspend" arity="1"/> - <name name="suspend" arity="2"/> + <name name="suspend" arity="1" since=""/> + <name name="suspend" arity="2" since=""/> <fsummary>Suspend the process.</fsummary> <desc> <p>Suspends the process. When the process is suspended, it @@ -482,8 +482,8 @@ </func> <func> - <name name="terminate" arity="2"/> - <name name="terminate" arity="3"/> + <name name="terminate" arity="2" since="OTP 18.0"/> + <name name="terminate" arity="3" since="OTP 18.0"/> <fsummary>Terminate the process.</fsummary> <desc> <p>Orders the process to terminate with the @@ -494,8 +494,8 @@ </func> <func> - <name name="trace" arity="2"/> - <name name="trace" arity="3"/> + <name name="trace" arity="2" since=""/> + <name name="trace" arity="3" since=""/> <fsummary>Print all system events on <c>standard_io</c>.</fsummary> <desc> <p>Prints all system events on <c>standard_io</c>. The events are @@ -518,7 +518,7 @@ <funcs> <func> - <name name="debug_options" arity="1"/> + <name name="debug_options" arity="1" since=""/> <fsummary>Convert a list of options to a debug structure.</fsummary> <desc> <p>Can be used by a process that initiates a debug @@ -529,7 +529,7 @@ </func> <func> - <name name="get_debug" arity="3"/> + <name name="get_debug" arity="3" since=""/> <fsummary>Get the data associated with a debug option.</fsummary> <desc> <p>Gets the data associated with a debug option. @@ -541,7 +541,7 @@ </func> <func> - <name name="handle_debug" arity="4"/> + <name name="handle_debug" arity="4" since=""/> <fsummary>Generate a system event.</fsummary> <desc> <p>This function is called by a process when it generates a @@ -556,7 +556,7 @@ </func> <func> - <name name="handle_system_msg" arity="6"/> + <name name="handle_system_msg" arity="6" since=""/> <fsummary>Take care of system messages.</fsummary> <desc> <p>This function is used by a process module to take care of system @@ -594,7 +594,7 @@ </func> <func> - <name name="print_log" arity="1"/> + <name name="print_log" arity="1" since=""/> <fsummary>Print the logged events in the debug structure.</fsummary> <desc> <p>Prints the logged system events in the debug structure, @@ -605,7 +605,7 @@ </func> <func> - <name>Module:system_code_change(Misc, Module, OldVsn, Extra) -> + <name since="">Module:system_code_change(Misc, Module, OldVsn, Extra) -> {ok, NMisc}</name> <fsummary>Called when the process is to perform a code change.</fsummary> <type> @@ -628,7 +628,7 @@ </func> <func> - <name>Module:system_continue(Parent, Debug, Misc) -> none()</name> + <name since="">Module:system_continue(Parent, Debug, Misc) -> none()</name> <fsummary>Called when the process is to continue its execution.</fsummary> <type> <v>Parent = pid()</v> @@ -644,7 +644,7 @@ </func> <func> - <name>Module:system_get_state(Misc) -> {ok, State}</name> + <name since="OTP 17.0">Module:system_get_state(Misc) -> {ok, State}</name> <fsummary>Called when the process is to return its current state. </fsummary> <type> @@ -661,7 +661,7 @@ </func> <func> - <name>Module:system_replace_state(StateFun, Misc) -> + <name since="OTP 17.0">Module:system_replace_state(StateFun, Misc) -> {ok, NState, NMisc}</name> <fsummary>Called when the process is to replace its current state. </fsummary> @@ -681,7 +681,7 @@ </func> <func> - <name>Module:system_terminate(Reason, Parent, Debug, Misc) -> none()</name> + <name since="">Module:system_terminate(Reason, Parent, Debug, Misc) -> none()</name> <fsummary>Called when the process is to terminate.</fsummary> <type> <v>Reason = term()</v> diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index e913e33589..165eecfbb0 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -32,7 +32,7 @@ <rev>D</rev> <file>timer.xml</file> </header> - <module>timer</module> + <module since="">timer</module> <modulesummary>Timer functions.</modulesummary> <description> <p>This module provides useful functions related to time. Unless otherwise @@ -62,7 +62,7 @@ <funcs> <func> - <name name="apply_after" arity="4"/> + <name name="apply_after" arity="4" since=""/> <fsummary>Apply <c>Module:Function(Arguments)</c> after a specified <c>Time</c>.</fsummary> <desc> @@ -75,7 +75,7 @@ </func> <func> - <name name="apply_interval" arity="4"/> + <name name="apply_interval" arity="4" since=""/> <fsummary>Evaluate <c>Module:Function(Arguments)</c> repeatedly at intervals of <c>Time</c>.</fsummary> <desc> @@ -88,7 +88,7 @@ </func> <func> - <name name="cancel" arity="1"/> + <name name="cancel" arity="1" since=""/> <fsummary>Cancel a previously requested time-out identified by <c>TRef</c>.</fsummary> <desc> @@ -101,8 +101,8 @@ </func> <func> - <name name="exit_after" arity="2"/> - <name name="exit_after" arity="3"/> + <name name="exit_after" arity="2" since=""/> + <name name="exit_after" arity="3" since=""/> <fsummary>Send an exit signal with <c>Reason</c> after a specified <c>Time</c>.</fsummary> <desc> @@ -117,7 +117,7 @@ </func> <func> - <name name="hms" arity="3"/> + <name name="hms" arity="3" since=""/> <fsummary>Convert <c>Hours</c>+<c>Minutes</c>+<c>Seconds</c> to <c>Milliseconds</c>.</fsummary> <desc> @@ -127,7 +127,7 @@ </func> <func> - <name name="hours" arity="1"/> + <name name="hours" arity="1" since=""/> <fsummary>Convert <c>Hours</c> to <c>Milliseconds</c>.</fsummary> <desc> <p>Returns the number of milliseconds in <c><anno>Hours</anno></c>.</p> @@ -135,8 +135,8 @@ </func> <func> - <name name="kill_after" arity="1"/> - <name name="kill_after" arity="2"/> + <name name="kill_after" arity="1" since=""/> + <name name="kill_after" arity="2" since=""/> <fsummary>Send an exit signal with <c>Reason</c> after a specified <c>Time</c>.</fsummary> <desc> @@ -148,7 +148,7 @@ </func> <func> - <name name="minutes" arity="1"/> + <name name="minutes" arity="1" since=""/> <fsummary>Converts <c>Minutes</c> to <c>Milliseconds</c>.</fsummary> <desc> <p>Returns the number of milliseconds in @@ -157,7 +157,7 @@ </func> <func> - <name name="now_diff" arity="2"/> + <name name="now_diff" arity="2" since=""/> <fsummary>Calculate time difference between time stamps.</fsummary> <type_desc variable="Tdiff">In microseconds</type_desc> <desc> @@ -173,7 +173,7 @@ </func> <func> - <name name="seconds" arity="1"/> + <name name="seconds" arity="1" since=""/> <fsummary>Convert <c>Seconds</c> to <c>Milliseconds</c>.</fsummary> <desc> <p>Returns the number of milliseconds in @@ -182,8 +182,8 @@ </func> <func> - <name name="send_after" arity="2"/> - <name name="send_after" arity="3"/> + <name name="send_after" arity="2" since=""/> + <name name="send_after" arity="3" since=""/> <fsummary>Send <c>Message</c> to <c>Pid</c> after a specified <c>Time</c>.</fsummary> <desc> @@ -206,8 +206,8 @@ </func> <func> - <name name="send_interval" arity="2"/> - <name name="send_interval" arity="3"/> + <name name="send_interval" arity="2" since=""/> + <name name="send_interval" arity="3" since=""/> <fsummary>Send <c>Message</c> repeatedly at intervals of <c>Time</c>. </fsummary> <desc> @@ -231,7 +231,7 @@ </func> <func> - <name name="sleep" arity="1"/> + <name name="sleep" arity="1" since=""/> <fsummary>Suspend the calling process for <c>Time</c> milliseconds. </fsummary> <desc> @@ -244,7 +244,7 @@ </func> <func> - <name name="start" arity="0"/> + <name name="start" arity="0" since=""/> <fsummary>Start a global timer server (named <c>timer_server</c>). </fsummary> <desc> @@ -258,9 +258,9 @@ </func> <func> - <name name="tc" arity="1"/> - <name name="tc" arity="2"/> - <name name="tc" arity="3"/> + <name name="tc" arity="1" since="OTP R14B03"/> + <name name="tc" arity="2" since="OTP R14B"/> + <name name="tc" arity="3" since=""/> <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c> or <c>apply(Fun, Arguments)</c>.</fsummary> <type_desc variable="Time">In microseconds</type_desc> diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml index d822aca89c..b7696a4b7e 100644 --- a/lib/stdlib/doc/src/unicode.xml +++ b/lib/stdlib/doc/src/unicode.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>unicode</module> + <module since="">unicode</module> <modulesummary>Functions for converting Unicode characters.</modulesummary> <description> <p>This module contains functions for converting between different character @@ -139,7 +139,7 @@ <funcs> <func> - <name name="bom_to_encoding" arity="1"/> + <name name="bom_to_encoding" arity="1" since=""/> <fsummary>Identify UTF byte order marks in a binary.</fsummary> <type name="endian"/> <type_desc variable="Bin"> @@ -156,7 +156,7 @@ </func> <func> - <name name="characters_to_binary" arity="1"/> + <name name="characters_to_binary" arity="1" since=""/> <fsummary>Convert a collection of characters to a UTF-8 binary.</fsummary> <desc> <p>Same as <c>characters_to_binary(<anno>Data</anno>, unicode, @@ -165,7 +165,7 @@ </func> <func> - <name name="characters_to_binary" arity="2"/> + <name name="characters_to_binary" arity="2" since=""/> <fsummary>Convert a collection of characters to a UTF-8 binary.</fsummary> <desc> <p>Same as <c>characters_to_binary(<anno>Data</anno>, @@ -174,7 +174,7 @@ </func> <func> - <name name="characters_to_binary" arity="3"/> + <name name="characters_to_binary" arity="3" since=""/> <fsummary>Convert a collection of characters to a UTF-8 binary.</fsummary> <desc> <p>Behaves as <seealso marker="#characters_to_list/2"> @@ -211,7 +211,7 @@ </func> <func> - <name name="characters_to_list" arity="1"/> + <name name="characters_to_list" arity="1" since=""/> <fsummary>Convert a collection of characters to a list of Unicode characters.</fsummary> <desc> @@ -220,7 +220,7 @@ </func> <func> - <name name="characters_to_list" arity="2"/> + <name name="characters_to_list" arity="2" since=""/> <fsummary>Convert a collection of characters to a list of Unicode characters.</fsummary> <desc> @@ -367,7 +367,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfc_list" arity="1"/> + <name name="characters_to_nfc_list" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a list of canonical equivalent composed Unicode characters.</fsummary> <desc> @@ -386,7 +386,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfc_binary" arity="1"/> + <name name="characters_to_nfc_binary" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a utf8 binary of canonical equivalent composed Unicode characters.</fsummary> <desc> @@ -404,7 +404,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfd_list" arity="1"/> + <name name="characters_to_nfd_list" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a list of canonical equivalent decomposed Unicode characters.</fsummary> <desc> @@ -423,7 +423,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfd_binary" arity="1"/> + <name name="characters_to_nfd_binary" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a utf8 binary of canonical equivalent decomposed Unicode characters.</fsummary> <desc> @@ -441,7 +441,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfkc_list" arity="1"/> + <name name="characters_to_nfkc_list" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a list of canonical equivalent composed Unicode characters.</fsummary> <desc> @@ -460,7 +460,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfkc_binary" arity="1"/> + <name name="characters_to_nfkc_binary" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a utf8 binary of compatibly equivalent composed Unicode characters.</fsummary> <desc> @@ -478,7 +478,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfkd_list" arity="1"/> + <name name="characters_to_nfkd_list" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a list of compatibly equivalent decomposed Unicode characters.</fsummary> <desc> @@ -497,7 +497,7 @@ decode_data(Data) -> </func> <func> - <name name="characters_to_nfkd_binary" arity="1"/> + <name name="characters_to_nfkd_binary" arity="1" since="OTP 20.0"/> <fsummary>Normalize characters to a utf8 binary of compatibly equivalent decomposed Unicode characters.</fsummary> <desc> @@ -515,7 +515,7 @@ decode_data(Data) -> </func> <func> - <name name="encoding_to_bom" arity="1"/> + <name name="encoding_to_bom" arity="1" since=""/> <fsummary>Create a binary UTF byte order mark from encoding.</fsummary> <type_desc variable="Bin"> A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>. diff --git a/lib/stdlib/doc/src/uri_string.xml b/lib/stdlib/doc/src/uri_string.xml index 88d4600611..ad443486c5 100644 --- a/lib/stdlib/doc/src/uri_string.xml +++ b/lib/stdlib/doc/src/uri_string.xml @@ -27,7 +27,7 @@ <date>2018-02-07</date> <rev>A</rev> </header> - <module>uri_string</module> + <module since="OTP 21.0">uri_string</module> <modulesummary>URI processing functions.</modulesummary> <description> <p>This module contains functions for parsing and handling URIs @@ -150,7 +150,7 @@ <funcs> <func> - <name name="compose_query" arity="1"/> + <name name="compose_query" arity="1" since="OTP 21.0"/> <fsummary>Compose urlencoded query string.</fsummary> <desc> <p>Composes a form-urlencoded <c><anno>QueryString</anno></c> based on a @@ -176,7 +176,7 @@ </func> <func> - <name name="compose_query" arity="2"/> + <name name="compose_query" arity="2" since="OTP 21.0"/> <fsummary>Compose urlencoded query string.</fsummary> <desc> <p>Same as <c>compose_query/1</c> but with an additional @@ -210,7 +210,7 @@ </func> <func> - <name name="dissect_query" arity="1"/> + <name name="dissect_query" arity="1" since="OTP 21.0"/> <fsummary>Dissect query string.</fsummary> <desc> <p>Dissects an urlencoded <c><anno>QueryString</anno></c> and returns a @@ -236,7 +236,7 @@ </func> <func> - <name name="normalize" arity="1"/> + <name name="normalize" arity="1" since="OTP 21.0"/> <fsummary>Syntax-based normalization.</fsummary> <desc> <p>Transforms an <c><anno>URI</anno></c> into a normalized form @@ -261,7 +261,7 @@ </func> <func> - <name name="normalize" arity="2"/> + <name name="normalize" arity="2" since="OTP 21.0"/> <fsummary>Syntax-based normalization.</fsummary> <desc> <p>Same as <c>normalize/1</c> but with an additional @@ -285,7 +285,7 @@ </func> <func> - <name name="parse" arity="1"/> + <name name="parse" arity="1" since="OTP 21.0"/> <fsummary>Parse URI into a map.</fsummary> <desc> <p>Parses an <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url> @@ -309,7 +309,7 @@ </func> <func> - <name name="recompose" arity="1"/> + <name name="recompose" arity="1" since="OTP 21.0"/> <fsummary>Recompose URI.</fsummary> <desc> <p>Creates an <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url> compliant @@ -332,7 +332,7 @@ </func> <func> - <name name="transcode" arity="2"/> + <name name="transcode" arity="2" since="OTP 21.0"/> <fsummary>Transcode URI.</fsummary> <desc> <p>Transcodes an <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url> diff --git a/lib/stdlib/doc/src/win32reg.xml b/lib/stdlib/doc/src/win32reg.xml index f4a4fa1626..5e2aed6062 100644 --- a/lib/stdlib/doc/src/win32reg.xml +++ b/lib/stdlib/doc/src/win32reg.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>win32reg.xml</file> </header> - <module>win32reg</module> + <module since="">win32reg</module> <modulesummary>Provides access to the registry on Windows.</modulesummary> <description> <p>This module provides read and write access to the @@ -112,7 +112,7 @@ hkdd HKEY_DYN_DATA</pre> <funcs> <func> - <name name="change_key" arity="2"/> + <name name="change_key" arity="2" since=""/> <fsummary>Move to a key in the registry.</fsummary> <desc> <p>Changes the current key to another key. Works like <c>cd</c>. @@ -122,7 +122,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="change_key_create" arity="2"/> + <name name="change_key_create" arity="2" since=""/> <fsummary>Move to a key, create it if it is not there.</fsummary> <desc> <p>Creates a key, or just changes to it, if it is already there. Works @@ -133,7 +133,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close the registry.</fsummary> <desc> <p>Closes the registry. After that, the <c><anno>RegHandle</anno></c> @@ -142,7 +142,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="current_key" arity="1"/> + <name name="current_key" arity="1" since=""/> <fsummary>Return the path to the current key.</fsummary> <desc> <p>Returns the path to the current key. This is the equivalent of @@ -153,7 +153,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="delete_key" arity="1"/> + <name name="delete_key" arity="1" since=""/> <fsummary>Delete the current key.</fsummary> <desc> <p>Deletes the current key, if it is valid. Calls the Win32 API @@ -166,7 +166,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="delete_value" arity="2"/> + <name name="delete_value" arity="2" since=""/> <fsummary>Delete the named value on the current key.</fsummary> <desc> <p>Deletes a named value on the current key. The atom <c>default</c> is @@ -176,7 +176,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="expand" arity="1"/> + <name name="expand" arity="1" since=""/> <fsummary>Expand a string with environment variables.</fsummary> <desc> <p>Expands a string containing environment variables between percent @@ -189,7 +189,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="format_error" arity="1"/> + <name name="format_error" arity="1" since=""/> <fsummary>Convert a POSIX error code to a string.</fsummary> <desc> <p>Converts a POSIX error code to a string @@ -198,7 +198,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="open" arity="1"/> + <name name="open" arity="1" since=""/> <fsummary>Open the registry for reading or writing.</fsummary> <desc> <p>Opens the registry for reading or writing. The current key is the @@ -211,7 +211,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="set_value" arity="3"/> + <name name="set_value" arity="3" since=""/> <fsummary>Set value at the current registry key with specified name. </fsummary> <desc> @@ -230,7 +230,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="sub_keys" arity="1"/> + <name name="sub_keys" arity="1" since=""/> <fsummary>Get subkeys to the current key.</fsummary> <desc> <p>Returns a list of subkeys to the current key. Calls the Win32 @@ -240,7 +240,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="value" arity="2"/> + <name name="value" arity="2" since=""/> <fsummary>Get the named value on the current key.</fsummary> <desc> <p>Retrieves the named value (or default) on the current key. @@ -251,7 +251,7 @@ hkdd HKEY_DYN_DATA</pre> </func> <func> - <name name="values" arity="1"/> + <name name="values" arity="1" since=""/> <fsummary>Get all values on the current key.</fsummary> <desc> <p>Retrieves a list of all values on the current key. The values diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index 0b5eac1e16..bb2ed7727a 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>zip.xml</file> </header> - <module>zip</module> + <module since="">zip</module> <modulesummary>Utility for reading and creating 'zip' archives. </modulesummary> <description> @@ -180,7 +180,7 @@ <funcs> <func> - <name name="foldl" arity="3"/> + <name name="foldl" arity="3" since="OTP R14B"/> <fsummary>Fold a function over all files in a zip archive.</fsummary> <desc> <p>Calls <c><anno>Fun</anno>(<anno>FileInArchive</anno>, <anno>GetInfo @@ -234,10 +234,10 @@ </func> <func> - <name name="list_dir" arity="1"/> - <name name="list_dir" arity="2"/> - <name name="table" arity="1" /> - <name name="table" arity="2"/> + <name name="list_dir" arity="1" since=""/> + <name name="list_dir" arity="2" since=""/> + <name name="table" arity="1" since=""/> + <name name="table" arity="2" since=""/> <fsummary>Retrieve the name of all files in a zip archive.</fsummary> <desc> <p><c>list_dir/1</c> retrieves all filenames in the zip archive @@ -263,7 +263,7 @@ </func> <func> - <name name="t" arity="1"/> + <name name="t" arity="1" since=""/> <fsummary>Print the name of each file in a zip archive.</fsummary> <desc> <p>Prints all filenames in the zip archive <c><anno>Archive</anno></c> @@ -272,7 +272,7 @@ </func> <func> - <name name="tt" arity="1"/> + <name name="tt" arity="1" since=""/> <fsummary>Print name and information for each file in a zip archive. </fsummary> <desc> @@ -283,10 +283,10 @@ </func> <func> - <name name="unzip" arity="1"/> - <name name="unzip" arity="2"/> - <name name="extract" arity="1"/> - <name name="extract" arity="2"/> + <name name="unzip" arity="1" since=""/> + <name name="unzip" arity="2" since=""/> + <name name="extract" arity="1" since=""/> + <name name="extract" arity="2" since=""/> <fsummary>Extract files from a zip archive.</fsummary> <desc> <p><c>unzip/1</c> extracts all files from a zip archive.</p> @@ -353,10 +353,10 @@ </func> <func> - <name name="zip" arity="2"/> - <name name="zip" arity="3"/> - <name name="create" arity="2"/> - <name name="create" arity="3"/> + <name name="zip" arity="2" since=""/> + <name name="zip" arity="3" since=""/> + <name name="create" arity="2" since=""/> + <name name="create" arity="3" since=""/> <fsummary>Create a zip archive with options.</fsummary> <desc> <p>Creates a zip archive containing the files specified in @@ -481,7 +481,7 @@ </func> <func> - <name name="zip_close" arity="1"/> + <name name="zip_close" arity="1" since=""/> <fsummary>Close an open archive.</fsummary> <desc> <p>Closes a zip archive, previously opened with @@ -492,8 +492,8 @@ </func> <func> - <name name="zip_get" arity="1"/> - <name name="zip_get" arity="2"/> + <name name="zip_get" arity="1" since=""/> + <name name="zip_get" arity="2" since=""/> <fsummary>Extract files from an open archive.</fsummary> <desc> <p>Extracts one or all files from an open archive.</p> @@ -505,7 +505,7 @@ </func> <func> - <name name="zip_list_dir" arity="1"/> + <name name="zip_list_dir" arity="1" since=""/> <fsummary>Return a table of files in open zip archive.</fsummary> <desc> <p>Returns the file list of an open zip archive. The first returned @@ -514,8 +514,8 @@ </func> <func> - <name name="zip_open" arity="1"/> - <name name="zip_open" arity="2"/> + <name name="zip_open" arity="1" since=""/> + <name name="zip_open" arity="2" since=""/> <fsummary>Open an archive and return a handle to it.</fsummary> <desc> <p>Opens a zip archive, and reads and saves its directory. This diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index bb5d450cd6..3a8fe2211b 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -357,13 +357,17 @@ rfc3339_to_system_time(DateTimeString) -> rfc3339_to_system_time(DateTimeString, Options) -> Unit = proplists:get_value(unit, Options, second), %% _T is the character separating the date and the time: - {DateStr, [_T|TimeStr]} = lists:split(10, DateTimeString), - {TimeStr2, TimeStr3} = lists:split(8, TimeStr), - {ok, [Hour, Min, Sec], []} = io_lib:fread("~d:~d:~d", TimeStr2), - {ok, [Year, Month, Day], []} = io_lib:fread("~d-~d-~d", DateStr), + [Y1, Y2, Y3, Y4, $-, Mon1, Mon2, $-, D1, D2, _T, + H1, H2, $:, Min1, Min2, $:, S1, S2 | TimeStr] = DateTimeString, + Hour = list_to_integer([H1, H2]), + Min = list_to_integer([Min1, Min2]), + Sec = list_to_integer([S1, S2]), + Year = list_to_integer([Y1, Y2, Y3, Y4]), + Month = list_to_integer([Mon1, Mon2]), + Day = list_to_integer([D1, D2]), DateTime = {{Year, Month, Day}, {Hour, Min, Sec}}, IsFractionChar = fun(C) -> C >= $0 andalso C =< $9 orelse C =:= $. end, - {FractionStr, UtcOffset} = lists:splitwith(IsFractionChar, TimeStr3), + {FractionStr, UtcOffset} = lists:splitwith(IsFractionChar, TimeStr), Time = datetime_to_system_time(DateTime), Secs = Time - offset_adjustment(Time, second, UtcOffset), check(DateTimeString, Options, Secs), @@ -451,8 +455,9 @@ system_time_to_rfc3339(Time, Options) -> DateTime = system_time_to_datetime(Secs), {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime, FractionStr = fraction_str(Factor, AdjustedTime), - flat_fwrite("~4.10.0B-~2.10.0B-~2.10.0B~c~2.10.0B:~2.10.0B:~2.10.0B~s~s", - [Year, Month, Day, T, Hour, Min, Sec, FractionStr, Offset]). + L = [pad4(Year), "-", pad2(Month), "-", pad2(Day), [T], + pad2(Hour), ":", pad2(Min), ":", pad2(Sec), FractionStr, Offset], + lists:append(L). %% time_difference(T1, T2) = Tdiff %% @@ -529,24 +534,41 @@ valid_date({Y, M, D}) -> %% day_to_year(DayOfEpoch) = {Year, DayOfYear} %% -%% The idea here is to first guess a year, and then adjust. Although -%% the implementation is recursive, at most 1 or 2 recursive steps +%% The idea here is to first set the upper and lower bounds for a year, +%% and then adjust a range by interpolation search. Although complexity +%% of the algorithm is log(log(n)), at most 1 or 2 recursive steps %% are taken. -%% If DayOfEpoch is very large, we need far more than 1 or 2 iterations, -%% since we just subtract a yearful of days at a time until we're there. %% -spec day_to_year(non_neg_integer()) -> {year(), day_of_year()}. day_to_year(DayOfEpoch) when DayOfEpoch >= 0 -> - Y0 = DayOfEpoch div ?DAYS_PER_YEAR, - {Y1, D1} = dty(Y0, DayOfEpoch, dy(Y0)), + YMax = DayOfEpoch div ?DAYS_PER_YEAR, + YMin = DayOfEpoch div ?DAYS_PER_LEAP_YEAR, + {Y1, D1} = dty(YMin, YMax, DayOfEpoch, dy(YMin), dy(YMax)), {Y1, DayOfEpoch - D1}. --spec dty(year(), non_neg_integer(), non_neg_integer()) -> +-spec dty(year(), year(), non_neg_integer(), non_neg_integer(), + non_neg_integer()) -> {year(), non_neg_integer()}. -dty(Y, D1, D2) when D1 < D2 -> - dty(Y-1, D1, dy(Y-1)); -dty(Y, _D1, D2) -> - {Y, D2}. +dty(Min, Max, _D1, DMin, _DMax) when Min == Max -> + {Min, DMin}; +dty(Min, Max, D1, DMin, DMax) -> + Diff = Max - Min, + Mid = Min + (Diff * (D1 - DMin)) div (DMax - DMin), + MidLength = + case is_leap_year(Mid) of + true -> ?DAYS_PER_LEAP_YEAR; + false -> ?DAYS_PER_YEAR + end, + case dy(Mid) of + D2 when D1 < D2 -> + NewMax = Mid - 1, + dty(Min, NewMax, D1, DMin, dy(NewMax)); + D2 when D1 - D2 >= MidLength -> + NewMin = Mid + 1, + dty(NewMin, Max, D1, dy(NewMin), DMax); + D2 -> + {Mid, D2} + end. %% %% The Gregorian days of the iso week 01 day 1 for a given year. @@ -663,7 +685,7 @@ offset(OffsetOption, Secs0) when OffsetOption =:= ""; Secs = abs(Secs0), Hour = Secs div 3600, Min = (Secs rem 3600) div 60, - io_lib:fwrite("~c~2.10.0B:~2.10.0B", [Sign, Hour, Min]); + [Sign | lists:append([pad2(Hour), ":", pad2(Min)])]; offset(OffsetOption, _Secs) -> OffsetOption. @@ -678,8 +700,10 @@ offset_string_adjustment(_Time, _Unit, "Z") -> 0; offset_string_adjustment(_Time, _Unit, "z") -> 0; -offset_string_adjustment(_Time, _Unit, [Sign|Tz]) -> - {ok, [Hour, Min], []} = io_lib:fread("~d:~d", Tz), +offset_string_adjustment(_Time, _Unit, Tz) -> + [Sign, H1, H2, $:, M1, M2] = Tz, + Hour = list_to_integer([H1, H2]), + Min = list_to_integer([M1, M2]), Adjustment = 3600 * Hour + 60 * Min, case Sign of $- -> -Adjustment; @@ -687,8 +711,9 @@ offset_string_adjustment(_Time, _Unit, [Sign|Tz]) -> end. local_offset(SystemTime, Unit) -> - LocalTime = system_time_to_local_time(SystemTime, Unit), + %% Not optimized for special cases. UniversalTime = system_time_to_universal_time(SystemTime, Unit), + LocalTime = erlang:universaltime_to_localtime(UniversalTime), LocalSecs = datetime_to_gregorian_seconds(LocalTime), UniversalSecs = datetime_to_gregorian_seconds(UniversalTime), LocalSecs - UniversalSecs. @@ -697,7 +722,8 @@ fraction_str(1, _Time) -> ""; fraction_str(Factor, Time) -> Fraction = Time rem Factor, - io_lib:fwrite(".~*..0B", [log10(Factor), abs(Fraction)]). + S = integer_to_list(abs(Fraction)), + [$. | pad(log10(Factor) - length(S), S)]. fraction(second, _) -> 0; @@ -718,5 +744,21 @@ log10(1000) -> 3; log10(1000000) -> 6; log10(1000000000) -> 9. -flat_fwrite(F, S) -> - lists:flatten(io_lib:fwrite(F, S)). +pad(0, S) -> + S; +pad(I, S) -> + [$0 | pad(I - 1, S)]. + +pad2(N) when N < 10 -> + [$0 | integer_to_list(N)]; +pad2(N) -> + integer_to_list(N). + +pad4(N) when N < 10 -> + [$0, $0, $0 | integer_to_list(N)]; +pad4(N) when N < 100 -> + [$0, $0 | integer_to_list(N)]; +pad4(N) when N < 1000 -> + [$0 | integer_to_list(N)]; +pad4(N) -> + integer_to_list(N). diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 9602f0bcd9..5fa9c4f75c 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -872,7 +872,7 @@ Erlang code. -type af_fun_type() :: {'type', anno(), 'fun', []} | {'type', anno(), 'fun', [{'type', anno(), 'any'} | abstract_type()]} - | {'type', anno(), 'fun', af_function_type()}. + | af_function_type(). -type af_integer_range_type() :: {'type', anno(), 'range', [af_singleton_integer_type()]}. @@ -924,10 +924,11 @@ Erlang code. -type af_function_constraint() :: [af_constraint()]. -type af_constraint() :: {'type', anno(), 'constraint', - af_lit_atom('is_subtype'), - [af_type_variable() | abstract_type()]}. % [V, T] + [af_lit_atom('is_subtype') | + [af_type_variable() | abstract_type()]]}. % [IsSubtype, [V, T]] -type af_singleton_integer_type() :: af_integer() + | af_character() | af_unary_op(af_singleton_integer_type()) | af_binary_op(af_singleton_integer_type()). diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index dd302a2880..ada3ff5de3 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -697,6 +697,8 @@ fun_info(Extra) -> %% BITS: +bit_grp([], _Opts) -> + leaf("<<>>"); bit_grp(Fs, Opts) -> append([['<<'], [bit_elems(Fs, Opts)], ['>>']]). diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 8223a52873..2b5a374cf2 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -87,6 +87,8 @@ -export([limit_term/2]). +-export([chars_length/1]). + -export_type([chars/0, latin1_string/0, continuation/0, fread_error/0, fread_item/0, format_spec/0, chars_limit/0]). @@ -1131,3 +1133,17 @@ test_limit_map_assoc(K, V, D) -> test_limit(V, D - 1). test_limit_bitstring(_, _) -> ok. + +-spec chars_length(chars()) -> non_neg_integer(). +%% Optimized for deep lists S such that deep_latin1_char_list(S) is +%% true. No binaries allowed! It is assumed that $\r is never followed +%% by $\n if S is an iolist() (string:length() assigns such a +%% sub-sequence length 1). +chars_length(S) -> + try + %% true = deep_latin1_char_list(S), + iolist_size(S) + catch + _:_ -> + string:length(S) + end. diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index ab9031573b..d1aa4cd157 100644 --- a/lib/stdlib/src/io_lib_format.erl +++ b/lib/stdlib/src/io_lib_format.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -248,7 +248,7 @@ count_small([#{control_char := $s}|Cs], #{w := W} = Cnts) -> count_small(Cs, Cnts#{w := W + 1}); count_small([S|Cs], #{other := Other} = Cnts) when is_list(S); is_binary(S) -> - count_small(Cs, Cnts#{other := Other + string:length(S)}); + count_small(Cs, Cnts#{other := Other + io_lib:chars_length(S)}); count_small([C|Cs], #{other := Other} = Cnts) when is_integer(C) -> count_small(Cs, Cnts#{other := Other + 1}); count_small([], #{p := P, s := S, w := W, other := Other}) -> @@ -280,10 +280,15 @@ build_limited([#{control_char := C, args := As, width := F, adjust := Ad, true -> MaxLen0 div Count0 end, S = control_limited(C, As, F, Ad, P, Pad, Enc, Str, MaxChars, I), - Len = string:length(S), NumOfPs = decr_pc(C, NumOfPs0), Count = Count0 - 1, - MaxLen = sub(MaxLen0, Len), + MaxLen = if + MaxLen0 < 0 -> % optimization + MaxLen0; + true -> + Len = io_lib:chars_length(S), + sub(MaxLen0, Len) + end, if NumOfPs > 0 -> [S|build_limited(Cs, NumOfPs, Count, MaxLen, indentation(S, I))]; @@ -406,7 +411,7 @@ base(B) when is_integer(B) -> term(T, none, _Adj, none, _Pad) -> T; term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad); term(T, F, Adj, P0, Pad) -> - L = string:length(T), + L = io_lib:chars_length(T), P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end), if L > P -> @@ -713,7 +718,7 @@ fwrite_g(Fl, F, Adj, P, Pad) when P >= 1 -> end. -%% iolist_to_chars(iolist()) -> deep_char_list() +%% iolist_to_chars(iolist()) -> io_lib:chars() iolist_to_chars([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> [C | iolist_to_chars(Cs)]; @@ -729,7 +734,7 @@ iolist_to_chars(B) when is_binary(B) -> %% cbinary() | nil()) %% cbinary() :: unicode:unicode_binary() | unicode:latin1_binary() -%% cdata_to_chars(cdata()) -> io_lib:deep_char_list() +%% cdata_to_chars(cdata()) -> io_lib:chars() cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 -> [C | cdata_to_chars(Cs)]; @@ -745,7 +750,7 @@ cdata_to_chars(B) when is_binary(B) -> limit_string(S, F, CharsLimit) when CharsLimit < 0; CharsLimit >= F -> S; limit_string(S, _F, CharsLimit) -> - case string:length(S) =< CharsLimit of + case io_lib:chars_length(S) =< CharsLimit of true -> S; false -> [string:slice(S, 0, sub(CharsLimit, 3)), "..."] end. @@ -759,11 +764,11 @@ limit_field(F, CharsLimit) -> string(S, none, _Adj, none, _Pad) -> S; string(S, F, Adj, none, Pad) -> - string_field(S, F, Adj, string:length(S), Pad); + string_field(S, F, Adj, io_lib:chars_length(S), Pad); string(S, none, _Adj, P, Pad) -> - string_field(S, P, left, string:length(S), Pad); + string_field(S, P, left, io_lib:chars_length(S), Pad); string(S, F, Adj, P, Pad) when F >= P -> - N = string:length(S), + N = io_lib:chars_length(S), if F > P -> if N > P -> adjust(flat_trunc(S, P), chars(Pad, F-P), Adj); diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl index ba9d9e8434..5483ea87b5 100644 --- a/lib/stdlib/src/io_lib_pretty.erl +++ b/lib/stdlib/src/io_lib_pretty.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -507,20 +507,20 @@ print_length(#{}=M, _D, _T, _RF, _Enc, _Str) when map_size(M) =:= 0 -> {"#{}", 3, 0, no_more}; print_length(Atom, _D, _T, _RF, Enc, _Str) when is_atom(Atom) -> S = write_atom(Atom, Enc), - {S, string:length(S), 0, no_more}; + {S, io_lib:chars_length(S), 0, no_more}; print_length(List, D, T, RF, Enc, Str) when is_list(List) -> %% only flat lists are "printable" case Str andalso printable_list(List, D, T, Enc) of true -> %% print as string, escaping double-quotes in the list S = write_string(List, Enc), - {S, string:length(S), 0, no_more}; + {S, io_lib:chars_length(S), 0, no_more}; {true, Prefix} -> %% Truncated lists when T < 0 could break some existing code. S = write_string(Prefix, Enc), %% NumOfDots = 0 to avoid looping--increasing the depth %% does not make Prefix longer. - {[S | "..."], 3 + string:length(S), 0, no_more}; + {[S | "..."], 3 + io_lib:chars_length(S), 0, no_more}; false -> case print_length_list(List, D, T, RF, Enc, Str) of {What, Len, Dots, _More} when Dots > 0 -> @@ -564,7 +564,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> {[$<,$<,S,$>,$>], 4 + length(S), 0, no_more}; {false, List} when is_list(List) -> S = io_lib:write_string(List, $"), %" - {[$<,$<,S,"/utf8>>"], 9 + string:length(S), 0, no_more}; + {[$<,$<,S,"/utf8>>"], 9 + io_lib:chars_length(S), 0, no_more}; {true, true, Prefix} -> S = io_lib:write_string(Prefix, $"), %" More = fun(T1, Dd) -> @@ -576,7 +576,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> More = fun(T1, Dd) -> ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str) end, - {[$<,$<,S|"/utf8...>>"], 12 + string:length(S), 3, More}; + {[$<,$<,S|"/utf8...>>"], 12 + io_lib:chars_length(S), 3, More}; false -> case io_lib:write_binary(Bin, D, T) of {S, <<>>} -> @@ -591,7 +591,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> print_length(Term, _D, _T, _RF, _Enc, _Str) -> S = io_lib:write(Term), %% S can contain unicode, so iolist_size(S) cannot be used here - {S, string:length(S), 0, no_more}. + {S, io_lib:chars_length(S), 0, no_more}. print_length_map(Map, 1, _T, RF, Enc, Str) -> More = fun(T1, Dd) -> ?FUNCTION_NAME(Map, 1+Dd, T1, RF, Enc, Str) end, @@ -651,7 +651,7 @@ print_length_record(Tuple, 1, _T, RF, RDefs, Enc, Str) -> {"{...}", 5, 3, More}; print_length_record(Tuple, D, T, RF, RDefs, Enc, Str) -> Name = [$# | write_atom(element(1, Tuple), Enc)], - NameL = string:length(Name), + NameL = io_lib:chars_length(Name), T1 = tsub(T, NameL+2), L = print_length_fields(RDefs, D - 1, T1, Tuple, 2, RF, Enc, Str), {Len, Dots} = list_length(L, NameL + 2, 0), @@ -677,7 +677,7 @@ print_length_fields([Def | Defs], D, T, Tuple, I, RF, Enc, Str) -> print_length_field(Def, D, T, E, RF, Enc, Str) -> Name = write_atom(Def, Enc), - NameL = string:length(Name) + 3, + NameL = io_lib:chars_length(Name) + 3, {_, Len, Dots, _} = Field = print_length(E, D, tsub(T, NameL), RF, Enc, Str), {{field, Name, NameL, Field}, NameL + Len, Dots, no_more}. @@ -738,7 +738,7 @@ printable_list(L, _D, T, _Uni) when T < 0-> io_lib:printable_list(L). slice(L, N) -> - try string:length(L) =< N of + try io_lib:chars_length(L) =< N of true -> all; false -> diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 6d243e1bec..97ec785c62 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -556,8 +556,8 @@ tg({call, Line, {remote,_,{atom,_,erlang},{atom, Line2, FunName}},ParaList}, FunName,length(ParaList)}}) end; tg({call, Line, {remote,_,{atom,_,ModuleName}, - {atom, _, FunName}},_ParaList},B) -> - throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName}}); + {atom, _, FunName}},ParaList},B) -> + throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName,length(ParaList)}}); tg({cons,Line, H, T},B) -> {cons, Line, tg(H,B), tg(T,B)}; tg({nil, Line},_B) -> diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 2a324aef82..9e5d6a3bd8 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -38,7 +38,9 @@ {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^3\\.6$">>,[restart_new_emulator]}, - {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}], + {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.7$">>,[restart_new_emulator]}, + {<<"^3\\.7\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}], [{<<"^3\\.4$">>,[restart_new_emulator]}, {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, @@ -50,4 +52,6 @@ {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^3\\.6$">>,[restart_new_emulator]}, - {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}. + {<<"^3\\.6\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.7$">>,[restart_new_emulator]}, + {<<"^3\\.7\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}. diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl index c5cfea5e9e..e0811f19cf 100644 --- a/lib/stdlib/test/binary_module_SUITE.erl +++ b/lib/stdlib/test/binary_module_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0, suite/0, interesting/1,scope_return/1,random_ref_comp/1,random_ref_sr_comp/1, random_ref_fla_comp/1,parts/1, bin_to_list/1, list_to_bin/1, - copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1]). + copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1, + check_no_invalid_read_bug/1]). -export([random_number/1, make_unaligned/1]). @@ -36,7 +37,7 @@ all() -> [scope_return,interesting, random_ref_fla_comp, random_ref_sr_comp, random_ref_comp, parts, bin_to_list, list_to_bin, copy, referenced, guard, encode_decode, badargs, - longest_common_trap]. + longest_common_trap, check_no_invalid_read_bug]. -define(MASK_ERROR(EXPR),mask_error((catch (EXPR)))). @@ -1361,3 +1362,13 @@ make_unaligned2(Bin0) when is_binary(Bin0) -> Bin. id(I) -> I. + +check_no_invalid_read_bug(Config) when is_list(Config) -> + check_no_invalid_read_bug(24); +check_no_invalid_read_bug(60) -> + ok; +check_no_invalid_read_bug(I) -> + N = 1 bsl I, + binary:encode_unsigned(N+N), + binary:encode_unsigned(N+N, little), + check_no_invalid_read_bug(I+1). diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl index df62c0921d..224c0d5625 100644 --- a/lib/stdlib/test/calendar_SUITE.erl +++ b/lib/stdlib/test/calendar_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, gregorian_days/1, + big_gregorian_days/1, gregorian_seconds/1, day_of_the_week/1, day_of_the_week_calibrate/1, @@ -36,13 +37,16 @@ -define(START_YEAR, 1947). -define(END_YEAR, 2012). +-define(BIG_START_YEAR, 20000000). +-define(BIG_END_YEAR, 20000020). + suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [gregorian_days, gregorian_seconds, day_of_the_week, day_of_the_week_calibrate, leap_years, last_day_of_the_month, local_time_to_universal_time_dst, - iso_week_number, system_time, rfc3339]. + iso_week_number, system_time, rfc3339, big_gregorian_days]. groups() -> []. @@ -67,6 +71,14 @@ gregorian_days(Config) when is_list(Config) -> MaxDays = calendar:date_to_gregorian_days({?END_YEAR, 1, 1}), check_gregorian_days(Days, MaxDays). +%% Tests that date_to_gregorian_days and gregorian_days_to_date +%% are each others inverses from ?BIG_START_YEAR-01-01 up to ?BIG_END_YEAR-01-01. +%% At the same time valid_date is tested. +big_gregorian_days(Config) when is_list(Config) -> + Days = calendar:date_to_gregorian_days({?BIG_START_YEAR, 1, 1}), + MaxDays = calendar:date_to_gregorian_days({?BIG_END_YEAR, 1, 1}), + check_gregorian_days(Days, MaxDays). + %% Tests that datetime_to_gregorian_seconds and %% gregorian_seconds_to_date are each others inverses for a sampled %% number of seconds from ?START_YEAR-01-01 up to ?END_YEAR-01-01: We check diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index dda8d0a12e..f5d80e7e68 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2018. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1, pr_1014/1, - otp_13662/1, otp_14285/1]). + otp_13662/1, otp_14285/1, otp_15592/1]). %% Internal export. -export([ehook/6]). @@ -81,7 +81,7 @@ groups() -> [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, otp_10302, otp_10820, otp_11100, otp_11861, pr_1014, otp_13662, - otp_14285]}]. + otp_14285, otp_15592]}]. init_per_suite(Config) -> Config. @@ -1167,6 +1167,11 @@ otp_14285(_Config) -> [{encoding,latin1}])), ok. +otp_15592(_Config) -> + ok = pp_expr(<<"long12345678901234567890123456789012345678901234" + "56789012345678901234:f(<<>>)">>), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index e0217418fe..d46173497b 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 3.7 +STDLIB_VSN = 3.7.1 diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 6906ef1553..6ad9bec2e6 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -1101,8 +1101,9 @@ lay_2(Node, Ctxt) -> Ctxt1 = reset_prec(Ctxt), D1 = lay(erl_syntax:constrained_function_type_body(Node), Ctxt1), + Ctxt2 = Ctxt1#ctxt{clause = undefined}, D2 = lay(erl_syntax:constrained_function_type_argument(Node), - Ctxt1), + Ctxt2), beside(D1, beside(floating(text(" when ")), D2)); @@ -1113,7 +1114,7 @@ lay_2(Node, Ctxt) -> _ -> {"fun(", ")"} end, - Ctxt1 = reset_prec(Ctxt), + Ctxt1 = (reset_prec(Ctxt))#ctxt{clause = undefined}, D1 = case erl_syntax:function_type_arguments(Node) of any_arity -> text("(...)"); diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index 9dbd0e302a..6b42f7a0a1 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -26,14 +26,14 @@ -export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1, revert_map_type/1, t_abstract_type/1,t_erl_parse_type/1,t_type/1, t_epp_dodger/1, - t_comment_scan/1,t_igor/1,t_erl_tidy/1]). + t_comment_scan/1,t_igor/1,t_erl_tidy/1,t_prettypr/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [app_test,appup_test,smoke_test,revert,revert_map,revert_map_type, t_abstract_type,t_erl_parse_type,t_type,t_epp_dodger, - t_comment_scan,t_igor,t_erl_tidy]. + t_comment_scan,t_igor,t_erl_tidy,t_prettypr]. groups() -> []. @@ -300,6 +300,14 @@ t_comment_scan(Config) when is_list(Config) -> ok = test_comment_scan(Filenames,DataDir), ok. +t_prettypr(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + Filenames = ["type_specs.erl", + "specs_and_funs.erl"], + ok = test_prettypr(Filenames,DataDir,PrivDir), + ok. + test_files(Config) -> DataDir = ?config(data_dir, Config), [ filename:join(DataDir,Filename) || Filename <- test_files() ]. @@ -307,7 +315,8 @@ test_files(Config) -> test_files() -> ["syntax_tools_SUITE_test_module.erl", "syntax_tools_test.erl", - "type_specs.erl"]. + "type_specs.erl", + "specs_and_funs.erl"]. t_igor(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -359,6 +368,27 @@ test_comment_scan([File|Files],DataDir) -> test_comment_scan(Files,DataDir). +test_prettypr([],_,_) -> ok; +test_prettypr([File|Files],DataDir,PrivDir) -> + Filename = filename:join(DataDir,File), + io:format("Parsing ~p~n", [Filename]), + {ok, Fs0} = epp:parse_file(Filename, [], []), + Fs = erl_syntax:form_list(Fs0), + PP = erl_prettypr:format(Fs, [{paper, 120}, {ribbon, 110}]), + io:put_chars(PP), + OutFile = filename:join(PrivDir, File), + ok = file:write_file(OutFile,iolist_to_binary(PP)), + io:format("Parsing OutFile: ~s~n", [OutFile]), + {ok, Fs2} = epp:parse_file(OutFile, [], []), + case [Error || {error, _} = Error <- Fs2] of + [] -> + ok; + Errors -> + ?t:fail(Errors) + end, + test_prettypr(Files,DataDir,PrivDir). + + test_epp_dodger([], _, _) -> ok; test_epp_dodger([Filename|Files],DataDir,PrivDir) -> io:format("Parsing ~p~n", [Filename]), diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/specs_and_funs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/specs_and_funs.erl new file mode 100644 index 0000000000..8dfeaf5a6b --- /dev/null +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/specs_and_funs.erl @@ -0,0 +1,18 @@ +-module(specs_and_funs). + +-export([my_apply/3, two/1]). + +%% OTP-15519, ERL-815 + +-spec my_apply(Fun, Arg, fun((A) -> A)) -> Result when + Fun :: fun((Arg) -> Result), + Arg :: any(), + Result :: any(). + +my_apply(Fun, Arg, _) -> + Fun(Arg). + +-spec two(fun((A) -> A)) -> fun((B) -> B). + +two(F) -> + F(fun(X) -> X end). diff --git a/lib/tftp/doc/src/tftp.xml b/lib/tftp/doc/src/tftp.xml index 4ed54bc462..57d64b7379 100644 --- a/lib/tftp/doc/src/tftp.xml +++ b/lib/tftp/doc/src/tftp.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>tftp</module> + <module since="">tftp</module> <modulesummary>Trivial FTP.</modulesummary> <description> <p>Interface module for the <c>tftp</c> application.</p> @@ -172,7 +172,7 @@ <funcs> <func> - <name>change_config(daemons, Options) -> [{Pid, Result}]</name> + <name since="">change_config(daemons, Options) -> [{Pid, Result}]</name> <fsummary>Changes configuration for all daemons. </fsummary> <type> @@ -187,7 +187,7 @@ </func> <func> - <name>change_config(servers, Options) -> [{Pid, Result}]</name> + <name since="">change_config(servers, Options) -> [{Pid, Result}]</name> <fsummary>Changes configuration for all servers. </fsummary> <type> @@ -202,7 +202,7 @@ </func> <func> - <name>change_config(Pid, Options) -> Result</name> + <name since="">change_config(Pid, Options) -> Result</name> <fsummary>Changes configuration for a TFTP daemon, server, or client process.</fsummary> <type> @@ -217,7 +217,7 @@ </func> <func> - <name>info(daemons) -> [{Pid, Options}]</name> + <name since="">info(daemons) -> [{Pid, Options}]</name> <fsummary>Returns information about all daemons.</fsummary> <type> <v>Pid = [pid()]</v> @@ -230,7 +230,7 @@ </func> <func> - <name>info(servers) -> [{Pid, Options}]</name> + <name since="">info(servers) -> [{Pid, Options}]</name> <fsummary>Returns information about all servers.</fsummary> <type> <v>Pid = [pid()]</v> @@ -243,7 +243,7 @@ </func> <func> - <name>info(Pid) -> {ok, Options} | {error, Reason}</name> + <name since="">info(Pid) -> {ok, Options} | {error, Reason}</name> <fsummary>Returns information about a daemon, server, or client process.</fsummary> <type> <v>Options = [option()]</v> @@ -255,7 +255,7 @@ </func> <func> - <name>read_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> + <name since="">read_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> <fsummary>Reads a (virtual) file from a TFTP server.</fsummary> <type> <v>RemoteFilename = string()</v> @@ -285,7 +285,7 @@ </func> <func> - <name>start(Options) -> {ok, Pid} | {error, Reason}</name> + <name since="">start(Options) -> {ok, Pid} | {error, Reason}</name> <fsummary>Starts a daemon process.</fsummary> <type> <v>Options = [option()]</v> @@ -301,7 +301,7 @@ </func> <func> - <name>write_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> + <name since="">write_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> <fsummary>Writes a (virtual) file to a TFTP server.</fsummary> <type> <v>RemoteFilename = string()</v> @@ -389,7 +389,7 @@ <funcs> <func> - <name>Module:abort(Code, Text, State) -> ok</name> + <name since="OTP 18.1">Module:abort(Code, Text, State) -> ok</name> <fsummary>Aborts the file transfer.</fsummary> <type> <v>Code = undef | enoent | eacces | enospc</v> @@ -413,7 +413,7 @@ </func> <func> - <name>Module:open(Peer, Access, Filename, Mode, SuggestedOptions, State) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name> + <name since="OTP 18.1">Module:open(Peer, Access, Filename, Mode, SuggestedOptions, State) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name> <fsummary>Opens a file for read or write access.</fsummary> <type> <v>Peer = {PeerType, PeerHost, PeerPort}</v> @@ -448,7 +448,7 @@ </func> <func> - <name>Module:prepare(Peer, Access, Filename, Mode, SuggestedOptions, InitialState) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name> + <name since="OTP 18.1">Module:prepare(Peer, Access, Filename, Mode, SuggestedOptions, InitialState) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name> <fsummary>Prepares to open a file on the client side.</fsummary> <type> <v>Peer = {PeerType, PeerHost, PeerPort}</v> @@ -483,7 +483,7 @@ </func> <func> - <name>Module:read(State) -> {more, Bin, NewState} | {last, Bin, FileSize} | {error, {Code, Text}}</name> + <name since="OTP 18.1">Module:read(State) -> {more, Bin, NewState} | {last, Bin, FileSize} | {error, {Code, Text}}</name> <fsummary>Reads a chunk from the file.</fsummary> <type> <v>State = NewState = term()</v> @@ -510,7 +510,7 @@ </func> <func> - <name>Module:write(Bin, State) -> {more, NewState} | {last, FileSize} | {error, {Code, Text}}</name> + <name since="OTP 18.1">Module:write(Bin, State) -> {more, NewState} | {last, FileSize} | {error, {Code, Text}}</name> <fsummary>Writes a chunk to the file.</fsummary> <type> <v>Bin = binary()</v> @@ -549,7 +549,7 @@ <funcs> <func> - <name>Logger:error_msg(Format, Data) -> ok | exit(Reason)</name> + <name since="OTP 18.1">Logger:error_msg(Format, Data) -> ok | exit(Reason)</name> <fsummary>Logs an error message.</fsummary> <type> <v>Format = string()</v> @@ -565,7 +565,7 @@ </func> <func> - <name>Logger:info_msg(Format, Data) -> ok | exit(Reason)</name> + <name since="OTP 18.1">Logger:info_msg(Format, Data) -> ok | exit(Reason)</name> <fsummary>Logs an info message.</fsummary> <type> <v>Format = string()</v> @@ -579,7 +579,7 @@ </func> <func> - <name>Logger:warning_msg(Format, Data) -> ok | exit(Reason)</name> + <name since="OTP 18.1">Logger:warning_msg(Format, Data) -> ok | exit(Reason)</name> <fsummary>Logs a warning message.</fsummary> <type> <v>Format = string()</v> diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml index 15cd784253..64c24cea2a 100644 --- a/lib/tools/doc/src/cover.xml +++ b/lib/tools/doc/src/cover.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>cover</module> + <module since="">cover</module> <modulesummary>A Coverage Analysis Tool for Erlang</modulesummary> <description> <p>The module <c>cover</c> provides a set of functions for coverage @@ -115,7 +115,7 @@ </description> <funcs> <func> - <name>start() -> {ok,Pid} | {error,Reason}</name> + <name since="">start() -> {ok,Pid} | {error,Reason}</name> <fsummary>Start Cover.</fsummary> <type> <v>Pid = pid()</v> @@ -128,7 +128,7 @@ </desc> </func> <func> - <name>start(Nodes) -> {ok,StartedNodes} | {error,not_main_node}</name> + <name since="">start(Nodes) -> {ok,StartedNodes} | {error,not_main_node}</name> <fsummary>Start Cover on remote nodes.</fsummary> <type> <v>Nodes = StartedNodes = [atom()]</v> @@ -139,10 +139,10 @@ </desc> </func> <func> - <name>compile(ModFiles) -> Result | [Result]</name> - <name>compile(ModFiles, Options) -> Result | [Result]</name> - <name>compile_module(ModFiles) -> Result | [Result]</name> - <name>compile_module(ModFiles, Options) -> Result | [Result]</name> + <name since="">compile(ModFiles) -> Result | [Result]</name> + <name since="">compile(ModFiles, Options) -> Result | [Result]</name> + <name since="">compile_module(ModFiles) -> Result | [Result]</name> + <name since="">compile_module(ModFiles, Options) -> Result | [Result]</name> <fsummary>Compile one or more modules for Cover analysis.</fsummary> <type> <v>ModFiles = ModFile | [ModFile]</v> @@ -176,9 +176,9 @@ </desc> </func> <func> - <name>compile_directory() -> [Result] | {error,Reason}</name> - <name>compile_directory(Dir) -> [Result] | {error,Reason}</name> - <name>compile_directory(Dir, Options) -> [Result] | {error,Reason}</name> + <name since="">compile_directory() -> [Result] | {error,Reason}</name> + <name since="">compile_directory(Dir) -> [Result] | {error,Reason}</name> + <name since="">compile_directory(Dir, Options) -> [Result] | {error,Reason}</name> <fsummary>Compile all modules in a directory for Cover analysis.</fsummary> <type> <v>Dir = string()</v> @@ -199,7 +199,7 @@ </desc> </func> <func> - <name>compile_beam(ModFiles) -> Result | [Result]</name> + <name since="">compile_beam(ModFiles) -> Result | [Result]</name> <fsummary>Compile one or more modules for Cover analysis, using existing beam(s).</fsummary> <type> <v>ModFiles = ModFile | [ModFile]</v> @@ -241,8 +241,8 @@ </desc> </func> <func> - <name>compile_beam_directory() -> [Result] | {error,Reason}</name> - <name>compile_beam_directory(Dir) -> [Result] | {error,Reason}</name> + <name since="">compile_beam_directory() -> [Result] | {error,Reason}</name> + <name since="">compile_beam_directory(Dir) -> [Result] | {error,Reason}</name> <fsummary>Compile all .beam files in a directory for Cover analysis.</fsummary> <type> <v>Dir = string()</v> @@ -260,14 +260,14 @@ </desc> </func> <func> - <name>analyse() -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Analysis) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Level) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules, Analysis) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Analysis, Level) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse(Modules, Analysis, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="OTP 18.0">analyse() -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Analysis) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Level) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules, Analysis) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Analysis, Level) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse(Modules, Analysis, Level) -> OneResult | {result,Ok,Fail} | {error,not_main_node}</name> <fsummary>Analyse one or more Cover compiled modules.</fsummary> <type> <v>Modules = Module | [Module]</v> @@ -305,10 +305,10 @@ </desc> </func> <func> - <name>analyse_to_file() -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse_to_file(Modules) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse_to_file(Options) -> {result,Ok,Fail} | {error,not_main_node}</name> - <name>analyse_to_file(Modules,Options) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="OTP 18.0">analyse_to_file() -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse_to_file(Modules) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse_to_file(Options) -> {result,Ok,Fail} | {error,not_main_node}</name> + <name since="">analyse_to_file(Modules,Options) -> Answer | {result,Ok,Fail} | {error,not_main_node}</name> <fsummary>Detailed coverage analysis of one or more Cover compiled modules.</fsummary> <type> <v>Modules = Module | [Module]</v> @@ -359,10 +359,10 @@ </desc> </func> <func> - <name>async_analyse_to_file(Module) -> </name> - <name>async_analyse_to_file(Module,Options) -> </name> - <name>async_analyse_to_file(Module, OutFile) -> </name> - <name>async_analyse_to_file(Module, OutFile, Options) -> pid()</name> + <name since="OTP R14B02">async_analyse_to_file(Module) -> </name> + <name since="OTP R14B02">async_analyse_to_file(Module,Options) -> </name> + <name since="OTP R14B02">async_analyse_to_file(Module, OutFile) -> </name> + <name since="OTP R14B02">async_analyse_to_file(Module, OutFile, Options) -> pid()</name> <fsummary>Asynchronous call to analyse_to_file.</fsummary> <type> <v>Module = atom()</v> @@ -384,7 +384,7 @@ </desc> </func> <func> - <name>modules() -> [Module] | {error,not_main_node}</name> + <name since="">modules() -> [Module] | {error,not_main_node}</name> <fsummary>Return all Cover compiled modules.</fsummary> <type> <v>Module = atom()</v> @@ -395,7 +395,7 @@ </desc> </func> <func> - <name>imported_modules() -> [Module] | {error,not_main_node}</name> + <name since="">imported_modules() -> [Module] | {error,not_main_node}</name> <fsummary>Return all modules for which there are imported data.</fsummary> <type> <v>Module = atom()</v> @@ -406,7 +406,7 @@ </desc> </func> <func> - <name>imported() -> [File] | {error,not_main_node}</name> + <name since="">imported() -> [File] | {error,not_main_node}</name> <fsummary>Return all imported files.</fsummary> <type> <v>File = string()</v> @@ -416,7 +416,7 @@ </desc> </func> <func> - <name>which_nodes() -> [Node] | {error,not_main_node}</name> + <name since="">which_nodes() -> [Node] | {error,not_main_node}</name> <fsummary>Return all nodes that are part of the coverage analysis.</fsummary> <type> <v>Node = atom()</v> @@ -428,7 +428,7 @@ </desc> </func> <func> - <name>is_compiled(Module) -> {file,File} | false | {error,not_main_node}</name> + <name since="">is_compiled(Module) -> {file,File} | false | {error,not_main_node}</name> <fsummary>Check if a module is Cover compiled.</fsummary> <type> <v>Module = atom()</v> @@ -442,8 +442,8 @@ </desc> </func> <func> - <name>reset(Module) -></name> - <name>reset() -> ok | {error,not_main_node}</name> + <name since="">reset(Module) -></name> + <name since="">reset() -> ok | {error,not_main_node}</name> <fsummary>Reset coverage data for Cover compiled modules.</fsummary> <type> <v>Module = atom()</v> @@ -458,8 +458,8 @@ </desc> </func> <func> - <name>export(ExportFile)</name> - <name>export(ExportFile,Module) -> ok | {error,Reason}</name> + <name since="">export(ExportFile)</name> + <name since="">export(ExportFile,Module) -> ok | {error,Reason}</name> <fsummary>Reset coverage data for Cover compiled modules.</fsummary> <type> <v>ExportFile = string()</v> @@ -480,7 +480,7 @@ </desc> </func> <func> - <name>import(ExportFile) -> ok | {error,Reason}</name> + <name since="">import(ExportFile) -> ok | {error,Reason}</name> <fsummary>Reset coverage data for Cover compiled modules.</fsummary> <type> <v>ExportFile = string()</v> @@ -504,14 +504,14 @@ </desc> </func> <func> - <name>stop() -> ok | {error,not_main_node}</name> + <name since="">stop() -> ok | {error,not_main_node}</name> <fsummary>Stop Cover.</fsummary> <desc> <p>Stops the Cover server and unloads all Cover compiled code.</p> </desc> </func> <func> - <name>stop(Nodes) -> ok | {error,not_main_node}</name> + <name since="">stop(Nodes) -> ok | {error,not_main_node}</name> <fsummary>Stop Cover on remote nodes.</fsummary> <type> <v>Nodes = [atom()]</v> @@ -523,7 +523,7 @@ </desc> </func> <func> - <name>flush(Nodes) -> ok | {error,not_main_node}</name> + <name since="OTP R16B">flush(Nodes) -> ok | {error,not_main_node}</name> <fsummary>Collect cover data from remote nodes.</fsummary> <type> <v>Nodes = [atom()]</v> diff --git a/lib/tools/doc/src/cprof.xml b/lib/tools/doc/src/cprof.xml index df0acbe617..b6af8b6d28 100644 --- a/lib/tools/doc/src/cprof.xml +++ b/lib/tools/doc/src/cprof.xml @@ -34,7 +34,7 @@ <rev>PA1</rev> <file>cprof.sgml</file> </header> - <module>cprof</module> + <module since="">cprof</module> <modulesummary>A simple Call Count Profiling Tool using breakpoints for minimal runtime performance impact.</modulesummary> <description> <p>The <c>cprof</c> module is used to profile a program @@ -65,10 +65,10 @@ </description> <funcs> <func> - <name>analyse() -> {AllCallCount, ModAnalysisList}</name> - <name>analyse(Limit) -> {AllCallCount, ModAnalysisList}</name> - <name>analyse(Mod) -> ModAnalysis</name> - <name>analyse(Mod, Limit) -> ModAnalysis</name> + <name since="">analyse() -> {AllCallCount, ModAnalysisList}</name> + <name since="">analyse(Limit) -> {AllCallCount, ModAnalysisList}</name> + <name since="">analyse(Mod) -> ModAnalysis</name> + <name since="">analyse(Mod, Limit) -> ModAnalysis</name> <fsummary>Collect and analyse call counters.</fsummary> <type> <v>Limit = integer()</v> @@ -122,7 +122,7 @@ </desc> </func> <func> - <name>pause() -> integer()</name> + <name since="">pause() -> integer()</name> <fsummary>Pause running call count trace for all functions.</fsummary> <desc> <p>Pause call count tracing for all functions in all modules @@ -137,9 +137,9 @@ </desc> </func> <func> - <name>pause(FuncSpec) -> integer()</name> - <name>pause(Mod, Func) -> integer()</name> - <name>pause(Mod, Func, Arity) -> integer()</name> + <name since="">pause(FuncSpec) -> integer()</name> + <name since="">pause(Mod, Func) -> integer()</name> + <name since="">pause(Mod, Func, Arity) -> integer()</name> <fsummary>Pause running call count trace for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> @@ -167,10 +167,10 @@ </desc> </func> <func> - <name>restart() -> integer()</name> - <name>restart(FuncSpec) -> integer()</name> - <name>restart(Mod, Func) -> integer()</name> - <name>restart(Mod, Func, Arity) -> integer()</name> + <name since="">restart() -> integer()</name> + <name since="">restart(FuncSpec) -> integer()</name> + <name since="">restart(Mod, Func) -> integer()</name> + <name since="">restart(Mod, Func, Arity) -> integer()</name> <fsummary>Restart existing call counters for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> @@ -197,7 +197,7 @@ </desc> </func> <func> - <name>start() -> integer()</name> + <name since="">start() -> integer()</name> <fsummary>Start call count tracing for all functions.</fsummary> <desc> <p>Start call count tracing for all functions in all modules, @@ -212,9 +212,9 @@ </desc> </func> <func> - <name>start(FuncSpec) -> integer()</name> - <name>start(Mod, Func) -> integer()</name> - <name>start(Mod, Func, Arity) -> integer()</name> + <name since="">start(FuncSpec) -> integer()</name> + <name since="">start(Mod, Func) -> integer()</name> + <name since="">start(Mod, Func, Arity) -> integer()</name> <fsummary>Start call count tracing for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> @@ -240,7 +240,7 @@ </desc> </func> <func> - <name>stop() -> integer()</name> + <name since="">stop() -> integer()</name> <fsummary>Stop call count tracing for all functions.</fsummary> <desc> <p>Stop call count tracing for all functions in all modules, @@ -255,9 +255,9 @@ </desc> </func> <func> - <name>stop(FuncSpec) -> integer()</name> - <name>stop(Mod, Func) -> integer()</name> - <name>stop(Mod, Func, Arity) -> integer()</name> + <name since="">stop(FuncSpec) -> integer()</name> + <name since="">stop(Mod, Func) -> integer()</name> + <name since="">stop(Mod, Func, Arity) -> integer()</name> <fsummary>Stop call count tracing for matching functions.</fsummary> <type> <v>FuncSpec = Mod | {Mod,Func,Arity}, {FS}</v> diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml index f098b7d39e..c9e4edd991 100644 --- a/lib/tools/doc/src/eprof.xml +++ b/lib/tools/doc/src/eprof.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>eprof</module> + <module since="">eprof</module> <modulesummary>A Time Profiling Tool for Erlang</modulesummary> <description> <p>The module <c>eprof</c> provides a set of functions for time @@ -40,7 +40,7 @@ </description> <funcs> <func> - <name>start() -> {ok,Pid} | {error,Reason}</name> + <name since="">start() -> {ok,Pid} | {error,Reason}</name> <fsummary>Start Eprof.</fsummary> <type> <v>Pid = pid()</v> @@ -51,9 +51,9 @@ </desc> </func> <func> - <name>start_profiling(Rootset) -> profiling | {error, Reason}</name> - <name>start_profiling(Rootset,Pattern) -> profiling | {error, Reason}</name> - <name>start_profiling(Rootset,Pattern,Options) -> profiling | {error, Reason}</name> + <name since="">start_profiling(Rootset) -> profiling | {error, Reason}</name> + <name since="OTP R14B">start_profiling(Rootset,Pattern) -> profiling | {error, Reason}</name> + <name since="OTP R16B01">start_profiling(Rootset,Pattern,Options) -> profiling | {error, Reason}</name> <fsummary>Start profiling.</fsummary> <type> <v>Rootset = [atom() | pid()]</v> @@ -79,7 +79,7 @@ </desc> </func> <func> - <name>stop_profiling() -> profiling_stopped | profiling_already_stopped</name> + <name since="">stop_profiling() -> profiling_stopped | profiling_already_stopped</name> <fsummary>Stop profiling.</fsummary> <desc> <p>Stops profiling started with <c>start_profiling/1</c> or @@ -87,14 +87,14 @@ </desc> </func> <func> - <name>profile(Fun) -> profiling | {error, Reason}</name> - <name>profile(Fun, Options) -> profiling | {error, Reason}</name> - <name>profile(Rootset) -> profiling | {error, Reason}</name> - <name>profile(Rootset,Fun) -> {ok, Value} | {error,Reason}</name> - <name>profile(Rootset,Fun,Pattern) -> {ok, Value} | {error, Reason}</name> - <name>profile(Rootset,Module,Function,Args) -> {ok, Value} | {error, Reason}</name> - <name>profile(Rootset,Module,Function,Args,Pattern) -> {ok, Value} | {error, Reason}</name> - <name>profile(Rootset,Module,Function,Args,Pattern,Options) -> {ok, Value} | {error, Reason}</name> + <name since="">profile(Fun) -> profiling | {error, Reason}</name> + <name since="">profile(Fun, Options) -> profiling | {error, Reason}</name> + <name since="">profile(Rootset) -> profiling | {error, Reason}</name> + <name since="">profile(Rootset,Fun) -> {ok, Value} | {error,Reason}</name> + <name since="OTP R14B">profile(Rootset,Fun,Pattern) -> {ok, Value} | {error, Reason}</name> + <name since="">profile(Rootset,Module,Function,Args) -> {ok, Value} | {error, Reason}</name> + <name since="OTP R14B">profile(Rootset,Module,Function,Args,Pattern) -> {ok, Value} | {error, Reason}</name> + <name since="OTP R16B01">profile(Rootset,Module,Function,Args,Pattern,Options) -> {ok, Value} | {error, Reason}</name> <fsummary>Start profiling.</fsummary> <type> <v>Rootset = [atom() | pid()]</v> @@ -128,9 +128,9 @@ </desc> </func> <func> - <name>analyze() -> ok</name> - <name>analyze(Type) -> ok</name> - <name>analyze(Type,Options) -> ok</name> + <name since="OTP R14B">analyze() -> ok</name> + <name since="OTP R14B">analyze(Type) -> ok</name> + <name since="OTP R14B">analyze(Type,Options) -> ok</name> <fsummary>Display profiling results per process.</fsummary> <type> <v>Type = procs | total</v> @@ -152,7 +152,7 @@ </desc> </func> <func> - <name>log(File) -> ok</name> + <name since="">log(File) -> ok</name> <fsummary>Activate logging of <c>eprof</c>printouts.</fsummary> <type> <v>File = atom() | string()</v> @@ -164,7 +164,7 @@ </desc> </func> <func> - <name>stop() -> stopped</name> + <name since="">stop() -> stopped</name> <fsummary>Stop Eprof.</fsummary> <desc> <p>Stops the Eprof server.</p> diff --git a/lib/tools/doc/src/fprof.xml b/lib/tools/doc/src/fprof.xml index 1fd828d127..4bb8862016 100644 --- a/lib/tools/doc/src/fprof.xml +++ b/lib/tools/doc/src/fprof.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>fprof.sgml</file> </header> - <module>fprof</module> + <module since="">fprof</module> <modulesummary>A Time Profiling Tool using trace to file for minimal runtime performance impact.</modulesummary> <description> <p>This module is used to profile a program @@ -101,7 +101,7 @@ </description> <funcs> <func> - <name>start() -> {ok, Pid} | {error, {already_started, Pid}}</name> + <name since="">start() -> {ok, Pid} | {error, {already_started, Pid}}</name> <fsummary>Starts the <c>fprof</c> server.</fsummary> <type> <v>Pid = pid()</v> @@ -117,14 +117,14 @@ </desc> </func> <func> - <name>stop() -> ok</name> + <name since="">stop() -> ok</name> <fsummary>Same as <c>stop(normal)</c>.</fsummary> <desc> <p>Same as <c>stop(normal)</c>.</p> </desc> </func> <func> - <name>stop(Reason) -> ok</name> + <name since="">stop(Reason) -> ok</name> <fsummary>Stops the <c>fprof</c> server.</fsummary> <type> <v>Reason = term()</v> @@ -149,7 +149,7 @@ </desc> </func> <func> - <name>apply(Func, Args) -> term()</name> + <name since="">apply(Func, Args) -> term()</name> <fsummary>Same as <c>apply(Func, Args, [])</c>.</fsummary> <type> <v>Func = function() | {Module, Function}</v> @@ -162,7 +162,7 @@ </desc> </func> <func> - <name>apply(Module, Function, Args) -> term()</name> + <name since="">apply(Module, Function, Args) -> term()</name> <fsummary>Same as <c>apply({Module, Function}, Args, [])</c>.</fsummary> <type> <v>Args = [term()]</v> @@ -174,7 +174,7 @@ </desc> </func> <func> - <name>apply(Func, Args, OptionList) -> term()</name> + <name since="">apply(Func, Args, OptionList) -> term()</name> <fsummary>Calls <c>erlang:apply(Func, Args)</c>surrounded by<c>trace([start | OptionList])</c>and<c>trace(stop)</c>.</fsummary> <type> <v>Func = function() | {Module, Function}</v> @@ -210,7 +210,7 @@ </desc> </func> <func> - <name>apply(Module, Function, Args, OptionList) -> term()</name> + <name since="">apply(Module, Function, Args, OptionList) -> term()</name> <fsummary>Same as <c>apply({Module, Function}, Args, OptionList)</c>.</fsummary> <type> <v>Module = atom()</v> @@ -228,7 +228,7 @@ </desc> </func> <func> - <name>trace(start, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(start, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([start, {file, Filename}])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -238,7 +238,7 @@ </desc> </func> <func> - <name>trace(verbose, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(verbose, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([start, verbose, {file, Filename}])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -249,7 +249,7 @@ </desc> </func> <func> - <name>trace(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -262,7 +262,7 @@ </desc> </func> <func> - <name>trace(verbose) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(verbose) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([start, verbose])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -272,7 +272,7 @@ </desc> </func> <func> - <name>trace(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([OptionName])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -283,7 +283,7 @@ </desc> </func> <func> - <name>trace({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>trace([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -296,7 +296,7 @@ </desc> </func> <func> - <name>trace([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">trace([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Starts or stops tracing.</fsummary> <type> <v>Option = start | stop | {procs, PidSpec} | {procs, [PidSpec]} | verbose | {verbose, bool()} | file | {file, Filename} | {tracer, Tracer}</v> @@ -360,7 +360,7 @@ </desc> </func> <func> - <name>profile() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -370,7 +370,7 @@ </desc> </func> <func> - <name>profile(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -383,7 +383,7 @@ </desc> </func> <func> - <name>profile(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([OptionName])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -394,7 +394,7 @@ </desc> </func> <func> - <name>profile({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>profile([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -407,7 +407,7 @@ </desc> </func> <func> - <name>profile([Option]) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">profile([Option]) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Compiles a trace into raw profile data held by the <c>fprof</c> server.</fsummary> <type> <v>Option = file | {file, Filename} | dump | {dump, Dump} | append | start | stop</v> @@ -465,7 +465,7 @@ </desc> </func> <func> - <name>analyse() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([])</c>.</fsummary> <type> <v>Reason = term()</v> @@ -475,7 +475,7 @@ </desc> </func> <func> - <name>analyse(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -488,7 +488,7 @@ </desc> </func> <func> - <name>analyse(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([OptionName])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -499,7 +499,7 @@ </desc> </func> <func> - <name>analyse({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Same as <c>analyse([{OptionName, OptionValue}])</c>.</fsummary> <type> <v>OptionName = atom()</v> @@ -512,7 +512,7 @@ </desc> </func> <func> - <name>analyse([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> + <name since="">analyse([Option]) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}</name> <fsummary>Analyses raw profile data in the <c>fprof</c> server.</fsummary> <type> <v>Option = dest | {dest, Dest} | append | {cols, Cols} | callers | {callers, bool()} | no_callers | {sort, SortSpec} | totals | {totals, bool()} | details | {details, bool()} | no_details</v> diff --git a/lib/tools/doc/src/instrument.xml b/lib/tools/doc/src/instrument.xml index 9fd9332373..75be22de9b 100644 --- a/lib/tools/doc/src/instrument.xml +++ b/lib/tools/doc/src/instrument.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>instrument.sgml</file> </header> - <module>instrument</module> + <module since="">instrument</module> <modulesummary>Analysis and Utility Functions for Instrumentation</modulesummary> <description> <p>The module <c>instrument</c> contains support for studying the resource @@ -92,7 +92,7 @@ <funcs> <func> - <name name="allocations" arity="0"/> + <name name="allocations" arity="0" since="OTP 21.0"/> <fsummary>Return a summary of all allocations in the system.</fsummary> <desc> <p>Shorthand for @@ -101,7 +101,7 @@ </func> <func> - <name name="allocations" arity="1"/> + <name name="allocations" arity="1" since="OTP 21.0"/> <fsummary>Return a summary of all allocations filtered by allocator type and scheduler id.</fsummary> <desc> @@ -170,7 +170,7 @@ </func> <func> - <name name="carriers" arity="0"/> + <name name="carriers" arity="0" since="OTP 21.0"/> <fsummary>Return a list of all carriers in the system.</fsummary> <desc> <p>Shorthand for @@ -179,7 +179,7 @@ </func> <func> - <name name="carriers" arity="1"/> + <name name="carriers" arity="1" since="OTP 21.0"/> <fsummary>Return a list of all carriers filtered by allocator type and scheduler id.</fsummary> <desc> diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml index d2595cdb60..1d434decfc 100644 --- a/lib/tools/doc/src/lcnt.xml +++ b/lib/tools/doc/src/lcnt.xml @@ -34,7 +34,7 @@ <rev>PA1</rev> <file>lcnt.xml</file> </header> - <module>lcnt</module> + <module since="OTP R13B04">lcnt</module> <modulesummary>A runtime system Lock Profiling tool.</modulesummary> <description> <p>The <c>lcnt</c> module is used to profile the internal ethread locks in the @@ -71,7 +71,7 @@ <funcs> <func> - <name>start() -> {ok, Pid} | {error, {already_started, Pid}} </name> + <name since="OTP R13B04">start() -> {ok, Pid} | {error, {already_started, Pid}} </name> <fsummary>Starts the lock profiler server.</fsummary> <type> <v>Pid = pid()</v> @@ -84,7 +84,7 @@ </func> <func> - <name>stop() -> ok</name> + <name since="OTP R13B04">stop() -> ok</name> <fsummary>Stops the lock profiler server.</fsummary> <desc> <p>Stops the lock profiler server.</p> @@ -92,13 +92,13 @@ </func> <func> - <name>collect() -> ok</name> + <name since="OTP R13B04">collect() -> ok</name> <fsummary>Same as <c>collect(node())</c>.</fsummary> <desc><p>Same as <c>collect(node())</c>.</p></desc> </func> <func> - <name>collect(Node) -> ok</name> + <name since="OTP R13B04">collect(Node) -> ok</name> <fsummary>Collects lock statistics from the runtime system.</fsummary> <type> <v>Node = node()</v> @@ -113,13 +113,13 @@ </func> <func> - <name>clear() -> ok</name> + <name since="OTP R13B04">clear() -> ok</name> <fsummary>Same as <c>clear(node())</c>.</fsummary> <desc><p>Same as <c>clear(node())</c>.</p></desc> </func> <func> - <name>clear(Node) -> ok</name> + <name since="OTP R13B04">clear(Node) -> ok</name> <fsummary>Clears the internal lock statistics from runtime system.</fsummary> <type> <v>Node = node()</v> @@ -133,12 +133,12 @@ </desc> </func> <func> - <name>conflicts() -> ok</name> + <name since="OTP R13B04">conflicts() -> ok</name> <fsummary>Same as <c>conflicts([])</c>.</fsummary> <desc><p>Same as <c>conflicts([])</c>.</p></desc> </func> <func> - <name>conflicts([Option]) -> ok</name> + <name since="OTP R13B04">conflicts([Option]) -> ok</name> <fsummary>Prints a list of internal lock counters.</fsummary> <type> <v>Option = {sort, Sort} | {reverse, bool()} | {thresholds, [Thresholds]} | {print, [Print | {Print, integer()}]} | {max_locks, MaxLocks} | {combine, bool()}</v> @@ -154,14 +154,14 @@ </func> <func> - <name>locations() -> ok</name> + <name since="OTP R13B04">locations() -> ok</name> <fsummary>Same as <c>locations([])</c>.</fsummary> <desc> <p>Same as <c>locations([])</c>.</p> </desc> </func> <func> - <name>locations([Option]) -> ok</name> + <name since="OTP R13B04">locations([Option]) -> ok</name> <fsummary>Prints a list of internal lock counters by source code locations.</fsummary> <type> <v>Option = {sort, Sort} | {thresholds, [Thresholds]} | {print, [Print | {Print, integer()}]} | {max_locks, MaxLocks} | {combine, bool()}</v> @@ -177,12 +177,12 @@ </func> <func> - <name>inspect(Lock) -> ok</name> + <name since="OTP R13B04">inspect(Lock) -> ok</name> <fsummary>Same as <c>inspect(Lock, [])</c>.</fsummary> <desc><p>Same as <c>inspect(Lock, [])</c>.</p></desc> </func> <func> - <name>inspect(Lock, [Option]) -> ok</name> + <name since="OTP R13B04">inspect(Lock, [Option]) -> ok</name> <fsummary>Prints a list of internal lock counters for a specific lock.</fsummary> <type> <v>Lock = Name | {Name, Id | [Id]}</v> @@ -268,7 +268,7 @@ </func> <func> - <name>information() -> ok</name> + <name since="OTP R13B04">information() -> ok</name> <fsummary>Prints lcnt server state and generic information about collected lock statistics.</fsummary> <desc> <p>Prints lcnt server state and generic information about collected lock statistics.</p> @@ -276,7 +276,7 @@ </func> <func> - <name>swap_pid_keys() -> ok</name> + <name since="OTP R13B04">swap_pid_keys() -> ok</name> <fsummary>Swaps places on <c>Name</c> and <c>Id</c> space for ports and processes.</fsummary> <desc> <p>Swaps places on <c>Name</c> and <c>Id</c> space for ports and processes.</p> @@ -284,7 +284,7 @@ </func> <func> - <name>load(Filename) -> ok</name> + <name since="OTP R13B04">load(Filename) -> ok</name> <fsummary>Restores previously saved data to the server.</fsummary> <type> <v>Filename = filename()</v> @@ -295,7 +295,7 @@ </func> <func> - <name>save(Filename) -> ok</name> + <name since="OTP R13B04">save(Filename) -> ok</name> <fsummary>Saves the collected data to file.</fsummary> <type> <v>Filename = filename()</v> @@ -312,7 +312,7 @@ </section> <funcs> <func> - <name>apply(Fun) -> term()</name> + <name since="OTP R13B04">apply(Fun) -> term()</name> <fsummary>Same as <c>apply(Fun, [])</c>.</fsummary> <type> <v>Fun = fun()</v> @@ -322,7 +322,7 @@ </desc> </func> <func> - <name>apply(Fun, Args) -> term()</name> + <name since="OTP R13B04">apply(Fun, Args) -> term()</name> <fsummary>Same as <c>apply(Module, Function, Args)</c>.</fsummary> <type> <v>Fun = fun()</v> @@ -333,7 +333,7 @@ </desc> </func> <func> - <name>apply(Module, Function, Args) -> term()</name> + <name since="OTP R13B04">apply(Module, Function, Args) -> term()</name> <fsummary>Clears counters, applies function and collects the profiling results.</fsummary> <type> <v>Module = atom()</v> @@ -358,12 +358,12 @@ </func> <func> - <name>pid(Id, Serial) -> pid()</name> + <name since="OTP R13B04">pid(Id, Serial) -> pid()</name> <fsummary>Same as <c>pid(node(), Id, Serial)</c>.</fsummary> <desc><p>Same as <c>pid(node(), Id, Serial)</c>.</p></desc> </func> <func> - <name>pid(Node, Id, Serial) -> pid()</name> + <name since="OTP R13B04">pid(Node, Id, Serial) -> pid()</name> <fsummary>Creates a process id with creation 0.</fsummary> <type> <v>Node = node()</v> @@ -376,12 +376,12 @@ </func> <func> - <name>port(Id) -> port()</name> + <name since="OTP R13B04">port(Id) -> port()</name> <fsummary>Same as <c>port(node(), Id)</c>.</fsummary> <desc><p>Same as <c>port(node(), Id)</c>.</p></desc> </func> <func> - <name>port(Node, Id) -> port()</name> + <name since="OTP R13B04">port(Node, Id) -> port()</name> <fsummary>Creates a port id with creation 0.</fsummary> <type> <v>Node = node()</v> @@ -399,12 +399,12 @@ <funcs> <func> - <name>rt_collect() -> [lock_counter_data()]</name> + <name since="OTP R13B04">rt_collect() -> [lock_counter_data()]</name> <fsummary>Same as <c>rt_collect(node())</c>.</fsummary> <desc> <p>Same as <c>rt_collect(node())</c>.</p> </desc> </func> <func> - <name>rt_collect(Node) -> [lock_counter_data()]</name> + <name since="OTP R13B04">rt_collect(Node) -> [lock_counter_data()]</name> <fsummary>Returns a list of raw lock counter data.</fsummary> <type> <v>Node = node()</v> @@ -413,12 +413,12 @@ </func> <func> - <name>rt_clear() -> ok</name> + <name since="OTP R13B04">rt_clear() -> ok</name> <fsummary>Same as <c>rt_clear(node())</c>.</fsummary> <desc> <p>Same as <c>rt_clear(node())</c>.</p> </desc> </func> <func> - <name>rt_clear(Node) -> ok</name> + <name since="OTP R13B04">rt_clear(Node) -> ok</name> <fsummary>Clears the internal counters.</fsummary> <type> <v>Node = node()</v> @@ -427,13 +427,13 @@ </func> <func> - <name>rt_mask() -> [category_atom()]</name> + <name since="OTP 20.1">rt_mask() -> [category_atom()]</name> <fsummary>Same as <c>rt_mask(node())</c>.</fsummary> <desc><p>Same as <c>rt_mask(node())</c>.</p></desc> </func> <func> - <name>rt_mask(Node) -> [category_atom()]</name> + <name since="OTP 20.1">rt_mask(Node) -> [category_atom()]</name> <fsummary>Returns the current lock category mask.</fsummary> <type> <v>Node = node()</v> @@ -447,7 +447,7 @@ </func> <func> - <name>rt_mask(Categories) -> ok | {error, copy_save_enabled}</name> + <name since="OTP 20.1">rt_mask(Categories) -> ok | {error, copy_save_enabled}</name> <fsummary>Same as <c>rt_mask(node(), Categories)</c>.</fsummary> <type> <v>Categories = [atom()]</v> @@ -456,7 +456,7 @@ </func> <func> - <name>rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name> + <name since="OTP 20.1">rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name> <fsummary>Changes the lock category mask.</fsummary> <type> <v>Node = node()</v> @@ -489,12 +489,12 @@ </func> <func> - <name>rt_opt({Type, bool()}) -> bool()</name> + <name since="OTP R13B04">rt_opt({Type, bool()}) -> bool()</name> <fsummary>Same as <c>rt_opt(node(), {Type, Opt})</c>.</fsummary> <desc> <p>Same as <c>rt_opt(node(), {Type, Opt})</c>.</p> </desc> </func> <func> - <name>rt_opt(Node, {Type, bool()}) -> bool()</name> + <name since="OTP R13B04">rt_opt(Node, {Type, bool()}) -> bool()</name> <fsummary>Changes the lock counter behavior and returns the previous behaviour.</fsummary> <type> <v>Node = node()</v> diff --git a/lib/tools/doc/src/make.xml b/lib/tools/doc/src/make.xml index 123fcd4afc..af2404707f 100644 --- a/lib/tools/doc/src/make.xml +++ b/lib/tools/doc/src/make.xml @@ -30,7 +30,7 @@ <date></date> <rev></rev> </header> - <module>make</module> + <module since="">make</module> <modulesummary>A Make Utility for Erlang</modulesummary> <description> <p>The module <c>make</c> provides a set of functions similar to @@ -38,8 +38,8 @@ </description> <funcs> <func> - <name>all() -> up_to_date | error</name> - <name>all(Options) -> up_to_date | error</name> + <name since="">all() -> up_to_date | error</name> + <name since="">all(Options) -> up_to_date | error</name> <fsummary>Compile a set of modules.</fsummary> <type> <v>Options = [Option]</v> @@ -87,8 +87,8 @@ </desc> </func> <func> - <name>files(ModFiles) -> up_to_date | error</name> - <name>files(ModFiles, Options) -> up_to_date | error</name> + <name since="">files(ModFiles) -> up_to_date | error</name> + <name since="">files(ModFiles, Options) -> up_to_date | error</name> <fsummary>Compile a set of modules.</fsummary> <type> <v>ModFiles = [Module | File]</v> diff --git a/lib/tools/doc/src/tags.xml b/lib/tools/doc/src/tags.xml index ea0ae5cc4d..90a8b28177 100644 --- a/lib/tools/doc/src/tags.xml +++ b/lib/tools/doc/src/tags.xml @@ -32,7 +32,7 @@ <rev>A</rev> <file>tags.sgml</file> </header> - <module>tags</module> + <module since="">tags</module> <modulesummary>Generate Emacs TAGS file from Erlang source files</modulesummary> <description> <p>A <c>TAGS</c> file is used by Emacs to find function and variable @@ -42,14 +42,14 @@ </description> <funcs> <func> - <name>file(File [, Options])</name> + <name since="">file(File [, Options])</name> <fsummary>Create a <c>TAGS</c>file for the file <c>File</c>.</fsummary> <desc> <p>Create a <c>TAGS</c> file for the file <c>File</c>.</p> </desc> </func> <func> - <name>files(FileList [, Options])</name> + <name since="">files(FileList [, Options])</name> <fsummary>Create a TAGS file for the files in the list<c>FileList</c>.</fsummary> <desc> <p>Create a TAGS file for the files in the list @@ -57,7 +57,7 @@ </desc> </func> <func> - <name>dir(Dir [, Options])</name> + <name since="">dir(Dir [, Options])</name> <fsummary>Create a TAGS file for all files in directory<c>Dir</c>.</fsummary> <desc> <p>Create a TAGS file for all files in directory @@ -65,7 +65,7 @@ </desc> </func> <func> - <name>dirs(DirList [, Options])</name> + <name since="">dirs(DirList [, Options])</name> <fsummary>Create a TAGS file for all files in any directory in<c>DirList</c>.</fsummary> <desc> <p>Create a TAGS file for all files in any directory in @@ -73,7 +73,7 @@ </desc> </func> <func> - <name>subdir(Dir [, Options])</name> + <name since="">subdir(Dir [, Options])</name> <fsummary>Descend recursively down the directory <c>Dir</c>and create a <c>TAGS</c>file based on all files found.</fsummary> <desc> <p>Descend recursively down the directory <c>Dir</c> and @@ -81,7 +81,7 @@ </desc> </func> <func> - <name>subdirs(DirList [, Options])</name> + <name since="">subdirs(DirList [, Options])</name> <fsummary>Descend recursively down all the directories in<c>DirList</c>and create a <c>TAGS</c>file based on all files found.</fsummary> <desc> <p>Descend recursively down all the directories in @@ -90,7 +90,7 @@ </desc> </func> <func> - <name>root([Options])</name> + <name since="">root([Options])</name> <fsummary>Create a <c>TAGS</c>file covering all files in the Erlang distribution.</fsummary> <desc> <p>Create a <c>TAGS</c> file covering all files in diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml index 6f833246ad..ab3641a52f 100644 --- a/lib/tools/doc/src/xref.xml +++ b/lib/tools/doc/src/xref.xml @@ -32,7 +32,7 @@ <rev>PA1</rev> <file>xref.sgml</file> </header> - <module>xref</module> + <module since="">xref</module> <modulesummary>A Cross Reference Tool for analyzing dependencies between functions, modules, applications and releases.</modulesummary> <description> <p>Xref is a cross reference tool that can be used for finding @@ -729,7 +729,7 @@ xref() = atom() | pid() </pre> </description> <funcs> <func> - <name>add_application(Xref, Directory [, Options]) -> {ok, application()} | Error</name> + <name since="">add_application(Xref, Directory [, Options]) -> {ok, application()} | Error</name> <fsummary>Add the modules of an application.</fsummary> <type> <v>Directory = directory()</v> @@ -761,7 +761,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>add_directory(Xref, Directory [, Options]) -> {ok, Modules} | Error</name> + <name since="">add_directory(Xref, Directory [, Options]) -> {ok, Modules} | Error</name> <fsummary>Add the modules in a directory.</fsummary> <type> <v>Directory = directory()</v> @@ -791,7 +791,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>add_module(Xref, File [, Options]) -> {ok, module()} | Error</name> + <name since="">add_module(Xref, File [, Options]) -> {ok, module()} | Error</name> <fsummary>Add a module.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -814,7 +814,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>add_release(Xref, Directory [, Options]) -> {ok, release()} | Error</name> + <name since="">add_release(Xref, Directory [, Options]) -> {ok, release()} | Error</name> <fsummary>Add the modules of a release.</fsummary> <type> <v>Directory = directory()</v> @@ -849,7 +849,7 @@ xref() = atom() | pid() </pre> </desc> </func> <func> - <name>analyze(Xref, Analysis [, Options]) -> {ok, Answer} | Error</name> + <name since="">analyze(Xref, Analysis [, Options]) -> {ok, Answer} | Error</name> <fsummary>Evaluate a predefined analysis.</fsummary> <type> <v>Analysis = undefined_function_calls | undefined_functions | locals_not_used | exports_not_used | deprecated_function_calls | {deprecated_function_calls, DeprFlag} | deprecated_functions | {deprecated_functions, DeprFlag} | {call, FuncSpec} | {use, FuncSpec} | {module_call, ModSpec} | {module_use, ModSpec} | {application_call, AppSpec} | {application_use, AppSpec} | {release_call, RelSpec} | {release_use, RelSpec}</v> @@ -939,7 +939,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>d(Directory) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> + <name since="">d(Directory) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> <fsummary>Check the modules in a directory using the code path.</fsummary> <type> <v>Directory = directory()</v> @@ -979,8 +979,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>forget(Xref) -> ok</name> - <name>forget(Xref, Variables) -> ok | Error</name> + <name since="">forget(Xref) -> ok</name> + <name since="">forget(Xref, Variables) -> ok | Error</name> <fsummary>Remove user variables and their values.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -994,7 +994,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>format_error(Error) -> Chars</name> + <name since="">format_error(Error) -> Chars</name> <fsummary>Return an English description of an Xref error reply.</fsummary> <type> <v>Error = {error, module(), term()}</v> @@ -1008,8 +1008,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>get_default(Xref) -> [{Option, Value}]</name> - <name>get_default(Xref, Option) -> {ok, Value} | Error</name> + <name since="">get_default(Xref) -> [{Option, Value}]</name> + <name since="">get_default(Xref, Option) -> {ok, Value} | Error</name> <fsummary>Return the default values of options.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1023,7 +1023,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>get_library_path(Xref) -> {ok, LibraryPath}</name> + <name since="">get_library_path(Xref) -> {ok, LibraryPath}</name> <fsummary>Return the library path.</fsummary> <type> <v>LibraryPath = library_path()</v> @@ -1034,9 +1034,9 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>info(Xref) -> [Info]</name> - <name>info(Xref, Category) -> [{Item, [Info]}]</name> - <name>info(Xref, Category, Items) -> [{Item, [Info]}]</name> + <name since="">info(Xref) -> [Info]</name> + <name since="">info(Xref, Category) -> [{Item, [Info]}]</name> + <name since="">info(Xref, Category, Items) -> [{Item, [Info]}]</name> <fsummary>Return information about an Xref server.</fsummary> <type> <v>Application = [] | [application()]</v> @@ -1220,8 +1220,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>m(Module) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> - <name>m(File) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> + <name since="">m(Module) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> + <name since="">m(File) -> [DebugInfoResult] | [NoDebugInfoResult] | Error</name> <fsummary>Check a module using the code path.</fsummary> <type> <v>DebugInfoResult = {deprecated, [funcall()]} | {undefined, [funcall()]} | {unused, [mfa()]}</v> @@ -1263,7 +1263,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>q(Xref, Query [, Options]) -> {ok, Answer} | Error</name> + <name since="">q(Xref, Query [, Options]) -> {ok, Answer} | Error</name> <fsummary>Evaluate a query.</fsummary> <type> <v>Answer = false | [constant()] | [Call] | [Component] | int() | [DefineAt] | [CallAt] | [AllLines]</v> @@ -1322,7 +1322,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>remove_application(Xref, Applications) -> ok | Error</name> + <name since="">remove_application(Xref, Applications) -> ok | Error</name> <fsummary>Remove applications and their modules.</fsummary> <type> <v>Applications = application() | [application()]</v> @@ -1335,7 +1335,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>remove_module(Xref, Modules) -> ok | Error</name> + <name since="">remove_module(Xref, Modules) -> ok | Error</name> <fsummary>Remove analyzed modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1348,7 +1348,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>remove_release(Xref, Releases) -> ok | Error</name> + <name since="">remove_release(Xref, Releases) -> ok | Error</name> <fsummary>Remove releases and their applications and modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1363,7 +1363,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>replace_application(Xref, Application, Directory [, Options]) -> {ok, application()} | Error</name> + <name since="">replace_application(Xref, Application, Directory [, Options]) -> {ok, application()} | Error</name> <fsummary>Replace an application's modules.</fsummary> <type> <v>Application = application()</v> @@ -1384,7 +1384,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>replace_module(Xref, Module, File [, Options]) -> {ok, module()} | Error</name> + <name since="">replace_module(Xref, Module, File [, Options]) -> {ok, module()} | Error</name> <fsummary>Replace an analyzed module.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1409,8 +1409,8 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>set_default(Xref, Option, Value) -> {ok, OldValue} | Error</name> - <name>set_default(Xref, OptionValues) -> ok | Error</name> + <name since="">set_default(Xref, Option, Value) -> {ok, OldValue} | Error</name> + <name since="">set_default(Xref, OptionValues) -> ok | Error</name> <fsummary>Set the default values of options.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1435,7 +1435,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>set_library_path(Xref, LibraryPath [, Options]) -> ok | Error</name> + <name since="">set_library_path(Xref, LibraryPath [, Options]) -> ok | Error</name> <fsummary>Set the library path and finds the library modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1469,7 +1469,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>start(NameOrOptions) -> Return</name> + <name since="">start(NameOrOptions) -> Return</name> <fsummary>Create an Xref server.</fsummary> <type> <v>NameOrOptions = Name | Options</v> @@ -1487,7 +1487,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>start(Name, Options) -> Return</name> + <name since="">start(Name, Options) -> Return</name> <fsummary>Create an Xref server.</fsummary> <type> <v>Name = atom()</v> @@ -1504,7 +1504,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>stop(Xref)</name> + <name since="">stop(Xref)</name> <fsummary>Delete an Xref server.</fsummary> <type> <v>Xref = xref()</v> @@ -1514,7 +1514,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>update(Xref [, Options]) -> {ok, Modules} | Error</name> + <name since="">update(Xref [, Options]) -> {ok, Modules} | Error</name> <fsummary>Replace newly compiled analyzed modules.</fsummary> <type> <v>Error = {error, module(), Reason}</v> @@ -1534,7 +1534,7 @@ Evaluates a predefined analysis. </desc> </func> <func> - <name>variables(Xref [, Options]) -> {ok, [VariableInfo]}</name> + <name since="">variables(Xref [, Options]) -> {ok, [VariableInfo]}</name> <fsummary>Return the names of variables.</fsummary> <type> <v>Options = [Option] | Option</v> diff --git a/lib/tools/priv/styles.css b/lib/tools/priv/styles.css index e10e94e3ad..84f00be9fd 100644 --- a/lib/tools/priv/styles.css +++ b/lib/tools/priv/styles.css @@ -53,21 +53,25 @@ table thead { display: none; } table td.line, +table td.line a, table td.hits { width: 20px; background: #eaeaea; text-align: center; + text-decoration: none; font-size: 11px; padding: 0 10px; color: #949494; } table td.hits { width: 10px; + text-align: right; padding: 2px 5px; color: rgba(0, 0, 0, 0.6); background-color: #f0f0f0; } tr.miss td.line, +tr.miss td.line a, tr.miss td.hits { background-color: #ffdce0; border-color: #fdaeb7; @@ -76,6 +80,7 @@ tr.miss td { background-color: #ffeef0; } tr.hit td.line, +tr.hit td.line a, tr.hit td.hits { background-color: #cdffd8; border-color: #bef5cb; diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index d7269e3f27..8d4561ca9e 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -2563,11 +2563,13 @@ table_row(Line, L) -> table_data(Line, L, N) -> LineNoNL = Line -- "\n", ["<td class=\"line\" id=\"L",integer_to_list(L),"\">", + "<a href=\"#L",integer_to_list(L),"\">", integer_to_list(L), - "</td>\n", + "</a></td>\n", "<td class=\"hits\">",maybe_integer_to_list(N),"</td>\n", "<td class=\"source\"><code>",LineNoNL,"</code></td>\n</tr>\n"]. +maybe_integer_to_list(0) -> "<pre style=\"display: inline;\">:-(</pre>"; maybe_integer_to_list(N) when is_integer(N) -> integer_to_list(N); maybe_integer_to_list(_) -> "". diff --git a/lib/wx/api_gen/wx_extra/added_func.h b/lib/wx/api_gen/wx_extra/added_func.h index bffe391140..28fecbf454 100644 --- a/lib/wx/api_gen/wx_extra/added_func.h +++ b/lib/wx/api_gen/wx_extra/added_func.h @@ -44,3 +44,9 @@ class wxWindowGTK { public: double GetContentScaleFactor(); }; + +class wxDisplay { + public: + // get the resolution of this monitor in pixels per inch + wxSize GetPPI() const; +}; diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl index cec6ac9ccf..8a00498319 100644 --- a/lib/wx/api_gen/wx_gen.erl +++ b/lib/wx/api_gen/wx_gen.erl @@ -701,8 +701,13 @@ parse_type2(["wxe_cb"|R],Info,Opts, T) -> parse_type2(R,Info,Opts,T#type{name=int,base=wxe_cb}); parse_type2([const|R],Info,Opts,T=#type{mod=Mod}) -> parse_type2(R,Info,Opts,T#type{mod=[const|Mod]}); -parse_type2(["unsigned"|R],Info,Opts,T=#type{mod=Mod}) -> - parse_type2(R,Info,Opts,T#type{mod=[unsigned|Mod]}); +parse_type2(["unsigned"|R],Info,Opts,T=#type{mod=Mod}) -> + case T#type.base of + undefined -> + parse_type2(R,Info,Opts,T#type{name=int, base=int, mod=[unsigned|Mod]}); + _ -> + parse_type2(R,Info,Opts,T#type{mod=[unsigned|Mod]}) + end; parse_type2(["int"|R],Info,Opts, T) -> parse_type2(R,Info,Opts,T#type{name=int,base=int}); parse_type2(["wxByte"|R],Info,Opts, T) -> diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl index f13d5873a0..c6f2534380 100644 --- a/lib/wx/api_gen/wx_gen_cpp.erl +++ b/lib/wx/api_gen/wx_gen_cpp.erl @@ -1165,6 +1165,7 @@ gen_macros() -> w("#include <wx/fontdlg.h>~n"), w("#include <wx/progdlg.h>~n"), w("#include <wx/printdlg.h>~n"), + w("#include <wx/display.h>~n"), w("#include <wx/dcbuffer.h>~n"), w("#include <wx/dcmirror.h>~n"), w("#include <wx/glcanvas.h>~n"), @@ -1176,6 +1177,7 @@ gen_macros() -> w("#include <wx/sashwin.h>~n"), w("#include <wx/laywin.h>~n"), w("#include <wx/graphics.h>~n"), + w("#include <wx/dcgraph.h>~n"), w("#include <wx/aui/aui.h>~n"), w("#include <wx/datectrl.h>~n"), w("#include <wx/filepicker.h>~n"), @@ -1330,8 +1332,10 @@ encode_events(Evs) -> w(" } else {~n"), w(" send_res = rt.send();~n"), w(" if(cb->skip) event->Skip();~n"), - #class{id=MouseId} = lists:keyfind("wxMouseEvent", #class.name, Evs), - w(" if(app->recurse_level < 1 && Etype->cID != ~p) {~n", [MouseId]), + #class{id=SizeId} = lists:keyfind("wxSizeEvent", #class.name, Evs), + #class{id=MoveId} = lists:keyfind("wxMoveEvent", #class.name, Evs), + w(" if(app->recurse_level < 1 && (Etype->cID == ~w || Etype->cID == ~w)) {~n", + [SizeId, MoveId]), w(" app->recurse_level++;~n"), w(" app->dispatch_cmds();~n"), w(" app->recurse_level--;~n"), diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf index c1b55b6875..9707fedf67 100644 --- a/lib/wx/api_gen/wxapi.conf +++ b/lib/wx/api_gen/wxapi.conf @@ -27,7 +27,7 @@ {not_const, [wxHAS_INT64,wxBYTE_ORDER,wxRETAINED, wxFONTENCODING_UTF32,wxFONTENCODING_UTF16, wxDEFAULT_CONTROL_BORDER,wxMOD_CMD, - wxMAJOR_VERSION, wxMINOR_VERSION, + wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER,wxSUBRELEASE_NUMBER,wxBETA_NUMBER, %% wxALWAYS_NATIVE_DOUBLE_BUFFER, @@ -37,16 +37,30 @@ wxCURSOR_DEFAULT, wxCURSOR_ARROWWAIT, wxCURSOR_MAX, - wxLanguage + wxLanguage, + wxFONTWEIGHT_NORMAL, + wxFONTWEIGHT_LIGHT, + wxFONTWEIGHT_BOLD, + wxFONTWEIGHT_MAX ]}. -{gvars, +{gvars, [ {wxITALIC_FONT, wxFont}, {wxNORMAL_FONT, wxFont}, {wxSMALL_FONT, wxFont}, {wxSWISS_FONT, wxFont}, - + + %% Added (enum) values in 3.1.2 + {wxFONTWEIGHT_INVALID, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_THIN, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_EXTRALIGHT, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_MEDIUM, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_SEMIBOLD, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_EXTRABOLD, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_HEAVY, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxFONTWEIGHT_EXTRAHEAVY, {test_if, "wxCHECK_VERSION(3,1,2)"}}, + {wxBLACK_DASHED_PEN, wxPen}, {wxBLACK_PEN, wxPen}, {wxCYAN_PEN, wxPen}, @@ -2016,3 +2030,17 @@ ['GetPosition', 'GetNumberOfFiles', {'GetFiles', [{return, [{single, {list, 'm_noFiles'}}]}]} ]}. + + +{class, wxDisplay, root, [{ifdef, wxUSE_DISPLAY}], + ['wxDisplay', '~wxDisplay', + 'IsOk', + {'GetClientArea', [{test_if, "wxCHECK_VERSION(2,8,12)"}]}, + 'GetGeometry', 'GetName', 'IsPrimary', + 'GetCount', 'GetFromPoint', 'GetFromWindow', + {'GetPPI', [{test_if, "wxCHECK_VERSION(3,1,2)"}]} + ]}. + +{class, wxGCDC, wxDC, [{ifdef, wxUSE_GRAPHICS_CONTEXT}], + ['wxGCDC', '~wxGCDC', 'GetGraphicsContext', 'SetGraphicsContext' + ]}. diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index daa8afce83..8ec64bea7e 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -181,6 +181,7 @@ release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/priv" $(INSTALL_DATA) ../priv/erlang-logo32.png "$(RELSYSDIR)/priv/" $(INSTALL_DATA) ../priv/erlang-logo64.png "$(RELSYSDIR)/priv/" + $(INSTALL_DATA) ../priv/erlang-logo128.png "$(RELSYSDIR)/priv/" $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) "$(RELSYSDIR)/priv/" $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) "$(RELSYSDIR)/priv/" diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h index fc0ae0d9fc..a7114eb188 100644 --- a/lib/wx/c_src/gen/wxe_derived_dest.h +++ b/lib/wx/c_src/gen/wxe_derived_dest.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2016. All Rights Reserved. + * Copyright Ericsson AB 2008-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -799,3 +799,11 @@ class EwxDCOverlay : public wxDCOverlay { EwxDCOverlay(wxOverlay& overlay,wxWindowDC * dc) : wxDCOverlay(overlay,dc) {}; }; +#if wxUSE_GRAPHICS_CONTEXT +class EwxGCDC : public wxGCDC { + public: ~EwxGCDC() {((WxeApp *)wxTheApp)->clearPtr(this);}; + EwxGCDC(const wxWindowDC& dc) : wxGCDC(dc) {}; + EwxGCDC() : wxGCDC() {}; +}; +#endif // wxUSE_GRAPHICS_CONTEXT + diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp index 01787c8a64..8c3283a670 100644 --- a/lib/wx/c_src/gen/wxe_events.cpp +++ b/lib/wx/c_src/gen/wxe_events.cpp @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2016. All Rights Reserved. + * Copyright Ericsson AB 2008-2019. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -910,7 +910,7 @@ case 238: {// wxDropFilesEvent } else { send_res = rt.send(); if(cb->skip) event->Skip(); - if(app->recurse_level < 1 && Etype->cID != 168) { + if(app->recurse_level < 1 && (Etype->cID == 171 || Etype->cID == 172)) { app->recurse_level++; app->dispatch_cmds(); app->recurse_level--; diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp index 74961b2e5e..32e4bf855b 100644 --- a/lib/wx/c_src/gen/wxe_funcs.cpp +++ b/lib/wx/c_src/gen/wxe_funcs.cpp @@ -32113,6 +32113,120 @@ case wxDropFilesEvent_GetFiles: { // wxDropFilesEvent::GetFiles rt.add(tmpArrayStr); break; } +#if wxUSE_DISPLAY +case wxDisplay_new: { // wxDisplay::wxDisplay + int n=0; + while( * (int*) bp) { switch (* (int*) bp) { + case 1: {bp += 4; + n = (int)*(unsigned int *) bp; bp += 4; + } break; + }}; + wxDisplay * Result = new wxDisplay(n); + newPtr((void *) Result, 239, memenv); + rt.addRef(getRef((void *)Result,memenv), "wxDisplay"); + break; +} +case wxDisplay_destruct: { // wxDisplay::~wxDisplay + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(This) { ((WxeApp *) wxTheApp)->clearPtr((void *) This); + delete This;} + break; +} +case wxDisplay_IsOk: { // wxDisplay::IsOk + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + bool Result = This->IsOk(); + rt.addBool(Result); + break; +} +#if wxCHECK_VERSION(2,8,12) +case wxDisplay_GetClientArea: { // wxDisplay::GetClientArea + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + wxRect Result = This->GetClientArea(); + rt.add(Result); + break; +} +#endif +case wxDisplay_GetGeometry: { // wxDisplay::GetGeometry + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + wxRect Result = This->GetGeometry(); + rt.add(Result); + break; +} +case wxDisplay_GetName: { // wxDisplay::GetName + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + wxString Result = This->GetName(); + rt.add(Result); + break; +} +case wxDisplay_IsPrimary: { // wxDisplay::IsPrimary + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + bool Result = This->IsPrimary(); + rt.addBool(Result); + break; +} +case wxDisplay_GetCount: { // wxDisplay::GetCount + int Result = wxDisplay::GetCount(); + rt.addUint(Result); + break; +} +case wxDisplay_GetFromPoint: { // wxDisplay::GetFromPoint + int * ptX = (int *) bp; bp += 4; + int * ptY = (int *) bp; bp += 4; + wxPoint pt = wxPoint(*ptX,*ptY); + int Result = wxDisplay::GetFromPoint(pt); + rt.addInt(Result); + break; +} +case wxDisplay_GetFromWindow: { // wxDisplay::GetFromWindow + wxWindow *window = (wxWindow *) getPtr(bp,memenv); bp += 4; + int Result = wxDisplay::GetFromWindow(window); + rt.addInt(Result); + break; +} +#if wxCHECK_VERSION(3,1,2) +case wxDisplay_GetPPI: { // wxDisplay::GetPPI + wxDisplay *This = (wxDisplay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + wxSize Result = This->GetPPI(); + rt.add(Result); + break; +} +#endif +#endif // wxUSE_DISPLAY +#if wxUSE_GRAPHICS_CONTEXT +case wxGCDC_new_1: { // wxGCDC::wxGCDC + wxWindowDC *dc = (wxWindowDC *) getPtr(bp,memenv); bp += 4; + wxGCDC * Result = new EwxGCDC(*dc); + newPtr((void *) Result, 8, memenv); + rt.addRef(getRef((void *)Result,memenv), "wxGCDC"); + break; +} +case wxGCDC_new_0: { // wxGCDC::wxGCDC + wxGCDC * Result = new EwxGCDC(); + newPtr((void *) Result, 8, memenv); + rt.addRef(getRef((void *)Result,memenv), "wxGCDC"); + break; +} +case wxGCDC_GetGraphicsContext: { // wxGCDC::GetGraphicsContext + wxGCDC *This = (wxGCDC *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + wxGraphicsContext * Result = (wxGraphicsContext*)This->GetGraphicsContext(); + rt.addRef(getRef((void *)Result,memenv,8), "wxGraphicsContext"); + break; +} +case wxGCDC_SetGraphicsContext: { // wxGCDC::SetGraphicsContext + wxGCDC *This = (wxGCDC *) getPtr(bp,memenv); bp += 4; + wxGraphicsContext *ctx = (wxGraphicsContext *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + This->SetGraphicsContext(ctx); + break; +} +#endif // wxUSE_GRAPHICS_CONTEXT default: { wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_"); error.addInt((int) op); @@ -32174,6 +32288,7 @@ bool WxeApp::delete_object(void *ptr, wxeRefData *refd) { case 231: delete (EwxLocale *) ptr; return false; case 236: delete (wxOverlay *) ptr; break; case 237: delete (EwxDCOverlay *) ptr; return false; + case 239: delete (wxDisplay *) ptr; break; default: delete (wxObject *) ptr; return false; } return true; diff --git a/lib/wx/c_src/gen/wxe_init.cpp b/lib/wx/c_src/gen/wxe_init.cpp index 6ce33a5449..5a52d69003 100644 --- a/lib/wx/c_src/gen/wxe_init.cpp +++ b/lib/wx/c_src/gen/wxe_init.cpp @@ -55,6 +55,14 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) { rt.addTupleCount(2); rt.addAtom("wxFONTENCODING_UTF32"); rt.addInt(wxFONTENCODING_UTF32); rt.addTupleCount(2); + rt.addAtom("wxFONTWEIGHT_BOLD"); rt.addInt(wxFONTWEIGHT_BOLD); + rt.addTupleCount(2); + rt.addAtom("wxFONTWEIGHT_LIGHT"); rt.addInt(wxFONTWEIGHT_LIGHT); + rt.addTupleCount(2); + rt.addAtom("wxFONTWEIGHT_MAX"); rt.addInt(wxFONTWEIGHT_MAX); + rt.addTupleCount(2); + rt.addAtom("wxFONTWEIGHT_NORMAL"); rt.addInt(wxFONTWEIGHT_NORMAL); + rt.addTupleCount(2); rt.addAtom("wxMOD_CMD"); rt.addInt(wxMOD_CMD); rt.addTupleCount(2); rt.addAtom("wxLANGUAGE_ABKHAZIAN"); rt.addInt(wxLANGUAGE_ABKHAZIAN); @@ -654,6 +662,62 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) { rt.addTupleCount(2); rt.addAtom("wxCYAN_PEN"); rt.addRef(getRef((void *)wxCYAN_PEN,memenv),"wxPen"); rt.addTupleCount(2); +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_EXTRABOLD"); rt.addInt(wxFONTWEIGHT_EXTRABOLD); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_EXTRABOLD"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_EXTRAHEAVY"); rt.addInt(wxFONTWEIGHT_EXTRAHEAVY); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_EXTRAHEAVY"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_EXTRALIGHT"); rt.addInt(wxFONTWEIGHT_EXTRALIGHT); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_EXTRALIGHT"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_HEAVY"); rt.addInt(wxFONTWEIGHT_HEAVY); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_HEAVY"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_INVALID"); rt.addInt(wxFONTWEIGHT_INVALID); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_INVALID"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_MEDIUM"); rt.addInt(wxFONTWEIGHT_MEDIUM); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_MEDIUM"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_SEMIBOLD"); rt.addInt(wxFONTWEIGHT_SEMIBOLD); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_SEMIBOLD"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif +#if wxCHECK_VERSION(3,1,2) + rt.addAtom("wxFONTWEIGHT_THIN"); rt.addInt(wxFONTWEIGHT_THIN); + rt.addTupleCount(2); +#else + rt.addAtom("wxFONTWEIGHT_THIN"); rt.addAtom("undefined"); + rt.addTupleCount(2); +#endif rt.addAtom("wxGREEN"); rt.add(*(wxGREEN)); rt.addTupleCount(2); rt.addAtom("wxGREEN_BRUSH"); rt.addRef(getRef((void *)wxGREEN_BRUSH,memenv),"wxBrush"); @@ -723,7 +787,7 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) { rt.addAtom("wx_GL_COMPAT_PROFILE"); rt.addAtom("undefined"); rt.addTupleCount(2); #endif - rt.endList(309); + rt.endList(321); rt.addTupleCount(2); rt.send(); } diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h index 4c8e52def2..c23e8a83bd 100644 --- a/lib/wx/c_src/gen/wxe_macros.h +++ b/lib/wx/c_src/gen/wxe_macros.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2017. All Rights Reserved. + * Copyright Ericsson AB 2008-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ #include <wx/fontdlg.h> #include <wx/progdlg.h> #include <wx/printdlg.h> +#include <wx/display.h> #include <wx/dcbuffer.h> #include <wx/dcmirror.h> #include <wx/glcanvas.h> @@ -46,6 +47,7 @@ #include <wx/sashwin.h> #include <wx/laywin.h> #include <wx/graphics.h> +#include <wx/dcgraph.h> #include <wx/aui/aui.h> #include <wx/datectrl.h> #include <wx/filepicker.h> @@ -3426,5 +3428,21 @@ #define wxDropFilesEvent_GetPosition 3597 #define wxDropFilesEvent_GetNumberOfFiles 3598 #define wxDropFilesEvent_GetFiles 3599 +#define wxDisplay_new 3600 +#define wxDisplay_destruct 3601 +#define wxDisplay_IsOk 3602 +#define wxDisplay_GetClientArea 3603 +#define wxDisplay_GetGeometry 3604 +#define wxDisplay_GetName 3605 +#define wxDisplay_IsPrimary 3606 +#define wxDisplay_GetCount 3607 +#define wxDisplay_GetFromPoint 3608 +#define wxDisplay_GetFromWindow 3609 +#define wxDisplay_GetPPI 3610 +#define wxGCDC_new_1 3611 +#define wxGCDC_new_0 3612 +#define wxGCDC_destruct 3613 +#define wxGCDC_GetGraphicsContext 3614 +#define wxGCDC_SetGraphicsContext 3615 diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp index bd22502d00..43b5476073 100644 --- a/lib/wx/c_src/wxe_impl.cpp +++ b/lib/wx/c_src/wxe_impl.cpp @@ -267,7 +267,7 @@ int WxeApp::dispatch_cmds() return more; } -#define BREAK_BATCH 10000 +#define CHECK_EVENTS 10000 int WxeApp::dispatch(wxeFifo * batch) { @@ -278,13 +278,14 @@ int WxeApp::dispatch(wxeFifo * batch) erl_drv_mutex_lock(wxe_batch_locker_m); while(true) { while((event = batch->Get()) != NULL) { + wait += 1; erl_drv_mutex_unlock(wxe_batch_locker_m); switch(event->op) { case WXE_BATCH_END: if(blevel>0) { blevel--; if(blevel==0) - wait += BREAK_BATCH/4; + wait += CHECK_EVENTS/4; } break; case WXE_BATCH_BEGIN: @@ -314,21 +315,18 @@ int WxeApp::dispatch(wxeFifo * batch) break; } event->Delete(); + if(wait > CHECK_EVENTS) + return 1; // Let wx check for events erl_drv_mutex_lock(wxe_batch_locker_m); batch->Cleanup(); } - if(blevel <= 0 || wait >= BREAK_BATCH) { + if(blevel <= 0) { erl_drv_mutex_unlock(wxe_batch_locker_m); - if(blevel > 0) { - return 1; // We are still in a batch but we can let wx check for events - } else { - return 0; - } + return 0; } // sleep until something happens // fprintf(stderr, "%s:%d sleep %d %d %d\r\n", __FILE__, __LINE__, batch->m_n, blevel, wait);fflush(stderr); wxe_needs_signal = 1; - wait += 1; while(batch->m_n == 0) { erl_drv_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m); } diff --git a/lib/wx/c_src/wxe_ps_init.c b/lib/wx/c_src/wxe_ps_init.c index 4b3b47a80b..62c7c51c13 100644 --- a/lib/wx/c_src/wxe_ps_init.c +++ b/lib/wx/c_src/wxe_ps_init.c @@ -64,6 +64,10 @@ void * wxe_ps_init2() { size_t app_len = 127; char app_title_buf[128]; char * app_title; + size_t app_icon_len = 1023; + char app_icon_buf[1024]; + char * app_icon; + // Setup and enable gui pool = [[NSAutoreleasePool alloc] init]; @@ -78,9 +82,15 @@ void * wxe_ps_init2() { if(!GetCurrentProcess(&psn)) { CPSSetProcessName(&psn, app_title?app_title:"Erlang"); } - // Load and set icon + // Enable setting custom application icon for Mac OS X + res = erl_drv_getenv("WX_APP_ICON", app_icon_buf, &app_icon_len); NSMutableString *file = [[NSMutableString alloc] init]; - [file appendFormat:@"%s/%s", erl_wx_privdir, "erlang-logo64.png"]; + if (res >= 0) { + [file appendFormat:@"%s", app_icon_buf]; + } else { + [file appendFormat:@"%s/%s", erl_wx_privdir, "erlang-logo128.png"]; + } + // Load and set icon NSImage *icon = [[NSImage alloc] initWithContentsOfFile: file]; [NSApp setApplicationIconImage: icon]; }; diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl index 23f3b95403..2c145595ee 100644 --- a/lib/wx/include/wx.hrl +++ b/lib/wx/include/wx.hrl @@ -398,6 +398,14 @@ -define(wxCYAN, wxe_util:get_const(wxCYAN)). -define(wxCYAN_BRUSH, wxe_util:get_const(wxCYAN_BRUSH)). -define(wxCYAN_PEN, wxe_util:get_const(wxCYAN_PEN)). +-define(wxFONTWEIGHT_EXTRABOLD, wxe_util:get_const(wxFONTWEIGHT_EXTRABOLD)). +-define(wxFONTWEIGHT_EXTRAHEAVY, wxe_util:get_const(wxFONTWEIGHT_EXTRAHEAVY)). +-define(wxFONTWEIGHT_EXTRALIGHT, wxe_util:get_const(wxFONTWEIGHT_EXTRALIGHT)). +-define(wxFONTWEIGHT_HEAVY, wxe_util:get_const(wxFONTWEIGHT_HEAVY)). +-define(wxFONTWEIGHT_INVALID, wxe_util:get_const(wxFONTWEIGHT_INVALID)). +-define(wxFONTWEIGHT_MEDIUM, wxe_util:get_const(wxFONTWEIGHT_MEDIUM)). +-define(wxFONTWEIGHT_SEMIBOLD, wxe_util:get_const(wxFONTWEIGHT_SEMIBOLD)). +-define(wxFONTWEIGHT_THIN, wxe_util:get_const(wxFONTWEIGHT_THIN)). -define(wxGREEN, wxe_util:get_const(wxGREEN)). -define(wxGREEN_BRUSH, wxe_util:get_const(wxGREEN_BRUSH)). -define(wxGREEN_PEN, wxe_util:get_const(wxGREEN_PEN)). @@ -1685,10 +1693,10 @@ -define(wxFONTSTYLE_SLANT, ?wxSLANT). -define(wxFONTSTYLE_MAX, (?wxSLANT+1)). % From "font.h": wxFontWeight --define(wxFONTWEIGHT_NORMAL, ?wxNORMAL). --define(wxFONTWEIGHT_LIGHT, ?wxLIGHT). --define(wxFONTWEIGHT_BOLD, ?wxBOLD). --define(wxFONTWEIGHT_MAX, (?wxBOLD+1)). +-define(wxFONTWEIGHT_NORMAL, wxe_util:get_const(wxFONTWEIGHT_NORMAL)). +-define(wxFONTWEIGHT_LIGHT, wxe_util:get_const(wxFONTWEIGHT_LIGHT)). +-define(wxFONTWEIGHT_BOLD, wxe_util:get_const(wxFONTWEIGHT_BOLD)). +-define(wxFONTWEIGHT_MAX, wxe_util:get_const(wxFONTWEIGHT_MAX)). % From "fontenc.h": wxFontEncoding -define(wxFONTENCODING_SYSTEM, -1). -define(wxFONTENCODING_DEFAULT, 0). diff --git a/lib/wx/src/gen/wxDisplay.erl b/lib/wx/src/gen/wxDisplay.erl new file mode 100644 index 0000000000..b6a2bf22ac --- /dev/null +++ b/lib/wx/src/gen/wxDisplay.erl @@ -0,0 +1,131 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% This file is generated DO NOT EDIT + +%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html">wxDisplay</a>. +%% @type wxDisplay(). An object reference, The representation is internal +%% and can be changed without notice. It can't be used for comparsion +%% stored on disc or distributed for use on other nodes. + +-module(wxDisplay). +-include("wxe.hrl"). +-export([destroy/1,getClientArea/1,getCount/0,getFromPoint/1,getFromWindow/1, + getGeometry/1,getName/1,getPPI/1,isOk/1,isPrimary/1,new/0,new/1]). + +%% inherited exports +-export([parent_class/1]). + +-export_type([wxDisplay/0]). +%% @hidden +parent_class(_Class) -> erlang:error({badtype, ?MODULE}). + +-type wxDisplay() :: wx:wx_object(). +%% @equiv new([]) +-spec new() -> wxDisplay(). + +new() -> + new([]). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaywxdisplay">external documentation</a>. +-spec new([Option]) -> wxDisplay() when + Option :: {'n', integer()}. +new(Options) + when is_list(Options) -> + MOpts = fun({n, N}, Acc) -> [<<1:32/?UI,N:32/?UI>>|Acc]; + (BadOpt, _) -> erlang:error({badoption, BadOpt}) end, + BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)), + wxe_util:construct(?wxDisplay_new, + <<BinOpt/binary>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplayisok">external documentation</a>. +-spec isOk(This) -> boolean() when + This::wxDisplay(). +isOk(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDisplay), + wxe_util:call(?wxDisplay_IsOk, + <<ThisRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetclientarea">external documentation</a>. +-spec getClientArea(This) -> {X::integer(), Y::integer(), W::integer(), H::integer()} when + This::wxDisplay(). +getClientArea(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDisplay), + wxe_util:call(?wxDisplay_GetClientArea, + <<ThisRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetgeometry">external documentation</a>. +-spec getGeometry(This) -> {X::integer(), Y::integer(), W::integer(), H::integer()} when + This::wxDisplay(). +getGeometry(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDisplay), + wxe_util:call(?wxDisplay_GetGeometry, + <<ThisRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetname">external documentation</a>. +-spec getName(This) -> unicode:charlist() when + This::wxDisplay(). +getName(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDisplay), + wxe_util:call(?wxDisplay_GetName, + <<ThisRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplayisprimary">external documentation</a>. +-spec isPrimary(This) -> boolean() when + This::wxDisplay(). +isPrimary(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDisplay), + wxe_util:call(?wxDisplay_IsPrimary, + <<ThisRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetcount">external documentation</a>. +-spec getCount() -> integer(). +getCount() -> + wxe_util:call(?wxDisplay_GetCount, + <<>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetfrompoint">external documentation</a>. +-spec getFromPoint(Pt) -> integer() when + Pt::{X::integer(), Y::integer()}. +getFromPoint({PtX,PtY}) + when is_integer(PtX),is_integer(PtY) -> + wxe_util:call(?wxDisplay_GetFromPoint, + <<PtX:32/?UI,PtY:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetfromwindow">external documentation</a>. +-spec getFromWindow(Window) -> integer() when + Window::wxWindow:wxWindow(). +getFromWindow(#wx_ref{type=WindowT,ref=WindowRef}) -> + ?CLASS(WindowT,wxWindow), + wxe_util:call(?wxDisplay_GetFromWindow, + <<WindowRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdisplay.html#wxdisplaygetppi">external documentation</a>. +-spec getPPI(This) -> {W::integer(), H::integer()} when + This::wxDisplay(). +getPPI(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDisplay), + wxe_util:call(?wxDisplay_GetPPI, + <<ThisRef:32/?UI>>). + +%% @doc Destroys this object, do not use object again +-spec destroy(This::wxDisplay()) -> 'ok'. +destroy(Obj=#wx_ref{type=Type}) -> + ?CLASS(Type,wxDisplay), + wxe_util:destroy(?wxDisplay_destruct,Obj), + ok. diff --git a/lib/wx/src/gen/wxGCDC.erl b/lib/wx/src/gen/wxGCDC.erl new file mode 100644 index 0000000000..467013b14e --- /dev/null +++ b/lib/wx/src/gen/wxGCDC.erl @@ -0,0 +1,287 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% This file is generated DO NOT EDIT + +%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html">wxGCDC</a>. +%% <p>This class is derived (and can use functions) from: +%% <br />{@link wxDC} +%% </p> +%% @type wxGCDC(). An object reference, The representation is internal +%% and can be changed without notice. It can't be used for comparsion +%% stored on disc or distributed for use on other nodes. + +-module(wxGCDC). +-include("wxe.hrl"). +-export([destroy/1,getGraphicsContext/1,new/0,new/1,setGraphicsContext/2]). + +%% inherited exports +-export([blit/5,blit/6,calcBoundingBox/3,clear/1,computeScaleAndOrigin/1,crossHair/2, + destroyClippingRegion/1,deviceToLogicalX/2,deviceToLogicalXRel/2, + deviceToLogicalY/2,deviceToLogicalYRel/2,drawArc/4,drawBitmap/3,drawBitmap/4, + drawCheckMark/2,drawCircle/3,drawEllipse/2,drawEllipse/3,drawEllipticArc/5, + drawIcon/3,drawLabel/3,drawLabel/4,drawLine/3,drawLines/2,drawLines/3, + drawPoint/2,drawPolygon/2,drawPolygon/3,drawRectangle/2,drawRectangle/3, + drawRotatedText/4,drawRoundedRectangle/3,drawRoundedRectangle/4, + drawText/3,endDoc/1,endPage/1,floodFill/3,floodFill/4,getBackground/1, + getBackgroundMode/1,getBrush/1,getCharHeight/1,getCharWidth/1,getClippingBox/1, + getFont/1,getLayoutDirection/1,getLogicalFunction/1,getMapMode/1, + getMultiLineTextExtent/2,getMultiLineTextExtent/3,getPPI/1,getPartialTextExtents/2, + getPen/1,getPixel/2,getSize/1,getSizeMM/1,getTextBackground/1,getTextExtent/2, + getTextExtent/3,getTextForeground/1,getUserScale/1,gradientFillConcentric/4, + gradientFillConcentric/5,gradientFillLinear/4,gradientFillLinear/5, + isOk/1,logicalToDeviceX/2,logicalToDeviceXRel/2,logicalToDeviceY/2, + logicalToDeviceYRel/2,maxX/1,maxY/1,minX/1,minY/1,parent_class/1,resetBoundingBox/1, + setAxisOrientation/3,setBackground/2,setBackgroundMode/2,setBrush/2, + setClippingRegion/2,setClippingRegion/3,setDeviceOrigin/3,setFont/2, + setLayoutDirection/2,setLogicalFunction/2,setMapMode/2,setPalette/2, + setPen/2,setTextBackground/2,setTextForeground/2,setUserScale/3,startDoc/2, + startPage/1]). + +-export_type([wxGCDC/0]). +-compile([{nowarn_deprecated_function, {wxDC,computeScaleAndOrigin,1}}]). + +%% @hidden +parent_class(wxDC) -> true; +parent_class(_Class) -> erlang:error({badtype, ?MODULE}). + +-type wxGCDC() :: wx:wx_object(). +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcwxgcdc">external documentation</a>. +-spec new() -> wxGCDC(). +new() -> + wxe_util:construct(?wxGCDC_new_0, + <<>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcwxgcdc">external documentation</a>. +-spec new(Dc) -> wxGCDC() when + Dc::wxWindowDC:wxWindowDC(). +new(#wx_ref{type=DcT,ref=DcRef}) -> + ?CLASS(DcT,wxWindowDC), + wxe_util:construct(?wxGCDC_new_1, + <<DcRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcgetgraphicscontext">external documentation</a>. +-spec getGraphicsContext(This) -> wxGraphicsContext:wxGraphicsContext() when + This::wxGCDC(). +getGraphicsContext(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxGCDC), + wxe_util:call(?wxGCDC_GetGraphicsContext, + <<ThisRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxgcdc.html#wxgcdcsetgraphicscontext">external documentation</a>. +-spec setGraphicsContext(This, Ctx) -> 'ok' when + This::wxGCDC(), Ctx::wxGraphicsContext:wxGraphicsContext(). +setGraphicsContext(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=CtxT,ref=CtxRef}) -> + ?CLASS(ThisT,wxGCDC), + ?CLASS(CtxT,wxGraphicsContext), + wxe_util:cast(?wxGCDC_SetGraphicsContext, + <<ThisRef:32/?UI,CtxRef:32/?UI>>). + +%% @doc Destroys this object, do not use object again +-spec destroy(This::wxGCDC()) -> 'ok'. +destroy(Obj=#wx_ref{type=Type}) -> + ?CLASS(Type,wxGCDC), + wxe_util:destroy(?DESTROY_OBJECT,Obj), + ok. + %% From wxDC +%% @hidden +startPage(This) -> wxDC:startPage(This). +%% @hidden +startDoc(This,Message) -> wxDC:startDoc(This,Message). +%% @hidden +setUserScale(This,X,Y) -> wxDC:setUserScale(This,X,Y). +%% @hidden +setTextForeground(This,Colour) -> wxDC:setTextForeground(This,Colour). +%% @hidden +setTextBackground(This,Colour) -> wxDC:setTextBackground(This,Colour). +%% @hidden +setPen(This,Pen) -> wxDC:setPen(This,Pen). +%% @hidden +setPalette(This,Palette) -> wxDC:setPalette(This,Palette). +%% @hidden +setMapMode(This,Mode) -> wxDC:setMapMode(This,Mode). +%% @hidden +setLogicalFunction(This,Function) -> wxDC:setLogicalFunction(This,Function). +%% @hidden +setLayoutDirection(This,Dir) -> wxDC:setLayoutDirection(This,Dir). +%% @hidden +setFont(This,Font) -> wxDC:setFont(This,Font). +%% @hidden +setDeviceOrigin(This,X,Y) -> wxDC:setDeviceOrigin(This,X,Y). +%% @hidden +setClippingRegion(This,Pt,Sz) -> wxDC:setClippingRegion(This,Pt,Sz). +%% @hidden +setClippingRegion(This,Region) -> wxDC:setClippingRegion(This,Region). +%% @hidden +setBrush(This,Brush) -> wxDC:setBrush(This,Brush). +%% @hidden +setBackgroundMode(This,Mode) -> wxDC:setBackgroundMode(This,Mode). +%% @hidden +setBackground(This,Brush) -> wxDC:setBackground(This,Brush). +%% @hidden +setAxisOrientation(This,XLeftRight,YBottomUp) -> wxDC:setAxisOrientation(This,XLeftRight,YBottomUp). +%% @hidden +resetBoundingBox(This) -> wxDC:resetBoundingBox(This). +%% @hidden +isOk(This) -> wxDC:isOk(This). +%% @hidden +minY(This) -> wxDC:minY(This). +%% @hidden +minX(This) -> wxDC:minX(This). +%% @hidden +maxY(This) -> wxDC:maxY(This). +%% @hidden +maxX(This) -> wxDC:maxX(This). +%% @hidden +logicalToDeviceYRel(This,Y) -> wxDC:logicalToDeviceYRel(This,Y). +%% @hidden +logicalToDeviceY(This,Y) -> wxDC:logicalToDeviceY(This,Y). +%% @hidden +logicalToDeviceXRel(This,X) -> wxDC:logicalToDeviceXRel(This,X). +%% @hidden +logicalToDeviceX(This,X) -> wxDC:logicalToDeviceX(This,X). +%% @hidden +gradientFillLinear(This,Rect,InitialColour,DestColour, Options) -> wxDC:gradientFillLinear(This,Rect,InitialColour,DestColour, Options). +%% @hidden +gradientFillLinear(This,Rect,InitialColour,DestColour) -> wxDC:gradientFillLinear(This,Rect,InitialColour,DestColour). +%% @hidden +gradientFillConcentric(This,Rect,InitialColour,DestColour,CircleCenter) -> wxDC:gradientFillConcentric(This,Rect,InitialColour,DestColour,CircleCenter). +%% @hidden +gradientFillConcentric(This,Rect,InitialColour,DestColour) -> wxDC:gradientFillConcentric(This,Rect,InitialColour,DestColour). +%% @hidden +getUserScale(This) -> wxDC:getUserScale(This). +%% @hidden +getTextForeground(This) -> wxDC:getTextForeground(This). +%% @hidden +getTextExtent(This,String, Options) -> wxDC:getTextExtent(This,String, Options). +%% @hidden +getTextExtent(This,String) -> wxDC:getTextExtent(This,String). +%% @hidden +getTextBackground(This) -> wxDC:getTextBackground(This). +%% @hidden +getSizeMM(This) -> wxDC:getSizeMM(This). +%% @hidden +getSize(This) -> wxDC:getSize(This). +%% @hidden +getPPI(This) -> wxDC:getPPI(This). +%% @hidden +getPixel(This,Pt) -> wxDC:getPixel(This,Pt). +%% @hidden +getPen(This) -> wxDC:getPen(This). +%% @hidden +getPartialTextExtents(This,Text) -> wxDC:getPartialTextExtents(This,Text). +%% @hidden +getMultiLineTextExtent(This,String, Options) -> wxDC:getMultiLineTextExtent(This,String, Options). +%% @hidden +getMultiLineTextExtent(This,String) -> wxDC:getMultiLineTextExtent(This,String). +%% @hidden +getMapMode(This) -> wxDC:getMapMode(This). +%% @hidden +getLogicalFunction(This) -> wxDC:getLogicalFunction(This). +%% @hidden +getLayoutDirection(This) -> wxDC:getLayoutDirection(This). +%% @hidden +getFont(This) -> wxDC:getFont(This). +%% @hidden +getClippingBox(This) -> wxDC:getClippingBox(This). +%% @hidden +getCharWidth(This) -> wxDC:getCharWidth(This). +%% @hidden +getCharHeight(This) -> wxDC:getCharHeight(This). +%% @hidden +getBrush(This) -> wxDC:getBrush(This). +%% @hidden +getBackgroundMode(This) -> wxDC:getBackgroundMode(This). +%% @hidden +getBackground(This) -> wxDC:getBackground(This). +%% @hidden +floodFill(This,Pt,Col, Options) -> wxDC:floodFill(This,Pt,Col, Options). +%% @hidden +floodFill(This,Pt,Col) -> wxDC:floodFill(This,Pt,Col). +%% @hidden +endPage(This) -> wxDC:endPage(This). +%% @hidden +endDoc(This) -> wxDC:endDoc(This). +%% @hidden +drawText(This,Text,Pt) -> wxDC:drawText(This,Text,Pt). +%% @hidden +drawRoundedRectangle(This,Pt,Sz,Radius) -> wxDC:drawRoundedRectangle(This,Pt,Sz,Radius). +%% @hidden +drawRoundedRectangle(This,R,Radius) -> wxDC:drawRoundedRectangle(This,R,Radius). +%% @hidden +drawRotatedText(This,Text,Pt,Angle) -> wxDC:drawRotatedText(This,Text,Pt,Angle). +%% @hidden +drawRectangle(This,Pt,Sz) -> wxDC:drawRectangle(This,Pt,Sz). +%% @hidden +drawRectangle(This,Rect) -> wxDC:drawRectangle(This,Rect). +%% @hidden +drawPoint(This,Pt) -> wxDC:drawPoint(This,Pt). +%% @hidden +drawPolygon(This,Points, Options) -> wxDC:drawPolygon(This,Points, Options). +%% @hidden +drawPolygon(This,Points) -> wxDC:drawPolygon(This,Points). +%% @hidden +drawLines(This,Points, Options) -> wxDC:drawLines(This,Points, Options). +%% @hidden +drawLines(This,Points) -> wxDC:drawLines(This,Points). +%% @hidden +drawLine(This,Pt1,Pt2) -> wxDC:drawLine(This,Pt1,Pt2). +%% @hidden +drawLabel(This,Text,Rect, Options) -> wxDC:drawLabel(This,Text,Rect, Options). +%% @hidden +drawLabel(This,Text,Rect) -> wxDC:drawLabel(This,Text,Rect). +%% @hidden +drawIcon(This,Icon,Pt) -> wxDC:drawIcon(This,Icon,Pt). +%% @hidden +drawEllipticArc(This,Pt,Sz,Sa,Ea) -> wxDC:drawEllipticArc(This,Pt,Sz,Sa,Ea). +%% @hidden +drawEllipse(This,Pt,Sz) -> wxDC:drawEllipse(This,Pt,Sz). +%% @hidden +drawEllipse(This,Rect) -> wxDC:drawEllipse(This,Rect). +%% @hidden +drawCircle(This,Pt,Radius) -> wxDC:drawCircle(This,Pt,Radius). +%% @hidden +drawCheckMark(This,Rect) -> wxDC:drawCheckMark(This,Rect). +%% @hidden +drawBitmap(This,Bmp,Pt, Options) -> wxDC:drawBitmap(This,Bmp,Pt, Options). +%% @hidden +drawBitmap(This,Bmp,Pt) -> wxDC:drawBitmap(This,Bmp,Pt). +%% @hidden +drawArc(This,Pt1,Pt2,Centre) -> wxDC:drawArc(This,Pt1,Pt2,Centre). +%% @hidden +deviceToLogicalYRel(This,Y) -> wxDC:deviceToLogicalYRel(This,Y). +%% @hidden +deviceToLogicalY(This,Y) -> wxDC:deviceToLogicalY(This,Y). +%% @hidden +deviceToLogicalXRel(This,X) -> wxDC:deviceToLogicalXRel(This,X). +%% @hidden +deviceToLogicalX(This,X) -> wxDC:deviceToLogicalX(This,X). +%% @hidden +destroyClippingRegion(This) -> wxDC:destroyClippingRegion(This). +%% @hidden +crossHair(This,Pt) -> wxDC:crossHair(This,Pt). +%% @hidden +computeScaleAndOrigin(This) -> wxDC:computeScaleAndOrigin(This). +%% @hidden +clear(This) -> wxDC:clear(This). +%% @hidden +calcBoundingBox(This,X,Y) -> wxDC:calcBoundingBox(This,X,Y). +%% @hidden +blit(This,DestPt,Sz,Source,SrcPt, Options) -> wxDC:blit(This,DestPt,Sz,Source,SrcPt, Options). +%% @hidden +blit(This,DestPt,Sz,Source,SrcPt) -> wxDC:blit(This,DestPt,Sz,Source,SrcPt). diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl index 533f9f2df0..b64a1b4c61 100644 --- a/lib/wx/src/gen/wxe_debug.hrl +++ b/lib/wx/src/gen/wxe_debug.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. +%% Copyright Ericsson AB 2008-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -3377,6 +3377,22 @@ wxdebug_table() -> {3597, {wxDropFilesEvent, getPosition, 0}}, {3598, {wxDropFilesEvent, getNumberOfFiles, 0}}, {3599, {wxDropFilesEvent, getFiles, 0}}, + {3600, {wxDisplay, new, 1}}, + {3601, {wxDisplay, destruct, 0}}, + {3602, {wxDisplay, isOk, 0}}, + {3603, {wxDisplay, getClientArea, 0}}, + {3604, {wxDisplay, getGeometry, 0}}, + {3605, {wxDisplay, getName, 0}}, + {3606, {wxDisplay, isPrimary, 0}}, + {3607, {wxDisplay, getCount, 0}}, + {3608, {wxDisplay, getFromPoint, 1}}, + {3609, {wxDisplay, getFromWindow, 1}}, + {3610, {wxDisplay, getPPI, 0}}, + {3611, {wxGCDC, new_1, 1}}, + {3612, {wxGCDC, new_0, 0}}, + {3613, {wxGCDC, destruct, 0}}, + {3614, {wxGCDC, getGraphicsContext, 0}}, + {3615, {wxGCDC, setGraphicsContext, 1}}, {-1, {mod, func, -1}} ]. diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl index 14b5545676..030f7f117d 100644 --- a/lib/wx/src/gen/wxe_funcs.hrl +++ b/lib/wx/src/gen/wxe_funcs.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. +%% Copyright Ericsson AB 2008-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -3374,3 +3374,19 @@ -define(wxDropFilesEvent_GetPosition, 3597). -define(wxDropFilesEvent_GetNumberOfFiles, 3598). -define(wxDropFilesEvent_GetFiles, 3599). +-define(wxDisplay_new, 3600). +-define(wxDisplay_destruct, 3601). +-define(wxDisplay_IsOk, 3602). +-define(wxDisplay_GetClientArea, 3603). +-define(wxDisplay_GetGeometry, 3604). +-define(wxDisplay_GetName, 3605). +-define(wxDisplay_IsPrimary, 3606). +-define(wxDisplay_GetCount, 3607). +-define(wxDisplay_GetFromPoint, 3608). +-define(wxDisplay_GetFromWindow, 3609). +-define(wxDisplay_GetPPI, 3610). +-define(wxGCDC_new_1, 3611). +-define(wxGCDC_new_0, 3612). +-define(wxGCDC_destruct, 3613). +-define(wxGCDC_GetGraphicsContext, 3614). +-define(wxGCDC_SetGraphicsContext, 3615). diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index a97036127e..7f6874e36b 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.19</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The charset detection parsing crash in some cases when + the XML directive is not syntactic correct.</p> + <p> + Own Id: OTP-15492 Aux Id: ERIERL-283 </p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.18</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -62,6 +77,21 @@ </section> +<section><title>Xmerl 1.3.16.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The charset detection parsing crash in some cases when + the XML directive is not syntactic correct.</p> + <p> + Own Id: OTP-15492 Aux Id: ERIERL-283 </p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.16</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -1412,4 +1442,3 @@ </section> </section> </chapter> - diff --git a/lib/xmerl/doc/src/xmerl_sax_parser.xml b/lib/xmerl/doc/src/xmerl_sax_parser.xml index 8ea197e209..2390779028 100644 --- a/lib/xmerl/doc/src/xmerl_sax_parser.xml +++ b/lib/xmerl/doc/src/xmerl_sax_parser.xml @@ -31,7 +31,7 @@ <rev></rev> </header> - <module>xmerl_sax_parser</module> + <module since="">xmerl_sax_parser</module> <modulesummary>XML SAX parser API</modulesummary> <description> @@ -325,7 +325,7 @@ <funcs> <func> - <name>file(Filename, Options) -> Result</name> + <name since="">file(Filename, Options) -> Result</name> <fsummary>Parse file containing an XML document.</fsummary> <type> <v>Filename = string()</v> @@ -347,7 +347,7 @@ </func> <func> - <name>stream(Xml, Options) -> Result</name> + <name since="">stream(Xml, Options) -> Result</name> <fsummary>Parse a stream containing an XML document.</fsummary> <type> <v>Xml = unicode_binary() | latin1_binary() | [unicode_char()]</v> @@ -381,7 +381,7 @@ <funcs> <func> - <name>ContinuationFun(State) -> {NewBytes, NewState}</name> + <name since="">ContinuationFun(State) -> {NewBytes, NewState}</name> <fsummary>Continuation call back function.</fsummary> <type> <v>State = NewState = term()</v> @@ -402,7 +402,7 @@ </func> <func> - <name>EventFun(Event, Location, State) -> NewState</name> + <name since="">EventFun(Event, Location, State) -> NewState</name> <fsummary>Event call back function.</fsummary> <type> <v>Event = event()</v> diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index e383c4c349..fe836fd8cd 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -1,8 +1,8 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2008-2018. All Rights Reserved. +%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,13 +14,13 @@ %% 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% %%---------------------------------------------------------------------- %% File : xmerl_sax_parser.erl %% Description : XML SAX parse API module. %% -%% Created : 4 Jun 2008 +%% Created : 4 Jun 2008 %%---------------------------------------------------------------------- -module(xmerl_sax_parser). @@ -72,9 +72,9 @@ file(Name,Options) -> CL = filename:absname(Dir), File = filename:basename(Name), ContinuationFun = fun default_continuation_cb/1, - Res = stream(<<>>, + Res = stream(<<>>, [{continuation_fun, ContinuationFun}, - {continuation_state, FD}, + {continuation_state, FD}, {current_location, CL}, {entity, File} |Options], @@ -101,39 +101,39 @@ stream(Xml, Options, InputType) when is_list(Xml), is_list(Options) -> State = parse_options(Options, initial_state()), case State#xmerl_sax_parser_state.file_type of dtd -> - xmerl_sax_parser_list:parse_dtd(Xml, + xmerl_sax_parser_list:parse_dtd(Xml, State#xmerl_sax_parser_state{encoding = list, input_type = InputType}); normal -> - xmerl_sax_parser_list:parse(Xml, + xmerl_sax_parser_list:parse(Xml, State#xmerl_sax_parser_state{encoding = list, input_type = InputType}) end; stream(Xml, Options, InputType) when is_binary(Xml), is_list(Options) -> - case parse_options(Options, initial_state()) of + case parse_options(Options, initial_state()) of {error, Reason} -> {error, Reason}; State -> - ParseFunction = + ParseFunction = case State#xmerl_sax_parser_state.file_type of dtd -> parse_dtd; normal -> parse end, - try + try {Xml1, State1} = detect_charset(Xml, State), parse_binary(Xml1, State1#xmerl_sax_parser_state{input_type = InputType}, ParseFunction) catch throw:{fatal_error, {State2, Reason}} -> - {fatal_error, + {fatal_error, { State2#xmerl_sax_parser_state.current_location, - State2#xmerl_sax_parser_state.entity, + State2#xmerl_sax_parser_state.entity, 1 }, - Reason, [], + Reason, [], State2#xmerl_sax_parser_state.event_state} end end. @@ -157,7 +157,7 @@ parse_binary(Xml, #xmerl_sax_parser_state{encoding={utf16,big}}=State, F) -> xmerl_sax_parser_utf16be:F(Xml, State); parse_binary(Xml, #xmerl_sax_parser_state{encoding=latin1}=State, F) -> xmerl_sax_parser_latin1:F(Xml, State); -parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) -> +parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) -> ?fatal_error(State, lists:flatten(io_lib:format("Charcter set ~p not supported", [Enc]))). %%---------------------------------------------------------------------- @@ -177,9 +177,9 @@ initial_state() -> %%---------------------------------------------------------------------- %% Function: parse_options(Options, State) %% Input: Options = [Option] -%% Option = {event_state, term()} | {event_fun, fun()} | +%% Option = {event_state, term()} | {event_fun, fun()} | %% {continuation_state, term()} | {continuation_fun, fun()} | -%% {encoding, Encoding} | {file_type, FT} +%% {encoding, Encoding} | {file_type, FT} %% FT = normal | dtd %% Encoding = utf8 | utf16le | utf16be | list | iso8859 %% State = #xmerl_sax_parser_state{} @@ -200,7 +200,7 @@ parse_options([{file_type, FT} |Options], State) when FT==normal; FT==dtd -> parse_options(Options, State#xmerl_sax_parser_state{file_type = FT}); parse_options([{encoding, E} |Options], State) -> case check_encoding_option(E) of - {error, Reason} -> + {error, Reason} -> {error, Reason}; Enc -> parse_options(Options, State#xmerl_sax_parser_state{encoding = Enc}) @@ -231,7 +231,7 @@ check_encoding_option(E) -> %% Description: Detects which character set is used in a binary stream. %%---------------------------------------------------------------------- detect_charset(<<>>, #xmerl_sax_parser_state{continuation_fun = undefined} = State) -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); detect_charset(<<>>, State) -> cf(<<>>, State, fun detect_charset/2); detect_charset(Bytes, State) -> @@ -269,22 +269,14 @@ detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D>> = Xml, State) -> cf(Xml, State, fun detect_charset_1/2); detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>>, State) -> {Xml3, State1} = read_until_end_of_xml_directive(Xml2, State), - case parse_xml_directive(Xml3) of - {error, Reason} -> - ?fatal_error(State, Reason); - AttrList -> - case lists:keysearch("encoding", 1, AttrList) of - {value, {_, E}} -> - case convert_encoding(E) of - {error, Reason} -> - ?fatal_error(State, Reason); - Enc -> - {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, - State1#xmerl_sax_parser_state{encoding=Enc}} - end; - _ -> - {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1} - end + AttrList = parse_xml_directive(Xml3, State), + case lists:keysearch("encoding", 1, AttrList) of + {value, {_, E}} -> + Enc = convert_encoding(E, State), + {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, + State1#xmerl_sax_parser_state{encoding=Enc}}; + _ -> + {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1} end; detect_charset_1(Xml, State) -> {Xml, State}. @@ -295,7 +287,7 @@ detect_charset_1(Xml, State) -> %% Output: utf8 | iso8859 %% Description: Converting 7,8 bit and utf8 encoding strings to internal format. %%---------------------------------------------------------------------- -convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 +convert_encoding(Enc, State) -> %% Just for 7,8 bit + utf8 case string:to_lower(Enc) of "utf-8" -> utf8; "us-ascii" -> utf8; @@ -309,19 +301,19 @@ convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 "iso-8859-7" -> latin1; "iso-8859-8" -> latin1; "iso-8859-9" -> latin1; - _ -> {error, "Unknown encoding: " ++ Enc} + _ -> ?fatal_error(State, "Unknown encoding: " ++ Enc) end. %%---------------------------------------------------------------------- %% Function: parse_xml_directive(Xml) %% Input: Xml = binary() %% Acc = list() -%% Output: +%% Output: %% Description: Parsing the xml declaration from the input stream. %%---------------------------------------------------------------------- -parse_xml_directive(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_xml_directive_1(Rest, []). - +parse_xml_directive(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_xml_directive_1(Rest, [], State). + %%---------------------------------------------------------------------- %% Function: parse_xml_directive_1(Xml, Acc) -> [{Name, Value}] %% Input: Xml = binary() @@ -331,20 +323,20 @@ parse_xml_directive(<<C, Rest/binary>>) when ?is_whitespace(C) -> %% Output: see above %% Description: Parsing the xml declaration from the input stream. %%---------------------------------------------------------------------- -parse_xml_directive_1(<<C, Rest/binary>>, Acc) when ?is_whitespace(C) -> - parse_xml_directive_1(Rest, Acc); -parse_xml_directive_1(<<"?>", _/binary>>, Acc) -> +parse_xml_directive_1(<<C, Rest/binary>>, Acc, State) when ?is_whitespace(C) -> + parse_xml_directive_1(Rest, Acc, State); +parse_xml_directive_1(<<"?>", _/binary>>, Acc, _State) -> Acc; -parse_xml_directive_1(<<C, Rest/binary>>, Acc) when 97 =< C, C =< 122 -> +parse_xml_directive_1(<<C, Rest/binary>>, Acc, State) when 97 =< C, C =< 122 -> {Name, Rest1} = parse_name(Rest, [C]), - Rest2 = parse_eq(Rest1), - {Value, Rest3} = parse_value(Rest2), - parse_xml_directive_1(Rest3, [{Name, Value} |Acc]); -parse_xml_directive_1(_, _) -> - {error, "Unknown attribute in xml directive"}. + Rest2 = parse_eq(Rest1, State), + {Value, Rest3} = parse_value(Rest2, State), + parse_xml_directive_1(Rest3, [{Name, Value} |Acc], State); +parse_xml_directive_1(_, _, State) -> + ?fatal_error(State, "Unknown attribute in xml directive"). %%---------------------------------------------------------------------- -%% Function: parse_xml_directive_1(Xml, Acc) -> Name +%% Function: parse_name(Xml, Acc) -> Name %% Input: Xml = binary() %% Acc = string() %% Output: Name = string() @@ -361,10 +353,12 @@ parse_name(Rest, Acc) -> %% Output: Rest = binary() %% Description: Reads an '=' from the stream. %%---------------------------------------------------------------------- -parse_eq(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_eq(Rest); -parse_eq(<<"=", Rest/binary>>) -> - Rest. +parse_eq(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_eq(Rest, State); +parse_eq(<<"=", Rest/binary>>, _State) -> + Rest; +parse_eq(_, State) -> + ?fatal_error(State, "expecting = or whitespace"). %%---------------------------------------------------------------------- %% Function: parse_value(Xml) -> {Value, Rest} @@ -373,10 +367,12 @@ parse_eq(<<"=", Rest/binary>>) -> %% Rest = binary() %% Description: Parsing an attribute value from the stream. %%---------------------------------------------------------------------- -parse_value(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_value(Rest); -parse_value(<<C, Rest/binary>>) when C == $'; C == $" -> - parse_value_1(Rest, C, []). +parse_value(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_value(Rest, State); +parse_value(<<C, Rest/binary>>, _State) when C == $'; C == $" -> + parse_value_1(Rest, C, []); +parse_value(_, State) -> + ?fatal_error(State, "\', \" or whitespace expected"). %%---------------------------------------------------------------------- %% Function: parse_value_1(Xml, Stop, Acc) -> {Value, Rest} @@ -431,7 +427,7 @@ read_until_end_of_xml_directive(Rest, State) -> nomatch -> case cf(Rest, State) of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewState} -> read_until_end_of_xml_directive(NewBytes, NewState) end; @@ -450,9 +446,9 @@ read_until_end_of_xml_directive(Rest, State) -> %% input stream and calls the fun in NextCall. %%---------------------------------------------------------------------- cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State) -> - ?fatal_error(State, "Continuation function undefined"); + ?fatal_error(State, "Continuation function undefined"); cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State) -> - Result = + Result = try CFun(CState) catch @@ -463,9 +459,9 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C end, case Result of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewContState} -> - {<<Rest/binary, NewBytes/binary>>, + {<<Rest/binary, NewBytes/binary>>, State#xmerl_sax_parser_state{continuation_state = NewContState}} end. @@ -479,10 +475,10 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C %% input stream and calls the fun in NextCall. %%---------------------------------------------------------------------- cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State, _) -> - ?fatal_error(State, "Continuation function undefined"); -cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State, + ?fatal_error(State, "Continuation function undefined"); +cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State, NextCall) -> - Result = + Result = try CFun(CState) catch @@ -493,8 +489,8 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C end, case Result of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewContState} -> - NextCall(<<Rest/binary, NewBytes/binary>>, + NextCall(<<Rest/binary, NewBytes/binary>>, State#xmerl_sax_parser_state{continuation_state = NewContState}) end. diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index 3a266a56bd..b6486681c2 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3.18 +XMERL_VSN = 1.3.19 diff --git a/make/otp_patch_solve_forward_merge_version b/make/otp_patch_solve_forward_merge_version index 7ed6ff82de..7f8f011eb7 100644 --- a/make/otp_patch_solve_forward_merge_version +++ b/make/otp_patch_solve_forward_merge_version @@ -1 +1 @@ -5 +7 diff --git a/make/otp_version_tickets b/make/otp_version_tickets index 81a6fb9117..b8220e1a87 100644 --- a/make/otp_version_tickets +++ b/make/otp_version_tickets @@ -1,64 +1 @@ -OTP-12242 -OTP-13468 -OTP-14669 -OTP-15094 -OTP-15121 -OTP-15245 -OTP-15302 -OTP-15312 -OTP-15314 -OTP-15338 -OTP-15339 -OTP-15341 -OTP-15343 -OTP-15346 -OTP-15357 -OTP-15360 -OTP-15364 -OTP-15365 -OTP-15368 -OTP-15371 -OTP-15377 -OTP-15378 -OTP-15384 -OTP-15387 -OTP-15388 -OTP-15389 -OTP-15391 -OTP-15400 -OTP-15402 -OTP-15412 -OTP-15413 -OTP-15415 -OTP-15416 -OTP-15418 -OTP-15419 -OTP-15420 -OTP-15424 -OTP-15426 -OTP-15428 -OTP-15435 -OTP-15436 -OTP-15437 -OTP-15446 -OTP-15449 -OTP-15450 -OTP-15453 -OTP-15454 -OTP-15461 -OTP-15463 -OTP-15464 -OTP-15465 -OTP-15466 -OTP-15467 -OTP-15469 -OTP-15470 -OTP-15471 -OTP-15472 -OTP-15473 -OTP-15474 -OTP-15475 -OTP-15476 -OTP-15478 -OTP-15481 -OTP-15482 +DEVELOPMENT diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge new file mode 100644 index 0000000000..32e6923ce9 --- /dev/null +++ b/make/otp_version_tickets_in_merge @@ -0,0 +1,2 @@ +OTP-14728 +OTP-15573 diff --git a/otp_versions.table b/otp_versions.table index 29cc725a1d..aea1e8d5cd 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,3 +1,9 @@ +OTP-21.2.6 : erts-10.2.4 stdlib-3.7.1 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.5 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 : +OTP-21.2.5 : inets-7.0.5 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 erts-10.2.3 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 : +OTP-21.2.4 : erts-10.2.3 inets-7.0.4 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 : +OTP-21.2.3 : compiler-7.3.1 erts-10.2.2 ssl-9.1.2 xmerl-1.3.19 # asn1-5.0.8 common_test-1.16.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.3 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 : +OTP-21.2.2 : ssh-4.7.3 # asn1-5.0.8 common_test-1.16.1 compiler-7.3 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 erts-10.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.3 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssl-9.1.1 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.18 : +OTP-21.2.1 : erts-10.2.1 ssl-9.1.1 # asn1-5.0.8 common_test-1.16.1 compiler-7.3 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.3 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.18 : OTP-21.2 : asn1-5.0.8 compiler-7.3 crypto-4.4 erts-10.2 et-1.6.4 hipe-3.18.2 inets-7.0.3 kernel-6.2 observer-2.8.2 os_mon-2.4.7 public_key-1.6.4 reltool-0.7.8 sasl-3.3 ssh-4.7.2 ssl-9.1 stdlib-3.7 tools-3.0.2 wx-1.8.6 # common_test-1.16.1 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 eunit-2.3.7 ftp-1.0.1 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.5 odbc-2.12.2 otp_mibs-1.2.1 parsetools-2.1.8 runtime_tools-1.13.1 snmp-5.2.12 syntax_tools-2.1.6 tftp-1.0.1 xmerl-1.3.18 : OTP-21.1.4 : kernel-6.1.1 # asn1-5.0.7 common_test-1.16.1 compiler-7.2.7 crypto-4.3.3 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 erts-10.1.3 et-1.6.3 eunit-2.3.7 ftp-1.0.1 hipe-3.18.1 inets-7.0.2 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.5 observer-2.8.1 odbc-2.12.2 os_mon-2.4.6 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.3 reltool-0.7.7 runtime_tools-1.13.1 sasl-3.2.1 snmp-5.2.12 ssh-4.7.1 ssl-9.0.3 stdlib-3.6 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.1 wx-1.8.5 xmerl-1.3.18 : OTP-21.1.3 : erts-10.1.3 # asn1-5.0.7 common_test-1.16.1 compiler-7.2.7 crypto-4.3.3 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.3 eunit-2.3.7 ftp-1.0.1 hipe-3.18.1 inets-7.0.2 jinterface-1.9.1 kernel-6.1 megaco-3.18.4 mnesia-4.15.5 observer-2.8.1 odbc-2.12.2 os_mon-2.4.6 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.3 reltool-0.7.7 runtime_tools-1.13.1 sasl-3.2.1 snmp-5.2.12 ssh-4.7.1 ssl-9.0.3 stdlib-3.6 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.1 wx-1.8.5 xmerl-1.3.18 : @@ -14,6 +20,11 @@ OTP-21.0.3 : erts-10.0.3 # asn1-5.0.6 common_test-1.16 compiler-7.2.2 crypto-4.3 OTP-21.0.2 : compiler-7.2.2 erts-10.0.2 public_key-1.6.1 stdlib-3.5.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 : +OTP-20.3.8.20 : common_test-1.15.4.1 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.9 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 : +OTP-20.3.8.19 : diameter-2.1.4.1 erts-9.3.3.9 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 : +OTP-20.3.8.18 : erts-9.3.3.8 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 : +OTP-20.3.8.17 : xmerl-1.3.16.1 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.7 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 : +OTP-20.3.8.16 : erts-9.3.3.7 ssh-4.6.9.3 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.8.15 : asn1-5.0.5.2 # common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.6 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.2 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.8.14 : ssh-4.6.9.2 # asn1-5.0.5.1 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.6 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.8.13 : ssl-8.2.6.4 # asn1-5.0.5.1 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.6 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.1 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : @@ -35,6 +46,7 @@ OTP-20.3.6 : crypto-4.2.2 ssh-4.6.9 # asn1-5.0.5 common_test-1.15.4 compiler-7.1 OTP-20.3.5 : erts-9.3.1 ssl-8.2.6 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 inets-6.5.1 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.10 ssh-4.6.8 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.4 : erl_interface-3.10.2 ic-4.4.4 inets-6.5.1 ssh-4.6.8 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.10 ssl-8.2.5 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.3 : sasl-3.1.2 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.3 inets-6.5 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 snmp-5.2.10 ssh-4.6.7 ssl-8.2.5 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : +OTP-20.3.2.1 : common_test-1.15.4.0.1 # asn1-5.0.5 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.3 inets-6.5 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.1 snmp-5.2.10 ssh-4.6.7 ssl-8.2.5 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.2 : ssh-4.6.7 stdlib-3.4.5 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.3 inets-6.5 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.1 snmp-5.2.10 ssl-8.2.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.1 : ssl-8.2.5 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.3 inets-6.5 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.1 snmp-5.2.10 ssh-4.6.6 stdlib-3.4.4 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3 : asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 crypto-4.2.1 dialyzer-3.2.4 diameter-2.1.4 erts-9.3 hipe-3.17.1 inets-6.5 kernel-5.4.3 observer-2.7 runtime_tools-1.12.5 snmp-5.2.10 ssh-4.6.6 ssl-8.2.4 stdlib-3.4.4 tools-2.11.2 # cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 et-1.6.1 eunit-2.3.5 ic-4.4.3 jinterface-1.8.1 megaco-3.18.3 mnesia-4.15.3 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 sasl-3.1.1 syntax_tools-2.1.4 wx-1.8.3 xmerl-1.3.16 : @@ -42,6 +54,7 @@ OTP-20.2.4 : ssh-4.6.5 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2 OTP-20.2.3 : erts-9.2.1 kernel-5.4.2 runtime_tools-1.12.4 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 megaco-3.18.3 mnesia-4.15.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 sasl-3.1.1 snmp-5.2.9 ssh-4.6.4 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 : OTP-20.2.2 : mnesia-4.15.3 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssh-4.6.4 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 : OTP-20.2.1 : ssh-4.6.4 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 mnesia-4.15.2 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 : +OTP-20.2.0.1 : erts-9.2.0.1 # asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 et-1.6.1 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 mnesia-4.15.2 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssh-4.6.3 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 : OTP-20.2 : asn1-5.0.4 common_test-1.15.3 compiler-7.1.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2 debugger-4.2.4 dialyzer-3.2.3 diameter-2.1.3 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.1 erts-9.2 eunit-2.3.5 hipe-3.17 ic-4.4.3 inets-6.4.5 jinterface-1.8.1 kernel-5.4.1 megaco-3.18.3 mnesia-4.15.2 observer-2.6 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 runtime_tools-1.12.3 sasl-3.1.1 snmp-5.2.9 ssh-4.6.3 ssl-8.2.3 stdlib-3.4.3 syntax_tools-2.1.4 tools-2.11.1 wx-1.8.3 xmerl-1.3.16 # et-1.6.1 reltool-0.7.5 : OTP-20.1.7.1 : kernel-5.4.0.1 # asn1-5.0.3 common_test-1.15.2 compiler-7.1.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.1 debugger-4.2.3 dialyzer-3.2.2 diameter-2.1.2 edoc-0.9.1 eldap-1.2.2 erl_docgen-0.7.1 erl_interface-3.10 erts-9.1.5 et-1.6.1 eunit-2.3.4 hipe-3.16.1 ic-4.4.2 inets-6.4.4 jinterface-1.8 megaco-3.18.2 mnesia-4.15.1 observer-2.5 odbc-2.12 orber-3.8.3 os_mon-2.4.3 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.5.1 reltool-0.7.5 runtime_tools-1.12.2 sasl-3.1 snmp-5.2.8 ssh-4.6.2 ssl-8.2.2 stdlib-3.4.2 syntax_tools-2.1.3 tools-2.11 wx-1.8.2 xmerl-1.3.15 : OTP-20.1.7 : public_key-1.5.1 ssl-8.2.2 # asn1-5.0.3 common_test-1.15.2 compiler-7.1.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.1 debugger-4.2.3 dialyzer-3.2.2 diameter-2.1.2 edoc-0.9.1 eldap-1.2.2 erl_docgen-0.7.1 erl_interface-3.10 erts-9.1.5 et-1.6.1 eunit-2.3.4 hipe-3.16.1 ic-4.4.2 inets-6.4.4 jinterface-1.8 kernel-5.4 megaco-3.18.2 mnesia-4.15.1 observer-2.5 odbc-2.12 orber-3.8.3 os_mon-2.4.3 otp_mibs-1.1.1 parsetools-2.1.5 reltool-0.7.5 runtime_tools-1.12.2 sasl-3.1 snmp-5.2.8 ssh-4.6.2 stdlib-3.4.2 syntax_tools-2.1.3 tools-2.11 wx-1.8.2 xmerl-1.3.15 : @@ -58,6 +71,7 @@ OTP-20.0.3 : asn1-5.0.2 compiler-7.1.1 erts-9.0.3 ssh-4.5.1 # common_test-1.15.1 OTP-20.0.2 : asn1-5.0.1 erts-9.0.2 kernel-5.3.1 # common_test-1.15.1 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12.1 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4.1 syntax_tools-2.1.2 tools-2.10.1 wx-1.8.1 xmerl-1.3.15 : OTP-20.0.1 : common_test-1.15.1 erts-9.0.1 runtime_tools-1.12.1 stdlib-3.4.1 tools-2.10.1 # asn1-5.0 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 syntax_tools-2.1.2 wx-1.8.1 xmerl-1.3.15 : OTP-20.0 : asn1-5.0 common_test-1.15 compiler-7.1 cosProperty-1.2.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 erl_docgen-0.7 erl_interface-3.10 erts-9.0 eunit-2.3.3 hipe-3.16 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 orber-3.8.3 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4 syntax_tools-2.1.2 tools-2.10 wx-1.8.1 xmerl-1.3.15 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 ic-4.4.2 odbc-2.12 os_mon-2.4.2 otp_mibs-1.1.1 : +OTP-19.3.6.13 : erts-8.3.5.7 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2.1 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.4 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 : OTP-19.3.6.12 : eldap-1.2.2.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.6 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.4 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 : OTP-19.3.6.11 : erts-8.3.5.6 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.4 ssl-8.1.3.1.1 stdlib-3.3 syntax_tools-2.1.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 : OTP-19.3.6.10 : erts-8.3.5.5 syntax_tools-2.1.1.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 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.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2.0.1 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2.4 ssl-8.1.3.1.1 stdlib-3.3 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 : |