diff options
Diffstat (limited to 'lib/kernel')
46 files changed, 2673 insertions, 891 deletions
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index da16964d61..29eaf348a9 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -253,15 +253,30 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </warning> </desc> </func> - <func> + <func> <name name="ensure_started" arity="1"/> <name name="ensure_started" arity="2"/> <fsummary>Load and start an application</fsummary> - <desc> - <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except - it returns <c>ok</c> for already started applications.</p> - </desc> - </func> + <desc> + <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except + it returns <c>ok</c> for already started applications.</p> + </desc> + </func> + <func> + <name name="ensure_all_started" arity="1"/> + <name name="ensure_all_started" arity="2"/> + <fsummary>Load and start an application and its dependencies, recursively</fsummary> + <desc> + <p>Equivalent to calling <seealso marker="#start/2"><c>application:start/1,2</c></seealso> + repeatedly on all dependencies that have not yet been started for an application. + The function returns <c>{ok, AppNames}</c> for a successful start or for an already started + application (which are however omitted from the <c>AppNames</c> list), and reports + <c>{error, {AppName,Reason}}</c> for errors, where <c>Reason</c> is any possible reason + returned by <seealso marker="#start/2"><c>application:start/1,2</c></seealso> when starting a + specific dependency. In case of an error, the applications that were started by the + function are stopped to bring the set of running applications back to its initial state.</p> + </desc> + </func> <func> <name name="start" arity="1"/> <name name="start" arity="2"/> diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 1118a8feee..0a4dd3ba47 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -826,6 +826,16 @@ <item> <p><c>File</c> must be <c>iodata()</c>. Returns an <c>fd()</c> which lets the <c>file</c> module operate on the data in-memory as if it is a file.</p> </item> + <tag><c>sync</c></tag> + <item> + <p>On platforms that support it, enables the POSIX <c>O_SYNC</c> synchronous I/O flag or its platform-dependent + equivalent (e.g., <c>FILE_FLAG_WRITE_THROUGH</c> on Windows) so that writes to the file block until the data has + been physically written to disk. Be aware, though, that the exact semantics of this flag differ from platform to + platform; for example, neither Linux nor Windows guarantees that all file metadata are also written before the call + returns. For precise semantics, check the details of your platform's documentation. On platforms with no + support for POSIX <c>O_SYNC</c> or equivalent, use of the <c>sync</c> flag causes <c>open</c> to return + <c>{error, enotsup}</c>.</p> + </item> </taglist> <p>Returns:</p> <taglist> diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index dacea7a239..dc9e4766a9 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -322,7 +322,7 @@ <p> Branch off an existing association <anno>Assoc</anno> in a socket <anno>Socket</anno> of type <c>seqpacket</c> - (one-to-may style) into + (one-to-many style) into a new socket <anno>NewSocket</anno> of type <c>stream</c> (one-to-one style). </p> @@ -496,9 +496,11 @@ orthogonal to the sets of TCP, UDP and generic INET options: only those options which are explicitly listed below are allowed for SCTP sockets. Options can be set on the socket using - <c>gen_sctp:open/1,2</c> or <c>inet:setopts/2</c>, - retrieved using <c>inet:getopts/2</c>, and when calling - <c>gen_sctp:connect/4,5</c> options can be changed.</p> + <seealso marker="#open/1"><c>gen_sctp:open/1,2</c></seealso> + or <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>, + retrieved using <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>, + and when calling <seealso marker="#connect/4"><c>gen_sctp:connect/4,5</c></seealso> + options can be changed.</p> <marker id="option-binary"></marker> <marker id="option-list"></marker> <taglist> @@ -507,7 +509,7 @@ <p>Determines the type of data returned from <c>gen_sctp:recv/1,2</c>.</p> <marker id="option-active"></marker> </item> - <tag><c>{active, true|false|once}</c></tag> + <tag><c>{active, true|false|once|N}</c></tag> <item> <list type="bulleted"> <item> @@ -524,11 +526,28 @@ </item> <item> <p>If <c>once</c>, only one message is automatically placed - in the message queue, after that the mode is automatically - re-set to passive. This provides flow control as well as + in the message queue, and after that the mode is automatically + reset to passive. This provides flow control as well as the possibility for the receiver to listen for its incoming SCTP data interleaved with other inter-process messages.</p> </item> + <item> + <p>If <c>active</c> is specified as an integer <c>N</c> in the + range -32768 to 32767 (inclusive), then that number is added to + the socket's count of the number of data messages to be + delivered to the controlling process. If the result of the + addition would be negative, the count is set to 0. Once the + count reaches 0, either through the delivery of messages or by + being explicitly set with <seealso + marker="inet#setopts/2">inet:setopts/2</seealso>, the socket's + mode is automatically reset to passive (<c>{active, + false}</c>) mode. When a socket in this active mode transitions to + passive mode, the message <c>{sctp_passive, Socket}</c> is sent + to the controlling process to notify it that if it wants to + receive more data messages from the socket, it must call + <seealso marker="inet#setopts/2">inet:setopts/2</seealso> to set + the socket back into an active mode.</p> + </item> </list> </item> <tag><c>{tos, integer()}</c></tag> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 7afd73f4bf..dbd0d3c815 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -148,6 +148,12 @@ do_recv(Sock, Bs) -> as messages:</p> <code type="none"> {tcp, Socket, Data}</code> + <p>If the socket is in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2"> + inet:setopts/2</seealso> for details) and its message counter + drops to 0, the following message is delivered to indicate that the + socket has transitioned to passive (<c>{active, false}</c>) mode:</p> + <code type="none"> +{tcp_passive, Socket}</code> <p>If the socket is closed, the following message is delivered:</p> <code type="none"> {tcp_closed, Socket}</code> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index faca31e928..503725fe18 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -145,14 +145,23 @@ <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> </item> </taglist> - <p>The returned socket <c><anno>Socket</anno></c> is used to send packets - from this port with <c>send/4</c>. When UDP packets arrive at - the opened port, they are delivered as messages:</p> + <p>The returned socket <c><anno>Socket</anno></c> is used to send + packets from this port with <c>send/4</c>. When UDP packets arrive + at the opened port, if the socket is in an active mode the packets + are delivered as messages to the controlling process:</p> <code type="none"> {udp, Socket, IP, InPortNo, Packet}</code> - <p>Note that arriving UDP packets that are longer than + <p>If the socket is not in an active mode, data can be + retrieved via the <seealso marker="#recv/2">recv/2,3</seealso> calls. + Note that arriving UDP packets that are longer than the receive buffer option specifies, might be truncated without warning.</p> + <p>When a socket in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2"> + inet:setopts/2</seealso> for details) transitions to passive + (<c>{active, false}</c>) mode, the controlling process is notified by a + message of the following form:</p> + <code type="none"> +{udp_passive, Socket}</code> <p><c>IP</c> and <c>InPortNo</c> define the address from which <c>Packet</c> came. <c>Packet</c> is a list of bytes if the option <c>list</c> was specified. <c>Packet</c> is a diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index f5028a5559..3ec33d2f18 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -118,8 +118,13 @@ the system. The new Erlang runtime system will (if it misbehaves) use the environment variable <c>HEART_COMMAND</c> to reboot.</p> - <p>Limitations: The length of the <c><anno>Cmd</anno></c> command string - must be less than 2047 characters.</p> + + <p>Limitations: The <c><anno>Cmd</anno></c> command string + will be sent to the heart program as a ISO-latin-1 or UTF-8 + encoded binary depending on the file name encoding mode of the + emulator (see + <seealso marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seealso>). + The size of the encoded binary must be less than 2047 bytes.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 119d7d8df9..4a48a5c3d8 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -76,11 +76,11 @@ FFFF::192.168.42.2 {16#3ffe,16#b80,16#1f8d,16#2,16#204,16#acff,16#fe17,16#bf38} fe80::204:acff:fe17:bf38 {16#fe80,0,0,0,0,16#204,16#acff,16#fe17,16#bf38}</code> - <p>A function that may be useful is <c>inet_parse:address/1</c>:</p> + <p>A function that may be useful is <seealso marker="#parse_address/1">parse_address/1</seealso>:</p> <pre> -1> <input>inet_parse:address("192.168.42.2").</input> +1> <input>inet:parse_address("192.168.42.2").</input> {ok,{192,168,42,2}} -2> <input>inet_parse:address("FFFF::192.168.42.2").</input> +2> <input>inet:parse_address("FFFF::192.168.42.2").</input> {ok,{65535,0,0,0,0,0,49320,10754}}</pre> </description> @@ -375,6 +375,13 @@ fe80::204:acff:fe17:bf38 </desc> </func> <func> + <name name="ntoa" arity="1" /> + <fsummary>Convert IPv6 / IPV4 adress to ascii</fsummary> + <desc> + <p>Parses an <a href="#type-ip_address">ip_address()</a> and returns an IPv4 or IPv6 address string.</p> + </desc> + </func> + <func> <name name="parse_ipv4_address" arity="1" /> <fsummary>Parse an IPv4 address</fsummary> <desc> @@ -423,8 +430,56 @@ fe80::204:acff:fe17:bf38 <name name="peername" arity="1"/> <fsummary>Return the address and port for the other end of a connection</fsummary> <desc> - <p>Returns the address and port for the other end of a - connection.</p> + <p> + Returns the address and port for the other end of a + connection. + </p> + <p> + Note that for SCTP sockets this function only returns + one of the socket's peer addresses. The function + <seealso marker="#peernames/1">peernames/1,2</seealso> + returns all. + </p> + </desc> + </func> + <func> + <name name="peernames" arity="1"/> + <fsummary> + Return all address/port numbers for the other end of a connection + </fsummary> + <desc> + <p> + Equivalent to + <seealso marker="#peernames/2"><c>peernames(<anno>Socket</anno>, 0)</c></seealso>. + Note that this function's behaviour for an SCTP + one-to-many style socket is not defined by the + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>. + </p> + </desc> + </func> + <func> + <name name="peernames" arity="2"/> + <fsummary> + Return all address/port numbers for the other end of a connection + </fsummary> + <desc> + <p> + Returns a list of all address/port number pairs for the other end + of a socket's association <c><anno>Assoc</anno></c>. + </p> + <p> + This function can return multiple addresses for multihomed + sockets such as SCTP sockets. For other sockets it + returns a one element list. + </p> + <p> + Note that the <c><anno>Assoc</anno></c> parameter is by the + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url> + defined to be ignored for + one-to-one style sockets. What the special value <c>0</c> + means hence its behaviour for one-to-many style sockets + is unfortunately not defined. + </p> </desc> </func> <func> @@ -439,6 +494,46 @@ fe80::204:acff:fe17:bf38 <fsummary>Return the local address and port number for a socket</fsummary> <desc> <p>Returns the local address and port number for a socket.</p> + <p> + Note that for SCTP sockets this function only returns + one of the socket addresses. The function + <seealso marker="#socknames/1">socknames/1,2</seealso> + returns all. + </p> + </desc> + </func> + <func> + <name name="socknames" arity="1"/> + <fsummary>Return all local address/port numbers for a socket</fsummary> + <desc> + <p> + Equivalent to + <seealso marker="#socknames/2"><c>socknames(<anno>Socket</anno>, 0)</c></seealso>. + </p> + </desc> + </func> + <func> + <name name="socknames" arity="2"/> + <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 + for the given association <c><anno>Assoc</anno></c>. + </p> + <p> + This function can return multiple addresses for multihomed + sockets such as SCTP sockets. For other sockets it + returns a one element list. + </p> + <p> + Note that the <c><anno>Assoc</anno></c> parameter is by the + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url> + defined to be ignored for one-to-one style sockets. + For one-to-many style sockets the special value <c>0</c> + is defined to mean that the returned addresses shall be + without regard to any particular association. + How different SCTP implementations interprets this varies somewhat. + </p> </desc> </func> <func> @@ -449,47 +544,66 @@ fe80::204:acff:fe17:bf38 <p>Sets one or more options for a socket. The following options are available:</p> <taglist> - <tag><c>{active, true | false | once}</c></tag> + <tag><c>{active, true | false | once | N}</c></tag> <item> <p>If the value is <c>true</c>, which is the default, everything received from the socket will be sent as messages to the receiving process. If the value is <c>false</c> (passive mode), the process must explicitly - receive incoming data by calling <c>gen_tcp:recv/2,3</c> - or <c>gen_udp:recv/2,3</c> (depending on the type of - socket).</p> + receive incoming data by calling + <seealso marker="gen_tcp#recv/2"><c>gen_tcp:recv/2,3</c></seealso>, + <seealso marker="gen_udp#recv/2"><c>gen_udp:recv/2,3</c></seealso> + or <seealso marker="gen_sctp#recv/1"><c>gen_sctp:recv/1,2</c></seealso> + (depending on the type of socket).</p> <p>If the value is <c>once</c> (<c>{active, once}</c>), <em>one</em> data message from the socket will be sent to the process. To receive one more message, <c>setopts/2</c> must be called again with the <c>{active, once}</c> option.</p> - <p>When using <c>{active, once}</c>, the socket changes - behaviour automatically when data is received. This can - sometimes be confusing in combination with connection - oriented sockets (i.e. <c>gen_tcp</c>) as a socket with - <c>{active, false}</c> behaviour reports closing + <p>If the value is an integer <c>N</c> in the range -32768 to 32767 + (inclusive), the value is added to the socket's count of data + messages sent to the controlling process. A socket's default + message count is 0. If a negative value is specified and its + magnitude is equal to or greater than the socket's current + message count, the socket's message count is set to 0. Once + the socket's message count reaches 0, either due to sending + received data messages to the process or by being explicitly set, + the process is then notified by a special message, specific to + the type of socket, that the socket has entered passive + mode. Once the socket enters passive mode, to receive more + messages <c>setopts/2</c> must be called again to set the + socket back into an active mode.</p> + <p>When using <c>{active, once}</c> or <c>{active, N}</c>, the + socket changes behaviour automatically when data is received. + This can sometimes be confusing in combination with + connection-oriented sockets (i.e. <c>gen_tcp</c>) as a socket + with <c>{active, false}</c> behaviour reports closing differently than a socket with <c>{active, true}</c> behaviour. To make programming easier, a socket where the peer closed and this was detected while in <c>{active, false}</c> mode, will still generate the message - <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c> or <c>{active, true}</c> mode. It is therefore + <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c>, + <c>{active, true}</c> or <c>{active, N}</c> mode. It is therefore safe to assume that the message <c>{tcp_closed,Socket}</c>, possibly followed by socket port termination (depending on the <c>exit_on_close</c> option) will eventually appear when a socket changes back and forth between <c>{active, true}</c> and - <c>{active, false}</c> mode. However, + <c>{active, false}</c> mode. However, <em>when</em> peer closing is detected is all up to the underlying TCP/IP stack and protocol.</p> - <p>Note that <c>{active,true}</c> mode provides no flow + <p>Note that <c>{active, true}</c> mode provides no flow control; a fast sender could easily overflow the - receiver with incoming messages. Use active mode only if + receiver with incoming messages. The same is true of + <c>{active, N}</c> mode while the message count is greater + than zero. Use active mode only if your high-level protocol provides its own flow control (for instance, acknowledging received messages) or the - amount of data exchanged is small. <c>{active,false}</c> - mode or use of the <c>{active, once}</c> mode provides - flow control; the other side will not be able send + amount of data exchanged is small. <c>{active, false}</c> + mode, use of the <c>{active, once}</c> mode or <c>{active, N}</c> + mode with values of <c>N</c> appropriate for the application + provides flow control; the other side will not be able send faster than the receiver can read.</p> </item> @@ -715,6 +829,59 @@ fe80::204:acff:fe17:bf38 <p>Received <c>Packet</c> is delivered as defined by Mode.</p> </item> + <tag><c>{netns, Namespace :: file:filename_all()}</c></tag> + <item> + <p>Set a network namespace for the socket. The <c>Namespace</c> + parameter is a filename defining the namespace for example + <c>"/var/run/netns/example"</c> typically created by the command + <c>ip netns add example</c>. This option must be used in a + function call that creates a socket i.e + <seealso marker="gen_tcp#connect/3"> + gen_tcp:connect/3,4</seealso>, + <seealso marker="gen_tcp#listen/2"> + gen_tcp:listen/2</seealso>, + <seealso marker="gen_udp#open/1"> + gen_udp:open/1,2</seealso> or + <seealso marker="gen_sctp#open/0"> + gen_sctp:open/0-2</seealso>. + </p> + <p>This option uses the Linux specific syscall + <c>setns()</c> such as in Linux kernel 3.0 or later + and therefore only exists when the runtime system + has been compiled for such an operating system. + </p> + <p> + The virtual machine also needs elevated privileges either + running as superuser or (for Linux) having the capability + <c>CAP_SYS_ADMIN</c> according to the documentation for setns(2). + However, during testing also <c>CAP_SYS_PTRACE</c> + and <c>CAP_DAC_READ_SEARCH</c> has proven to be necessary. + Example:<code> +setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp +</code> + Note also that the filesystem containing the virtual machine + executable (<c>beam.smp</c> in the example above) has to be local, + mounted without the <c>nosetuid</c> flag, + support extended attributes and that + the kernel has to support file capabilities. + All this runs out of the box on at least Ubuntu 12.04 LTS, + except that SCTP sockets appears to not support + network namespaces. + </p> + <p>The <c>Namespace</c> is a file name and is encoded + and decoded as discussed in + <seealso marker="file">file</seealso> + except that the emulator flag <c>+fnu</c> is ignored and + <seealso marker="#getopts/2">getopts/2</seealso> + for this option will return a binary for the filename + if the stored filename can not be decoded, + which should only happen if you set the option using a binary + that can not be decoded with the emulator's filename encoding: + <seealso marker="file#native_name_encoding/0"> + file:native_name_encoding/0</seealso>. + </p> + </item> + <tag><c>list</c></tag> <item> <p>Received <c>Packet</c> is delivered as a list.</p> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 1b14d11e62..b2e89ea850 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -30,6 +30,214 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 2.16.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix the typespec for the inet:ifget/2 and inet:ifget/3 + return value. Thanks to Ali Sabil.</p> + <p> + Own Id: OTP-11377</p> + </item> + <item> + <p> + Fix various typos in erts, kernel and ssh. Thanks to + Martin Hässler.</p> + <p> + Own Id: OTP-11414</p> + </item> + <item> + <p> + Fix rpc multicall sample code. Thanks to Edwin Fine.</p> + <p> + Own Id: OTP-11471</p> + </item> + <item> + <p> + Under rare circumstances a process calling <seealso + marker="kernel:inet#close/1"><c>inet:close/1</c></seealso>, + <seealso + marker="kernel:gen_tcp#close/1"><c>gen_tcp:close/1</c></seealso>, + <seealso + marker="kernel:gen_udp#close/1"><c>gen_udp:close/1</c></seealso>, + or <seealso + marker="kernel:gen_sctp#close/1"><c>gen_sctp:close/1</c></seealso> + could hang in the call indefinitely.</p> + <p> + Own Id: OTP-11491</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add more SCTP errors as described in RFC 4960. Thanks to + Artem Teslenko.</p> + <p> + Own Id: OTP-11379</p> + </item> + <item> + <p> + Add new BIF os:unsetenv/1 which deletes an environment + variable. Thanks to Martin Hässler.</p> + <p> + Own Id: OTP-11446</p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 2.16.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix indentation of User switch command help in Erlang + shell. Thanks to Sylvain Benner.</p> + <p> + Own Id: OTP-11209</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The previous undocumented function ntoa/1 has been added + to inet docs and exported in the inet module.</p> + <p> + Own Id: OTP-10676 Aux Id: OTP-10314 </p> + </item> + <item> + <p> + Fix typo in abcast() function comment. Thanks to Johannes + Weissl.</p> + <p> + Own Id: OTP-11219</p> + </item> + <item> + <p> + Add application:ensure_all_started/1-2. Thanks to Fred + Hebert.</p> + <p> + Own Id: OTP-11250</p> + </item> + <item> + <p> + Make edlin understand a few important control keys. + Thanks to Stefan Zegenhagen.</p> + <p> + Own Id: OTP-11251</p> + </item> + <item> + <p> + Cleanup of hipe_unified_loader, eliminating uses of + is_subtype/2 in specs, change module-local void functions + to return 'ok' instead of [] and made sure there are no + dialyzer warnings with --Wunmatched_returns. Thanks to + Kostis Sagonas.</p> + <p> + Own Id: OTP-11301</p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 2.16.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A bug in prim_inet has been corrected. If the port owner + was killed at a bad time while closing the socket port + the port could become orphaned hence causing port and + socket leaking. Reported by Fred Herbert, Dmitry Belyaev + and others.</p> + <p> + Own Id: OTP-10497 Aux Id: OTP-10562 </p> + </item> + <item> + <p> + A few bugs regarding case sensitivity for hostname + resolution while using e.g the internal lookup types + 'file' and 'dns' has been corrected. When looking up + hostnames ASCII letters a-z are to be regarded as the + same as A-Z according to RFC 4343 "Domain Name System + (DNS) Case Insensitivity Clarification", and this was not + always the case.</p> + <p> + Own Id: OTP-10689 Aux Id: seq12227 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add <c>application:ensure_started/1,2</c>. It is + equivavlent to <c>application:start/1,2</c> except it + returns <c>ok</c> for already started applications.</p> + <p> + Own Id: OTP-10910</p> + </item> + <item> + <p> + Optimize communication with file io server. Thanks to + Anthony Ramine.</p> + <p> + Own Id: OTP-11040</p> + </item> + <item> + <p>Erlang source files with non-ASCII characters are now + encoded in UTF-8 (instead of latin1).</p> + <p> + Own Id: OTP-11041 Aux Id: OTP-10907 </p> + </item> + <item> + <p> + Optimization of simultaneous <c>inet_db</c> operations on + the same socket by using a lock free implementation.</p> + <p> + Impact on the characteristics of the system: Improved + performance.</p> + <p> + Own Id: OTP-11074</p> + </item> + <item> + <p> + The <c>high_msgq_watermark</c> and + <c>low_msgq_watermark</c> <c>inet</c> socket options + introduced in OTP-R16A could only be set on TCP sockets. + These options are now generic and can be set on all types + of sockets.</p> + <p> + Own Id: OTP-11075 Aux Id: OTP-10336 </p> + </item> + <item> + <p> + Fix deep list argument error under Windows in os:cmd/1. + Thanks to Aleksandr Vinokurov .</p> + <p> + Own Id: OTP-11104</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 2.16.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 1e66f124ee..2b57e75023 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -177,6 +177,17 @@ format_utc_timestamp() -> </desc> </func> <func> + <name name="unsetenv" arity="1"/> + <fsummary>Delete an environment variable</fsummary> + <desc> + <p>Deletes the environment variable <c><anno>VarName</anno></c>.</p> + <p>If Unicode filename encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the string (<c><anno>VarName</anno></c>) may + contain characters with codepoints > 255.</p> + </desc> + </func> + <func> <name name="version" arity="0"/> <fsummary>Return the Operating System version</fsummary> <desc> diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index 7026c7a07e..e6c896f18d 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -185,7 +185,7 @@ {Mod, Bin, File} = code:get_object_code(Mod), %% and load it on all nodes including this one -{ResL, _} = rpc:multicall(code, load_binary, [Mod, Bin, File,]), +{ResL, _} = rpc:multicall(code, load_binary, [Mod, File, Bin]), %% and then maybe check the ResL list.</code> </desc> diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl index 5dd6b73857..4e8ba1b78a 100644 --- a/lib/kernel/src/application.erl +++ b/lib/kernel/src/application.erl @@ -18,7 +18,8 @@ %% -module(application). --export([start/1, start/2, start_boot/1, start_boot/2, stop/1, +-export([ensure_all_started/1, ensure_all_started/2, start/1, start/2, + start_boot/1, start_boot/2, stop/1, load/1, load/2, unload/1, takeover/2, which_applications/0, which_applications/1, loaded_applications/0, permit/2]). @@ -113,6 +114,46 @@ load1(Application, DistNodes) -> unload(Application) -> application_controller:unload_application(Application). + +-spec ensure_all_started(Application) -> {'ok', Started} | {'error', Reason} when + Application :: atom(), + Started :: [atom()], + Reason :: term(). +ensure_all_started(Application) -> + ensure_all_started(Application, temporary). + +-spec ensure_all_started(Application, Type) -> {'ok', Started} | {'error', Reason} when + Application :: atom(), + Type :: restart_type(), + Started :: [atom()], + Reason :: term(). +ensure_all_started(Application, Type) -> + case ensure_all_started(Application, Type, []) of + {ok, Started} -> + {ok, lists:reverse(Started)}; + {error, Reason, Started} -> + [stop(App) || App <- Started], + {error, Reason} + end. + +ensure_all_started(Application, Type, Started) -> + case start(Application, Type) of + ok -> + {ok, [Application | Started]}; + {error, {already_started, Application}} -> + {ok, Started}; + {error, {not_started, Dependency}} -> + case ensure_all_started(Dependency, Type, Started) of + {ok, NewStarted} -> + ensure_all_started(Application, Type, NewStarted); + Error -> + Error + end; + {error, Reason} -> + {error, {Application, Reason}, Started} + end. + + -spec start(Application) -> 'ok' | {'error', Reason} when Application :: atom(), Reason :: term(). diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 9358e2201e..fc7ac08699 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1410,45 +1410,236 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> do_purge(Mod0) -> Mod = to_atom(Mod0), case erlang:check_old_code(Mod) of - false -> false; - true -> do_purge(processes(), Mod, false) - end. - -do_purge([P|Ps], Mod, Purged) -> - case erlang:check_process_code(P, Mod) of + false -> + false; true -> - Ref = erlang:monitor(process, P), - exit(P, kill), - receive - {'DOWN',Ref,process,_Pid,_} -> ok + Res = check_proc_code(erlang:processes(), Mod, true), + try + erlang:purge_module(Mod) + catch + _:_ -> ignore end, - do_purge(Ps, Mod, true); - false -> - do_purge(Ps, Mod, Purged) - end; -do_purge([], Mod, Purged) -> - catch erlang:purge_module(Mod), - Purged. + Res + end. %% do_soft_purge(Module) %% Purge old code only if no procs remain that run old code. %% Return true in that case, false if procs remain (in this %% case old code is not purged) -do_soft_purge(Mod) -> +do_soft_purge(Mod0) -> + Mod = to_atom(Mod0), case erlang:check_old_code(Mod) of - false -> true; - true -> do_soft_purge(processes(), Mod) + false -> + true; + true -> + case check_proc_code(erlang:processes(), Mod, false) of + false -> + false; + true -> + try + erlang:purge_module(Mod) + catch + _:_ -> ignore + end, + true + end end. -do_soft_purge([P|Ps], Mod) -> - case erlang:check_process_code(P, Mod) of - true -> false; - false -> do_soft_purge(Ps, Mod) +%% +%% check_proc_code(Pids, Mod, Hard) - Send asynchronous +%% requests to all processes to perform a check_process_code +%% operation. Each process will check their own state and +%% reply with the result. If 'Hard' equals +%% - true, processes that refer 'Mod' will be killed. If +%% any processes were killed true is returned; otherwise, +%% false. +%% - false, and any processes refer 'Mod', false will +%% returned; otherwise, true. +%% +%% Requests will be sent to all processes identified by +%% Pids at once, but without allowing GC to be performed. +%% Check process code operations that are aborted due to +%% GC need, will be restarted allowing GC. However, only +%% ?MAX_CPC_GC_PROCS outstanding operation allowing GC at +%% a time will be allowed. This in order not to blow up +%% memory wise. +%% +%% We also only allow ?MAX_CPC_NO_OUTSTANDING_KILLS +%% outstanding kills. This both in order to avoid flooding +%% our message queue with 'DOWN' messages and limiting the +%% amount of memory used to keep references to all +%% outstanding kills. +%% + +%% We maybe should allow more than two outstanding +%% GC requests, but for now we play it safe... +-define(MAX_CPC_GC_PROCS, 2). +-define(MAX_CPC_NO_OUTSTANDING_KILLS, 10). + +-record(cpc_static, {hard, module, tag}). + +-record(cpc_kill, {outstanding = [], + no_outstanding = 0, + waiting = [], + killed = false}). + +check_proc_code(Pids, Mod, Hard) -> + Tag = erlang:make_ref(), + CpcS = #cpc_static{hard = Hard, + module = Mod, + tag = Tag}, + check_proc_code(CpcS, cpc_init(CpcS, Pids, 0), 0, [], #cpc_kill{}, true). + +check_proc_code(#cpc_static{hard = true}, 0, 0, [], + #cpc_kill{outstanding = [], waiting = [], killed = Killed}, + true) -> + %% No outstanding requests. We did a hard check, so result is whether or + %% not we killed any processes... + Killed; +check_proc_code(#cpc_static{hard = false}, 0, 0, [], _KillState, Success) -> + %% No outstanding requests and we did a soft check... + Success; +check_proc_code(#cpc_static{hard = false, tag = Tag} = CpcS, NoReq0, NoGcReq0, + [], _KillState, false) -> + %% Failed soft check; just cleanup the remaining replies corresponding + %% to the requests we've sent... + {NoReq1, NoGcReq1} = receive + {check_process_code, {Tag, _P, GC}, _Res} -> + case GC of + false -> {NoReq0-1, NoGcReq0}; + true -> {NoReq0, NoGcReq0-1} + end + end, + check_proc_code(CpcS, NoReq1, NoGcReq1, [], _KillState, false); +check_proc_code(#cpc_static{tag = Tag} = CpcS, NoReq0, NoGcReq0, NeedGC0, + KillState0, Success) -> + + %% Check if we should request a GC operation + {NoGcReq1, NeedGC1} = case NoGcReq0 < ?MAX_CPC_GC_PROCS of + GcOpAllowed when GcOpAllowed == false; + NeedGC0 == [] -> + {NoGcReq0, NeedGC0}; + _ -> + {NoGcReq0+1, cpc_request_gc(CpcS,NeedGC0)} + end, + + %% Wait for a cpc reply or 'DOWN' message + {NoReq1, NoGcReq2, Pid, Result, KillState1} = cpc_recv(Tag, + NoReq0, + NoGcReq1, + KillState0), + + %% Check the result of the reply + case Result of + aborted -> + %% Operation aborted due to the need to GC in order to + %% determine if the process is referring the module. + %% Schedule the operation for restart allowing GC... + check_proc_code(CpcS, NoReq1, NoGcReq2, [Pid|NeedGC1], KillState1, + Success); + false -> + %% Process not referring the module; done with this process... + check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, KillState1, + Success); + true -> + %% Process referring the module... + case CpcS#cpc_static.hard of + false -> + %% ... and soft check. The whole operation failed so + %% no point continuing; clean up and fail... + check_proc_code(CpcS, NoReq1, NoGcReq2, [], KillState1, + false); + true -> + %% ... and hard check; schedule kill of it... + check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, + cpc_sched_kill(Pid, KillState1), Success) + end; + 'DOWN' -> + %% Handled 'DOWN' message + check_proc_code(CpcS, NoReq1, NoGcReq2, NeedGC1, + KillState1, Success) + end. + +cpc_recv(Tag, NoReq, NoGcReq, #cpc_kill{outstanding = []} = KillState) -> + receive + {check_process_code, {Tag, Pid, GC}, Res} -> + cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState) end; -do_soft_purge([], Mod) -> - catch erlang:purge_module(Mod), - true. +cpc_recv(Tag, NoReq, NoGcReq, + #cpc_kill{outstanding = [R0, R1, R2, R3, R4 | _]} = KillState) -> + receive + {'DOWN', R, process, _, _} when R == R0; + R == R1; + R == R2; + R == R3; + R == R4 -> + cpc_handle_down(NoReq, NoGcReq, R, KillState); + {check_process_code, {Tag, Pid, GC}, Res} -> + cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState) + end; +cpc_recv(Tag, NoReq, NoGcReq, #cpc_kill{outstanding = [R|_]} = KillState) -> + receive + {'DOWN', R, process, _, _} -> + cpc_handle_down(NoReq, NoGcReq, R, KillState); + {check_process_code, {Tag, Pid, GC}, Res} -> + cpc_handle_cpc(NoReq, NoGcReq, GC, Pid, Res, KillState) + end. + +cpc_handle_down(NoReq, NoGcReq, R, #cpc_kill{outstanding = Rs, + no_outstanding = N} = KillState) -> + {NoReq, NoGcReq, undefined, 'DOWN', + cpc_sched_kill_waiting(KillState#cpc_kill{outstanding = cpc_list_rm(R, Rs), + no_outstanding = N-1})}. + +cpc_list_rm(R, [R|Rs]) -> + Rs; +cpc_list_rm(R0, [R1|Rs]) -> + [R1|cpc_list_rm(R0, Rs)]. + +cpc_handle_cpc(NoReq, NoGcReq, false, Pid, Res, KillState) -> + {NoReq-1, NoGcReq, Pid, Res, KillState}; +cpc_handle_cpc(NoReq, NoGcReq, true, Pid, Res, KillState) -> + {NoReq, NoGcReq-1, Pid, Res, KillState}. + +cpc_sched_kill_waiting(#cpc_kill{waiting = []} = KillState) -> + KillState; +cpc_sched_kill_waiting(#cpc_kill{outstanding = Rs, + no_outstanding = N, + waiting = [P|Ps]} = KillState) -> + R = erlang:monitor(process, P), + exit(P, kill), + KillState#cpc_kill{outstanding = [R|Rs], + no_outstanding = N+1, + waiting = Ps, + killed = true}. + +cpc_sched_kill(Pid, #cpc_kill{no_outstanding = N, waiting = Pids} = KillState) + when N >= ?MAX_CPC_NO_OUTSTANDING_KILLS -> + KillState#cpc_kill{waiting = [Pid|Pids]}; +cpc_sched_kill(Pid, + #cpc_kill{outstanding = Rs, no_outstanding = N} = KillState) -> + R = erlang:monitor(process, Pid), + exit(Pid, kill), + KillState#cpc_kill{outstanding = [R|Rs], + no_outstanding = N+1, + killed = true}. + +cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) -> + erlang:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, + {allow_gc, AllowGc}]). + +cpc_request_gc(CpcS, [Pid|Pids]) -> + cpc_request(CpcS, Pid, true), + Pids. + +cpc_init(_CpcS, [], NoReqs) -> + NoReqs; +cpc_init(CpcS, [Pid|Pids], NoReqs) -> + cpc_request(CpcS, Pid, false), + cpc_init(CpcS, Pids, NoReqs+1). + +% end of check_proc_code() implementation. is_loaded(M, Db) -> case ets:lookup(Db, M) of diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 5c0f3b7ceb..b5152018e0 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -95,7 +95,8 @@ Delay :: non_neg_integer()} | 'delayed_write' | {'read_ahead', Size :: pos_integer()} | 'read_ahead' | 'compressed' - | {'encoding', unicode:encoding()}. + | {'encoding', unicode:encoding()} + | sync. -type deep_list() :: [char() | atom() | deep_list()]. -type name() :: string() | atom() | deep_list(). -type name_all() :: string() | atom() | deep_list() | (RawFilename :: binary()). diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index 0d005eb651..adaa3159ec 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -36,7 +36,7 @@ -type assoc_id() :: term(). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {dontroute, boolean()} | {high_msgq_watermark, pos_integer()} | @@ -423,7 +423,11 @@ error_string(9) -> error_string(10) -> "Cookie Received While Shutting Down"; error_string(11) -> + "Restart of an Association with New Addresses"; +error_string(12) -> "User Initiated Abort"; +error_string(13) -> + "Protocol Violation"; %% For more info on principal SCTP error codes: phone +44 7981131933 error_string(N) when is_integer(N) -> unknown_error; diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 96026aa428..bc8ffbe5e3 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -30,7 +30,7 @@ -include("file.hrl"). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {delay_send, boolean()} | {deliver, port | term} | diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index fb5737f82e..70dceb3679 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ -include("inet_int.hrl"). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {add_membership, {inet:ip_address(), inet:ip_address()}} | {broadcast, boolean()} | {buffer, non_neg_integer()} | diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index aa65c3a474..daed6dd488 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.erl @@ -184,14 +184,18 @@ wait_ack(Port) -> loop(Parent, Port, Cmd) -> _ = send_heart_beat(Port), receive - {From, set_cmd, NewCmd} when length(NewCmd) < 2047 -> - _ = send_heart_cmd(Port, NewCmd), - _ = wait_ack(Port), - From ! {heart, ok}, - loop(Parent, Port, NewCmd); - {From, set_cmd, NewCmd} -> - From ! {heart, {error, {bad_cmd, NewCmd}}}, - loop(Parent, Port, Cmd); + {From, set_cmd, NewCmd0} -> + Enc = file:native_name_encoding(), + case catch unicode:characters_to_binary(NewCmd0,Enc,Enc) of + NewCmd when is_binary(NewCmd), byte_size(NewCmd) < 2047 -> + _ = send_heart_cmd(Port, NewCmd), + _ = wait_ack(Port), + From ! {heart, ok}, + loop(Parent, Port, NewCmd); + _ -> + From ! {heart, {error, {bad_cmd, NewCmd0}}}, + loop(Parent, Port, Cmd) + end; {From, clear_cmd} -> From ! {heart, ok}, _ = send_heart_cmd(Port, ""), diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index cc51b5ae07..e111cb800e 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -85,7 +85,7 @@ chunk_name(Architecture) -> %%======================================================================== -spec load_native_code(Mod, binary()) -> 'no_native' | {'module', Mod} - when is_subtype(Mod, atom()). + when Mod :: atom(). %% @doc %% Loads the native code of a module Mod. %% Returns {module,Mod} on success (for compatibility with @@ -148,8 +148,8 @@ version_check(Version, Mod) when is_atom(Mod) -> %%======================================================================== --spec load_module(Mod, binary(), _) -> 'bad_crc' | {'module',Mod} - when is_subtype(Mod,atom()). +-spec load_module(Mod, binary(), _) -> 'bad_crc' | {'module', Mod} + when Mod :: atom(). load_module(Mod, Bin, Beam) -> erlang:system_flag(multi_scheduling, block), try @@ -169,8 +169,8 @@ load_module(Mod, Bin, Beam, OldReferencesToPatch) -> %%======================================================================== --spec load(Mod, binary()) -> 'bad_crc' | {'module',Mod} - when is_subtype(Mod,atom()). +-spec load(Mod, binary()) -> 'bad_crc' | {'module', Mod} when Mod :: atom(). + load(Mod, Bin) -> erlang:system_flag(multi_scheduling, block), try @@ -204,15 +204,17 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) -> bad_crc; true -> %% Create data segment - {ConstAddr,ConstMap2} = create_data_segment(ConstAlign, ConstSize, ConstMap), + {ConstAddr,ConstMap2} = + create_data_segment(ConstAlign, ConstSize, ConstMap), %% Find callees for which we may need trampolines. CalleeMFAs = find_callee_mfas(Refs), %% Write the code to memory. - {CodeAddress,Trampolines} = enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam), + {CodeAddress,Trampolines} = + enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam), %% Construct CalleeMFA-to-trampoline mapping. TrampolineMap = mk_trampoline_map(CalleeMFAs, Trampolines), %% Patch references to code labels in data seg. - patch_consts(LabelMap, ConstAddr, CodeAddress), + ok = patch_consts(LabelMap, ConstAddr, CodeAddress), %% Find out which functions are being loaded (and where). %% Note: Addresses are sorted descending. {MFAs,Addresses} = exports(ExportMap, CodeAddress), @@ -221,7 +223,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) -> ok = remove_refs_from(MFAs), %% Patch all dynamic references in the code. %% Function calls, Atoms, Constants, System calls - patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap), + ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap), %% Tell the system where the loaded funs are. %% (patches the BEAM code to redirect to native.) case Beam of @@ -324,7 +326,7 @@ trampoline_map_get(MFA, Map) -> gb_trees:get(MFA, Map). trampoline_map_lookup(_, []) -> []; % archs not using trampolines trampoline_map_lookup(Primop, Map) -> case gb_trees:lookup(Primop, Map) of - {value,X} -> X; + {value, X} -> X; _ -> [] end. @@ -371,7 +373,7 @@ offsets_to_addresses(Os, Base) -> find_closure_patches([{Type,Refs} | Rest]) -> case ?EXT2PATCH_TYPE(Type) of load_address -> - find_closure_refs(Refs,Rest); + find_closure_refs(Refs, Rest); _ -> find_closure_patches(Rest) end; @@ -406,16 +408,17 @@ export_funs([FunDef | Addresses]) -> hipe_bifs:set_native_address(MFA, Address, IsClosure), export_funs(Addresses); export_funs([]) -> - true. + ok. export_funs(Mod, Beam, Addresses, ClosuresToPatch) -> - Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- Addresses], - code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch}). + Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- Addresses], + Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch}), + ok. %%======================================================================== %% Patching %% @spec patch(refs(), BaseAddress::integer(), ConstAndZone::term(), -%% Addresses::term(), TrampolineMap::term()) -> term() +%% Addresses::term(), TrampolineMap::term()) -> 'ok'. %% @type refs()=[{RefType::integer(), Reflist::reflist()} | refs()] %% %% @type reflist()= [{Data::term(), Offsets::offests()}|reflist()] @@ -428,7 +431,7 @@ export_funs(Mod, Beam, Addresses, ClosuresToPatch) -> %% patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, Addresses, TrampolineMap) -> - ?debug_msg("Patching ~w at [~w+offset] with ~w\n", + ?debug_msg("Patching ~w at [~w+offset] with ~w\n", [Type,CodeAddress,SortedRefs]), case ?EXT2PATCH_TYPE(Type) of call_local -> @@ -439,7 +442,7 @@ patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, Addresses, TrampolineMap patch_all(Other, SortedRefs, CodeAddress, {ConstMap2,CodeAddress}, Addresses) end, patch(Rest, CodeAddress, ConstMap2, Addresses, TrampolineMap); -patch([], _, _, _, _) -> true. +patch([], _, _, _, _) -> ok. %%---------------------------------------------------------------- %% Handle a 'call_local' or 'call_remote' patch. @@ -461,14 +464,14 @@ patch_call([{DestMFA,Offsets}|SortedRefs], BaseAddress, Addresses, RemoteOrLocal end, patch_call(SortedRefs, BaseAddress, Addresses, RemoteOrLocal, TrampolineMap); patch_call([], _, _, _, _) -> - true. + ok. patch_bif_call_list([Offset|Offsets], BaseAddress, BifAddress, Trampoline) -> CallAddress = BaseAddress+Offset, ?ASSERT(assert_local_patch(CallAddress)), patch_call_insn(CallAddress, BifAddress, Trampoline), patch_bif_call_list(Offsets, BaseAddress, BifAddress, Trampoline); -patch_bif_call_list([], _, _, _) -> []. +patch_bif_call_list([], _, _, _) -> ok. patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline) -> CallAddress = BaseAddress+Offset, @@ -476,7 +479,7 @@ patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, Address ?ASSERT(assert_local_patch(CallAddress)), patch_call_insn(CallAddress, DestAddress, Trampoline), patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline); -patch_mfa_call_list([], _, _, _, _, _, _) -> []. +patch_mfa_call_list([], _, _, _, _, _, _) -> ok. patch_call_insn(CallAddress, DestAddress, Trampoline) -> %% This assertion is false when we're called from redirect/2. @@ -489,7 +492,7 @@ patch_call_insn(CallAddress, DestAddress, Trampoline) -> patch_all(Type, [{Dest,Offsets}|Rest], BaseAddress, ConstAndZone, Addresses)-> patch_all_offsets(Type, Dest, Offsets, BaseAddress, ConstAndZone, Addresses), patch_all(Type, Rest, BaseAddress, ConstAndZone, Addresses); -patch_all(_, [], _, _, _) -> true. +patch_all(_, [], _, _, _) -> ok. patch_all_offsets(Type, Data, [Offset|Offsets], BaseAddress, ConstAndZone, Addresses) -> @@ -499,7 +502,7 @@ patch_all_offsets(Type, Data, [Offset|Offsets], BaseAddress, patch_offset(Type, Data, Address, ConstAndZone, Addresses), ?debug_msg("Patching done\n",[]), patch_all_offsets(Type, Data, Offsets, BaseAddress, ConstAndZone, Addresses); -patch_all_offsets(_, _, [], _, _, _) -> true. +patch_all_offsets(_, _, [], _, _, _) -> ok. %%---------------------------------------------------------------- %% Handle any patch type except 'call_local' or 'call_remote'. diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 1a1f7b8cbc..792593246a 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -24,6 +24,7 @@ %% socket -export([peername/1, sockname/1, port/1, send/2, + peernames/1, peernames/2, socknames/1, socknames/2, setopts/2, getopts/2, getifaddrs/0, getifaddrs/1, getif/1, getif/0, getiflist/0, getiflist/1, @@ -32,7 +33,7 @@ ip/1, stats/0, options/0, pushf/3, popf/1, close/1, gethostname/0, gethostname/1, parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1, - parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1]). + parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, ntoa/1]). -export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]). @@ -120,6 +121,17 @@ 'addr' | 'broadaddr' | 'dstaddr' | 'mtu' | 'netmask' | 'flags' |'hwaddr'. +-type if_getopt_result() :: + {'addr', ip_address()} | + {'broadaddr', ip_address()} | + {'dstaddr', ip_address()} | + {'mtu', non_neg_integer()} | + {'netmask', ip_address()} | + {'flags', ['up' | 'down' | 'broadcast' | 'no_broadcast' | + 'pointtopoint' | 'no_pointtopoint' | + 'running' | 'multicast' | 'loopback']} | + {'hwaddr', ether_address()}. + -type address_family() :: 'inet' | 'inet6'. -type socket_protocol() :: 'tcp' | 'udp' | 'sctp'. -type socket_type() :: 'stream' | 'dgram' | 'seqpacket'. @@ -146,6 +158,7 @@ close(Socket) -> ok end. + -spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when Socket :: socket(), Address :: ip_address(), @@ -162,6 +175,24 @@ setpeername(Socket, {IP,Port}) -> setpeername(Socket, undefined) -> prim_inet:setpeername(Socket, undefined). +-spec peernames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when + Socket :: socket(), + Address :: ip_address(), + Port :: non_neg_integer(). + +peernames(Socket) -> + prim_inet:peernames(Socket). + +-spec peernames(Socket, Assoc) -> + {ok, [{Address, Port}]} | {error, posix()} when + Socket :: socket(), + Assoc :: #sctp_assoc_change{} | gen_sctp:assoc_id(), + Address :: ip_address(), + Port :: non_neg_integer(). + +peernames(Socket, Assoc) -> + prim_inet:peernames(Socket, Assoc). + -spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when Socket :: socket(), @@ -179,6 +210,25 @@ setsockname(Socket, {IP,Port}) -> setsockname(Socket, undefined) -> prim_inet:setsockname(Socket, undefined). +-spec socknames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when + Socket :: socket(), + Address :: ip_address(), + Port :: non_neg_integer(). + +socknames(Socket) -> + prim_inet:socknames(Socket). + +-spec socknames(Socket, Assoc) -> + {ok, [{Address, Port}]} | {error, posix()} when + Socket :: socket(), + Assoc :: #sctp_assoc_change{} | gen_sctp:assoc_id(), + Address :: ip_address(), + Port :: non_neg_integer(). + +socknames(Socket, Assoc) -> + prim_inet:socknames(Socket, Assoc). + + -spec port(Socket) -> {'ok', Port} | {'error', any()} when Socket :: socket(), Port :: port_number(). @@ -200,7 +250,14 @@ send(Socket, Packet) -> Options :: [socket_setopt()]. setopts(Socket, Opts) -> - prim_inet:setopts(Socket, Opts). + SocketOpts = + [case Opt of + {netns,NS} -> + {netns,filename2binary(NS)}; + _ -> + Opt + end || Opt <- Opts], + prim_inet:setopts(Socket, SocketOpts). -spec getopts(Socket, Options) -> {'ok', OptionValues} | {'error', posix()} when @@ -209,7 +266,18 @@ setopts(Socket, Opts) -> OptionValues :: [socket_setopt()]. getopts(Socket, Opts) -> - prim_inet:getopts(Socket, Opts). + case prim_inet:getopts(Socket, Opts) of + {ok,OptionValues} -> + {ok, + [case OptionValue of + {netns,Bin} -> + {netns,binary2filename(Bin)}; + _ -> + OptionValue + end || OptionValue <- OptionValues]}; + Other -> + Other + end. -spec getifaddrs(Socket :: socket()) -> {'ok', [string()]} | {'error', posix()}. @@ -248,13 +316,13 @@ getiflist() -> -spec ifget(Socket :: socket(), Name :: string() | atom(), Opts :: [if_getopt()]) -> - {'ok', [if_setopt()]} | {'error', posix()}. + {'ok', [if_getopt_result()]} | {'error', posix()}. ifget(Socket, Name, Opts) -> prim_inet:ifget(Socket, Name, Opts). -spec ifget(Name :: string() | atom(), Opts :: [if_getopt()]) -> - {'ok', [if_setopt()]} | {'error', posix()}. + {'ok', [if_getopt_result()]} | {'error', posix()}. ifget(Name, Opts) -> withsocket(fun(S) -> prim_inet:ifget(S, Name, Opts) end). @@ -529,6 +597,13 @@ getservbyname(Name, Protocol) when is_atom(Name) -> Error -> Error end. +-spec ntoa(IpAddress) -> + {ok, Address} | {error, einval} when + Address :: string(), + IpAddress :: ip_address(). +ntoa(Addr) -> + inet_parse:ntoa(Addr). + -spec parse_ipv4_address(Address) -> {ok, IPv4Address} | {error, einval} when Address :: string(), @@ -634,6 +709,17 @@ con_opt([Opt | Opts], R, As) -> {tcp_module,_} -> con_opt(Opts, R, As); inet -> con_opt(Opts, R, As); inet6 -> con_opt(Opts, R, As); + {netns,NS} -> + BinNS = filename2binary(NS), + case prim_inet:is_sockopt_val(netns, BinNS) of + true -> + con_opt(Opts, R#connect_opts { fd = [{netns,BinNS}] }, As); + false -> + {error, badarg} + end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#connect_opts.opts), + con_opt(Opts, R#connect_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> con_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -692,6 +778,17 @@ list_opt([Opt | Opts], R, As) -> {tcp_module,_} -> list_opt(Opts, R, As); inet -> list_opt(Opts, R, As); inet6 -> list_opt(Opts, R, As); + {netns,NS} -> + BinNS = filename2binary(NS), + case prim_inet:is_sockopt_val(netns, BinNS) of + true -> + list_opt(Opts, R#listen_opts { fd = [{netns,BinNS}] }, As); + false -> + {error, badarg} + end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#listen_opts.opts), + list_opt(Opts, R#listen_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> list_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -738,6 +835,17 @@ udp_opt([Opt | Opts], R, As) -> {udp_module,_} -> udp_opt(Opts, R, As); inet -> udp_opt(Opts, R, As); inet6 -> udp_opt(Opts, R, As); + {netns,NS} -> + BinNS = filename2binary(NS), + case prim_inet:is_sockopt_val(netns, BinNS) of + true -> + list_opt(Opts, R#udp_opts { fd = [{netns,BinNS}] }, As); + false -> + {error, badarg} + end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#udp_opts.opts), + udp_opt(Opts, R#udp_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -756,7 +864,7 @@ udp_add(Name, Val, R, Opts, As) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Currently supported options include: % (*) {mode, list|binary} or just list|binary -% (*) {active, true|false|once} +% (*) {active, true|false|once|N} % (*) {sctp_module, inet_sctp|inet6_sctp} or just inet|inet6 % (*) options set via setsockopt. % The full list is below in sctp_options/0 . @@ -807,6 +915,20 @@ sctp_opt([Opt|Opts], Mod, R, As) -> {sctp_module,_} -> sctp_opt (Opts, Mod, R, As); % Done with inet -> sctp_opt (Opts, Mod, R, As); % Done with inet6 -> sctp_opt (Opts, Mod, R, As); % Done with + {netns,NS} -> + BinNS = filename2binary(NS), + case prim_inet:is_sockopt_val(netns, BinNS) of + true -> + sctp_opt( + Opts, Mod, + R#sctp_opts { fd = [{netns,BinNS}] }, + As); + false -> + {error, badarg} + end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#sctp_opts.opts), + sctp_opt(Opts, Mod, R#sctp_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val); _ -> {error,badarg} end; @@ -851,6 +973,39 @@ add_opt(Name, Val, Opts, As) -> end. +%% Passthrough all unknown - catch type errors later +filename2binary(List) when is_list(List) -> + OutEncoding = file:native_name_encoding(), + try unicode:characters_to_binary(List, unicode, OutEncoding) of + Bin when is_binary(Bin) -> + Bin; + _ -> + List + catch + error:badarg -> + List + end; +filename2binary(Bin) -> + Bin. + +binary2filename(Bin) -> + InEncoding = file:native_name_encoding(), + case unicode:characters_to_list(Bin, InEncoding) of + Filename when is_list(Filename) -> + Filename; + _ -> + %% For getopt/setopt of netns this should only happen if + %% a binary with wrong encoding was used when setting the + %% option, hence the user shall eat his/her own medicine. + %% + %% I.e passthrough here too for now. + %% Future usecases will most probably not want this, + %% rather Unicode error or warning + %% depending on emulator flag instead. + Bin + end. + + translate_ip(any, inet) -> {0,0,0,0}; translate_ip(loopback, inet) -> {127,0,0,1}; translate_ip(any, inet6) -> {0,0,0,0,0,0,0,0}; @@ -947,21 +1102,34 @@ gethostbyname_self(Name, Type) when is_atom(Name) -> gethostbyname_self(Name, Type) when is_list(Name), Type =:= inet; is_list(Name), Type =:= inet6 -> - case inet_db:gethostname() of - Name -> - {ok,make_hostent(Name, - [translate_ip(loopback, Type)], - [], Type)}; - Self -> + N = inet_db:tolower(Name), + Self = inet_db:gethostname(), + %% + %% This is the final fallback that pretends /etc/hosts has got + %% a line for the hostname on the loopback address. + %% Lookups into /etc/hosts are case insensitive and return + %% what is in the file. Therefore the letter case may differ between + %% the returned hostent record and the hostname that was asked for. + %% + case inet_db:tolower(Self) of + N -> + {ok, + make_hostent( + Self, [translate_ip(loopback, Type)], [], Type)}; + _ -> case inet_db:res_option(domain) of - "" -> {error,nxdomain}; + "" -> + {error,nxdomain}; Domain -> - case lists:append([Self,".",Domain]) of - Name -> - {ok,make_hostent(Name, - [translate_ip(loopback, Type)], - [], Type)}; - _ -> {error,nxdomain} + FQDN = lists:append([Self,".",Domain]), + case inet_db:tolower(FQDN) of + N -> + {ok, + make_hostent( + FQDN, + [translate_ip(loopback, Type)], [], Type)}; + _ -> + {error,nxdomain} end end end; @@ -1050,7 +1218,7 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) -> Result -> Result end. --spec open(Fd :: integer(), +-spec open(Fd_or_OpenOpts :: integer() | list(), Addr :: ip_address(), Port :: port_number(), Opts :: [socket_setopt()], @@ -1060,8 +1228,14 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) -> Module :: atom()) -> {'ok', socket()} | {'error', posix()}. -open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 -> - case prim_inet:open(Protocol, Family, Type) of +open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module) + when is_integer(FdO), FdO < 0; + is_list(FdO) -> + OpenOpts = + if is_list(FdO) -> FdO; + true -> [] + end, + case prim_inet:open(Protocol, Family, Type, OpenOpts) of {ok,S} -> case prim_inet:setopts(S, Opts) of ok -> @@ -1084,7 +1258,8 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 -> Error -> Error end; -open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) -> +open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) + when is_integer(Fd) -> fdopen(Fd, Opts, Protocol, Family, Type, Module). bindx(S, [Addr], Port0) -> diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl index 8a2994579b..2ebdc0f554 100644 --- a/lib/kernel/src/inet_db.erl +++ b/lib/kernel/src/inet_db.erl @@ -845,7 +845,8 @@ init([]) -> process_flag(trap_exit, true), Db = ets:new(inet_db, [public, named_table]), reset_db(Db), - Cache = ets:new(inet_cache, [public, bag, {keypos,2}, named_table]), + CacheOpts = [public, bag, {keypos,#dns_rr.domain}, named_table], + Cache = ets:new(inet_cache, CacheOpts), BynameOpts = [protected, bag, named_table, {keypos,1}], ByaddrOpts = [protected, bag, named_table, {keypos,3}], HostsByname = ets:new(inet_hosts_byname, BynameOpts), @@ -901,15 +902,21 @@ reset_db(Db) -> handle_call(Request, From, #state{db=Db}=State) -> case Request of {load_hosts_file,IPNmAs} when is_list(IPNmAs) -> - NIPs = lists:flatten([ [{N,if tuple_size(IP) =:= 4 -> inet; - tuple_size(IP) =:= 8 -> inet6 - end,IP} || N <- [Nm|As]] - || {IP,Nm,As} <- IPNmAs]), + NIPs = + lists:flatten( + [ [{N, + if tuple_size(IP) =:= 4 -> inet; + tuple_size(IP) =:= 8 -> inet6 + end,IP} || N <- [Nm|As]] + || {IP,Nm,As} <- IPNmAs]), Byname = State#state.hosts_file_byname, Byaddr = State#state.hosts_file_byaddr, ets:delete_all_objects(Byname), ets:delete_all_objects(Byaddr), - ets:insert(Byname, NIPs), + %% Byname has lowercased names while Byaddr keep the name casing. + %% This is to be able to reconstruct the original + %% /etc/hosts entry. + ets:insert(Byname, [{tolower(N),Type,IP} || {N,Type,IP} <- NIPs]), ets:insert(Byaddr, NIPs), {reply, ok, State}; @@ -938,16 +945,14 @@ handle_call(Request, From, #state{db=Db}=State) -> {reply, ok, State}; {add_rr, RR} when is_record(RR, dns_rr) -> - RR1 = lower_rr(RR), - ?dbg("add_rr: ~p~n", [RR1]), - do_add_rr(RR1, Db, State), + ?dbg("add_rr: ~p~n", [RR]), + do_add_rr(RR, Db, State), {reply, ok, State}; {del_rr, RR} when is_record(RR, dns_rr) -> - RR1 = lower_rr(RR), %% note. del_rr will handle wildcards !!! Cache = State#state.cache, - ets:match_delete(Cache, RR1), + ets:match_delete(Cache, RR), {reply, ok, State}; {lookup_rr, Domain, Class, Type} -> @@ -1225,15 +1230,19 @@ handle_set_file(ParseFun, Bin, From, State) -> handle_rc_list(Opts, From, State) end. +%% Byname has lowercased names while Byaddr keep the name casing. +%% This is to be able to reconstruct the original /etc/hosts entry. + do_add_host(Byname, Byaddr, Names, Type, IP) -> do_del_host(Byname, Byaddr, IP), - NIPs = [{tolower(N),Type,IP} || N <- Names], - ets:insert(Byname, NIPs), - ets:insert(Byaddr, NIPs), + ets:insert(Byname, [{tolower(N),Type,IP} || N <- Names]), + ets:insert(Byaddr, [{N,Type,IP} || N <- Names]), ok. do_del_host(Byname, Byaddr, IP) -> - _ = [ets:delete_object(Byname, NIP) || NIP <- ets:lookup(Byaddr, IP)], + _ = + [ets:delete_object(Byname, {tolower(Name),Type,Addr}) || + {Name,Type,Addr} <- ets:lookup(Byaddr, IP)], ets:delete(Byaddr, IP), ok. @@ -1369,7 +1378,7 @@ times() -> %% lookup and remove old entries do_lookup_rr(Domain, Class, Type) -> - match_rr(#dns_rr{domain = tolower(Domain), class = Class,type = Type, + match_rr(#dns_rr{domain = Domain, class = Class,type = Type, cnt = '_', tm = '_', ttl = '_', bm = '_', func = '_', data = '_'}). @@ -1393,23 +1402,20 @@ filter_rr([], _Time) -> []. %% -%% Lower case the domain name before storage -%% -lower_rr(RR) -> - Dn = RR#dns_rr.domain, - if is_list(Dn) -> - RR#dns_rr { domain = tolower(Dn) }; - true -> RR - end. - +%% Case fold upper-case to lower-case according to RFC 4343 +%% "Domain Name System (DNS) Case Insensitivity Clarification". %% -%% Map upper-case to lower-case %% NOTE: this code is in kernel and we don't want to relay -%% to much on stdlib +%% to much on stdlib. Furthermore string:to_lower/1 +%% does not follow RFC 4343. %% tolower([]) -> []; -tolower([C|Cs]) when C >= $A, C =< $Z -> [(C-$A)+$a|tolower(Cs)]; -tolower([C|Cs]) -> [C|tolower(Cs)]. +tolower([C|Cs]) when is_integer(C) -> + if C >= $A, C =< $Z -> + [(C-$A)+$a|tolower(Cs)]; + true -> + [C|tolower(Cs)] + end. dn_ip6_int(A,B,C,D,E,F,G,H) -> dnib(H) ++ dnib(G) ++ dnib(F) ++ dnib(E) ++ diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl index df1d4fc0be..6e9719b4aa 100644 --- a/lib/kernel/src/inet_hosts.erl +++ b/lib/kernel/src/inet_hosts.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,9 +38,12 @@ gethostbyname(_) -> {error, formerr}. gethostbyname(Name, Type) when is_list(Name), is_atom(Type) -> - case gethostbyname(Name, Type, inet_hosts_byname, inet_hosts_byaddr) of + %% Byname has lowercased names while Byaddr keep the name casing. + %% This is to be able to reconstruct the original /etc/hosts entry. + N = inet_db:tolower(Name), + case gethostbyname(N, Type, inet_hosts_byname, inet_hosts_byaddr) of false -> - case gethostbyname(Name, Type, + case gethostbyname(N, Type, inet_hosts_file_byname, inet_hosts_file_byaddr) of false -> {error,nxdomain}; diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index a01c733d81..889b596a22 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -46,6 +46,7 @@ -define(INET_PASSIVE, 0). -define(INET_ACTIVE, 1). -define(INET_ONCE, 2). % Active once then passive +-define(INET_MULTI, 3). % Active N then passive %% state codes (getstatus, INET_REQ_GETSTATUS) -define(INET_F_OPEN, 16#0001). @@ -86,6 +87,8 @@ -define(INET_REQ_ACCEPT, 26). -define(INET_REQ_LISTEN, 27). -define(INET_REQ_IGNOREFD, 28). +-define(INET_REQ_GETLADDRS, 29). +-define(INET_REQ_GETPADDRS, 30). %% TCP requests %%-define(TCP_REQ_ACCEPT, 40). MOVED @@ -143,6 +146,7 @@ -define(INET_LOPT_TCP_SEND_TIMEOUT_CLOSE, 35). -define(INET_LOPT_MSGQ_HIWTRMRK, 36). -define(INET_LOPT_MSGQ_LOWTRMRK, 37). +-define(INET_LOPT_NETNS, 38). % Specific SCTP options: separate range: -define(SCTP_OPT_RTOINFO, 100). -define(SCTP_OPT_ASSOCINFO, 101). diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index d24bda0c3d..a88c94a453 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -722,7 +722,9 @@ ntoa({0,0,0,0,0,16#ffff,A,B}) -> "::FFFF:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); ntoa({_,_,_,_,_,_,_,_}=T) -> %% Find longest sequence of zeros, at least 2, to replace with "::" - ntoa(tuple_to_list(T), []). + ntoa(tuple_to_list(T), []); +ntoa(_) -> + {error, einval}. %% Find first double zero ntoa([], R) -> diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index ab9cbf0617..6037da1d22 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -181,8 +181,8 @@ lookup(Name, Class, Type, Opts, Timeout) -> lookup_filter({ok,#dns_rec{anlist=Answers}}, Class, Type) -> [A#dns_rr.data || A <- Answers, - A#dns_rr.class =:= Class, - A#dns_rr.type =:= Type]; + Class =:= any orelse A#dns_rr.class =:= Class, + Type =:= any orelse A#dns_rr.type =:= Type]; lookup_filter({error,_}, _, _) -> []. %% -------------------------------------------------------------------------- diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index ae2666d496..b946c2d1af 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -17,11 +17,11 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max two major revisions back - [{<<"2\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15 %% Down to - max two major revisions back - [{<<"2\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15 }. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index c92bb463c1..9ffa9adeab 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -26,7 +26,7 @@ %%% BIFs --export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0]). +-export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0, unsetenv/1]). -spec getenv() -> [string()]. @@ -58,6 +58,12 @@ putenv(_, _) -> timestamp() -> erlang:nif_error(undef). +-spec unsetenv(VarName) -> true when + VarName :: string(). + +unsetenv(_) -> + erlang:nif_error(undef). + %%% End of BIFs -spec type() -> {Osfamily, Osname} when diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 78fec2aa2b..7dc51495f7 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -407,7 +407,7 @@ cast(Node, Mod, Fun, Args) -> true. -%% Asynchronous broadcast, returns nothing, it's just send'n prey +%% Asynchronous broadcast, returns nothing, it's just send 'n' pray -spec abcast(Name, Msg) -> abcast when Name :: atom(), Msg :: term(). diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl index 6544426a54..a91c23539d 100644 --- a/lib/kernel/src/user_drv.erl +++ b/lib/kernel/src/user_drv.erl @@ -418,7 +418,7 @@ list_commands(Iport, Oport) -> true -> []; false -> - [{put_chars,unicode," q - quit erlang\n"}] + [{put_chars, unicode," q - quit erlang\n"}] end, io_requests([{put_chars, unicode," c [nn] - connect to job\n"}, {put_chars, unicode," i [nn] - interrupt job\n"}, diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index cb11d4e899..f1b8a105ed 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -145,7 +145,7 @@ release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) kernel.spec $(EMAKEFILE)\ + $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\ $(COVERFILE) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 1ff291be54..9ec8a15861 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -35,7 +35,7 @@ -export([config_change/1, distr_changed_tc1/1, distr_changed_tc2/1, - ensure_started/1, + ensure_started/1, ensure_all_started/1, shutdown_func/1, do_shutdown/1, shutdown_timeout/1]). -define(TESTCASE, testcase_name). @@ -52,7 +52,7 @@ all() -> [failover, failover_comp, permissions, load, 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, + permit_false_start_dist, get_key, get_env, ensure_all_started, {group, distr_changed}, config_change, shutdown_func, shutdown_timeout]. groups() -> @@ -978,6 +978,85 @@ ensure_started(Conf) -> ok = application:unload(app1), ok. +ensure_all_started(suite) -> []; +ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."]; +ensure_all_started(Conf) -> + + {ok, Fd1} = file:open("app1.app", [write]), + w_app1(Fd1), + file:close(Fd1), + {ok, Fd9} = file:open("app9.app", [write]), + w_app9(Fd9), + file:close(Fd9), + {ok, Fd10} = file:open("app10.app", [write]), + w_app10_dep9(Fd10), + file:close(Fd10), + {ok, FdErr} = file:open("app_chain_error.app", [write]), + w_app(FdErr, app_chain_error()), + file:close(FdErr), + {ok, FdErr2} = file:open("app_chain_error2.app", [write]), + w_app(FdErr2, app_chain_error2()), + file:close(FdErr2), + + %% Single app start/stop + false = lists:keyfind(app1, 1, application:which_applications()), + {ok, [app1]} = application:ensure_all_started(app1), % app1 started + {app1, _, _} = lists:keyfind(app1, 1, application:which_applications()), + {ok, []} = application:ensure_all_started(app1), % no start needed + ok = application:stop(app1), + false = lists:keyfind(app1, 1, application:which_applications()), + ok = application:unload(app1), + + %% App or dependency not found. + Name = hopefully_not_an_existing_app_file, + {error,{Name, {"no such file or directory", _ }}} = + application:ensure_all_started(Name), + + %% Start dependencies. + {error, {not_started, app9}} = application:start(app10), + {ok, [app9,app10]} = application:ensure_all_started(app10, temporary), + {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()), + {app10, _, _} = lists:keyfind(app10, 1, application:which_applications()), + %% Only report apps/dependencies that actually needed to start + ok = application:stop(app10), + ok = application:unload(app10), + {ok, [app10]} = application:ensure_all_started(app10, temporary), + ok = application:stop(app9), + ok = application:unload(app9), + ok = application:stop(app10), + ok = application:unload(app10), + + %% Deeper failure chain. We have the following dependencies: + %% app_chain_error -> app_chain_error2 + %% app_chain_error2 -> app10 + %% app_chain_error2 -> hopefully_not_an_existing_app + %% app10 -> app 9 + %% First we have none running and we expect to have neither app9 + %% nor app10 running after failing to start + %% hopefully_not_an_existing_app + {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}= + application:ensure_all_started(app_chain_error), + false = lists:keyfind(app9, 1, application:which_applications()), + false = lists:keyfind(app10, 1, application:which_applications()), + false = lists:keyfind(app_chain_error2,1,application:which_applications()), + false = lists:keyfind(app_chain_error, 1, application:which_applications()), + %% Here we will have app9 already running, and app10 should be + %% able to boot fine. + %% In this dependency failing, we expect app9 to still be running, but + %% not app10 after failing to start hopefully_not_an_existing_app + {ok, [app9]} = application:ensure_all_started(app9, temporary), + {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}= + application:ensure_all_started(app_chain_error), + {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()), + false = lists:keyfind(app10, 1, application:which_applications()), + false = lists:keyfind(app_chain_error2,1,application:which_applications()), + false = lists:keyfind(app_chain_error, 1, application:which_applications()), + ok = application:stop(app9), + ok = application:unload(app9), + ok = application:unload(app10), + ok = application:unload(app_chain_error2), + ok = application:unload(app_chain_error), + ok. %%%----------------------------------------------------------------- %%% Testing of reported bugs and other tickets. @@ -2125,6 +2204,24 @@ app_start_error() -> {applications, [kernel]}, {mod, {app_start_error, []}}]}. +app_chain_error() -> + {application, app_chain_error, + [{description, "ERTS CXC 138 ce"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel, app_chain_error2]}, + {mod, {ch_sup, {app_chain_error, 20,20}}}]}. + +app_chain_error2() -> + {application, app_chain_error2, + [{description, "ERTS CXC 138 ce2"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel, app10, hopefully_not_an_existing_app]}, + {mod, {ch_sup, {app_chain_error2, 21,21}}}]}. + app_group_leader() -> {application, group_leader, [{description, "GROUP_LEADER CXC 138 11"}, @@ -2374,6 +2471,12 @@ w_app7(Fd) -> w_app8(Fd) -> io:format(Fd, "~p.\n", [app8()]). +w_app9(Fd) -> + io:format(Fd, "~p.\n", [app9()]). + +w_app10_dep9(Fd) -> + io:format(Fd, "~p.\n", [app10_dep9()]). + w_app_start_error(Fd) -> io:format(Fd, "~p.\n", [app_start_error()]). diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index fc17db2745..42b81d16b3 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -23,7 +23,8 @@ -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). -export([set_path/1, get_path/1, add_path/1, add_paths/1, del_path/1, replace_path/1, load_file/1, load_abs/1, ensure_loaded/1, - delete/1, purge/1, soft_purge/1, is_loaded/1, all_loaded/1, + delete/1, purge/1, purge_many_exits/1, soft_purge/1, is_loaded/1, + all_loaded/1, load_binary/1, dir_req/1, object_code/1, set_path_file/1, upgrade/1, sticky_dir/1, pa_pz_option/1, add_del_path/1, @@ -35,7 +36,7 @@ on_load_embedded/1, on_load_errors/1, big_boot_embedded/1, native_early_modules/1, get_mode/1]). --export([init_per_testcase/2, end_per_testcase/2, +-export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1, sticky_compiler/1]). @@ -48,10 +49,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> [set_path, get_path, add_path, add_paths, del_path, replace_path, load_file, load_abs, ensure_loaded, - delete, purge, soft_purge, is_loaded, all_loaded, + delete, purge, purge_many_exits, soft_purge, is_loaded, all_loaded, load_binary, dir_req, object_code, set_path_file, upgrade, pa_pz_option, add_del_path, dir_disappeared, @@ -62,7 +63,7 @@ all() -> on_load_binary, on_load_embedded, on_load_errors, big_boot_embedded, native_early_modules, get_mode]. -groups() -> +groups() -> []. init_per_group(_GroupName, Config) -> @@ -76,10 +77,10 @@ init_per_suite(Config) -> %% the module name does not match the filename, so %% we must compile to a binary and write the Beam file %% ourselves. - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line File = filename:join(Dir, "code_a_test"), - ?line {ok,code_b_test,Code} = compile:file(File, [binary]), - ?line ok = file:write_file(File++".beam", Code), + Dir = filename:dirname(code:which(?MODULE)), + File = filename:join(Dir, "code_a_test"), + {ok,code_b_test,Code} = compile:file(File, [binary]), + ok = file:write_file(File++".beam", Code), Config. end_per_suite(Config) -> @@ -98,7 +99,7 @@ init_per_testcase(_Func, Config) -> P=code:get_path(), [{watchdog, Dog}, {code_path, P}|Config]. -end_per_testcase(TC, Config) when TC == mult_lib_roots; +end_per_testcase(TC, Config) when TC == mult_lib_roots; TC == big_boot_embedded -> {ok, HostName} = inet:gethostname(), NodeName = list_to_atom(atom_to_list(TC)++"@"++HostName), @@ -121,51 +122,49 @@ set_path(doc) -> []; set_path(Config) when is_list(Config) -> P = code:get_path(), NonExDir = filename:join(?config(priv_dir, Config), ?t:temp_name("hej")), - ?line {'EXIT',_} = (catch code:set_path({a})), - ?line {error, bad_directory} = (catch code:set_path([{a}])), - ?line {error, bad_directory} = code:set_path(NonExDir), - ?line P = code:get_path(), % still the same path. - ?line true = code:set_path(P), % set the same path again. - ?line P = code:get_path(), % still the same path. + {'EXIT',_} = (catch code:set_path({a})), + {error, bad_directory} = (catch code:set_path([{a}])), + {error, bad_directory} = code:set_path(NonExDir), + P = code:get_path(), % still the same path. + true = code:set_path(P), % set the same path again. + P = code:get_path(), % still the same path. LibDir = code:lib_dir(), - ?line true = code:set_path([LibDir | P]), - ?line [LibDir | P] = code:get_path(), - ?line true = code:set_path([LibDir]), - ?line [LibDir] = code:get_path(), + true = code:set_path([LibDir | P]), + [LibDir | P] = code:get_path(), + true = code:set_path([LibDir]), + [LibDir] = code:get_path(), ok. get_path(suite) -> []; get_path(doc) -> []; get_path(Config) when is_list(Config) -> - ?line P = code:get_path(), + P = code:get_path(), % test that all directories are strings (lists). - ?line [] = lists:filter(fun(Dir) when is_list(Dir) -> - false; - (_) -> - true - end, - P), + [] = lists:filter(fun + (Dir) when is_list(Dir) -> false; + (_) -> true + end, P), ok. add_path(suite) -> []; add_path(doc) -> []; add_path(Config) when is_list(Config) -> P = code:get_path(), - ?line {'EXIT',_} = (catch code:add_path({})), - ?line {'EXIT',_} = (catch code:add_patha({})), - ?line {'EXIT',_} = (catch code:add_pathz({})), - ?line {error, bad_directory} = code:add_path("xyz"), - ?line {error, bad_directory} = code:add_patha("xyz"), - ?line {error, bad_directory} = code:add_pathz("xyz"), + {'EXIT',_} = (catch code:add_path({})), + {'EXIT',_} = (catch code:add_patha({})), + {'EXIT',_} = (catch code:add_pathz({})), + {error, bad_directory} = code:add_path("xyz"), + {error, bad_directory} = code:add_patha("xyz"), + {error, bad_directory} = code:add_pathz("xyz"), LibDir = code:lib_dir(), - ?line true = code:add_path(LibDir), - ?line LibDir = lists:last(code:get_path()), + true = code:add_path(LibDir), + LibDir = lists:last(code:get_path()), code:set_path(P), - ?line true = code:add_pathz(LibDir), - ?line LibDir = lists:last(code:get_path()), + true = code:add_pathz(LibDir), + LibDir = lists:last(code:get_path()), code:set_path(P), - ?line true = code:add_patha(LibDir), - ?line [LibDir|_] = code:get_path(), + true = code:add_patha(LibDir), + [LibDir|_] = code:get_path(), code:set_path(P), ok. @@ -173,134 +172,134 @@ add_paths(suite) -> []; add_paths(doc) -> []; add_paths(Config) when is_list(Config) -> P = code:get_path(), - ?line ok = code:add_paths([{}]), - ?line ok = code:add_pathsa([{}]), - ?line ok = code:add_pathsz([{}]), - ?line ok = code:add_paths(["xyz"]), - ?line ok = code:add_pathsa(["xyz"]), - ?line ok = code:add_pathsz(["xyz"]), + ok = code:add_paths([{}]), + ok = code:add_pathsa([{}]), + ok = code:add_pathsz([{}]), + ok = code:add_paths(["xyz"]), + ok = code:add_pathsa(["xyz"]), + ok = code:add_pathsz(["xyz"]), P = code:get_path(), % check that no directory is added. LibDir = code:lib_dir(), - ?line ok = code:add_paths([LibDir]), - ?line LibDir = lists:last(code:get_path()), + ok = code:add_paths([LibDir]), + LibDir = lists:last(code:get_path()), code:set_path(P), - ?line ok = code:add_pathsz([LibDir]), - ?line LibDir = lists:last(code:get_path()), + ok = code:add_pathsz([LibDir]), + LibDir = lists:last(code:get_path()), code:set_path(P), - ?line ok = code:add_pathsa([LibDir]), - ?line [LibDir|P] = code:get_path(), + ok = code:add_pathsa([LibDir]), + [LibDir|P] = code:get_path(), code:set_path(P), RootDir = code:root_dir(), Res = P ++ [LibDir, RootDir], - ?line ok = code:add_paths([LibDir, RootDir]), - ?line Res = code:get_path(), + ok = code:add_paths([LibDir, RootDir]), + Res = code:get_path(), code:set_path(P), - ?line ok = code:add_pathsz([LibDir, RootDir]), - ?line Res = code:get_path(), + ok = code:add_pathsz([LibDir, RootDir]), + Res = code:get_path(), code:set_path(P), - ?line ok = code:add_pathsa([LibDir, RootDir]), - ?line [RootDir, LibDir|P] = code:get_path(), + ok = code:add_pathsa([LibDir, RootDir]), + [RootDir, LibDir|P] = code:get_path(), code:set_path(P), - ?line ok = code:add_paths([LibDir, "xyz"]), + ok = code:add_paths([LibDir, "xyz"]), Res1 = P ++ [LibDir], - ?line Res1 = code:get_path(), + Res1 = code:get_path(), code:set_path(P), - ?line ok = code:add_pathsz([LibDir, "xyz"]), - ?line Res1 = code:get_path(), + ok = code:add_pathsz([LibDir, "xyz"]), + Res1 = code:get_path(), code:set_path(P), - ?line ok = code:add_pathsa([LibDir, "xyz"]), - ?line [LibDir|P] = code:get_path(), + ok = code:add_pathsa([LibDir, "xyz"]), + [LibDir|P] = code:get_path(), code:set_path(P), ok. del_path(suite) -> []; del_path(doc) -> []; del_path(Config) when is_list(Config) -> - ?line P = code:get_path(), + P = code:get_path(), test_server:format("Initial code:get_path()=~p~n",[P]), - ?line {'EXIT',_} = (catch code:del_path(3)), - ?line false = code:del_path(my_dummy_name), - ?line false = code:del_path("/kdlk/my_dummy_dir"), + {'EXIT',_} = (catch code:del_path(3)), + false = code:del_path(my_dummy_name), + false = code:del_path("/kdlk/my_dummy_dir"), Dir = filename:join([code:lib_dir(kernel),"ebin"]), test_server:format("kernel dir: ~p~n",[Dir]), - ?line true = code:del_path(kernel), + true = code:del_path(kernel), NewP = code:get_path(), test_server:format("Path after removing 'kernel':~p~n",[NewP]), ReferenceP = lists:delete(Dir,P), test_server:format("Reference path:~p~n",[ReferenceP]), - ?line NewP = ReferenceP, % check that dir is deleted + NewP = ReferenceP, % check that dir is deleted code:set_path(P), - ?line true = code:del_path(Dir), + true = code:del_path(Dir), NewP1 = code:get_path(), - ?line NewP1 = lists:delete(Dir,P), % check that dir is deleted + NewP1 = lists:delete(Dir,P), % check that dir is deleted code:set_path(P), ok. replace_path(suite) -> []; replace_path(doc) -> []; replace_path(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line P = code:get_path(), - ?line {'EXIT',_} = (catch code:replace_path(3,"")), - ?line {error, bad_name} = code:replace_path(dummy_name,""), - ?line {error, bad_name} = code:replace_path(kernel, + PrivDir = ?config(priv_dir, Config), + P = code:get_path(), + {'EXIT',_} = (catch code:replace_path(3,"")), + {error, bad_name} = code:replace_path(dummy_name,""), + {error, bad_name} = code:replace_path(kernel, "/kdlk/my_dummy_dir"), - ?line {error, bad_directory} = code:replace_path(kernel, + {error, bad_directory} = code:replace_path(kernel, "/kdlk/kernel-1.2"), - ?line P = code:get_path(), % Check that path is not changed. + P = code:get_path(), % Check that path is not changed. - ?line ok = file:set_cwd(PrivDir), + ok = file:set_cwd(PrivDir), %% Replace an existing application. file:make_dir("./kernel-2.11"), {ok, Cwd} = file:get_cwd(), NewDir = Cwd ++ "/kernel-2.11", - ?line true = code:replace_path(kernel, NewDir), - ?line NewDir = code:lib_dir(kernel), - ?line true = code:set_path(P), %Reset path - ?line ok = file:del_dir("./kernel-2.11"), + true = code:replace_path(kernel, NewDir), + NewDir = code:lib_dir(kernel), + true = code:set_path(P), %Reset path + ok = file:del_dir("./kernel-2.11"), %% Add a completly new application. NewAppName = 'blurf_blarfer', - ?line NewAppDir = filename:join(Cwd, atom_to_list(NewAppName) ++ "-6.33.1"), - ?line ok = file:make_dir(NewAppDir), - ?line true = code:replace_path(NewAppName, NewAppDir), - ?line NewAppDir = code:lib_dir(NewAppName), - ?line NewAppDir = lists:last(code:get_path()), - ?line true = code:set_path(P), %Reset path - ?line ok = file:del_dir(NewAppDir), + NewAppDir = filename:join(Cwd, atom_to_list(NewAppName) ++ "-6.33.1"), + ok = file:make_dir(NewAppDir), + true = code:replace_path(NewAppName, NewAppDir), + NewAppDir = code:lib_dir(NewAppName), + NewAppDir = lists:last(code:get_path()), + true = code:set_path(P), %Reset path + ok = file:del_dir(NewAppDir), ok. dir_disappeared(suite) -> []; dir_disappeared(doc) -> ["OTP-3977"]; dir_disappeared(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Dir = filename:join(PrivDir, "temp"), - ?line ok = file:make_dir(Dir), - ?line true = code:add_path(Dir), - ?line ok = file:del_dir(Dir), - ?line non_existing = code:which(bubbelskrammel), + PrivDir = ?config(priv_dir, Config), + Dir = filename:join(PrivDir, "temp"), + ok = file:make_dir(Dir), + true = code:add_path(Dir), + ok = file:del_dir(Dir), + non_existing = code:which(bubbelskrammel), ok. load_file(suite) -> []; load_file(doc) -> []; load_file(Config) when is_list(Config) -> - ?line {error, nofile} = code:load_file(duuuumy_mod), - ?line {error, badfile} = code:load_file(code_a_test), - ?line {'EXIT', _} = (catch code:load_file(123)), - ?line {module, code_b_test} = code:load_file(code_b_test), + {error, nofile} = code:load_file(duuuumy_mod), + {error, badfile} = code:load_file(code_a_test), + {'EXIT', _} = (catch code:load_file(123)), + {module, code_b_test} = code:load_file(code_b_test), TestDir = test_dir(), code:stick_dir(TestDir), - ?line {error, sticky_directory} = code:load_file(code_b_test), + {error, sticky_directory} = code:load_file(code_b_test), code:unstick_dir(TestDir), ok. @@ -311,30 +310,30 @@ load_abs(suite) -> []; load_abs(doc) -> []; load_abs(Config) when is_list(Config) -> TestDir = test_dir(), - ?line {error, nofile} = code:load_abs(TestDir ++ "/duuuumy_mod"), - ?line {error, badfile} = code:load_abs(TestDir ++ "/code_a_test"), - ?line {'EXIT', _} = (catch code:load_abs({})), - ?line {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), + {error, nofile} = code:load_abs(TestDir ++ "/duuuumy_mod"), + {error, badfile} = code:load_abs(TestDir ++ "/code_a_test"), + {'EXIT', _} = (catch code:load_abs({})), + {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), code:stick_dir(TestDir), - ?line {error, sticky_directory} = code:load_abs(TestDir ++ "/code_b_test"), + {error, sticky_directory} = code:load_abs(TestDir ++ "/code_b_test"), code:unstick_dir(TestDir), ok. ensure_loaded(suite) -> []; ensure_loaded(doc) -> []; ensure_loaded(Config) when is_list(Config) -> - ?line {module, lists} = code:ensure_loaded(lists), + {module, lists} = code:ensure_loaded(lists), case init:get_argument(mode) of {ok, [["embedded"]]} -> - ?line {error, embedded} = code:ensure_loaded(code_b_test), - ?line {error, badarg} = code:ensure_loaded(34), + {error, embedded} = code:ensure_loaded(code_b_test), + {error, badarg} = code:ensure_loaded(34), ok; _ -> - ?line {error, nofile} = code:ensure_loaded(duuuumy_mod), - ?line {error, badfile} = code:ensure_loaded(code_a_test), - ?line {'EXIT', _} = (catch code:ensure_loaded(34)), - ?line {module, code_b_test} = code:ensure_loaded(code_b_test), - ?line {module, code_b_test} = code:ensure_loaded(code_b_test), + {error, nofile} = code:ensure_loaded(duuuumy_mod), + {error, badfile} = code:ensure_loaded(code_a_test), + {'EXIT', _} = (catch code:ensure_loaded(34)), + {module, code_b_test} = code:ensure_loaded(code_b_test), + {module, code_b_test} = code:ensure_loaded(code_b_test), ok end. @@ -343,15 +342,15 @@ delete(doc) -> []; delete(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), code:purge(code_b_test), - ?line Pid = code_b_test:do_spawn(), - ?line true = code:delete(code_b_test), - ?line {'EXIT',_} = (catch code:delete(122)), - ?line false = code_b_test:check_exit(Pid), - ?line false = code:delete(code_b_test), - ?line false = code_b_test:check_exit(Pid), + Pid = code_b_test:do_spawn(), + true = code:delete(code_b_test), + {'EXIT',_} = (catch code:delete(122)), + false = code_b_test:check_exit(Pid), + false = code:delete(code_b_test), + false = code_b_test:check_exit(Pid), exit(Pid,kill), - ?line true = code_b_test:check_exit(Pid), - ?line false = code:delete(code_b_test), + true = code_b_test:check_exit(Pid), + false = code:delete(code_b_test), code:purge(code_b_test), process_flag(trap_exit, OldFlag), ok. @@ -361,31 +360,67 @@ purge(doc) -> []; purge(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), code:purge(code_b_test), - ?line {'EXIT',_} = (catch code:purge({})), - ?line false = code:purge(code_b_test), - ?line Pid = code_b_test:do_spawn(), - ?line true = code:delete(code_b_test), - ?line false = code_b_test:check_exit(Pid), - ?line true = code:purge(code_b_test), - ?line true = code_b_test:check_exit(Pid), + {'EXIT',_} = (catch code:purge({})), + false = code:purge(code_b_test), + Pid = code_b_test:do_spawn(), + true = code:delete(code_b_test), + false = code_b_test:check_exit(Pid), + true = code:purge(code_b_test), + true = code_b_test:check_exit(Pid), process_flag(trap_exit, OldFlag), ok. +purge_many_exits(Config) when is_list(Config) -> + OldFlag = process_flag(trap_exit, true), + code:purge(code_b_test), + {'EXIT',_} = (catch code:purge({})), + false = code:purge(code_b_test), + TPids = lists:map(fun (_) -> + {code_b_test:do_spawn(), + spawn_link(fun () -> + receive + after infinity -> ok + end + end)} + end, + lists:seq(1, 1000)), + % Give them time to start... + receive after 1000 -> ok end, + true = code:delete(code_b_test), + lists:foreach(fun ({Pid1, Pid2}) -> + true = erlang:is_process_alive(Pid1), + false = code_b_test:check_exit(Pid1), + true = erlang:is_process_alive(Pid2) + end, TPids), + true = code:purge(code_b_test), + lists:foreach(fun ({Pid1, Pid2}) -> + false = erlang:is_process_alive(Pid1), + true = code_b_test:check_exit(Pid1), + true = erlang:is_process_alive(Pid2), + exit(Pid2, kill) + end, TPids), + lists:foreach(fun ({_Pid1, Pid2}) -> + receive {'EXIT', Pid2, _} -> ok end + end, TPids), + process_flag(trap_exit, OldFlag), + ok. + + soft_purge(suite) -> []; soft_purge(doc) -> []; soft_purge(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), code:purge(code_b_test), - ?line {'EXIT',_} = (catch code:soft_purge(23)), - ?line true = code:soft_purge(code_b_test), - ?line Pid = code_b_test:do_spawn(), - ?line true = code:delete(code_b_test), - ?line false = code_b_test:check_exit(Pid), - ?line false = code:soft_purge(code_b_test), - ?line false = code_b_test:check_exit(Pid), + {'EXIT',_} = (catch code:soft_purge(23)), + true = code:soft_purge(code_b_test), + Pid = code_b_test:do_spawn(), + true = code:delete(code_b_test), + false = code_b_test:check_exit(Pid), + false = code:soft_purge(code_b_test), + false = code_b_test:check_exit(Pid), exit(Pid,kill), - ?line true = code_b_test:check_exit(Pid), - ?line true = code:soft_purge(code_b_test), + true = code_b_test:check_exit(Pid), + true = code:soft_purge(code_b_test), process_flag(trap_exit, OldFlag), ok. @@ -394,12 +429,12 @@ is_loaded(doc) -> []; is_loaded(Config) when is_list(Config) -> code:purge(code_b_test), code:delete(code_b_test), - ?line false = code:is_loaded(duuuuuumy_mod), - ?line {'EXIT',_} = (catch code:is_loaded(23)), - ?line {file, preloaded} = code:is_loaded(init), + false = code:is_loaded(duuuuuumy_mod), + {'EXIT',_} = (catch code:is_loaded(23)), + {file, preloaded} = code:is_loaded(init), TestDir = test_dir(), - ?line {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), - ?line {file, _Loaded} = code:is_loaded(code_b_test), + {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), + {file, _Loaded} = code:is_loaded(code_b_test), code:purge(code_b_test), code:delete(code_b_test), ok. @@ -413,21 +448,19 @@ all_loaded(Config) when is_list(Config) -> end. all_loaded_1() -> - ?line Preloaded = [{M,preloaded} || M <- lists:sort(erlang:pre_loaded())], + Preloaded = [{M,preloaded} || M <- lists:sort(erlang:pre_loaded())], - ?line Loaded0 = lists:sort(code:all_loaded()), - ?line all_unique(Loaded0), - ?line Loaded1 = lists:keysort(2, Loaded0), - ?line Loaded2 = match_and_remove(Preloaded, Loaded1), + Loaded0 = lists:sort(code:all_loaded()), + all_unique(Loaded0), + Loaded1 = lists:keysort(2, Loaded0), + Loaded2 = match_and_remove(Preloaded, Loaded1), ObjExt = code:objfile_extension(), - ?line [] = lists:filter(fun({Mod,AbsName}) when is_atom(Mod), - is_list(AbsName) -> - Mod =/= list_to_atom(filename:basename(AbsName, - ObjExt)); - (_) -> true - end, - Loaded2), + [] = lists:filter(fun + ({Mod,AbsName}) when is_atom(Mod), is_list(AbsName) -> + Mod =/= list_to_atom(filename:basename(AbsName, ObjExt)); + (_) -> true + end, Loaded2), ok. match_and_remove([], List) -> List; @@ -442,19 +475,19 @@ load_binary(doc) -> []; load_binary(Config) when is_list(Config) -> TestDir = test_dir(), File = TestDir ++ "/code_b_test" ++ code:objfile_extension(), - ?line {ok,Bin} = file:read_file(File), - ?line {'EXIT',_} = (catch code:load_binary(12, File, Bin)), - ?line {'EXIT',_} = (catch code:load_binary(code_b_test, 12, Bin)), - ?line {'EXIT',_} = (catch code:load_binary(code_b_test, File, 12)), - ?line {module, code_b_test} = code:load_binary(code_b_test, File, Bin), + {ok,Bin} = file:read_file(File), + {'EXIT',_} = (catch code:load_binary(12, File, Bin)), + {'EXIT',_} = (catch code:load_binary(code_b_test, 12, Bin)), + {'EXIT',_} = (catch code:load_binary(code_b_test, File, 12)), + {module, code_b_test} = code:load_binary(code_b_test, File, Bin), code:stick_dir(TestDir), - ?line {error, sticky_directory} = code:load_binary(code_b_test, File, Bin), + {error, sticky_directory} = code:load_binary(code_b_test, File, Bin), code:unstick_dir(TestDir), code:purge(code_b_test), code:delete(code_b_test), ok. -upgrade(Config) -> +upgrade(Config) -> DataDir = ?config(data_dir, Config), %%T = [beam, hipe], @@ -462,28 +495,28 @@ upgrade(Config) -> [upgrade_do(DataDir, Client, U1, U2, O1, O2) || Client<-T, U1<-T, U2<-T, O1<-T, O2<-T], - + ok. upgrade_do(DataDir, Client, U1, U2, O1, O2) -> - compile_load(upgrade_client, DataDir, undefined, Client), + compile_load(upgrade_client, DataDir, undefined, Client), upgrade_client:run(DataDir, U1, U2, O1, O2), ok. compile_load(Mod, Dir, Ver, CodeType) -> Version = case Ver of - undefined -> - io:format("Compiling '~p' as ~p\n", [Mod, CodeType]), - []; - _ -> - io:format("Compiling version ~p of '~p' as ~p\n", - [Ver, Mod, CodeType]), - [{d,list_to_atom("VERSION_" ++ integer_to_list(Ver))}] - end, + undefined -> + io:format("Compiling '~p' as ~p\n", [Mod, CodeType]), + []; + _ -> + io:format("Compiling version ~p of '~p' as ~p\n", + [Ver, Mod, CodeType]), + [{d,list_to_atom("VERSION_" ++ integer_to_list(Ver))}] + end, Target = case CodeType of - beam -> []; - hipe -> [native] - end, + beam -> []; + hipe -> [native] + end, CompOpts = [binary, report] ++ Target ++ Version, Src = filename:join(Dir, atom_to_list(Mod) ++ ".erl"), @@ -497,17 +530,17 @@ compile_load(Mod, Dir, Ver, CodeType) -> dir_req(suite) -> []; dir_req(doc) -> []; dir_req(Config) when is_list(Config) -> - ?line {ok,[[Root0]]} = init:get_argument(root), - ?line Root = filename:join([Root0]), % Normalised form. - ?line Root = code:root_dir(), + {ok,[[Root0]]} = init:get_argument(root), + Root = filename:join([Root0]), % Normalised form. + Root = code:root_dir(), LibDir = Root ++ "/lib", - ?line LibDir = code:lib_dir(), - ?line code:compiler_dir(), - ?line {error, bad_name} = code:lib_dir(duuumy), - ?line KernLib = code:lib_dir(kernel), - ?line Priv = KernLib ++ "/priv", - ?line Priv = code:priv_dir(kernel), - ?line {error, bad_name} = code:priv_dir(duuumy), + LibDir = code:lib_dir(), + code:compiler_dir(), + {error, bad_name} = code:lib_dir(duuumy), + KernLib = code:lib_dir(kernel), + Priv = KernLib ++ "/priv", + Priv = code:priv_dir(kernel), + {error, bad_name} = code:priv_dir(duuumy), ok. object_code(suite) -> []; @@ -517,19 +550,19 @@ object_code(Config) when is_list(Config) -> P = code:get_path(), P = code:get_path(), code:add_path(TestDir), - ?line {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), + {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), LoadedFile = filename:absname(TestDir ++ "/code_b_test" ++ code:objfile_extension()), - ?line case code:get_object_code(code_b_test) of + case code:get_object_code(code_b_test) of {code_b_test,Bin,LoadedFile} when is_binary(Bin) -> ok end, code:purge(code_b_test), code:delete(code_b_test), - ?line error = code:get_object_code(dddddddduuuuuuumy), - ?line {'EXIT',_} = (catch code:get_object_code(23)), - ?line code:set_path(P), - ?line P=code:get_path(), + error = code:get_object_code(dddddddduuuuuuumy), + {'EXIT',_} = (catch code:get_object_code(23)), + code:set_path(P), + P=code:get_path(), ok. set_path_file(suite) -> []; @@ -537,17 +570,17 @@ set_path_file(doc) -> ["Test that set_path does not accept ", "files as pathnames (known previous bug)"]; set_path_file(Config) when is_list(Config) -> File=filename:join(?config(priv_dir, Config), "testfil"), - ?line ok=file:write_file(File, list_to_binary("lite data")), - ?line {error, bad_directory}=code:set_path([File]). + ok=file:write_file(File, list_to_binary("lite data")), + {error, bad_directory}=code:set_path([File]). sticky_dir(suite) -> []; sticky_dir(doc) -> ["Test that a module with the same name as a module in ", "a sticky directory cannot be loaded."]; sticky_dir(Config) when is_list(Config) -> MyDir=filename:dirname(code:which(?MODULE)), - ?line {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa \""++MyDir++"\""}]), + {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa \""++MyDir++"\""}]), File=filename:join([?config(data_dir, Config), "calendar"]), - ?line Ret=rpc:call(Node, ?MODULE, sticky_compiler, [File]), + Ret=rpc:call(Node, ?MODULE, sticky_compiler, [File]), case Ret of fail -> ?t:fail("c:c allowed a sticky module to be compiled and loaded."); @@ -607,70 +640,70 @@ add_del_path(Config) when is_list(Config) -> Dir1 = filename:join(DDir,"dummy_app-1.0/ebin"), Dir2 = filename:join(DDir,"dummy_app-2.0/ebin"), code:add_patha(Dir1), - ?line PrivDir1 = filename:join(DDir,"dummy_app-1.0/priv"), - ?line PrivDir1 = code:priv_dir(dummy_app), - ?line code:add_path(Dir2), % put last in path - ?line PrivDir1 = code:priv_dir(dummy_app), - ?line code:del_path(Dir2), - ?line PrivDir1 = code:priv_dir(dummy_app), + PrivDir1 = filename:join(DDir,"dummy_app-1.0/priv"), + PrivDir1 = code:priv_dir(dummy_app), + code:add_path(Dir2), % put last in path + PrivDir1 = code:priv_dir(dummy_app), + code:del_path(Dir2), + PrivDir1 = code:priv_dir(dummy_app), ok. clash(Config) when is_list(Config) -> DDir = ?config(data_dir,Config)++"clash/", P = code:get_path(), - [TestServerPath|_] = [Path || Path <- code:get_path(), - re:run(Path,"test_server/?$",[]) /= nomatch], + [TestServerPath|_] = [Path || Path <- code:get_path(), + re:run(Path,"test_server/?$",[unicode]) /= nomatch], %% test non-clashing entries %% remove TestServerPath to prevent clash with test-server path - ?line true = code:del_path(TestServerPath), - ?line true = code:add_path(DDir++"foobar-0.1/ebin"), - ?line true = code:add_path(DDir++"zork-0.8/ebin"), + true = code:del_path(TestServerPath), + true = code:add_path(DDir++"foobar-0.1/ebin"), + true = code:add_path(DDir++"zork-0.8/ebin"), test_server:capture_start(), - ?line ok = code:clash(), + ok = code:clash(), test_server:capture_stop(), - ?line [OKMsg|_] = test_server:capture_get(), - ?line true = lists:prefix("** Found 0 name clashes", OKMsg), - ?line true = code:set_path(P), + [OKMsg|_] = test_server:capture_get(), + true = lists:prefix("** Found 0 name clashes", OKMsg), + true = code:set_path(P), %% test clashing entries %% remove TestServerPath to prevent clash with test-server path - ?line true = code:del_path(TestServerPath), - ?line true = code:add_path(DDir++"foobar-0.1/ebin"), - ?line true = code:add_path(DDir++"foobar-0.1.ez/foobar-0.1/ebin"), + true = code:del_path(TestServerPath), + true = code:add_path(DDir++"foobar-0.1/ebin"), + true = code:add_path(DDir++"foobar-0.1.ez/foobar-0.1/ebin"), test_server:capture_start(), - ?line ok = code:clash(), + ok = code:clash(), test_server:capture_stop(), - ?line [ClashMsg|_] = test_server:capture_get(), - ?line {match, [" hides "]} = re:run(ClashMsg, "\\*\\* .*( hides ).*", + [ClashMsg|_] = test_server:capture_get(), + {match, [" hides "]} = re:run(ClashMsg, "\\*\\* .*( hides ).*", [{capture,all_but_first,list}]), - ?line true = code:set_path(P), + true = code:set_path(P), %% test "Bad path can't read" %% remove TestServerPath to prevent clash with test-server path Priv = ?config(priv_dir, Config), - ?line true = code:del_path(TestServerPath), + true = code:del_path(TestServerPath), TmpEzFile = Priv++"foobar-0.tmp.ez", - ?line {ok, _} = file:copy(DDir++"foobar-0.1.ez", TmpEzFile), - ?line true = code:add_path(TmpEzFile++"/foobar-0.1/ebin"), + {ok, _} = file:copy(DDir++"foobar-0.1.ez", TmpEzFile), + true = code:add_path(TmpEzFile++"/foobar-0.1/ebin"), case os:type() of {win32,_} -> - %% The file wont be deleted on windows until it's closed, why we + %% The file wont be deleted on windows until it's closed, why we %% need to rename instead. - ?line ok = file:rename(TmpEzFile,TmpEzFile++".moved"); + ok = file:rename(TmpEzFile,TmpEzFile++".moved"); _ -> - ?line ok = file:delete(TmpEzFile) + ok = file:delete(TmpEzFile) end, test_server:capture_start(), - ?line ok = code:clash(), + ok = code:clash(), test_server:capture_stop(), - ?line [BadPathMsg|_] = test_server:capture_get(), - ?line true = lists:prefix("** Bad path can't read", BadPathMsg), - ?line true = code:set_path(P), + [BadPathMsg|_] = test_server:capture_get(), + true = lists:prefix("** Bad path can't read", BadPathMsg), + true = code:set_path(P), file:delete(TmpEzFile++".moved"), %% Only effect on windows ok. @@ -687,7 +720,7 @@ ext_mod_dep(Config) when is_list(Config) -> xref:add_directory(s, filename:join(code:lib_dir(kernel),"ebin")), xref:add_directory(s, filename:join(code:lib_dir(stdlib),"ebin")), case catch ext_mod_dep2() of - {'EXIT', Reason} -> + {'EXIT', Reason} -> xref:stop(s), exit(Reason); Else -> @@ -699,7 +732,7 @@ ext_mod_dep(Config) when is_list(Config) -> end. ext_mod_dep2() -> - Exports0 = code_server:module_info(exports) -- + Exports0 = code_server:module_info(exports) -- [{module_info,0},{module_info,1}], Exports = [{code_server,M,A} || {M,A} <- Exports0], case analyse(Exports, [], [], 0) of @@ -709,17 +742,17 @@ ext_mod_dep2() -> {not_verified,ErrCnt} end. -analyse([], [], Visited, ErrCnt) -> +analyse([], [], Visited, ErrCnt) -> {Visited,ErrCnt}; analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) -> %% The code_server has been granted to use the following modules, - %% These modules should be loaded by code.erl before + %% These modules should be loaded by code.erl before %% the code_server is started. OK = [erlang, os, prim_file, erl_prim_loader, init, ets, code_server, lists, lists_sort, unicode, binary, filename, gb_sets, gb_trees, hipe_unified_loader, hipe_bifs, prim_zip, zlib], - ErrCnt1 = + ErrCnt1 = case lists:member(M, OK) or erlang:is_builtin(M,F,A) of true -> 0; @@ -729,7 +762,7 @@ analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) -> {Visited, ErrCnt1+ErrCnt0}; analyse([MFA|R], Path, Visited0, ErrCnt0) -> case lists:member(MFA,Visited0) of - false -> + false -> {Visited,ErrCnt1} = analyse2(MFA, Path, Visited0), analyse(R, Path, Visited, ErrCnt1+ErrCnt0); true -> @@ -814,7 +847,7 @@ check_funs({'$M_EXPR','$F_EXPR',_}, {code_server,start_link,1}]) -> 0; check_funs({'$M_EXPR','$F_EXPR',_}, [{erlang,spawn_link,1},{code_server,start_link,1}]) -> 0; -check_funs({'$M_EXPR',module_info,1}, +check_funs({'$M_EXPR',module_info,1}, [{hipe_unified_loader,patch_to_emu_step1,1} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',2}, [{lists,foldl,3}, @@ -829,7 +862,7 @@ check_funs({'$M_EXPR','$F_EXPR',1}, check_funs({'$M_EXPR',warning_msg,2}, [{code_server,finish_on_load_report,2} | _]) -> 0; %% This is cheating! /raimo -%% +%% %% check_funs(This = {M,_,_}, Path) -> %% case catch atom_to_list(M) of %% [$h,$i,$p,$e | _] -> @@ -861,9 +894,9 @@ load_cached(suite) -> load_cached(doc) -> []; load_cached(Config) when is_list(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line WD = filename:dirname(code:which(?MODULE)), - ?line {ok,Node} = + Priv = ?config(priv_dir, Config), + WD = filename:dirname(code:which(?MODULE)), + {ok,Node} = ?t:start_node(code_cache_node, peer, [{args, "-pa \"" ++ WD ++ "\""}, {erl, [this]}]), @@ -873,7 +906,7 @@ load_cached(Config) when is_list(Config) -> _ -> false end end, - ?line Tabs = rpc:call(Node, ets, all, []), + Tabs = rpc:call(Node, ets, all, []), case rpc:call(Node, lists, any, [CCTabCreated,Tabs]) of true -> ?t:stop_node(Node), @@ -881,25 +914,25 @@ load_cached(Config) when is_list(Config) -> false -> ok end, - ?line rpc:call(Node, code, del_path, [Priv]), - ?line rpc:call(Node, code, add_pathz, [Priv]), + rpc:call(Node, code, del_path, [Priv]), + rpc:call(Node, code, add_pathz, [Priv]), FullModName = Priv ++ "/code_cache_test", - ?line {ok,Dev} = file:open(FullModName ++ ".erl", [write]), - ?line io:format(Dev, "-module(code_cache_test). -export([a/0]). a() -> ok.~n", []), - ?line ok = file:close(Dev), - ?line {ok,code_cache_test} = compile:file(FullModName, [{outdir,Priv}]), + {ok,Dev} = file:open(FullModName ++ ".erl", [write]), + io:format(Dev, "-module(code_cache_test). -export([a/0]). a() -> ok.~n", []), + ok = file:close(Dev), + {ok,code_cache_test} = compile:file(FullModName, [{outdir,Priv}]), F = fun load_loop/2, N = 1000, - ?line {T0,T1} = rpc:call(Node, erlang, apply, [F, [N,code_cache_test]]), + {T0,T1} = rpc:call(Node, erlang, apply, [F, [N,code_cache_test]]), TNoCache = now_diff(T1, T0), - ?line rpc:call(Node, code, rehash, []), - ?line {T2,T3} = rpc:call(Node, erlang, apply, [F, [N,code_cache_test]]), - ?line TCache = now_diff(T3, T2), + rpc:call(Node, code, rehash, []), + {T2,T3} = rpc:call(Node, erlang, apply, [F, [N,code_cache_test]]), + TCache = now_diff(T3, T2), AvgNoCache = TNoCache/N, AvgCache = TCache/N, - ?line io:format("Avg. load time (no_cache/cache): ~w/~w~n", [AvgNoCache,AvgCache]), + io:format("Avg. load time (no_cache/cache): ~w/~w~n", [AvgNoCache,AvgCache]), ?t:stop_node(Node), if AvgNoCache =< AvgCache -> ?t:fail("Cache not working properly."); @@ -916,7 +949,7 @@ load_loop(N, M, T0) -> code:delete(M), code:purge(M), load_loop(N-1, M, T0). - + now_diff({A2, B2, C2}, {A1, B1, C1}) -> ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. @@ -925,30 +958,30 @@ start_node_with_cache(suite) -> start_node_with_cache(doc) -> []; start_node_with_cache(Config) when is_list(Config) -> - ?line {ok,Node} = - ?t:start_node(code_cache_node, peer, [{args, + {ok,Node} = + ?t:start_node(code_cache_node, peer, [{args, "-code_path_cache"}, {erl, [this]}]), - ?line Tabs = rpc:call(Node, ets, all, []), + Tabs = rpc:call(Node, ets, all, []), io:format("Tabs: ~w~n", [Tabs]), CCTabCreated = fun(Tab) -> case rpc:call(Node, ets, info, [Tab,name]) of code_cache -> true; _ -> false end - end, - ?line true = lists:any(CCTabCreated, Tabs), + end, + true = lists:any(CCTabCreated, Tabs), ?t:stop_node(Node), ok. - + add_and_rehash(suite) -> []; add_and_rehash(doc) -> []; add_and_rehash(Config) when is_list(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line WD = filename:dirname(code:which(?MODULE)), - ?line {ok,Node} = + Priv = ?config(priv_dir, Config), + WD = filename:dirname(code:which(?MODULE)), + {ok,Node} = ?t:start_node(code_cache_node, peer, [{args, "-pa \"" ++ WD ++ "\""}, {erl, [this]}]), @@ -958,7 +991,7 @@ add_and_rehash(Config) when is_list(Config) -> _ -> false end end, - ?line Tabs0 = rpc:call(Node, ets, all, []), + Tabs0 = rpc:call(Node, ets, all, []), case rpc:call(Node, lists, any, [CCTabCreated,Tabs0]) of true -> ?t:stop_node(Node), @@ -966,36 +999,36 @@ add_and_rehash(Config) when is_list(Config) -> false -> ok end, - ?line ok = rpc:call(Node, code, rehash, []), % create cache - ?line Tabs1 = rpc:call(Node, ets, all, []), - ?line true = rpc:call(Node, lists, any, [CCTabCreated,Tabs1]), % cache table created - ?line ok = rpc:call(Node, code, rehash, []), + ok = rpc:call(Node, code, rehash, []), % create cache + Tabs1 = rpc:call(Node, ets, all, []), + true = rpc:call(Node, lists, any, [CCTabCreated,Tabs1]), % cache table created + ok = rpc:call(Node, code, rehash, []), OkDir = filename:join(Priv, ""), BadDir = filename:join(Priv, "guggemuffsussiputt"), - ?line CP = [OkDir | rpc:call(Node, code, get_path, [])], - ?line true = rpc:call(Node, code, set_path, [CP]), + CP = [OkDir | rpc:call(Node, code, get_path, [])], + true = rpc:call(Node, code, set_path, [CP]), CP1 = [BadDir | CP], - ?line {error,_} = rpc:call(Node, code, set_path, [CP1]), - ?line true = rpc:call(Node, code, del_path, [OkDir]), - ?line true = rpc:call(Node, code, add_path, [OkDir]), - ?line true = rpc:call(Node, code, add_path, [OkDir]), - ?line {error,_} = rpc:call(Node, code, add_path, [BadDir]), - ?line ok = rpc:call(Node, code, rehash, []), + {error,_} = rpc:call(Node, code, set_path, [CP1]), + true = rpc:call(Node, code, del_path, [OkDir]), + true = rpc:call(Node, code, add_path, [OkDir]), + true = rpc:call(Node, code, add_path, [OkDir]), + {error,_} = rpc:call(Node, code, add_path, [BadDir]), + ok = rpc:call(Node, code, rehash, []), ?t:stop_node(Node), ok. - + where_is_file_no_cache(suite) -> []; where_is_file_no_cache(doc) -> []; where_is_file_no_cache(Config) when is_list(Config) -> - ?line {T,KernelBeamFile} = timer:tc(code, where_is_file, ["kernel.beam"]), + {T,KernelBeamFile} = timer:tc(code, where_is_file, ["kernel.beam"]), io:format("Load time: ~w ms~n", [T]), - ?line KernelEbinDir = filename:dirname(KernelBeamFile), - ?line AppFile = filename:join(KernelEbinDir, "kernel.app"), - ?line AppFile = code:where_is_file("kernel.app"), - ?line non_existing = code:where_is_file("kernel"), % no such file + KernelEbinDir = filename:dirname(KernelBeamFile), + AppFile = filename:join(KernelEbinDir, "kernel.app"), + AppFile = code:where_is_file("kernel.app"), + non_existing = code:where_is_file("kernel"), % no such file ok. where_is_file_cached(suite) -> @@ -1003,97 +1036,97 @@ where_is_file_cached(suite) -> where_is_file_cached(doc) -> []; where_is_file_cached(Config) when is_list(Config) -> - ?line {ok,Node} = - ?t:start_node(code_cache_node, peer, [{args, + {ok,Node} = + ?t:start_node(code_cache_node, peer, [{args, "-code_path_cache"}, {erl, [this]}]), - ?line Tabs = rpc:call(Node, ets, all, []), + Tabs = rpc:call(Node, ets, all, []), io:format("Tabs: ~w~n", [Tabs]), CCTabCreated = fun(Tab) -> case rpc:call(Node, ets, info, [Tab,name]) of code_cache -> true; _ -> false end - end, - ?line true = lists:any(CCTabCreated, Tabs), - ?line KernelBeamFile = rpc:call(Node, code, where_is_file, ["kernel.beam"]), - ?line {T,KernelBeamFile} = rpc:call(Node, timer, tc, [code,where_is_file,["kernel.beam"]]), + end, + true = lists:any(CCTabCreated, Tabs), + KernelBeamFile = rpc:call(Node, code, where_is_file, ["kernel.beam"]), + {T,KernelBeamFile} = rpc:call(Node, timer, tc, [code,where_is_file,["kernel.beam"]]), io:format("Load time: ~w ms~n", [T]), - ?line KernelEbinDir = rpc:call(Node, filename, dirname, [KernelBeamFile]), - ?line AppFile = rpc:call(Node, filename, join, [KernelEbinDir,"kernel.app"]), - ?line AppFile = rpc:call(Node, code, where_is_file, ["kernel.app"]), - ?line non_existing = rpc:call(Node, code, where_is_file, ["kernel"]), % no such file + KernelEbinDir = rpc:call(Node, filename, dirname, [KernelBeamFile]), + AppFile = rpc:call(Node, filename, join, [KernelEbinDir,"kernel.app"]), + AppFile = rpc:call(Node, code, where_is_file, ["kernel.app"]), + non_existing = rpc:call(Node, code, where_is_file, ["kernel"]), % no such file ?t:stop_node(Node), ok. - + purge_stacktrace(suite) -> []; purge_stacktrace(doc) -> ["Test that stacktrace is deleted when purging a referred module"]; purge_stacktrace(Config) when is_list(Config) -> - ?line code:purge(code_b_test), + code:purge(code_b_test), try code_b_test:call(fun(b) -> ok end, a) catch error:function_clause -> - ?line code:load_file(code_b_test), - ?line case erlang:get_stacktrace() of + code:load_file(code_b_test), + case erlang:get_stacktrace() of [{?MODULE,_,[a],_}, {code_b_test,call,2,_}, {?MODULE,purge_stacktrace,1,_}|_] -> - ?line false = code:purge(code_b_test), - ?line [] = erlang:get_stacktrace() + false = code:purge(code_b_test), + [] = erlang:get_stacktrace() end end, try code_b_test:call(nofun, 2) catch error:function_clause -> - ?line code:load_file(code_b_test), - ?line case erlang:get_stacktrace() of + code:load_file(code_b_test), + case erlang:get_stacktrace() of [{code_b_test,call,[nofun,2],_}, {?MODULE,purge_stacktrace,1,_}|_] -> - ?line false = code:purge(code_b_test), - ?line [] = erlang:get_stacktrace() + false = code:purge(code_b_test), + [] = erlang:get_stacktrace() end end, Args = [erlang,error,[badarg]], try code_b_test:call(erlang, error, [badarg,Args]) catch error:badarg -> - ?line code:load_file(code_b_test), - ?line case erlang:get_stacktrace() of + code:load_file(code_b_test), + case erlang:get_stacktrace() of [{code_b_test,call,Args,_}, {?MODULE,purge_stacktrace,1,_}|_] -> - ?line false = code:purge(code_b_test), - ?line [] = erlang:get_stacktrace() + false = code:purge(code_b_test), + [] = erlang:get_stacktrace() end end, ok. mult_lib_roots(Config) when is_list(Config) -> - ?line DataDir = filename:join(?config(data_dir, Config), "mult_lib_roots"), - ?line mult_lib_compile(DataDir, "my_dummy_app-b/ebin/lists"), - ?line mult_lib_compile(DataDir, + DataDir = filename:join(?config(data_dir, Config), "mult_lib_roots"), + mult_lib_compile(DataDir, "my_dummy_app-b/ebin/lists"), + mult_lib_compile(DataDir, "my_dummy_app-c/ebin/code_SUITE_mult_root_module"), %% Set up ERL_LIBS and start a slave node. ErlLibs = filename:join(DataDir, "first_root") ++ mult_lib_sep() ++ filename:join(DataDir, "second_root"), - ?line {ok,Node} = + {ok,Node} = ?t:start_node(mult_lib_roots, slave, [{args,"-env ERL_LIBS "++ErlLibs}]), - ?line TSPath = filename:dirname(code:which(test_server)), - ?line Path0 = rpc:call(Node, code, get_path, []), - ?line [TSPath,"."|Path1] = Path0, - ?line [Kernel|Path2] = Path1, - ?line [Stdlib|Path3] = Path2, - ?line mult_lib_verify_lib(Kernel, "kernel"), - ?line mult_lib_verify_lib(Stdlib, "stdlib"), - ?line [Lib1,Lib2,Lib3,Lib4,Lib5|Path] = Path3, + TSPath = filename:dirname(code:which(test_server)), + Path0 = rpc:call(Node, code, get_path, []), + [TSPath,"."|Path1] = Path0, + [Kernel|Path2] = Path1, + [Stdlib|Path3] = Path2, + mult_lib_verify_lib(Kernel, "kernel"), + mult_lib_verify_lib(Stdlib, "stdlib"), + [Lib1,Lib2,Lib3,Lib4,Lib5|Path] = Path3, + - ["first_root/my_dummy_app-a/ebin", "first_root/my_dummy_app-b/ebin", "first_root/my_dummy_app-c/ebin", @@ -1103,7 +1136,7 @@ mult_lib_roots(Config) when is_list(Config) -> E <- lists:sort([Lib1,Lib2,Lib3,Lib4,Lib5])], io:format("~p\n", [Path]), - ?line true = rpc:call(Node, code_SUITE_mult_root_module, works_fine, []), + true = rpc:call(Node, code_SUITE_mult_root_module, works_fine, []), ok. @@ -1113,7 +1146,7 @@ mult_lib_compile(Root, Last) -> Dir = filename:dirname(Name), {ok,Mod} = compile:file(Name, [report,{outdir,Dir}]), ok. - + mult_lib_sep() -> case os:type() of {win32,_} -> ";"; @@ -1123,23 +1156,23 @@ mult_lib_sep() -> mult_lib_verify_lib(Path, Expected) -> Dir = filename:basename(filename:dirname(Path)), true = lists:prefix(Expected, Dir). - + mult_lib_remove_prefix([H|T1], [H|T2]) -> mult_lib_remove_prefix(T1, T2); mult_lib_remove_prefix([$/|T], []) -> T. bad_erl_libs(Config) when is_list(Config) -> - ?line {ok,Node} = + {ok,Node} = ?t:start_node(mult_lib_roots, slave, [{args,"-env ERL_LIBS "}]), - ?line ?t:stop_node(Node), + ?t:stop_node(Node), - ?line {ok,Node2} = + {ok,Node2} = ?t:start_node(mult_lib_roots, slave, [{args,"-env ERL_LIBS /no/such/dir"}]), - ?line ?t:stop_node(Node2), + ?t:stop_node(Node2), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1158,55 +1191,55 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) -> PrivDir = ?config(priv_dir, Config), App = code_archive_dict, VsnBase = atom_to_list(App) ++ "-1.0", - Base = + Base = case StripVsn of true -> atom_to_list(App); false -> VsnBase end, Ext = init:archive_extension(), RootDir = filename:join([PrivDir, Root]), - ?line ok = file:make_dir(RootDir), + ok = file:make_dir(RootDir), Archive = filename:join([RootDir, VsnBase ++ Ext]), - ?line {ok, _} = zip:create(Archive, [VsnBase], + {ok, _} = zip:create(Archive, [VsnBase], [{compress, []}, {cwd, DataDir}]), - ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), + {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), case StripVsn of true -> - ?line ok = file:rename(filename:join([PrivDir, VsnBase]), + ok = file:rename(filename:join([PrivDir, VsnBase]), filename:join([PrivDir, Base])); false -> ok end, - + io:format("DEBUG: ~p\n", [?LINE]), %% Compile the code - ?line ok = compile_app(PrivDir, Base), - + ok = compile_app(PrivDir, Base), + %% Create the archive - ?line ok = file:delete(Archive), - ?line {ok, _} = zip:create(Archive, [Base], + ok = file:delete(Archive), + {ok, _} = zip:create(Archive, [Base], [{compress, []}, {cwd, PrivDir}]), %% Set up ERL_LIBS and start a slave node. - ?line {ok, Node} = + {ok, Node} = ?t:start_node(code_archive, slave, [{args,"-env ERL_LIBS " ++ RootDir}]), - ?line CodePath = rpc:call(Node, code, get_path, []), + CodePath = rpc:call(Node, code, get_path, []), AppEbin = filename:join([Archive, Base, "ebin"]), io:format("AppEbin: ~p\n", [AppEbin]), io:format("CodePath: ~p\n", [CodePath]), io:format("Archive: ~p\n", [erl_prim_loader:read_file_info(Archive)]), - ?line true = lists:member(AppEbin, CodePath), + true = lists:member(AppEbin, CodePath), %% Start the app - ?line ok = rpc:call(Node, application, start, [App]), - + ok = rpc:call(Node, application, start, [App]), + %% Access the app priv dir AppPrivDir = rpc:call(Node, code, priv_dir, [App]), - ?line AppPrivFile = filename:join([AppPrivDir, "code_archive.txt"]), + AppPrivFile = filename:join([AppPrivDir, "code_archive.txt"]), io:format("AppPrivFile: ~p\n", [AppPrivFile]), - ?line {ok, _Bin, _Path} = + {ok, _Bin, _Path} = rpc:call(Node, erl_prim_loader, get_file, [AppPrivFile]), %% Use the app @@ -1221,14 +1254,14 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) -> error = rpc:call(Node, App, find, [Tab, Key]), ok = rpc:call(Node, App, erase, [Tab]), - ?line ?t:stop_node(Node), + ?t:stop_node(Node), ok. compile_app(TopDir, AppName) -> AppDir = filename:join([TopDir, AppName]), SrcDir = filename:join([AppDir, "src"]), OutDir = filename:join([AppDir, "ebin"]), - ?line {ok, Files} = file:list_dir(SrcDir), + {ok, Files} = file:list_dir(SrcDir), compile_files(Files, SrcDir, OutDir). compile_files([File | Files], SrcDir, OutDir) -> @@ -1253,27 +1286,27 @@ big_boot_embedded(doc) -> ["Test that a boot file with (almost) all of OTP can be used to start an" " embeddedd system."]; big_boot_embedded(Config) when is_list(Config) -> - ?line {BootArg,AppsInBoot} = create_big_boot(Config), - ?line {ok, Node} = + {BootArg,AppsInBoot} = create_big_boot(Config), + {ok, Node} = ?t:start_node(big_boot_embedded, slave, [{args,"-boot "++BootArg++" -mode embedded"}]), - ?line RemoteNodeApps = - [ {X,Y} || {X,_,Y} <- + RemoteNodeApps = + [ {X,Y} || {X,_,Y} <- rpc:call(Node,application,loaded_applications,[]) ], - ?line true = lists:sort(AppsInBoot) =:= lists:sort(RemoteNodeApps), + true = lists:sort(AppsInBoot) =:= lists:sort(RemoteNodeApps), ok. on_load(Config) when is_list(Config) -> Master = on_load_test_case_process, - ?line Data = filename:join([?config(data_dir, Config),"on_load"]), - ?line ok = file:set_cwd(Data), - ?line up_to_date = make:all([{d,'MASTER',Master}]), + Data = filename:join([?config(data_dir, Config),"on_load"]), + ok = file:set_cwd(Data), + up_to_date = make:all([{d,'MASTER',Master}]), %% Register a name for this process. - ?line register(Master, self()), - - ?line {_,Ref} = spawn_monitor(fun() -> + register(Master, self()), + + {_,Ref} = spawn_monitor(fun() -> exit(on_load_a:data()) end), receive @@ -1285,8 +1318,8 @@ on_load(Config) when is_list(Config) -> receive {on_load_c,PidC} -> ok end, - - ?line Refs = on_load_massive_spawn(lists:seq(1, 50)), + + Refs = on_load_massive_spawn(lists:seq(1, 50)), receive after 7 -> ok end, PidC ! go, @@ -1304,13 +1337,13 @@ on_load(Config) when is_list(Config) -> receive {'DOWN',Ref,process,_,Res} -> - ?line [a,b,c] = Res + [a,b,c] = Res end, on_load_wait_for_all(Refs), receive Any -> - ?line ?t:fail({unexpected,Any}) + ?t:fail({unexpected,Any}) after 10 -> ok end. @@ -1377,13 +1410,13 @@ on_load_embedded(Config) when is_list(Config) -> end. on_load_embedded_1(Config) -> - ?line DataDir = ?config(data_dir, Config), + DataDir = ?config(data_dir, Config), %% Link the on_load_app application into the lib directory. - ?line LibRoot = code:lib_dir(), - ?line LinkName = filename:join(LibRoot, "on_load_app-1.0"), - ?line OnLoadApp = filename:join(DataDir, "on_load_app-1.0"), - ?line del_link(LinkName), + LibRoot = code:lib_dir(), + LinkName = filename:join(LibRoot, "on_load_app-1.0"), + OnLoadApp = filename:join(DataDir, "on_load_app-1.0"), + del_link(LinkName), io:format("LinkName :~p, OnLoadApp: ~p~n",[LinkName,OnLoadApp]), case file:make_symlink(OnLoadApp, LinkName) of {error,enotsup} -> @@ -1392,28 +1425,28 @@ on_load_embedded_1(Config) -> end, %% Compile the code. - ?line OnLoadAppEbin = filename:join(LinkName, "ebin"), - ?line {ok,_ } = compile:file(filename:join([OnLoadApp,"src", + OnLoadAppEbin = filename:join(LinkName, "ebin"), + {ok,_ } = compile:file(filename:join([OnLoadApp,"src", "on_load_embedded"]), [{outdir,OnLoadAppEbin}]), %% Create and compile a boot file. - ?line true = code:add_pathz(OnLoadAppEbin), + true = code:add_pathz(OnLoadAppEbin), Options = case is_source_dir() of true -> [local]; false -> [] end, - ?line BootScript = create_boot(Config, Options), - ?line true = code:del_path(OnLoadAppEbin), + BootScript = create_boot(Config, Options), + true = code:del_path(OnLoadAppEbin), %% Start the node and check that the on_load function was run. - ?line {ok,Node} = start_node(on_load_embedded, + {ok,Node} = start_node(on_load_embedded, "-mode embedded -boot " ++ BootScript), ok = rpc:call(Node, on_load_embedded, status, []), %% Clean up. - ?line stop_node(Node), - ?line ok = del_link(LinkName). + stop_node(Node), + ok = del_link(LinkName). del_link(LinkName) -> case file:delete(LinkName) of @@ -1421,100 +1454,94 @@ del_link(LinkName) -> file:del_dir(LinkName); Other -> Other - end. + end. create_boot(Config, Options) -> - ?line {ok, OldDir} = file:get_cwd(), - ?line {LatestDir,LatestName} = create_script(Config), - ?line ok = file:set_cwd(LatestDir), - ?line ok = systools:make_script(LatestName, Options), - ?line ok = file:set_cwd(OldDir), + {ok, OldDir} = file:get_cwd(), + {LatestDir,LatestName} = create_script(Config), + ok = file:set_cwd(LatestDir), + ok = systools:make_script(LatestName, Options), + ok = file:set_cwd(OldDir), filename:join(LatestDir, LatestName). create_script(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Name = PrivDir ++ "on_load_test", - ?line Apps = application_controller:which_applications(), - ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel, 1, Apps), - ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib, 1, Apps), - ?line {ok,Fd} = file:open(Name ++ ".rel", [write]), - ?line io:format(Fd, + PrivDir = ?config(priv_dir, Config), + Name = PrivDir ++ "on_load_test", + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel, 1, Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib, 1, Apps), + {ok,Fd} = file:open(Name ++ ".rel", [write]), + io:format(Fd, "{release, {\"Test release 3\", \"P2A\"}, \n" " {erts, \"9.42\"}, \n" " [{kernel, \"~s\"}, {stdlib, \"~s\"}," " {on_load_app, \"1.0\"}]}.\n", [KernelVer,StdlibVer]), - ?line file:close(Fd), + file:close(Fd), {filename:dirname(Name),filename:basename(Name)}. create_big_boot(Config) -> - ?line {ok, OldDir} = file:get_cwd(), - ?line {Options,Local} = case is_source_dir() of - true -> {[no_module_tests,local],true}; - _ -> {[no_module_tests],false} + {ok, OldDir} = file:get_cwd(), + {Options,Local} = case is_source_dir() of + true -> {[no_module_tests,local],true}; + _ -> {[no_module_tests],false} end, - ?line {LatestDir,LatestName,Apps} = create_big_script(Config,Local), - ?line ok = file:set_cwd(LatestDir), - ?line ok = systools:make_script(LatestName, Options), - ?line ok = file:set_cwd(OldDir), + {LatestDir,LatestName,Apps} = create_big_script(Config,Local), + ok = file:set_cwd(LatestDir), + ok = systools:make_script(LatestName, Options), + ok = file:set_cwd(OldDir), {filename:join(LatestDir, LatestName),Apps}. -% The following apps cannot be loaded +% The following apps cannot be loaded % hipe .app references (or can reference) files that have no % corresponding beam file (if hipe is not enabled) -filter_app("hipe",_) -> - false; +filter_app("hipe",_) -> false; % Dialyzer and typer depends on hipe -filter_app("dialyzer",_) -> - false; -filter_app("typer",_) -> - false; +filter_app("dialyzer",_) -> false; +filter_app("typer",_) -> false; % Orber requires explicit configuration -filter_app("orber",_) -> - false; +filter_app("orber",_) -> false; % cos* depends on orber -filter_app("cos"++_,_) -> - false; +filter_app("cos"++_,_) -> false; % ic has a mod instruction in the app file but no corresponding start function -filter_app("ic",_) -> - false; +filter_app("ic",_) -> false; % Netconf has some dependency that I really do not understand (maybe like orber) -filter_app("netconf",_) -> - false; +filter_app("netconf",_) -> false; % Safe has the same kind of error in the .app file as ic -filter_app("safe",_) -> - false; +filter_app("safe",_) -> false; % Comte cannot be started in the "usual" way -filter_app("comte",_) -> - false; +filter_app("comte",_) -> false; % OS_mon does not find it's port program when running cerl -filter_app("os_mon",true) -> - false; +filter_app("os_mon",true) -> false; +% erts is not a "real" app either =/ +filter_app("erts",_) -> false; % Other apps should be OK. -filter_app(_,_) -> - true. +filter_app(_,_) -> true. create_big_script(Config,Local) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Name = filename:join(PrivDir,"full_script_test"), - ?line InitialApplications=application:loaded_applications(), + PrivDir = ?config(priv_dir, Config), + Name = filename:join(PrivDir,"full_script_test"), + InitialApplications=application:loaded_applications(), %% Applications left loaded by the application suite, unload them! - ?line UnloadFix=[app0,app1,app2,group_leader,app_start_error], - ?line [application:unload(Leftover) || + UnloadFix=[app0,app1,app2,group_leader,app_start_error], + [application:unload(Leftover) || Leftover <- UnloadFix, lists:keymember(Leftover,1,InitialApplications) ], %% Now we should have only "real" applications... - ?line [application:load(list_to_atom(Y)) || {match,[Y]} <- [ re:run(X,code:lib_dir()++"/"++"([^/-]*).*/ebin",[{capture,[1],list}]) || X <- code:get_path()],filter_app(Y,Local)], - ?line Apps = [ {N,V} || {N,_,V} <- application:loaded_applications()], - ?line {ok,Fd} = file:open(Name ++ ".rel", [write]), - ?line io:format(Fd, + [application:load(list_to_atom(Y)) + || {match,[Y]} <- [re:run(X,code:lib_dir()++"/"++"([^/-]*).*/ebin", + [{capture,[1],list},unicode]) || + X <- code:get_path()],filter_app(Y,Local)], + Apps = [ {N,V} || {N,_,V} <- application:loaded_applications()], + {ok,Fd} = file:open(Name ++ ".rel", [write]), + io:format(Fd, "{release, {\"Test release 3\", \"P2A\"}, \n" " {erts, \"9.42\"}, \n" " ~p}.\n", [Apps]), - ?line file:close(Fd), - ?line NewlyLoaded = + file:close(Fd), + NewlyLoaded = application:loaded_applications() -- InitialApplications, - ?line [ application:unload(N) || {N,_,_} <- NewlyLoaded], + [ application:unload(N) || {N,_,_} <- NewlyLoaded], {filename:dirname(Name),filename:basename(Name),Apps}. is_source_dir() -> @@ -1523,35 +1550,35 @@ is_source_dir() -> on_load_errors(Config) when is_list(Config) -> Master = on_load_error_test_case_process, - ?line register(Master, self()), + register(Master, self()), - ?line Data = filename:join([?config(data_dir, Config),"on_load_errors"]), - ?line ok = file:set_cwd(Data), - ?line up_to_date = make:all([{d,'MASTER',Master}]), + Data = filename:join([?config(data_dir, Config),"on_load_errors"]), + ok = file:set_cwd(Data), + up_to_date = make:all([{d,'MASTER',Master}]), - ?line do_on_load_error(an_atom), + do_on_load_error(an_atom), - ?line error_logger:add_report_handler(?MODULE, self()), + error_logger:add_report_handler(?MODULE, self()), - ?line do_on_load_error({something,terrible,is,wrong}), + do_on_load_error({something,terrible,is,wrong}), receive Any1 -> - ?line {_, "The on_load function"++_, + {_, "The on_load function"++_, [on_load_error, {something,terrible,is,wrong},_]} = Any1 end, - ?line do_on_load_error(fail), %Cause exception. + do_on_load_error(fail), %Cause exception. receive Any2 -> - ?line {_, "The on_load function"++_, + {_, "The on_load function"++_, [on_load_error,{failed,[_|_]},_]} = Any2 end, %% There should be no more messages. receive Unexpected -> - ?line ?t:fail({unexpected,Unexpected}) + ?t:fail({unexpected,Unexpected}) after 10 -> ok end, @@ -1559,14 +1586,14 @@ on_load_errors(Config) when is_list(Config) -> ok. do_on_load_error(ReturnValue) -> - ?line {_,Ref} = spawn_monitor(fun() -> + {_,Ref} = spawn_monitor(fun() -> exit(on_load_error:main()) end), receive {on_load_error,ErrorPid} -> ok end, - ?line ErrorPid ! ReturnValue, + ErrorPid ! ReturnValue, receive {'DOWN',Ref,process,_,Exit} -> - ?line {undef,[{on_load_error,main,[],_}|_]} = Exit + {undef,[{on_load_error,main,[],_}|_]} = Exit end. native_early_modules(suite) -> []; @@ -1580,10 +1607,10 @@ native_early_modules(Config) when is_list(Config) -> end. native_early_modules_1(Architecture) -> - ?line {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists), - ?line ChunkName = hipe_unified_loader:chunk_name(Architecture), - ?line NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]), - ?line IsHipeCompiled = case NativeChunk of + {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists), + ChunkName = hipe_unified_loader:chunk_name(Architecture), + NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]), + IsHipeCompiled = case NativeChunk of {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> true; {error, beam_lib, _} -> false end, @@ -1591,10 +1618,10 @@ native_early_modules_1(Architecture) -> false -> {skip,"OTP apparently not configured with --enable-native-libs"}; true -> - ?line true = lists:all(fun code:is_module_native/1, - [ets,file,filename,gb_sets,gb_trees, - %%hipe_unified_loader, no_native as workaround - lists,os]), + true = lists:all(fun code:is_module_native/1, + [ets,file,filename,gb_sets,gb_trees, + %%hipe_unified_loader, no_native as workaround + lists,os]), ok end. diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl index 35502a1d27..b2ca3bdbc2 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,7 +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]). --export([get_path/1, set_path/1, get_file/1, +-export([get_path/1, set_path/1, get_file/1, normalize_and_backslash/1, inet_existing/1, inet_coming_up/1, inet_disconnects/1, multiple_slaves/1, file_requests/1, local_archive/1, remote_archive/1, @@ -39,7 +39,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [get_path, set_path, get_file, inet_existing, + [get_path, set_path, get_file, + normalize_and_backslash, inet_existing, inet_coming_up, inet_disconnects, multiple_slaves, file_requests, local_archive, remote_archive, primary_archive, virtual_dir_in_archive]. @@ -107,6 +108,26 @@ get_file(Config) when is_list(Config) -> ?line error = erl_prim_loader:get_file({dummy}), ok. +normalize_and_backslash(Config) -> + %% Test OTP-11170 + case os:type() of + {win32,_} -> + {skip, "not on windows"}; + _ -> + test_normalize_and_backslash(Config) + end. +test_normalize_and_backslash(Config) -> + PrivDir = ?config(priv_dir,Config), + Dir = filename:join(PrivDir,"\\"), + File = filename:join(Dir,"file-OTP-11170"), + ok = file:make_dir(Dir), + ok = file:write_file(File,"a file to test OTP-11170"), + {ok,["file-OTP-11170"]} = file:list_dir(Dir), + {ok,["file-OTP-11170"]} = erl_prim_loader:list_dir(Dir), + ok = file:delete(File), + ok = file:del_dir(Dir), + ok. + inet_existing(doc) -> ["Start a node using the 'inet' loading method, ", "from an already started boot server."]; inet_existing(Config) when is_list(Config) -> diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index e4c8f0ffaf..c75639ae7e 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -805,6 +805,20 @@ new_modes(Config) when is_list(Config) -> ?line {ok, [$\[]} = ?FILE_MODULE:read(Fd6, 1), ?line ok = ?FILE_MODULE:close(Fd6), + %% write and sync + case ?FILE_MODULE:open(Name1, [write, sync]) of + {ok, Fd7} -> + ok = io:write(Fd7, Marker), + ok = io:put_chars(Fd7, ".\n"), + ok = ?FILE_MODULE:close(Fd7), + {ok, Fd8} = ?FILE_MODULE:open(Name1, [read]), + {ok, Marker} = io:read(Fd8, prompt), + ok = ?FILE_MODULE:close(Fd8); + {error, enotsup} -> + %% for platforms that don't support the sync option + ok + end, + ?line [] = flush(), ?line test_server:timetrap_cancel(Dog), ok. diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index c5d8becfd3..881aaed429 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -1,7 +1,7 @@ -%% +%% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,8 +35,11 @@ open_unihoming_ipv6_socket/1, open_multihoming_ipv6_socket/1, open_multihoming_ipv4_and_ipv6_socket/1, - basic_stream/1, xfer_stream_min/1, peeloff_active_once/1, - peeloff_active_true/1, buffers/1]). + basic_stream/1, xfer_stream_min/1, active_n/1, + peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1, + buffers/1, + names_unihoming_ipv4/1, names_unihoming_ipv6/1, + names_multihoming_ipv4/1, names_multihoming_ipv6/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -46,9 +49,11 @@ all() -> open_multihoming_ipv4_socket, open_unihoming_ipv6_socket, open_multihoming_ipv6_socket, - open_multihoming_ipv4_and_ipv6_socket, + open_multihoming_ipv4_and_ipv6_socket, active_n, basic_stream, xfer_stream_min, peeloff_active_once, - peeloff_active_true, buffers]. + peeloff_active_true, peeloff_active_n, buffers, + names_unihoming_ipv4, names_unihoming_ipv6, + names_multihoming_ipv4, names_multihoming_ipv6]. groups() -> []. @@ -107,7 +112,7 @@ xfer_min(Config) when is_list(Config) -> ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]), ?line {ok,Pb} = inet:port(Sb), ?line ok = gen_sctp:listen(Sb, true), - + ?line {ok,Sa} = gen_sctp:open(), ?line {ok,Pa} = inet:port(Sa), ?line {ok,#sctp_assoc_change{state=comm_up, @@ -118,18 +123,18 @@ xfer_min(Config) when is_list(Config) -> gen_sctp:connect(Sa, Loopback, Pb, []), ?line {SbAssocId,SaOutboundStreams,SaInboundStreams} = case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of - {Loopback,Pa, - #sctp_assoc_change{state=comm_up, - error=0, - outbound_streams=SbOutboundStreams, - inbound_streams=SbInboundStreams, - assoc_id=AssocId}} -> - {AssocId,SbInboundStreams,SbOutboundStreams}; - {Loopback,Pa, - #sctp_paddr_change{state=addr_confirmed, - addr={Loopback,Pa}, - error=0, - assoc_id=AssocId}} -> + {Loopback,Pa, + #sctp_assoc_change{state=comm_up, + error=0, + outbound_streams=SbOutboundStreams, + inbound_streams=SbInboundStreams, + assoc_id=AssocId}} -> + {AssocId,SbInboundStreams,SbOutboundStreams}; + {Loopback,Pa, + #sctp_paddr_change{state=addr_confirmed, + addr={Loopback,Pa}, + error=0, + assoc_id=AssocId}} -> {Loopback,Pa, #sctp_assoc_change{state=comm_up, error=0, @@ -148,17 +153,20 @@ xfer_min(Config) when is_list(Config) -> assoc_id=SbAssocId}], Data} -> ok; Event1 -> - {Loopback,Pa, - #sctp_paddr_change{addr = {Loopback,_}, - state = addr_available, - error = 0, - assoc_id = SbAssocId}} = - recv_event(Event1), - {ok,{Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data}} = gen_sctp:recv(Sb, infinity) + case recv_event(Event1) of + {Loopback,Pa, + #sctp_paddr_change{addr = {Loopback,_}, + state = State, + error = 0, + assoc_id = SbAssocId}} + when State =:= addr_available; + State =:= addr_confirmed -> + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} = log_ok(gen_sctp:recv(Sb, infinity)) + end end, ?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data), ?line case log_ok(gen_sctp:recv(Sa, infinity)) of @@ -197,7 +205,7 @@ xfer_min(Config) when is_list(Config) -> recv_event(log_ok(gen_sctp:recv(Sb, infinity))), ?line ok = gen_sctp:close(Sa), ?line ok = gen_sctp:close(Sb), - + ?line receive Msg -> test_server:fail({received,Msg}) after 17 -> ok @@ -216,7 +224,7 @@ xfer_active(Config) when is_list(Config) -> ?line {ok,Sb} = gen_sctp:open([{active,true}]), ?line {ok,Pb} = inet:port(Sb), ?line ok = gen_sctp:listen(Sb, true), - + ?line {ok,Sa} = gen_sctp:open([{active,true}]), ?line {ok,Pa} = inet:port(Sa), ?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []), @@ -348,7 +356,7 @@ def_sndrcvinfo(Config) when is_list(Config) -> %% ?line S1 = log_ok(gen_sctp:open( - 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])), + 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])), ?LOGVAR(S1), ?line P1 = log_ok(inet:port(S1)), @@ -455,18 +463,22 @@ def_sndrcvinfo(Config) when is_list(Config) -> stream=1, ppid=0, context=0, assoc_id=S1AssocId}], <<"3: ",Data/binary>>} -> ok; Event2 -> - {Loopback,P2, - #sctp_paddr_change{ - addr={Loopback,_}, state=addr_available, - error=0, assoc_id=S1AssocId}} = - recv_event(Event2), - ?line case log_ok(gen_sctp:recv(S1)) of - {Loopback,P2, - [#sctp_sndrcvinfo{ - stream=1, ppid=0, context=0, - assoc_id=S1AssocId}], - <<"3: ",Data/binary>>} -> ok - end + case recv_event(Event2) of + {Loopback,P2, + #sctp_paddr_change{ + addr={Loopback,_}, + state=State, + error=0, assoc_id=S1AssocId}} + when State =:= addr_available; + State =:= addr_confirmed -> + ?line case log_ok(gen_sctp:recv(S1)) of + {Loopback,P2, + [#sctp_sndrcvinfo{ + stream=1, ppid=0, context=0, + assoc_id=S1AssocId}], + <<"3: ",Data/binary>>} -> ok + end + end end, ?line ok = do_from_other_process( @@ -509,6 +521,13 @@ log_ok(X) -> log(ok(X)). ok({ok,X}) -> X. +err([], Result) -> + erlang:error(Result); +err([Reason|_], {error,Reason}) -> + ok; +err([_|Reasons], Result) -> + err(Reasons, Result). + log(X) -> io:format("LOG[~w]: ~p~n", [self(),X]), X. @@ -529,57 +548,57 @@ api_open_close(Config) when is_list(Config) -> ?line {ok,S1} = gen_sctp:open(0), ?line {ok,P} = inet:port(S1), ?line ok = gen_sctp:close(S1), - + ?line {ok,S2} = gen_sctp:open(P), ?line {ok,P} = inet:port(S2), ?line ok = gen_sctp:close(S2), - + ?line {ok,S3} = gen_sctp:open([{port,P}]), ?line {ok,P} = inet:port(S3), ?line ok = gen_sctp:close(S3), - + ?line {ok,S4} = gen_sctp:open(P, []), ?line {ok,P} = inet:port(S4), ?line ok = gen_sctp:close(S4), - + ?line {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]), ?line {ok,P} = inet:port(S5), ?line ok = gen_sctp:close(S5), ?line ok = gen_sctp:close(S5), - + ?line try gen_sctp:close(0) catch error:badarg -> ok end, - + ?line try gen_sctp:open({}) catch error:badarg -> ok end, - + ?line try gen_sctp:open(-1) catch error:badarg -> ok end, - + ?line try gen_sctp:open(65536) catch error:badarg -> ok end, - + ?line try gen_sctp:open(make_ref(), []) catch error:badarg -> ok end, - + ?line try gen_sctp:open(0, {}) catch error:badarg -> ok end, - + ?line try gen_sctp:open(0, [make_ref()]) catch error:badarg -> ok end, - + ?line try gen_sctp:open([{invalid_option,0}]) catch error:badarg -> ok end, - + ?line try gen_sctp:open(0, [{mode,invalid_mode}]) catch error:badarg -> ok end, @@ -591,11 +610,11 @@ api_listen(suite) -> []; api_listen(Config) when is_list(Config) -> ?line Localhost = {127,0,0,1}, - + ?line try gen_sctp:listen(0, true) catch error:badarg -> ok end, - + ?line {ok,S} = gen_sctp:open(), ?line {ok,Pb} = inet:port(S), ?line try gen_sctp:listen(S, not_allowed_for_listen) @@ -603,7 +622,7 @@ api_listen(Config) when is_list(Config) -> end, ?line ok = gen_sctp:close(S), ?line {error,closed} = gen_sctp:listen(S, true), - + ?line {ok,Sb} = gen_sctp:open(Pb), ?line {ok,Sa} = gen_sctp:open(), ?line case gen_sctp:connect(Sa, localhost, Pb, []) of @@ -615,8 +634,8 @@ api_listen(Config) when is_list(Config) -> gen_sctp:recv(Sa, infinity); {error,#sctp_assoc_change{state=cant_assoc}} -> ok%; - %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} -> - %% ok + %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} -> + %% ok end, ?line ok = gen_sctp:listen(Sb, true), ?line {ok,#sctp_assoc_change{state=comm_up, @@ -767,6 +786,106 @@ implicit_inet6(S1, Addr) -> end, ?line ok = gen_sctp:close(S2). +active_n(doc) -> + "Verify {active,N} socket management"; +active_n(suite) -> + []; +active_n(Config) when is_list(Config) -> + N = 3, + S1 = ok(gen_sctp:open([{active,N}])), + [{active,N}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,-N}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,0}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + ok = inet:setopts(S1, [{active,32767}]), + {error,einval} = inet:setopts(S1, [{active,1}]), + {error,einval} = inet:setopts(S1, [{active,-32769}]), + ok = inet:setopts(S1, [{active,-32768}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,true}]), + [{active,true}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,once}]), + [{active,once}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(S1, [{active,32768}]), + ok = inet:setopts(S1, [{active,false}]), + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = gen_sctp:listen(S1, true), + S1Port = ok(inet:port(S1)), + S2 = ok(gen_sctp:open(0, [{active,false}])), + Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])), + ok = inet:setopts(S1, [{active,N}]), + [{active,N}] = ok(inet:getopts(S1, [active])), + LoopFun = fun(Count, Count, _Fn) -> + receive + {sctp_passive,S1} -> + ok + after + 5000 -> + exit({error,timeout}) + end; + (I, Count, Fn) -> + Msg = list_to_binary("message "++integer_to_list(I)), + ok = gen_sctp:send(S2, Assoc, 0, Msg), + receive + {sctp,S1,_,_,{[SR],Msg}} when is_record(SR, sctp_sndrcvinfo) -> + Fn(I+1, Count, Fn); + {sctp,S1,_,_,_} -> + %% ignore non-data messages + ok = inet:setopts(S1, [{active,1}]), + Fn(I, Count, Fn); + Other -> + exit({unexpected, Other}) + after + 5000 -> + exit({error,timeout}) + end + end, + ok = LoopFun(1, N, LoopFun), + S3 = ok(gen_sctp:open([{active,0}])), + receive + {sctp_passive,S3} -> + [{active,false}] = ok(inet:getopts(S3, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + ok = gen_sctp:close(S3), + ok = gen_sctp:close(S2), + ok = gen_sctp:close(S1), + ok. + basic_stream(doc) -> "Hello world stream socket"; basic_stream(suite) -> @@ -840,23 +959,36 @@ xfer_stream_min(Config) when is_list(Config) -> ?line SbOutboundStreams = SaInboundStreams, ?line ?LOGVAR(SbOutboundStreams), ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data), - ?line case gen_sctp:recv(Sb, infinity) of - {ok,{Loopback, + ?line case log_ok(gen_sctp:recv(Sb, infinity)) of + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} -> ok; + {Loopback, + Pa,[], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_available, + error = 0, + assoc_id = SbAssocId}} -> + {Loopback, Pa, [#sctp_sndrcvinfo{stream=Stream, assoc_id=SbAssocId}], - Data}} -> ok; - {ok,{Loopback, - Pa,[], - #sctp_paddr_change{addr = {Loopback,_}, - state = addr_available, - error = 0, - assoc_id = SbAssocId}}} -> - {ok,{Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data}} = gen_sctp:recv(Sb, infinity) + Data} = log_ok(gen_sctp:recv(Sb, infinity)); + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_confirmed, + error = 0, + assoc_id = SbAssocId}} -> + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} = log_ok(gen_sctp:recv(Sb, infinity)) end, ?line ok = do_from_other_process( @@ -941,6 +1073,14 @@ peeloff_active_true(suite) -> peeloff_active_true(Config) -> peeloff(Config, [{active,true}]). +peeloff_active_n(doc) -> + "Peel off an SCTP stream socket ({active,N})"; +peeloff_active_n(suite) -> + []; + +peeloff_active_n(Config) -> + peeloff(Config, [{active,1}]). + peeloff(Config, SockOpts) when is_list(Config) -> ?line Addr = {127,0,0,1}, ?line Stream = 0, @@ -972,10 +1112,10 @@ peeloff(Config, SockOpts) when is_list(Config) -> ?line ?LOGVAR(S2Ai), ?line S1Ai = receive - {S1,{Addr,P2, - #sctp_assoc_change{ - state=comm_up, - assoc_id=AssocId1}}} -> AssocId1 + {S1,{Addr,P2, + #sctp_assoc_change{ + state=comm_up, + assoc_id=AssocId1}}} -> AssocId1 after Timeout -> socket_bailout([S1,S2]) end, @@ -1003,8 +1143,8 @@ peeloff(Config, SockOpts) when is_list(Config) -> ?line P3 = case P3_X of 0 -> P1; _ -> P3_X end, ?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] = socket_call(S3, - {getopts,[{sctp_get_peer_addr_info, - #sctp_paddrinfo{address={Addr,P2}}}]}), + {getopts,[{sctp_get_peer_addr_info, + #sctp_paddrinfo{address={Addr,P2}}}]}), %%?line S3Ai = S1Ai, ?line ?LOGVAR(S3Ai), %% @@ -1087,9 +1227,9 @@ buffers(Config) when is_list(Config) -> %% ?line socket_call(S1, {setopts,[{recbuf,Limit}]}), ?line Recbuf = - case socket_call(S1, {getopts,[recbuf]}) of - [{recbuf,RB1}] when RB1 >= Limit -> RB1 - end, + case socket_call(S1, {getopts,[recbuf]}) of + [{recbuf,RB1}] when RB1 >= Limit -> RB1 + end, ?line Data = mk_data(Recbuf+Limit), ?line socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}), ?line socket_call(S2, {send,S2Ai,Stream,Data}), @@ -1190,6 +1330,93 @@ open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) -> {skip, Reason} end. +names_unihoming_ipv4(doc) -> + "Test inet:socknames/peernames on unihoming IPv4 sockets"; +names_unihoming_ipv4(suite) -> + []; +names_unihoming_ipv4(Config) when is_list(Config) -> + ?line do_names(Config, inet, 1). + +names_unihoming_ipv6(doc) -> + "Test inet:socknames/peernames on unihoming IPv6 sockets"; +names_unihoming_ipv6(suite) -> + []; +names_unihoming_ipv6(Config) when is_list(Config) -> + ?line do_names(Config, inet6, 1). + +names_multihoming_ipv4(doc) -> + "Test inet:socknames/peernames on multihoming IPv4 sockets"; +names_multihoming_ipv4(suite) -> + []; +names_multihoming_ipv4(Config) when is_list(Config) -> + ?line do_names(Config, inet, 2). + +names_multihoming_ipv6(doc) -> + "Test inet:socknames/peernames on multihoming IPv6 sockets"; +names_multihoming_ipv6(suite) -> + []; +names_multihoming_ipv6(Config) when is_list(Config) -> + ?line do_names(Config, inet6, 2). + + + +do_names(_, FamilySpec, AddressCount) -> + Fun = + fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) -> + ?line ServerSocknamesNoassoc = + lists:sort(ok(inet:socknames(ServerSocket))), + ?line ?LOGVAR(ServerSocknamesNoassoc), + ?line ServerSocknames = + lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))), + ?line ?LOGVAR(ServerSocknames), + ?line [_|_] = + ordsets:intersection + (ServerSocknamesNoassoc, ServerSocknames), + ?line ClientSocknamesNoassoc = + lists:sort(ok(inet:socknames(ClientSocket))), + ?line ?LOGVAR(ClientSocknamesNoassoc), + ?line ClientSocknames = + lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))), + ?line ?LOGVAR(ClientSocknames), + ?line [_|_] = + ordsets:intersection + (ClientSocknamesNoassoc, ClientSocknames), + ?line err([einval,enotconn], inet:peernames(ServerSocket)), + ?line ServerPeernames = + lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))), + ?line ?LOGVAR(ServerPeernames), + ?line err([einval,enotconn], inet:peernames(ClientSocket)), + ?line ClientPeernames = + lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))), + ?line ?LOGVAR(ClientPeernames), + ?line ServerSocknames = ClientPeernames, + ?line ClientSocknames = ServerPeernames, + ?line {ok,Socket} = + gen_sctp:peeloff(ServerSocket, ServerAssoc), + ?line SocknamesNoassoc = + lists:sort(ok(inet:socknames(Socket))), + ?line ?LOGVAR(SocknamesNoassoc), + ?line Socknames = + lists:sort(ok(inet:socknames(Socket, ServerAssoc))), + ?line ?LOGVAR(Socknames), + ?line true = + ordsets:is_subset(SocknamesNoassoc, Socknames), + ?line Peernames = + lists:sort(ok(inet:peernames(Socket, ServerAssoc))), + ?line ?LOGVAR(Peernames), + ?line ok = gen_sctp:close(Socket), + ?line Socknames = ClientPeernames, + ?line ClientSocknames = Peernames, + ok + end, + ?line case get_addrs_by_family(FamilySpec, AddressCount) of + {ok, Addresses} when length(Addresses) =:= AddressCount -> + ?line do_open_and_connect(Addresses, hd(Addresses), Fun); + {error, Reason} -> + {skip, Reason} + end. + + get_addrs_by_family(Family, NumAddrs) -> case os:type() of @@ -1274,6 +1501,10 @@ f(F, A) -> lists:flatten(io_lib:format(F, A)). do_open_and_connect(ServerAddresses, AddressToConnectTo) -> + ?line Fun = fun (_, _, _, _, _, _) -> ok end, + ?line do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun). +%% +do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) -> ?line ServerFamily = get_family_by_addrs(ServerAddresses), ?line io:format("Serving ~p addresses: ~p~n", [ServerFamily, ServerAddresses]), @@ -1284,14 +1515,26 @@ do_open_and_connect(ServerAddresses, AddressToConnectTo) -> ?line ClientFamily = get_family_by_addr(AddressToConnectTo), ?line io:format("Connecting to ~p ~p~n", [ClientFamily, AddressToConnectTo]), - ?line S2 = ok(gen_sctp:open(0, [ClientFamily])), + ?line ClientOpts = + [ClientFamily | + case ClientFamily of + inet6 -> + [{ipv6_v6only,true}]; + _ -> + [] + end], + ?line S2 = ok(gen_sctp:open(0, ClientOpts)), + log(open), %% Verify client can connect - ?line #sctp_assoc_change{state=comm_up} = + ?line #sctp_assoc_change{state=comm_up} = S2Assoc = ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])), + log(comm_up), %% verify server side also receives comm_up from client - ?line recv_comm_up_eventually(S1), + ?line S1Assoc = recv_comm_up_eventually(S1), + ?line Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc), ?line ok = gen_sctp:close(S2), - ?line ok = gen_sctp:close(S1). + ?line ok = gen_sctp:close(S1), + Result. %% If at least one of the addresses is an ipv6 address, return inet6, else inet. get_family_by_addrs(Addresses) -> @@ -1306,9 +1549,11 @@ get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6. recv_comm_up_eventually(S) -> ?line case ok(gen_sctp:recv(S)) of - {_Addr, _Port, _Info, #sctp_assoc_change{state=comm_up}} -> - ok; - {_Addr, _Port, _Info, _OtherSctpMsg} -> + {_Addr, _Port, _Info, + #sctp_assoc_change{state=comm_up} = Assoc} -> + Assoc; + {_Addr, _Port, _Info, _OtherSctpMsg} = Msg -> + ?line log({unexpected,Msg}), ?line recv_comm_up_eventually(S) end. @@ -1367,10 +1612,10 @@ socket_bailout([]) -> socket_history({State,Flush}) -> {lists:keysort( - 2, - lists:flatten( - [[{Key,Val} || Val <- Vals] - || {Key,Vals} <- gb_trees:to_list(State)])), + 2, + lists:flatten( + [[{Key,Val} || Val <- Vals] + || {Key,Vals} <- gb_trees:to_list(State)])), Flush}. s_handler(Socket) -> @@ -1453,7 +1698,7 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> #sctp_assoc_change{ state=comm_up, inbound_streams=Is}}}|_] - when 0 =< Stream, Stream < Is-> ok; + when 0 =< Stream, Stream < Is-> ok; [] -> ok end, Key = {msg,AssocId,Stream}, @@ -1473,7 +1718,7 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> case {gb_get(Key, State),St} of {[],_} -> ok; {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} - when St =:= comm_lost; St =:= shutdown_comp -> ok + when St =:= comm_lost; St =:= shutdown_comp -> ok end, NewState = gb_push(Key, Val, State), Parent ! {self(),{Addr,Port,SAC}}, @@ -1489,8 +1734,9 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case {gb_get({assoc_change,AssocId}, State),St} of - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_], - addr_available} -> ok; + {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + when St =:= addr_available; + St =:= addr_confirmed -> ok; {[],addr_confirmed} -> ok end, Key = {paddr_change,AssocId}, @@ -1519,7 +1765,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> end. again(Socket) -> - inet:setopts(Socket, [{active,once}]). + receive + {sctp_passive,Socket} -> + [{active, false}] = ok(inet:getopts(Socket, [active])), + ok = inet:setopts(Socket,[{active,1}]) + after 0 -> + ok = inet:setopts(Socket, [{active,once}]) + end. gb_push(Key, Val, GBT) -> case gb_trees:lookup(Key, GBT) of diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 2d5827282f..ee8bfcceb1 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2012. All Rights Reserved. +%% Copyright Ericsson AB 1998-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, - no_accept/1, close_with_pending_output/1, + no_accept/1, close_with_pending_output/1, active_n/1, data_before_close/1, iter_max_socks/1, get_status/1, passive_sockets/1, accept_closed_by_other_process/1, init_per_testcase/2, end_per_testcase/2, @@ -70,7 +70,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, - iter_max_socks, passive_sockets, + iter_max_socks, passive_sockets, active_n, accept_closed_by_other_process, otp_3924, closed_socket, shutdown_active, shutdown_passive, shutdown_pending, default_options, http_bad_packet, busy_send, @@ -407,6 +407,114 @@ send_loop(Sock, Data, Left) -> ok = gen_tcp:send(Sock, Data), send_loop(Sock, Data, Left-1). +%% Test {active,N} option +active_n(doc) -> + ["Verify operation of the {active,N} option."]; +active_n(suite) -> []; +active_n(Config) when is_list(Config) -> + N = 3, + LS = ok(gen_tcp:listen(0, [{active,N}])), + [{active,N}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,-N}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,0}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + ok = inet:setopts(LS, [{active,32767}]), + {error,einval} = inet:setopts(LS, [{active,1}]), + {error,einval} = inet:setopts(LS, [{active,-32769}]), + ok = inet:setopts(LS, [{active,-32768}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,N}]), + ok = inet:setopts(LS, [{active,true}]), + [{active,true}] = ok(inet:getopts(LS, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(LS, [{active,N}]), + ok = inet:setopts(LS, [{active,once}]), + [{active,once}] = ok(inet:getopts(LS, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(LS, [{active,32768}]), + ok = inet:setopts(LS, [{active,false}]), + [{active,false}] = ok(inet:getopts(LS, [active])), + Port = ok(inet:port(LS)), + C = ok(gen_tcp:connect("localhost", Port, [{active,N}])), + [{active,N}] = ok(inet:getopts(C, [active])), + S = ok(gen_tcp:accept(LS)), + ok = inet:setopts(S, [{active,N}]), + [{active,N}] = ok(inet:getopts(S, [active])), + repeat(3, + fun(I) -> + Msg = "message "++integer_to_list(I), + ok = gen_tcp:send(C, Msg), + receive + {tcp,S,Msg} -> + ok = gen_tcp:send(S, Msg) + after + 5000 -> + exit({error,timeout}) + end, + receive + {tcp,C,Msg} -> + ok + after + 5000 -> + exit({error,timeout}) + end + end), + receive + {tcp_passive,S} -> + [{active,false}] = ok(inet:getopts(S, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + receive + {tcp_passive,C} -> + [{active,false}] = ok(inet:getopts(C, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + LS2 = ok(gen_tcp:listen(0, [{active,0}])), + receive + {tcp_passive,LS2} -> + [{active,false}] = ok(inet:getopts(LS2, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + ok = gen_tcp:close(LS2), + ok = gen_tcp:close(C), + ok = gen_tcp:close(S), + ok = gen_tcp:close(LS), + ok. + -define(OTP_3924_MAX_DELAY, 100). %% Taken out of the blue, but on intra host connections %% I expect propagation of a close to be quite fast @@ -2659,3 +2767,5 @@ oct_aloop(S,X,Times) -> gen_tcp:close(S), closed end. + +ok({ok,V}) -> V. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 4cc6018614..6bb41999c5 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -33,7 +33,7 @@ init_per_group/2,end_per_group/2]). -export([init_per_testcase/2, end_per_testcase/2]). --export([send_to_closed/1, +-export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1]). @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [send_to_closed, buffer_size, binary_passive_recv, bad_address, read_packets, open_fd, connect, - implicit_inet6]. + implicit_inet6, active_n]. groups() -> []. @@ -465,6 +465,108 @@ open_fd(Config) when is_list(Config) -> ?t:fail(io_lib:format("~w", [flush()])) end. +active_n(Config) when is_list(Config) -> + N = 3, + S1 = ok(gen_udp:open(0, [{active,N}])), + [{active,N}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,-N}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,0}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + ok = inet:setopts(S1, [{active,32767}]), + {error,einval} = inet:setopts(S1, [{active,1}]), + {error,einval} = inet:setopts(S1, [{active,-32769}]), + ok = inet:setopts(S1, [{active,-32768}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,true}]), + [{active,true}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,once}]), + [{active,once}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(S1, [{active,32768}]), + ok = inet:setopts(S1, [{active,false}]), + [{active,false}] = ok(inet:getopts(S1, [active])), + S1Port = ok(inet:port(S1)), + S2 = ok(gen_udp:open(0, [{active,N}])), + S2Port = ok(inet:port(S2)), + [{active,N}] = ok(inet:getopts(S2, [active])), + ok = inet:setopts(S1, [{active,N}]), + [{active,N}] = ok(inet:getopts(S1, [active])), + lists:foreach( + fun(I) -> + Msg = "message "++integer_to_list(I), + ok = gen_udp:send(S2, "localhost", S1Port, Msg), + receive + {udp,S1,_,S2Port,Msg} -> + ok = gen_udp:send(S1, "localhost", S2Port, Msg) + after + 5000 -> + exit({error,timeout}) + end, + receive + {udp,S2,_,S1Port,Msg} -> + ok + after + 5000 -> + exit({error,timeout}) + end + end, lists:seq(1,N)), + receive + {udp_passive,S1} -> + [{active,false}] = ok(inet:getopts(S1, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + receive + {udp_passive,S2} -> + [{active,false}] = ok(inet:getopts(S2, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + S3 = ok(gen_udp:open(0, [{active,0}])), + receive + {udp_passive,S3} -> + [{active,false}] = ok(inet:getopts(S3, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + ok = gen_udp:close(S3), + ok = gen_udp:close(S2), + ok = gen_udp:close(S1), + ok. % % Utils diff --git a/lib/kernel/test/global_SUITE_data/global_trace.erl b/lib/kernel/test/global_SUITE_data/global_trace.erl index 7da4980263..1396d86c79 100644 --- a/lib/kernel/test/global_SUITE_data/global_trace.erl +++ b/lib/kernel/test/global_SUITE_data/global_trace.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 1f7724d0dc..ed43749cc0 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -38,10 +38,10 @@ gethostnative_debug_level/0, gethostnative_debug_level/1, getif/1, getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1, - parse_strict_address/1]). + parse_strict_address/1, simple_netns/1]). -export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1, - kill_gethost/0, parallell_gethost/0]). + kill_gethost/0, parallell_gethost/0, test_netns/0]). -export([init_per_testcase/2, end_per_testcase/2]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -53,7 +53,7 @@ all() -> t_gethostnative, gethostnative_parallell, cname_loop, gethostnative_debug_level, gethostnative_soft_restart, getif, getif_ifr_name_overflow, getservbyname_overflow, - getifaddrs, parse_strict_address]. + getifaddrs, parse_strict_address, simple_netns]. groups() -> [{parse, [], [parse_hosts, parse_address]}]. @@ -183,80 +183,74 @@ t_gethostbyname(Config) when is_list(Config) -> h_addr_list = [IP]}, ?line HEntF_ = HEntF, ?line check_elems([{HEnt#hostent.h_aliases,[[],Aliases]}]), + %% + ?line FullNameU = toupper(FullName), + ?line {ok,HEntU} = inet:gethostbyname(FullNameU), + ?line FullNameU = toupper(HEntU#hostent.h_name), + ?line #hostent{ + h_addrtype = inet, + h_length = 4, + h_addr_list = [IP]} = HEntU, + ?line check_elems( + [{[toupper(H) || H <- HEntU#hostent.h_aliases], + [[],[toupper(A) || A <- Aliases]]}]), ?line {DName, _DFullName, _DIPStr, _DIP, _, _, _} = ct:get_config(test_dummy_host), ?line {error,nxdomain} = inet:gethostbyname(DName), - ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str). + ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str), + ok. t_gethostbyname_v6() -> required(v6). t_gethostbyname_v6(doc) -> "Test the inet:gethostbyname/1 inet6 function."; t_gethostbyname_v6(suite) -> []; t_gethostbyname_v6(Config) when is_list(Config) -> - ?line {Name, _, _, _,Aliases,IP_46_Str,IP_46} = - ct:get_config(test_host_ipv4_only), + {Name, FullName, IPStr, IP, Aliases} = + ct:get_config(test_host_ipv6_only), - case {inet:gethostbyname(IP_46_Str, inet6), - inet:gethostbyname(Name, inet6)} of - {{ok,HEnt46},{ok,_}} -> - ?line HEnt46_ = HEnt46#hostent{h_name = IP_46_Str, - h_addrtype = inet6, - h_length = 16, - h_addr_list = [IP_46]}, - ?line HEnt46_ = HEnt46, - ?line check_elems([{HEnt46#hostent.h_aliases,[[],Aliases]}]), - - ?line {Name6, FullName6, IPStr6, IP6, Aliases6} = - ct:get_config(test_host_ipv6_only), - ?line {ok,_} = inet:gethostbyname(IPStr6, inet6), - ?line {ok,HEnt6} = inet:gethostbyname(Name6, inet6), - ?line {ok,HEnt6} = inet:gethostbyname(list_to_atom(Name6), inet6), - ?line case HEnt6#hostent.h_addr_list of - [IP6] -> % ipv6 ok - ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6, - h_length = 16, - h_addr_list = [IP6]}, - ?line HEnt6_ = HEnt6, - ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]}, - {HEnt6#hostent.h_aliases,[[],Aliases6]}]); - _ -> % ipv4 compatible addr - ?line {ok,HEnt4} = inet:gethostbyname(Name6, inet), - ?line [IP4] = HEnt4#hostent.h_addr_list, - ?line {ok,IP46_2} = - inet_parse:ipv6_address("::ffff:"++inet_parse:ntoa(IP4)), - ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6, - h_length = 16, - h_addr_list = [IP46_2]}, - ?line HEnt6_ = HEnt6, - ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]}]) - end, - - ?line {ok,HEntF6} = inet:gethostbyname(FullName6, inet6), - ?line case HEntF6#hostent.h_addr_list of - [IP6] -> % ipv6 ok - ?line HEntF6_ = HEntF6#hostent{h_name = FullName6, - h_addrtype = inet6, - h_length = 16, - h_addr_list = [IP6]}, - ?line HEntF6_ = HEntF6, - ?line check_elems([{HEntF6#hostent.h_aliases,[[],Aliases6]}]); - _ -> % ipv4 compatible addr - ?line {ok,HEntF4} = inet:gethostbyname(FullName6, inet), - ?line [IPF4] = HEntF4#hostent.h_addr_list, - ?line {ok,IPF46_2} = - inet_parse:ipv6_address("::ffff:"++inet_parse:ntoa(IPF4)), - ?line HEntF6_ = HEntF6#hostent{h_addrtype = inet6, - h_length = 16, - h_addr_list = [IPF46_2]}, - ?line HEntF6_ = HEntF6, - ?line check_elems([{HEntF6#hostent.h_name,[Name6,FullName6]}]) - end, - - ?line {DName6, _DFullName6, _DIPStr6, _DIP6, _} = - ct:get_config(test_dummy_ipv6_host), - ?line {error,nxdomain} = inet:gethostbyname(DName6, inet6), - ok; - {_,_} -> + case inet:gethostbyname(Name, inet6) of + {ok,HEnt} -> + {ok,_} = inet:gethostbyname(IPStr, inet6), + {ok,HEnt} = inet:gethostbyname(list_to_atom(Name), inet6), + case HEnt#hostent.h_addr_list of + [IP] -> % IPv6 address + #hostent{h_addrtype = inet6, + h_length = 16} = HEnt, + check_elems( + [{HEnt#hostent.h_name,[Name,FullName]}, + {HEnt#hostent.h_aliases,[[],Aliases]}]); + [IP46] -> % IPv4 compatible address + {ok,HEnt4} = inet:gethostbyname(Name, inet), + #hostent{h_addrtype = inet, + h_length = 4, + h_addr_list = [IP4]} = HEnt4, + {ok,IP46} = + inet_parse:ipv6_address( + "::ffff:" ++ inet:ntoa(IP4)), + check_elems( + [{HEnt#hostent.h_name,[Name,FullName]}]) + end, + + {ok,HEntF} = inet:gethostbyname(FullName, inet6), + case HEntF#hostent.h_addr_list of + [IP] -> % IPv6 address + #hostent{h_name = FullName, + h_addrtype = inet6, + h_length = 16} = HEntF, + check_elems( + [{HEnt#hostent.h_aliases,[[],Aliases]}]); + [IP46F] -> % IPv4 compatible address + {ok,HEnt4F} = inet:gethostbyname(FullName, inet), + #hostent{h_addrtype = inet, + h_length = 4, + h_addr_list = [IP4F]} = HEnt4F, + {ok,IP46F} = + inet_parse:ipv6_address( + "::ffff:" ++ inet:ntoa(IP4F)), + check_elems( + [{HEntF#hostent.h_name,[Name,FullName]}]) + end; + _ -> {skip, "IPv6 is not supported on this host"} end. @@ -290,47 +284,35 @@ t_getaddr(Config) when is_list(Config) -> ?line {error,nxdomain} = inet:getaddr(DName, inet), ?line {error,nxdomain} = inet:getaddr(DFullName, inet), ?line {ok,DIP} = inet:getaddr(DIPStr, inet), - ?line {ok,DIP} = inet:getaddr(DIP, inet). + ?line {ok,DIP} = inet:getaddr(DIP, inet), + ok. t_getaddr_v6() -> required(v4) ++ required(v6). t_getaddr_v6(doc) -> "Test the inet:getaddr/2 function."; t_getaddr_v6(suite) -> []; t_getaddr_v6(Config) when is_list(Config) -> - ?line {Name,FullName,IPStr,_IP,_,IP_46_Str,IP46} = - ct:get_config(test_host_ipv4_only), - case {inet:getaddr(IP_46_Str, inet6),inet:getaddr(Name, inet6)} of - {{ok,IP46},{ok,V4Addr}} when V4Addr /= {0,0,0,0,0,0,0,1} -> - %% Since we suceeded in parsing an IPv6 address string and - %% look up the name, this computer fully supports IPv6. - ?line {ok,IP46} = inet:getaddr(IP46, inet6), - ?line {ok,IP46} = inet:getaddr(Name, inet6), - ?line {ok,IP46} = inet:getaddr(FullName, inet6), - ?line {ok,IP46} = inet:getaddr(IPStr, inet6), -%% ?line IP4toIP6 = inet:getaddr(IPStr, inet6), -%% ?line case IP4toIP6 of -%% {ok,IP46} -> -%% ?line ok; -%% {error,nxdomain} -> -%% ?line false = -%% lists:member(native, -%% inet_db:res_option(lookup)) -%% end, - ?line {Name6, FullName6, IPStr6, IP6, _} = - ct:get_config(test_host_ipv6_only), - ?line {ok,_} = inet:getaddr(list_to_atom(Name6), inet6), - ?line {ok,_} = inet:getaddr(Name6, inet6), - ?line {ok,_} = inet:getaddr(FullName6, inet6), - ?line {ok,IP6} = inet:getaddr(IP6, inet6), - ?line {ok,IP6} = inet:getaddr(IPStr6, inet6), - - ?line {DName6, DFullName6, DIPStr6, DIP6, _} = + {Name,FullName,IPStr,IP,_} = + ct:get_config(test_host_ipv6_only), + + case inet:getaddr(Name, inet6) of + {ok,Addr} -> + IP = Addr, + {ok,IP} = inet:getaddr(toupper(Name), inet6), + {ok,IP} = inet:getaddr(list_to_atom(Name), inet6), + {ok,IP} = inet:getaddr(list_to_atom(toupper(Name)), inet6), + {ok,IP} = inet:getaddr(FullName, inet6), + {ok,IP} = inet:getaddr(toupper(FullName), inet6), + {ok,IP} = inet:getaddr(IP, inet6), + {ok,IP} = inet:getaddr(IPStr, inet6), + %% + {DName,DFullName,DIPStr,DIP,_} = ct:get_config(test_dummy_ipv6_host), - ?line {error,nxdomain} = inet:getaddr(DName6, inet6), - ?line {error,nxdomain} = inet:getaddr(DFullName6, inet6), - ?line {ok,DIP6} = inet:getaddr(DIPStr6, inet6), - ?line {ok,DIP6} = inet:getaddr(DIP6, inet6), + {error,nxdomain} = inet:getaddr(DName, inet6), + {error,nxdomain} = inet:getaddr(DFullName, inet6), + {ok,DIP} = inet:getaddr(DIPStr, inet6), + {ok,DIP} = inet:getaddr(DIP, inet6), ok; - {_,_} -> + _ -> {skip, "IPv6 is not supported on this host"} end. @@ -608,8 +590,12 @@ t_parse_address(Func, [String|L]) -> t_parse_address(Func, L). parse_strict_address(Config) when is_list(Config) -> - {ok, Ipv4} = inet:parse_strict_address("127.0.0.1"), - {ok, Ipv6} = inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088"). + {ok, {127,0,0,1}} = + inet:parse_strict_address("127.0.0.1"), + {ok, {3089,3106,23603,50240,21952,50796,119,136}} = + inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088"), + {ok, {3089,3106,23603,50240,0,0,119,136}} = + inet:parse_strict_address("c11:0c22:5c33:c440::077:0088"). t_gethostnative(suite) ->[]; t_gethostnative(doc) ->[]; @@ -1102,3 +1088,107 @@ ip_member({127,_,_,_}, [{127,_,_,_}|_]) -> true; ip_member(K, [K|_]) -> true; ip_member(K, [_|T]) -> ip_member(K, T); ip_member(_, []) -> false. + +%% Case fold to upper case according to RFC 4343 +%% +toupper([C|Cs]) when is_integer(C) -> + if $a =< C, C =< $z -> + [(C - $a + $A)|toupper(Cs)]; + true -> + [C|toupper(Cs)] + end; +toupper([]) -> + []. + + +simple_netns(Config) when is_list(Config) -> + {ok,U} = gen_udp:open(0), + case inet:setopts(U, [{netns,""}]) of + ok -> + jog_netns_opt(U), + ok = gen_udp:close(U), + %% + {ok,L} = gen_tcp:listen(0, []), + jog_netns_opt(L), + ok = gen_tcp:close(L), + %% + {ok,S} = gen_sctp:open(), + jog_netns_opt(S), + ok = gen_sctp:close(S); + {error,einval} -> + {skip,"setns() not supported"} + end. + +jog_netns_opt(S) -> + %% This is just jogging the option mechanics + ok = inet:setopts(S, [{netns,""}]), + {ok,[{netns,""}]} = inet:getopts(S, [netns]), + ok = inet:setopts(S, [{netns,"/proc/self/ns/net"}]), + {ok,[{netns,"/proc/self/ns/net"}]} = inet:getopts(S, [netns]), + ok. + + +%% Manual test to be run outside test_server in an emulator +%% started by root, in a machine with setns() support... +test_netns() -> + DefaultIF = v1, + DefaultIP = {192,168,1,17}, + Namespace = "test", + NamespaceIF = v2, + NamespaceIP = {192,168,1,18}, + %% + DefaultIPString = inet_parse:ntoa(DefaultIP), + NamespaceIPString = inet_parse:ntoa(NamespaceIP), + cmd("ip netns add ~s", + [Namespace]), + cmd("ip link add name ~w type veth peer name ~w netns ~s", + [DefaultIF,NamespaceIF,Namespace]), + cmd("ip netns exec ~s ip addr add ~s/30 dev ~w", + [Namespace,NamespaceIPString,NamespaceIF]), + cmd("ip netns exec ~s ip link set ~w up", + [Namespace,NamespaceIF]), + cmd("ip addr add ~s/30 dev ~w", + [DefaultIPString,DefaultIF]), + cmd("ip link set ~w up", + [DefaultIF]), + try test_netns( + {DefaultIF,DefaultIP}, + filename:join("/var/run/netns/", Namespace), + {NamespaceIF,NamespaceIP}) of + Result -> + io:put_chars(["#### Test done",io_lib:nl()]), + Result + after + cmd("ip link delete ~w type veth", + [DefaultIF]), + cmd("ip netns delete ~s", + [Namespace]) + end. + +test_netns({DefaultIF,DefaultIP}, Namespace, {NamespaceIF,NamespaceIP}) -> + {ok,ListenSocket} = gen_tcp:listen(0, [{active,false}]), + {ok,[{addr,DefaultIP}]} = inet:ifget(ListenSocket, DefaultIF, [addr]), + {ok,ListenPort} = inet:port(ListenSocket), + {ok,ConnectSocket} = + gen_tcp:connect( + DefaultIP, ListenPort, [{active,false},{netns,Namespace}], 3000), + {ok,[{addr,NamespaceIP}]} = inet:ifget(ConnectSocket, NamespaceIF, [addr]), + {ok,ConnectPort} = inet:port(ConnectSocket), + {ok,AcceptSocket} = gen_tcp:accept(ListenSocket, 0), + {ok,AcceptPort} = inet:port(AcceptSocket), + {ok,{NamespaceIP,ConnectPort}} = inet:peername(AcceptSocket), + {ok,{DefaultIP,AcceptPort}} = inet:peername(ConnectSocket), + ok = gen_tcp:send(ConnectSocket, "data"), + ok = gen_tcp:close(ConnectSocket), + {ok,"data"} = gen_tcp:recv(AcceptSocket, 4, 1000), + {error,closed} = gen_tcp:recv(AcceptSocket, 1, 1000), + ok = gen_tcp:close(AcceptSocket), + ok = gen_tcp:close(ListenSocket). + +cmd(Cmd, Args) -> + cmd(io_lib:format(Cmd, Args)). +%% +cmd(CmdString) -> + io:put_chars(["# ",CmdString,io_lib:nl()]), + io:put_chars([os:cmd(CmdString++" ; echo ' =>' $?")]), + ok. diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index f3ba28e4f9..1bc93e3138 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -282,6 +282,7 @@ basic(doc) -> basic(Config) when is_list(Config) -> NS = ns(Config), Name = "ns.otptest", + NameC = caseflip(Name), IP = {127,0,0,254}, %% %% nslookup @@ -292,6 +293,17 @@ basic(Config) when is_list(Config) -> Bin1 = inet_dns:encode(Msg1), %%io:format("Bin1 = ~w~n", [Bin1]), {ok,Msg1} = inet_dns:decode(Bin1), + %% Now with scrambled case + {ok,Msg1b} = inet_res:nslookup(NameC, in, a, [NS]), + io:format("~p~n", [Msg1b]), + [RR1b] = inet_dns:msg(Msg1b, anlist), + IP = inet_dns:rr(RR1b, data), + Bin1b = inet_dns:encode(Msg1b), + %%io:format("Bin1b = ~w~n", [Bin1b]), + {ok,Msg1b} = inet_dns:decode(Bin1b), + true = + (tolower(inet_dns:rr(RR1, domain)) + =:= tolower(inet_dns:rr(RR1b, domain))), %% %% resolve {ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]},verbose]), @@ -301,15 +313,29 @@ basic(Config) when is_list(Config) -> Bin2 = inet_dns:encode(Msg2), %%io:format("Bin2 = ~w~n", [Bin2]), {ok,Msg2} = inet_dns:decode(Bin2), + %% Now with scrambled case + {ok,Msg2b} = inet_res:resolve(NameC, in, a, [{nameservers,[NS]},verbose]), + io:format("~p~n", [Msg2b]), + [RR2b] = inet_dns:msg(Msg2b, anlist), + IP = inet_dns:rr(RR2b, data), + Bin2b = inet_dns:encode(Msg2b), + %%io:format("Bin2b = ~w~n", [Bin2b]), + {ok,Msg2b} = inet_dns:decode(Bin2b), + true = + (tolower(inet_dns:rr(RR2, domain)) + =:= tolower(inet_dns:rr(RR2b, domain))), %% %% lookup [IP] = inet_res:lookup(Name, in, a, [{nameservers,[NS]},verbose]), + [IP] = inet_res:lookup(NameC, in, a, [{nameservers,[NS]},verbose]), %% %% gethostbyname {ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(Name), + {ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(NameC), %% %% getbyname {ok,#hostent{h_addr_list=[IP]}} = inet_res:getbyname(Name, a), + {ok,#hostent{h_addr_list=[IP]}} = inet_res:getbyname(NameC, a), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -317,63 +343,115 @@ basic(Config) when is_list(Config) -> resolve(doc) -> ["Lookup different records using resolve/2..4"]; resolve(Config) when is_list(Config) -> + Class = in, NS = ns(Config), Domain = "otptest", RDomain4 = "0.0.127.in-addr.arpa", RDomain6 = "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", Name = "resolve."++Domain, - L = [{in,a,Name,[{127,0,0,28}],undefined}, - {in,aaaa,Name,[{0,0,0,0,0,0,32512,28}],undefined}, - {in,cname,"cname."++Name,[Name],undefined}, - {in,a,"cname."++Name,[Name,{127,0,0,28}],undefined}, - {in,ns,"ns."++Name,[],[Name]}, - {in,soa,Domain,[],[{"ns.otptest","lsa.otptest",1,60,10,300,30}]}, + L = [{a,Name,[{a,{127,0,0,28}}],undefined}, + {aaaa,Name,[{aaaa,{0,0,0,0,0,0,32512,28}}],undefined}, + {cname,"cname."++Name,[{cname,Name}],undefined}, + {a,"cname."++Name,[{cname,Name},{a,{127,0,0,28}}],undefined}, + {ns,"ns."++Name,[],[{ns,Name}]}, + {soa,Domain,[],[{soa,{"ns.otptest","lsa.otptest",1,60,10,300,30}}]}, %% WKS: protocol TCP (6), services (bits) TELNET (23) and SMTP (25) - {in,wks,"wks."++Name,[{{127,0,0,28},6,<<0,0,1,64>>}],undefined}, - {in,ptr,"28."++RDomain4,[Name],undefined}, - {in,ptr,"c.1.0.0.0.0.f.7."++RDomain6,[Name],undefined}, - {in,hinfo,Name,[{"BEAM","Erlang/OTP"}],undefined}, - {in,mx,RDomain4,[{10,"mx."++Domain}],undefined}, - {in,srv,"_srv._tcp."++Name,[{10,3,4711,Name}],undefined}, - {in,naptr,"naptr."++Name, - [{10,5,"s","http","","_srv._tcp."++Name}],undefined}, - {in,txt,"txt."++Name, - [["Hej ","du ","glade "],["ta ","en ","spade!"]],undefined}, - {in,mb,"mb."++Name,["mx."++Name],undefined}, - {in,mg,"mg."++Name,["lsa."++Domain],undefined}, - {in,mr,"mr."++Name,["lsa."++Domain],undefined}, - {in,minfo,"minfo."++Name, - [{"minfo-owner."++Name,"minfo-bounce."++Name}],undefined}, - {in,any,"cname."++Name,[Name],undefined}, - {in,any,Name,[{127,0,0,28}, - {0,0,0,0,0,0,32512,28}, - {"BEAM","Erlang/OTP"}],undefined} + {wks,"wks."++Name,[{wks,{{127,0,0,28},6,<<0,0,1,64>>}}],undefined}, + {ptr,"28."++RDomain4,[{ptr,Name}],undefined}, + {ptr,"c.1.0.0.0.0.f.7."++RDomain6,[{ptr,Name}],undefined}, + {hinfo,Name,[{hinfo,{"BEAM","Erlang/OTP"}}],undefined}, + {mx,RDomain4,[{mx,{10,"mx."++Domain}}],undefined}, + {srv,"_srv._tcp."++Name,[{srv,{10,3,4711,Name}}],undefined}, + {naptr,"naptr."++Name, + [{naptr,{10,5,"s","http","","_srv._tcp."++Name}}], + undefined}, + {txt,"txt."++Name, + [{txt,["Hej ","du ","glade "]},{txt,["ta ","en ","spade!"]}], + undefined}, + {mb,"mb."++Name,[{mb,"mx."++Name}],undefined}, + {mg,"mg."++Name,[{mg,"Lsa."++Domain}],undefined}, + {mr,"mr."++Name,[{mr,"LSA."++Domain}],undefined}, + {minfo,"minfo."++Name, + [{minfo,{"minfo-OWNER."++Name,"MinfoBounce."++Name}}], + undefined}, + {any,"cname."++Name,[{cname,Name}],undefined}, + {any,Name, + [{a,{127,0,0,28}}, + {aaaa,{0,0,0,0,0,0,32512,28}}, + {hinfo,{"BEAM","Erlang/OTP"}}], + undefined} ], - resolve([{edns,false},{nameservers,[NS]}], L), - resolve([{edns,0},{nameservers,[NS]}], L). - -resolve(_Opts, []) -> ok; -resolve(Opts, [{Class,Type,Name,Answers,Authority}=Q|Qs]) -> + resolve(Class, [{edns,0},{nameservers,[NS]}], L), + resolve(Class, [{edns,false},{nameservers,[NS]}], L), + %% Again, to see ensure the cache does not mess things up + resolve(Class, [{edns,0},{nameservers,[NS]}], L), + resolve(Class, [{edns,false},{nameservers,[NS]}], L). + +resolve(_Class, _Opts, []) -> + ok; +resolve(Class, Opts, [{Type,Nm,Answers,Authority}=Q|Qs]) -> io:format("Query: ~p~nOptions: ~p~n", [Q,Opts]), - {ok,Msg} = inet_res:resolve(Name, Class, Type, Opts), + {Name,NameC} = + case erlang:phash2(Q) band 4 of + 0 -> + {Nm,caseflip(Nm)}; + _ -> + {caseflip(Nm),Nm} + end, AnList = if Answers =/= undefined -> - lists:sort(Answers); + normalize_answers(Answers); true -> undefined end, NsList = if Authority =/= undefined -> - lists:sort(Authority); + normalize_answers(Authority); true -> undefined end, - case {lists:sort - ([inet_dns:rr(RR, data) || RR <- inet_dns:msg(Msg, anlist)]), - lists:sort - ([inet_dns:rr(RR, data) || RR <- inet_dns:msg(Msg, nslist)])} of + {ok,Msg} = inet_res:resolve(Name, Class, Type, Opts), + check_msg(Class, Type, Msg, AnList, NsList), + {ok,MsgC} = inet_res:resolve(NameC, Class, Type, Opts), + check_msg(Class, Type, MsgC, AnList, NsList), + resolve(Class, Opts, Qs). + + + +normalize_answers(AnList) -> + lists:sort([normalize_answer(Answer) || Answer <- AnList]). + +normalize_answer({soa,{NS,HM,Ser,Ref,Ret,Exp,Min}}) -> + {tolower(NS),tolower_email(HM),Ser,Ref,Ret,Exp,Min}; +normalize_answer({mx,{Prio,DN}}) -> + {Prio,tolower(DN)}; +normalize_answer({srv,{Prio,Weight,Port,DN}}) -> + {Prio,Weight,Port,tolower(DN)}; +normalize_answer({naptr,{Order,Pref,Flags,Service,RE,Repl}}) -> + {Order,Pref,Flags,Service,RE,tolower(Repl)}; +normalize_answer({minfo,{RespM,ErrM}}) -> + {tolower_email(RespM),tolower_email(ErrM)}; +normalize_answer({T,MN}) when T =:= mg; T =:= mr -> + tolower_email(MN); +normalize_answer({T,DN}) when T =:= cname; T =:= ns; T =:= ptr; T =:= mb -> + tolower(DN); +normalize_answer(Answer) -> + Answer. + +check_msg(Class, Type, Msg, AnList, NsList) -> + io:format("check_msg Type: ~p, Msg: ~p~n.", [Type,Msg]), + case {normalize_answers( + [begin + Class = inet_dns:rr(RR, class), + {inet_dns:rr(RR, type),inet_dns:rr(RR, data)} + end || RR <- inet_dns:msg(Msg, anlist)]), + normalize_answers( + [begin + Class = inet_dns:rr(RR, class), + {inet_dns:rr(RR, type),inet_dns:rr(RR, data)} + end || RR <- inet_dns:msg(Msg, nslist)])} of {AnList,NsList} -> ok; {NsList,AnList} when Type =:= ns -> @@ -389,7 +467,7 @@ resolve(Opts, [{Class,Type,Name,Answers,Authority}=Q|Qs]) -> end, Buf = inet_dns:encode(Msg), {ok,Msg} = inet_dns:decode(Buf), - resolve(Opts, Qs). + ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -497,6 +575,7 @@ files_monitor(Config) when is_list(Config) -> do_files_monitor(Config) -> Dir = ?config(priv_dir, Config), {ok,Hostname} = inet:gethostname(), + io:format("Hostname = ~p.~n", [Hostname]), FQDN = case inet_db:res_option(domain) of "" -> @@ -504,11 +583,13 @@ do_files_monitor(Config) -> _ -> Hostname++"."++inet_db:res_option(domain) end, + io:format("FQDN = ~p.~n", [FQDN]), HostsFile = filename:join(Dir, "files_monitor_hosts"), ResolvConf = filename:join(Dir, "files_monitor_resolv.conf"), ok = inet_db:res_option(resolv_conf, ResolvConf), ok = inet_db:res_option(hosts_file, HostsFile), [] = inet_db:res_option(search), + %% The inet function will use its final fallback to find this host {ok,#hostent{h_name = Hostname, h_addrtype = inet, h_length = 4, @@ -521,6 +602,7 @@ do_files_monitor(Config) -> {error,nxdomain} = inet_res:gethostbyname(FQDN), {ok,{127,0,0,10}} = inet:getaddr("mx.otptest", inet), {ok,{0,0,0,0,0,0,32512,28}} = inet:getaddr("resolve.otptest", inet6), + %% The inet function will use its final fallback to find this host {ok,#hostent{h_name = Hostname, h_addrtype = inet6, h_length = 16, @@ -603,3 +685,41 @@ ipv4_to_ipv6() -> inet_SUITE:ipv4_to_ipv6(). ipv4_to_ipv6(Config) -> inet_SUITE:ipv4_to_ipv6(Config). host_and_addr() -> inet_SUITE:host_and_addr(). host_and_addr(Config) -> inet_SUITE:host_and_addr(Config). + + + +%% Case flip helper + +caseflip([C|Cs]) when is_integer(C), $a =< C, C =< $z -> + [(C - $a + $A)|caseflip_skip(Cs)]; +caseflip([C|Cs]) when is_integer(C), $A =< C, C =< $Z -> + [(C - $A + $a)|caseflip_skip(Cs)]; +caseflip([C|Cs]) -> + [C|caseflip(Cs)]; +caseflip([]) -> + []. + +caseflip_skip([C|Cs]) when is_integer(C), $a =< C, C =< $z -> + [C|caseflip(Cs)]; +caseflip_skip([C|Cs]) when is_integer(C), $A =< C, C =< $Z -> + [C|caseflip(Cs)]; +caseflip_skip([C|Cs]) -> + [C|caseflip_skip(Cs)]; +caseflip_skip([]) -> + []. + +tolower_email([$.|Cs]) -> + [$.|tolower(Cs)]; +tolower_email([C|Cs]) -> + [C|tolower_email(Cs)]. + +%% Case fold to lower case according to RFC 4343 +%% +tolower([C|Cs]) when is_integer(C) -> + if $A =< C, C =< $Z -> + [(C - $A + $a)|tolower(Cs)]; + true -> + [C|tolower(Cs)] + end; +tolower([]) -> + []. diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf b/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf index 0b01b25204..2d68f6e59c 100644 --- a/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf +++ b/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf @@ -2,11 +2,11 @@ zone "." in { type master; file "root.zone"; }; -zone "0.0.127.in-addr.arpa" in { +zone "0.0.127.in-addr.arpa." in { type master; file "0.0.127.in-addr.arpa.zone"; }; -zone "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" in { +zone "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." in { type master; file "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.zone"; -};
\ No newline at end of file +}; diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone b/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone index 11cba18d45..5a56eac95c 100644 --- a/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone +++ b/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone @@ -43,8 +43,8 @@ naptr.resolve.otptest IN NAPTR 10 5 "S" "HTTP" "" _srv._tcp.resolve.otptest txt.resolve.otptest IN TXT "Hej " "du " "glade " txt.resolve.otptest IN TXT "ta " "en " "spade!" mb.resolve.otptest IN MB mx.resolve.otptest -mg.resolve.otptest IN MG lsa.otptest -mr.resolve.otptest IN MR lsa.otptest -minfo.resolve.otptest IN MINFO minfo-owner.resolve.otptest minfo-bounce.resolve.otptest +mg.resolve.otptest IN MG Lsa.otptest +mr.resolve.otptest IN MR LSA.otptest +minfo.resolve.otptest IN MINFO minfo-OWNER.resolve.otptest MinfoBounce.resolve.otptest ns.otptest IN A 127.0.0.254 diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index 185751fead..9d236a8a0a 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index d7d9434b1f..a375adceea 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -22,7 +22,7 @@ init_per_group/2,end_per_group/2, get_columns_and_rows/1, exit_initial/1, job_control_local/1, job_control_remote/1, - job_control_remote_noshell/1]). + job_control_remote_noshell/1,ctrl_keys/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% For spawn @@ -41,7 +41,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [get_columns_and_rows, exit_initial, job_control_local, - job_control_remote, job_control_remote_noshell]. + job_control_remote, job_control_remote_noshell, + ctrl_keys]. groups() -> []. @@ -289,7 +290,51 @@ job_control_remote_noshell(Config) when is_list(Config) -> ?line stop_noshell_node(NSNode), ?line Res end. - + +ctrl_keys(suite) -> []; +ctrl_keys(doc) -> ["Tests various control keys"]; +ctrl_keys(_Conf) when is_list(_Conf) -> + Cu=[$\^u], + Cw=[$\^w], + Home=[27,$O,$H], + End=[27,$O,$F], + rtnode([{putline,""}, + {putline,"2."}, + {getline,"2"}, + {putline,"\"hello "++Cw++"world\"."}, % test <CTRL>+W + {getline,"\"world\""}, + {putline,"\"hello "++Cu++"\"world\"."}, % test <CTRL>+U + {getline,"\"world\""}, + {putline,"world\"."++Home++"\"hello "}, % test <HOME> + {getline,"\"hello world\""}, + {putline,"world"++Home++"\"hello "++End++"\"."}, % test <END> + {getline,"\"hello world\""}] + ++wordLeft()++wordRight(),[]). + + +wordLeft() -> + L1=[27,27,$[,$D], + L2=[27]++"[5D", + L3=[27]++"[1;5D", + wordLeft(L1)++wordLeft(L2)++wordLeft(L3). + +wordLeft(Chars) -> + End=[27,$O,$F], + [{putline,"\"world\""++Chars++"hello "++End++"."}, + {getline,"\"hello world\""}]. + +wordRight() -> + R1=[27,27,$[,$C], + R2=[27]++"[5C", + R3=[27]++"[1;5C", + wordRight(R1)++wordRight(R2)++wordRight(R3). + +wordRight(Chars) -> + Home=[27,$O,$H], + [{putline,"world"++Home++"\"hello "++Chars++"\"."}, + {getline,"\"hello world\""}]. + + rtnode(C,N) -> rtnode(C,N,[]). rtnode(Commands,Nodename,ErlPrefix) -> diff --git a/lib/kernel/test/kernel_smoke.spec b/lib/kernel/test/kernel_smoke.spec new file mode 100644 index 0000000000..e5d8273c56 --- /dev/null +++ b/lib/kernel/test/kernel_smoke.spec @@ -0,0 +1,9 @@ +{config, "../test_server/ts.config"}. +{config, "../test_server/ts.unix.config"}. + +{cases,"../kernel_test", inet_SUITE,[t_gethostbyaddr,t_gethostbyname, + t_gethostbyaddr_v6,t_gethostbyname_v6,t_gethostnative,getifaddrs]}. +{cases,"../kernel_test", inet_res_SUITE,[gethostbyaddr,gethostbyname, + gethostbyaddr_v6,gethostbyname_v6,basic]}. +{cases,"../kernel_test", gen_tcp_echo_SUITE,[active_echo]}. +{cases,"../kernel_test", heart_SUITE,[reboot]}. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index bd237cb513..3be6f39d95 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -178,7 +178,7 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z)) end, lists:seq(1,8)), - Strategies = [filtered,huffman_only,default], + Strategies = [filtered,huffman_only,rle,default], lists:foreach(fun(Strategy) -> ?line Z = zlib:open(), ?m(ok, zlib:deflateInit(Z,best_speed,deflated,-15,8,Strategy)), @@ -220,7 +220,6 @@ api_deflateParams(Config) when is_list(Config) -> ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(ok, zlib:deflateParams(Z1, best_compression, huffman_only)), ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), - ?m({'EXIT',_}, zlib:deflateParams(Z1,best_speed, filtered)), ?m(ok, zlib:close(Z1)). api_deflate(doc) -> "Test deflate"; @@ -565,8 +564,8 @@ intro(Config) when is_list(Config) -> large_deflate(doc) -> "Test deflate large file, which had a bug reported on erlang-bugs"; large_deflate(suite) -> []; large_deflate(Config) when is_list(Config) -> - large_deflate(). -large_deflate() -> + large_deflate_do(). +large_deflate_do() -> ?line Z = zlib:open(), ?line Plain = rand_bytes(zlib:getBufSize(Z)*5), ?line ok = zlib:deflateInit(Z), @@ -899,7 +898,7 @@ worker(Seed, FnATpl, Parent) -> Parent ! self(). worker_loop(0, _FnATpl) -> - large_deflate(), % the time consuming one as finale + large_deflate_do(), % the time consuming one as finale ok; worker_loop(N, FnATpl) -> {F,A} = element(random:uniform(size(FnATpl)),FnATpl), diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 23af0599fc..dd5316b825 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 2.17 +KERNEL_VSN = 3.0 |