aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/Makefile22
-rw-r--r--lib/kernel/doc/src/app.xml4
-rw-r--r--lib/kernel/doc/src/code.xml6
-rw-r--r--lib/kernel/doc/src/error_logger.xml4
-rw-r--r--lib/kernel/doc/src/file.xml114
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml95
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml63
-rw-r--r--lib/kernel/doc/src/gen_udp.xml57
-rw-r--r--lib/kernel/doc/src/heart.xml2
-rw-r--r--lib/kernel/doc/src/inet.xml103
-rw-r--r--lib/kernel/doc/src/kernel_app.xml13
-rw-r--r--lib/kernel/doc/src/make.dep28
-rw-r--r--lib/kernel/doc/src/net_kernel.xml4
-rw-r--r--lib/kernel/doc/src/notes.xml362
-rw-r--r--lib/kernel/doc/src/os.xml15
-rw-r--r--lib/kernel/doc/src/packages.xml6
-rw-r--r--lib/kernel/examples/Makefile4
-rw-r--r--lib/kernel/examples/uds_dist/c_src/uds_drv.c4
-rw-r--r--lib/kernel/include/dist.hrl2
-rw-r--r--lib/kernel/include/dist_util.hrl2
-rw-r--r--lib/kernel/include/file.hrl11
-rw-r--r--lib/kernel/include/net_address.hrl2
-rw-r--r--lib/kernel/src/Makefile16
-rw-r--r--lib/kernel/src/application.erl2
-rw-r--r--lib/kernel/src/application_controller.erl19
-rw-r--r--lib/kernel/src/auth.erl14
-rw-r--r--lib/kernel/src/code.erl42
-rw-r--r--lib/kernel/src/code_server.erl49
-rw-r--r--lib/kernel/src/disk_log.erl59
-rw-r--r--lib/kernel/src/disk_log.hrl4
-rw-r--r--lib/kernel/src/erts_debug.erl22
-rw-r--r--lib/kernel/src/file.erl204
-rw-r--r--lib/kernel/src/file_io_server.erl5
-rw-r--r--lib/kernel/src/file_server.erl11
-rw-r--r--lib/kernel/src/gen_sctp.erl43
-rw-r--r--lib/kernel/src/gen_tcp.erl14
-rw-r--r--lib/kernel/src/gen_udp.erl5
-rw-r--r--lib/kernel/src/global.erl35
-rw-r--r--lib/kernel/src/heart.erl4
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl18
-rw-r--r--lib/kernel/src/inet.erl112
-rw-r--r--lib/kernel/src/inet6_sctp.erl17
-rw-r--r--lib/kernel/src/inet6_tcp.erl8
-rw-r--r--lib/kernel/src/inet6_udp.erl6
-rw-r--r--lib/kernel/src/inet_int.hrl18
-rw-r--r--lib/kernel/src/inet_sctp.erl21
-rw-r--r--lib/kernel/src/inet_tcp.erl8
-rw-r--r--lib/kernel/src/inet_udp.erl4
-rw-r--r--lib/kernel/src/kernel.appup.src28
-rw-r--r--lib/kernel/src/rpc.erl2
-rw-r--r--lib/kernel/src/user_drv.erl10
-rw-r--r--lib/kernel/src/user_sup.erl8
-rw-r--r--lib/kernel/test/Makefile15
-rw-r--r--lib/kernel/test/application_SUITE.erl30
-rw-r--r--lib/kernel/test/application_SUITE_data/Makefile.src5
-rw-r--r--lib/kernel/test/application_SUITE_data/deadlock/deadlock.app8
-rw-r--r--lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl69
-rw-r--r--lib/kernel/test/bif_SUITE.erl94
-rw-r--r--lib/kernel/test/code_SUITE.erl63
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl24
-rw-r--r--lib/kernel/test/erl_boot_server_SUITE.erl2
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl18
-rw-r--r--lib/kernel/test/file_SUITE.erl171
-rw-r--r--lib/kernel/test/file_name_SUITE.erl52
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl1148
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl10
-rw-r--r--lib/kernel/test/gen_tcp_echo_SUITE.erl11
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl159
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl69
-rw-r--r--lib/kernel/test/global_SUITE.erl17
-rw-r--r--lib/kernel/test/inet_SUITE.erl8
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl13
-rwxr-xr-xlib/kernel/test/inet_res_SUITE_data/run-named36
-rw-r--r--lib/kernel/test/init_SUITE.erl4
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl6
-rw-r--r--lib/kernel/test/kernel_SUITE.erl64
-rw-r--r--lib/kernel/test/os_SUITE.erl18
-rw-r--r--lib/kernel/test/pg2_SUITE.erl1
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl217
-rw-r--r--lib/kernel/test/sendfile_SUITE.erl407
-rw-r--r--lib/kernel/test/seq_trace_SUITE_data/echo_drv.c21
-rw-r--r--lib/kernel/test/wrap_log_reader_SUITE.erl2
-rw-r--r--lib/kernel/test/zlib_SUITE.erl21
-rw-r--r--lib/kernel/vsn.mk2
84 files changed, 3776 insertions, 740 deletions
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index 214e994889..78e5f7bc26 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -155,18 +155,18 @@ $(SPECDIR)/specs_zlib_stub.xml:
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(HTMLDIR)/* \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man4
- $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
- $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6
+ "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4"
+ $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
+ $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
release_spec:
diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml
index ff8a12fe97..1914844b37 100644
--- a/lib/kernel/doc/src/app.xml
+++ b/lib/kernel/doc/src/app.xml
@@ -4,7 +4,7 @@
<fileref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -75,7 +75,7 @@ MaxT int() infinity
Names [Name] []
Apps [App] []
Env [{Par,Val}] []
-Start {Module,StartArgs} undefined
+Start {Module,StartArgs} []
Phases [{Phase,PhaseArgs}] undefined
Module = Name = App = Par = Phase = atom()
Val = StartArgs = PhaseArgs = term()</code>
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index 6b89711924..ee687511a3 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -229,10 +229,10 @@
<c>-code_path_choice Choice</c>. If the flag is set to <c>relaxed</c>,
the code server will instead choose a suitable directory
depending on the actual file structure. If there exists a regular
- application ebin directory,situation it will be choosen. But if it does
- not exist, the ebin directory in the archive is choosen if it
+ application ebin directory,situation it will be chosen. But if it does
+ not exist, the ebin directory in the archive is chosen if it
exists. If neither of them exists the original directory will be
- choosen.</p>
+ chosen.</p>
<p>The command line flag <c>-code_path_choice Choice</c> does also
affect how <c>init</c> interprets the <c>boot script</c>. The
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index 2d95f96ac7..ec3274965a 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -49,7 +49,7 @@
that events are logged to file instead, or not logged at all, see
<seealso marker="kernel_app">kernel(6)</seealso>.</p>
<p>Also the SASL application, if started, adds its own event
- handler, which by default writes supervisor-, crash- and progress
+ handler, which by default writes supervisor, crash and progress
reports to tty. See
<seealso marker="sasl:sasl_app">sasl(6)</seealso>.</p>
<p>It is recommended that user defined applications should report
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 7db20e6343..772eff13cc 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -149,6 +149,12 @@
<datatype>
<name name="mode"/>
</datatype>
+ <datatype>
+ <name name="file_info_option"/>
+ </datatype>
+ <datatype>
+ <name name="sendfile_option"/>
+ </datatype>
</datatypes>
<funcs>
@@ -409,7 +415,7 @@
<name>file_info(Filename) -> {ok, FileInfo} | {error, Reason}</name>
<fsummary>Get information about a file (deprecated)</fsummary>
<desc>
- <p>This function is obsolete. Use <c>read_file_info/1</c>
+ <p>This function is obsolete. Use <c>read_file_info/1,2</c>
instead.</p>
</desc>
</func>
@@ -536,7 +542,7 @@
<desc>
<p>Makes a hard link from <c><anno>Existing</anno></c> to
<c><anno>New</anno></c>, on
- platforms that support links (Unix). This function returns
+ platforms that support links (Unix and Windows). This function returns
<c>ok</c> if the link was successfully created, or
<c>{error, <anno>Reason</anno>}</c>. On platforms that do not support
links, <c>{error,enotsup}</c> is returned.</p>
@@ -563,11 +569,12 @@
<name name="make_symlink" arity="2"/>
<fsummary>Make a symbolic link to a file or directory</fsummary>
<desc>
- <p>This function creates a symbolic link <c><anno>Name2</anno></c> to
- the file or directory <c><anno>Name1</anno></c>, on platforms that
- support
- symbolic links (most Unix systems). <c><anno>Name1</anno></c> need not
- exist. This function returns <c>ok</c> if the link was
+ <p>This function creates a symbolic link <c><anno>New</anno></c> to
+ the file or directory <c><anno>Existing</anno></c>, on platforms that
+ support symbolic links (most Unix systems and Windows beginning with
+ Vista).
+ <c><anno>Existing</anno></c> need not exist.
+ This function returns <c>ok</c> if the link was
successfully created, or <c>{error, <anno>Reason</anno>}</c>.
On platforms
that do not support symbolic links, <c>{error, enotsup}</c>
@@ -577,11 +584,11 @@
<tag><c>eacces</c></tag>
<item>
<p>Missing read or write permissions for the parent directories
- of <c><anno>Name1</anno></c> or <c><anno>Name2</anno></c>.</p>
+ of <c><anno>Existing</anno></c> or <c><anno>New</anno></c>.</p>
</item>
<tag><c>eexist</c></tag>
<item>
- <p><c><anno>Name2</anno></c> already exists.</p>
+ <p><c><anno>New</anno></c> already exists.</p>
</item>
<tag><c>enotsup</c></tag>
<item>
@@ -1185,6 +1192,7 @@
</func>
<func>
<name name="read_file_info" arity="1"/>
+ <name name="read_file_info" arity="2"/>
<fsummary>Get information about a file</fsummary>
<desc>
<p>Retrieves information about a file. Returns
@@ -1196,6 +1204,20 @@
from which the function is called:</p>
<code type="none">
-include_lib("kernel/include/file.hrl").</code>
+ <p>The time type returned in <c>atime</c>, <c>mtime</c> and <c>ctime</c>
+ is dependent on the time type set in <c>Opts :: {time, Type}</c>.
+ Type <c>local</c> will return local time, <c>universal</c> will
+ return universal time and <c>posix</c> will return seconds since
+ or before unix time epoch which is 1970-01-01 00:00 UTC.
+ Default is <c>{time, local}</c>.
+ </p>
+ <note>
+ <p>
+ Since file times is stored in posix time on most OS it is
+ faster to query file information with the <c>posix</c> option.
+ </p>
+ </note>
+
<p>The record <c>file_info</c> contains the following fields.</p>
<taglist>
<tag><c>size = integer()</c></tag>
@@ -1210,15 +1232,15 @@
<item>
<p>The current system access to the file.</p>
</item>
- <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso></c></tag>
+ <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso> | integer() </c></tag>
<item>
- <p>The last (local) time the file was read.</p>
+ <p>The last time the file was read.</p>
</item>
- <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso></c></tag>
+ <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso> | integer() </c></tag>
<item>
- <p>The last (local) time the file was written.</p>
+ <p>The last time the file was written.</p>
</item>
- <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso></c></tag>
+ <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso> | integer() </c></tag>
<item>
<p>The interpretation of this time field depends on
the operating system. On Unix, it is the last time
@@ -1374,9 +1396,11 @@
</func>
<func>
<name name="read_link_info" arity="1"/>
+ <name name="read_link_info" arity="2"/>
<fsummary>Get information about a link or file</fsummary>
<desc>
- <p>This function works like <c>read_file_info/1</c>, except that
+ <p>This function works like
+ <seealso marker="#read_file_info/2">read_file_info/1,2</seealso> except that
if <c><anno>Name</anno></c> is a symbolic link, information about
the link will be returned in the <c>file_info</c> record and
the <c>type</c> field of the record will be set to
@@ -1574,6 +1598,48 @@
</desc>
</func>
<func>
+ <name name="sendfile" arity="2"/>
+ <fsummary>send a file to a socket</fsummary>
+ <desc>
+ <p>Sends the file <c>Filename</c> to <c>Socket</c>.
+ Returns <c>{ok, BytesSent}</c> if successful,
+ otherwise <c>{error, Reason}</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="sendfile" arity="5"/>
+ <fsummary>send a file to a socket</fsummary>
+ <desc>
+ <p>Sends <c>Bytes</c> from the file
+ referenced by <c>RawFile</c> beginning at <c>Offset</c> to
+ <c>Socket</c>.
+ Returns <c>{ok, BytesSent}</c> if successful,
+ otherwise <c>{error, Reason}</c>. If <c>Bytes</c> is set to
+ 0 all data after the given <c>Offset</c> is sent.</p>
+ <p>The file used must be opened using the raw flag, and the process
+ calling sendfile must be the controlling process of the socket.
+ See <seealso marker="gen_tcp#controlling_process-2">gen_tcp:controlling_process/2</seealso></p>
+ <p>If the OS used does not support sendfile, an Erlang fallback
+ using file:read and gen_tcp:send is used.</p>
+ <p>The option list can contain the following options:
+ <taglist>
+ <tag><c>chunk_size</c></tag>
+ <item>The chunk size used by the erlang fallback to send
+ data. If using the fallback, this should be set to a value
+ which comfortably fits in the systems memory. Default is 20 MB.</item>
+ </taglist>
+ </p>
+ <p>On operating systems with thread support, it is recommended to use
+ async threads. See the command line flag
+ <c>+A</c> in <seealso marker="erts:erl">erl(1)</seealso>. If it is not
+ possible to use async threads for sendfile, it is recommended to use
+ a relatively small value for the send buffer on the socket. Otherwise
+ the Erlang VM might loose some of its soft realtime guarantees.
+ Which size to use depends on the OS/hardware and the requirements
+ of the application.</p>
+ </desc>
+ </func>
+ <func>
<name name="write" arity="2"/>
<fsummary>Write to a file</fsummary>
<desc>
@@ -1645,6 +1711,7 @@
</func>
<func>
<name name="write_file_info" arity="2"/>
+ <name name="write_file_info" arity="3"/>
<fsummary>Change information about a file</fsummary>
<desc>
<p>Change file information. Returns <c>ok</c> if successful,
@@ -1655,18 +1722,25 @@
from which the function is called:</p>
<code type="none">
-include_lib("kernel/include/file.hrl").</code>
+ <p>The time type set in <c>atime</c>, <c>mtime</c> and <c>ctime</c>
+ is dependent on the time type set in <c>Opts :: {time, Type}</c>.
+ Type <c>local</c> will interpret the time set as local, <c>universal</c> will
+ interpret it as universal time and <c>posix</c> must be seconds since
+ or before unix time epoch which is 1970-01-01 00:00 UTC.
+ Default is <c>{time, local}</c>.
+ </p>
<p>The following fields are used from the record, if they are
given.</p>
<taglist>
- <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso></c></tag>
+ <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso> | integer()</c></tag>
<item>
- <p>The last (local) time the file was read.</p>
+ <p>The last time the file was read.</p>
</item>
- <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso></c></tag>
+ <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso> | integer()</c></tag>
<item>
- <p>The last (local) time the file was written.</p>
+ <p>The last time the file was written.</p>
</item>
- <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso></c></tag>
+ <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso> | integer()</c></tag>
<item>
<p>On Unix, any value give for this field will be ignored
(the "ctime" for the file will be set to the current
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index 688cd0f78f..e327a4f907 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -45,7 +45,15 @@
SUSE Linux Enterprise Server 10 (x86_64) kernel 2.6.16.27-0.6-smp,
with lksctp-tools-1.0.6, briefly on Solaris 10, and later on
SUSE Linux Enterprise Server 10 Service Pack 1 (x86_64)
- kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7.</p>
+ kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7,
+ and later also on FreeBSD 8.2.
+ </p>
+ <p>
+ This module was written for one-to-many style sockets
+ (type <c>seqpacket</c>). With the addition of
+ <seealso marker="#peeloff/2">peeloff/2</seealso>, one-to-one style
+ sockets (type <c>stream</c>) were introduced.
+ </p>
<p>Record definitions for the <c>gen_sctp</c> module can be found using:</p>
<pre> -include_lib("kernel/include/inet_sctp.hrl"). </pre>
<p>These record definitions use the "new" spelling 'adaptation',
@@ -115,7 +123,7 @@
<p>Completely closes the socket and all associations on it. The unsent
data is flushed as in <c>eof/2</c>. The <c>close/1</c> call
is blocking or otherwise depending of the value of
- the <seealso marker="#option-linger">linger</seealso> socket
+ the <seealso marker="inet#option-linger">linger</seealso> socket
<seealso marker="#options">option</seealso>.
If <c>close</c> does not linger or linger timeout expires,
the call returns and the data is flushed in the background.</p>
@@ -254,15 +262,19 @@
</desc>
</func>
<func>
- <name name="listen" arity="2"/>
+ <name name="listen" arity="2" clause_i="1"/>
+ <name name="listen" arity="2" clause_i="2"/>
<fsummary>Set up a socket to listen.</fsummary>
<desc>
<p>Sets up a socket to listen on the IP address and port number
- it is bound to. <c><anno>IsServer</anno></c> must be <c>true</c>
- or <c>false</c>.
- In the contrast to TCP, in SCTP there is no listening queue length.
- If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e.
- it will become an SCTP server socket.</p>
+ it is bound to.</p>
+ <p>For type <c>seqpacket</c> sockets (the default)
+ <c><anno>IsServer</anno></c> must be <c>true</c> or <c>false</c>.
+ In contrast to TCP, in SCTP there is no listening queue length.
+ If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e.
+ it will become an SCTP server socket.</p>
+ <p>For type <c>stream</c> sockets <anno>Backlog</anno> defines
+ the backlog queue length just like in TCP.</p>
</desc>
</func>
<func>
@@ -295,9 +307,37 @@
is used. In particular, the socket is opened in
<seealso marker="#option-binary">binary</seealso> and
<seealso marker="#option-active">passive</seealso> mode,
+ with <anno>SockType</anno> <c>seqpacket</c>,
and with reasonably large
- <seealso marker="#option-sndbuf">kernel</seealso> and driver
- <seealso marker="#option-buffer">buffers.</seealso></p>
+ <seealso marker="inet#option-sndbuf">kernel</seealso> and driver
+ <seealso marker="inet#option-buffer">buffers.</seealso></p>
+ </desc>
+ </func>
+ <func>
+ <name name="peeloff" arity="2"/>
+ <fsummary>
+ Peel off a type <c>stream</c> socket from a type <c>seqpacket</c> one
+ </fsummary>
+ <desc>
+ <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
+ a new socket <anno>NewSocket</anno> of type <c>stream</c>
+ (one-to-one style).
+ </p>
+ <p>
+ The existing association argument <anno>Assoc</anno>
+ can be either a
+ <seealso marker="#record-sctp_assoc_change">
+ #sctp_assoc_change{}
+ </seealso>
+ record as returned from e.g
+ <seealso marker="#recv-2">recv/*</seealso>,
+ <seealso marker="#connect-5">connect/*</seealso> or
+ from a listening socket in active mode. Or it can be just
+ the field <c>assoc_id</c> integer from such a record.
+ </p>
</desc>
</func>
<func>
@@ -490,19 +530,8 @@
SCTP data interleaved with other inter-process messages.</p>
</item>
</list>
- <marker id="option-buffer"></marker>
- </item>
- <tag><c>{buffer, integer()}</c></tag>
- <item>
- <p>Determines the size of the user-level software buffer used by
- the SCTP driver. Not to be confused with <c>sndbuf</c>
- and <c>recbuf</c> options which correspond to
- the kernel socket buffers. It is recommended
- to have <c>val(buffer) &gt;= max(val(sndbuf),val(recbuf))</c>.
- In fact, the <c>val(buffer)</c> is automatically set to
- the above maximum when <c>sndbuf</c> or <c>recbuf</c> values are set.</p>
</item>
- <tag><c>{tos, integer()}</c></tag>
+ <tag><c>{tos, integer()}</c></tag>
<item>
<p>Sets the Type-Of-Service field on the IP datagrams being sent,
to the given value, which effectively determines a prioritization
@@ -527,19 +556,8 @@
<c>{IP,Port}</c> of the socket can be re-used immediately:
no waiting in the CLOSE_WAIT state is performed (may be
required for high-throughput servers).</p>
- <marker id="option-linger"></marker>
</item>
- <tag><c>{linger, {true|false, integer()}</c></tag>
- <item>
- <p>Determines the timeout in seconds for flushing unsent data in the
- <c>gen_sctp:close/1</c> socket call. If the 1st component of the value
- tuple is <c>false</c>, the 2nd one is ignored, which means that
- <c>gen_sctp:close/1</c> returns immediately not waiting
- for data to be flushed. Otherwise, the 2nd component is
- the flushing time-out in seconds.</p>
- <marker id="option-sndbuf"></marker>
- </item>
- <tag><c>{sndbuf, integer()}</c></tag>
+ <tag><c>{sndbuf, integer()}</c></tag>
<item>
<p>The size, in bytes, of the *kernel* send buffer for this socket.
Sending errors would occur for datagrams larger than
@@ -553,6 +571,15 @@
<c>val(sndbuf)</c>. Setting this option also adjusts
the size of the driver buffer (see <c>buffer</c> above).</p>
</item>
+
+ <tag><c>{sctp_module, module()}</c></tag>
+ <item> <p>
+ Override which callback module is used. Defaults to
+ <c>inet_sctp</c> for IPv4 and <c>inet6_sctp</c> for IPv6.
+ </p>
+ </item>
+
+
<tag><c>{sctp_rtoinfo, #sctp_rtoinfo{}}</c></tag>
<item>
<pre> #sctp_rtoinfo{
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 8a5d40bb16..11a0843c10 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -96,37 +96,47 @@ do_recv(Sock, Bs) ->
can be either a hostname, or an IP address.</p>
<p>The available options are:</p>
<taglist>
- <tag><c>list</c></tag>
- <item>
- <p>Received <c>Packet</c> is delivered as a list.</p>
- </item>
- <tag><c>binary</c></tag>
- <item>
- <p>Received <c>Packet</c> is delivered as a binary.</p>
- </item>
- <tag><c>{ip, ip_address()}</c></tag>
+ <tag><c>{ip, ip_address()}</c></tag>
<item>
<p>If the host has several network interfaces, this option
specifies which one to use.</p>
</item>
- <tag><c>{port, Port}</c></tag>
+
+ <tag><c>{ifaddr, ip_address()}</c></tag>
<item>
- <p>Specify which local port number to use.</p>
- </item>
+ <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option
+ specifies which one to use.</p>
+ </item>
+
<tag><c>{fd, integer() >= 0}</c></tag>
<item>
<p>If a socket has somehow been connected without using
<c>gen_tcp</c>, use this option to pass the file
descriptor for it.</p>
</item>
- <tag><c>inet6</c></tag>
+
+ <tag><c>inet</c></tag>
<item>
+ <p>Set up the socket for IPv4.</p>
+ </item>
+
+ <tag><c>inet6</c></tag>
+ <item>
<p>Set up the socket for IPv6.</p>
</item>
- <tag><c>inet</c></tag>
+
+ <tag><c>{port, Port}</c></tag>
<item>
- <p>Set up the socket for IPv4.</p>
+ <p>Specify which local port number to use.</p>
</item>
+
+ <tag><c>{tcp_module, module()}</c></tag>
+ <item> <p>
+ Override which callback module is used. Defaults to
+ <c>inet_tcp</c> for IPv4 and <c>inet6_tcp</c> for IPv6.
+ </p>
+ </item>
+
<tag><c>Opt</c></tag>
<item>
<p>See
@@ -197,6 +207,13 @@ do_recv(Sock, Bs) ->
<c>gen_tcp</c>, use this option to pass the file
descriptor for it.</p>
</item>
+
+ <tag><c>{ifaddr, ip_address()}</c></tag>
+ <item>
+ <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option
+ specifies which one to use.</p>
+ </item>
+
<tag><c>inet6</c></tag>
<item>
<p>Set up the socket for IPv6.</p>
@@ -205,6 +222,14 @@ do_recv(Sock, Bs) ->
<item>
<p>Set up the socket for IPv4.</p>
</item>
+
+ <tag><c>{tcp_module, module()}</c></tag>
+ <item> <p>
+ Override which callback module is used. Defaults to
+ <c>inet_tcp</c> for IPv4 and <c>inet6_tcp</c> for IPv6.
+ </p>
+ </item>
+
<tag><c>Opt</c></tag>
<item>
<p>See
@@ -235,7 +260,9 @@ do_recv(Sock, Bs) ->
<p>Returns <c>{ok, <anno>Socket</anno>}</c> if a connection is established,
or <c>{error, closed}</c> if <c><anno>ListenSocket</anno></c> is closed,
or <c>{error, timeout}</c> if no connection is established
- within the specified time. May also return a POSIX error
+ within the specified time,
+ or <c>{error, system_limit}</c> if all available ports in the
+ Erlang emulator are in use. May also return a POSIX error
value if something else goes wrong, see inet(3) for possible
error values.</p>
<p>Packets can be sent to the returned socket <c><anno>Socket</anno></c>
@@ -297,7 +324,7 @@ do_recv(Sock, Bs) ->
<c><anno>Socket</anno></c>. The controlling process is the process which
receives messages from the socket. If called by any other
process than the current controlling process,
- <c>{error, eperm}</c> is returned.</p>
+ <c>{error, not_owner}</c> is returned.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index daa9b7d887..726dc30546 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -72,6 +72,14 @@
<p>If the host has several network interfaces, this option
specifies which one to use.</p>
</item>
+
+ <tag><c>{ifaddr, ip_address()}</c></tag>
+ <item>
+ <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option
+ specifies which one to use.</p>
+ </item>
+
+
<tag><c>{fd, integer() >= 0}</c></tag>
<item>
<p>If a socket has somehow been opened without using
@@ -86,6 +94,51 @@
<item>
<p>Set up the socket for IPv4.</p>
</item>
+
+ <tag><c>{udp_module, module()}</c></tag>
+ <item> <p>
+ Override which callback module is used. Defaults to
+ <c>inet_udp</c> for IPv4 and <c>inet6_udp</c> for IPv6.
+ </p>
+ </item>
+
+ <tag><c>{multicast_if, Address}</c></tag>
+ <item>
+ <p>Set the local device for a multicast socket.</p>
+ </item>
+
+ <tag><c>{multicast_loop, true | false}</c></tag>
+ <item>
+ <p>
+ When <c>true</c> sent multicast packets will be looped back to the local
+ sockets.
+ </p>
+ </item>
+
+ <tag><c>{multicast_ttl, Integer}</c></tag>
+ <item>
+ <p>
+ The <c>multicast_ttl</c> option changes the time-to-live (TTL) for
+ outgoing multicast datagrams in order to control the scope of the
+ multicasts.
+ </p>
+ <p>
+ Datagrams with a TTL of 1 are not forwarded beyond the local
+ network.
+ <br />Default: 1
+ </p>
+ </item>
+
+ <tag><c>{add_membership, {MultiAddress, InterfaceAddress}}</c></tag>
+ <item>
+ <p>Join a multicast group. </p>
+ </item>
+
+ <tag><c>{drop_membership, {MultiAddress, InterfaceAddress}}</c></tag>
+ <item>
+ <p>Leave multicast group.</p>
+ </item>
+
<tag><c>Opt</c></tag>
<item>
<p>See
@@ -136,7 +189,9 @@
<desc>
<p>Assigns a new controlling process <c><anno>Pid</anno></c> to
<c><anno>Socket</anno></c>. The controlling process is the process which
- receives messages from the socket.</p>
+ receives messages from the socket. If called by any other
+ process than the current controlling process,
+ <c>{error, not_owner}</c> is returned.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml
index e2dbcbe63d..26d1e27822 100644
--- a/lib/kernel/doc/src/heart.xml
+++ b/lib/kernel/doc/src/heart.xml
@@ -42,7 +42,7 @@
system.</p>
<p>An Erlang runtime system to be monitored by a heart program,
should be started with the command line flag <c>-heart</c> (see
- also <seealso marker="erts:erl">erl(1)</seealso>. The <c>heart</c>
+ also <seealso marker="erts:erl">erl(1)</seealso>). The <c>heart</c>
process is then started automatically:</p>
<pre>
% <input>erl -heart ...</input></pre>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index fad5af85bb..b727960d96 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -149,7 +149,7 @@ fe80::204:acff:fe17:bf38
<fsummary>Return a descriptive string for an error reason</fsummary>
<desc>
<p>Returns a diagnostic error string. See the section below
- for possible <c><anno>Posix</anno></c> values and the corresponding
+ for possible Posix values and the corresponding
strings.</p>
</desc>
</func>
@@ -445,10 +445,34 @@ fe80::204:acff:fe17:bf38
flow control; the other side will not be able send
faster than the receiver can read.</p>
</item>
+
+ <tag><c>{bit8, clear | set | on | off}</c></tag>
+ <item>
+ <p>
+ Scans every byte in received data-packets and checks if the 8 bit
+ is set in any of them. Information is retrieved with
+ <c>inet:getopts/2</c>.
+ </p>
+ <p>Note that the <c>bit8</c> option is deprecated and will be removed in Erlang/OTP R16.</p>
+ </item>
+
<tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag>
<item>
<p>Enable/disable permission to send broadcasts.</p>
+ <marker id="option-buffer"></marker>
</item>
+
+ <tag><c>{buffer, Size}</c></tag>
+ <item>
+ <p>Determines the size of the user-level software buffer used by
+ the driver. Not to be confused with <c>sndbuf</c>
+ and <c>recbuf</c> options which correspond to
+ the kernel socket buffers. It is recommended
+ to have <c>val(buffer) &gt;= max(val(sndbuf),val(recbuf))</c>.
+ In fact, the <c>val(buffer)</c> is automatically set to
+ the above maximum when <c>sndbuf</c> or <c>recbuf</c> values are set.</p>
+ </item>
+
<tag><c>{delay_send, Boolean}</c></tag>
<item>
<p>Normally, when an Erlang process sends to a socket,
@@ -463,10 +487,19 @@ fe80::204:acff:fe17:bf38
real property of the socket. Needless to say it is an
implementation specific option. Default is <c>false</c>.</p>
</item>
+
+ <tag><c>{deliver, port | term}</c></tag>
+ <item> <p> When <c>{active, true}</c> delivers data on the forms
+ <c>port</c> : <c>{S, {data, [H1,..Hsz | Data]}}</c> or
+ <c>term</c> : <c>{tcp, S, [H1..Hsz | Data]}</c>.
+ </p>
+ </item>
+
<tag><c>{dontroute, Boolean}</c></tag>
<item>
<p>Enable/disable routing bypass for outgoing messages.</p>
</item>
+
<tag><c>{exit_on_close, Boolean}</c></tag>
<item>
<p>By default this option is set to <c>true</c>.</p>
@@ -476,6 +509,7 @@ fe80::204:acff:fe17:bf38
<seealso marker="gen_tcp#shutdown/2">gen_tcp:shutdown/2</seealso>
to shutdown the write side.</p>
</item>
+
<tag><c>{header, Size}</c></tag>
<item>
<p>This option is only meaningful if the <c>binary</c>
@@ -487,6 +521,15 @@ fe80::204:acff:fe17:bf38
example <c>Size == 2</c>, the data received will match
<c>[Byte1,Byte2|Binary]</c>.</p>
</item>
+
+ <tag><c>{high_watermark, Size}</c></tag>
+ <item> <p>
+ Sender is forced busy if sent and enqueued data
+ reaches the highwater mark.
+ <br /> Default: 8192 kB.
+ </p>
+ </item>
+
<tag><c>{keepalive, Boolean}</c>(TCP/IP sockets)</tag>
<item>
<p>Enables/disables periodic transmission on a connected
@@ -494,7 +537,43 @@ fe80::204:acff:fe17:bf38
the other end does not respond, the connection is
considered broken and an error message will be sent to
the controlling process. Default disabled.</p>
+ <marker id="option-linger"></marker>
</item>
+
+ <tag><c>{linger, {true|false, Seconds}}</c></tag>
+ <item>
+ <p>Determines the timeout in seconds for flushing unsent data in the
+ <c>close/1</c> socket call. If the 1st component of the value
+ tuple is <c>false</c>, the 2nd one is ignored, which means that
+ <c>close/1</c> returns immediately not waiting
+ for data to be flushed. Otherwise, the 2nd component is
+ the flushing time-out in seconds.</p>
+ </item>
+
+ <tag><c>{low_watermark, Size}</c></tag>
+ <item> <p>
+ If the port has reached its <c>high_watermark</c> it will
+ force busy onto senders. When the port data queue reaches the
+ <c>low_watermark</c> callers are no longer forced busy.
+ <br /> Default: 4096 kB.
+ </p>
+ </item>
+
+ <tag><c>{mode, Mode :: binary | list}</c></tag>
+ <item>
+ <p>Received <c>Packet</c> is delivered as defined by Mode.</p>
+ </item>
+
+ <tag><c>list</c></tag>
+ <item>
+ <p>Received <c>Packet</c> is delivered as a list.</p>
+ </item>
+
+ <tag><c>binary</c></tag>
+ <item>
+ <p>Received <c>Packet</c> is delivered as a binary.</p>
+ </item>
+
<tag><c>{nodelay, Boolean}</c>(TCP/IP sockets)</tag>
<item>
<p>If <c>Boolean == true</c>, the <c>TCP_NODELAY</c> option
@@ -573,7 +652,21 @@ fe80::204:acff:fe17:bf38
is longer than the max allowed length, the packet is
considered invalid. The same happens if the packet header
is too big for the socket receive buffer.</p>
+ <p>For line oriented protocols (<c>line</c>,<c>http*</c>),
+ option <c>packet_size</c> also guarantees that lines up to the
+ indicated length are accepted and not considered invalid due
+ to internal buffer limitations.</p>
</item>
+
+ <tag><c>{priority, Priority}</c></tag>
+ <item> <p>Set the protocol-defined priority for all packets to be sent
+ on this socket.</p>
+ </item>
+
+ <tag><c>{raw, Protocol, OptionNum, ValueBin}</c></tag>
+ <item> <p>See below.</p>
+ </item>
+
<tag><c>{read_packets, Integer}</c>(UDP sockets)</tag>
<item>
<p>Sets the max number of UDP packets to read without
@@ -585,7 +678,7 @@ fe80::204:acff:fe17:bf38
high the system can become unresponsive due to
UDP packet flooding.</p>
</item>
- <tag><c>{recbuf, Integer}</c></tag>
+ <tag><c>{recbuf, Size}</c></tag>
<item>
<p>Gives the size of the receive buffer to use for
the socket.</p>
@@ -614,9 +707,10 @@ fe80::204:acff:fe17:bf38
returns <c>{error,timeout}</c>. The recommended setting is
<c>true</c> which will automatically close the socket.
Default is <c>false</c> due to backward compatibility.</p>
+ <marker id="option-sndbuf"></marker>
</item>
- <tag><c>{sndbuf, Integer}</c></tag>
+ <tag><c>{sndbuf, Size}</c></tag>
<item>
<p>Gives the size of the send buffer to use for the socket.</p>
</item>
@@ -635,6 +729,7 @@ fe80::204:acff:fe17:bf38
not implemented. Use with caution.</p>
</item>
</taglist>
+
<p>In addition to the options mentioned above, <em>raw</em>
option specifications can be used. The raw options are
specified as a tuple of arity four, beginning with the tag
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index bf513b7815..faa8f86dbd 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -104,7 +104,7 @@
that node. <c>Value</c> is one of:</p>
<taglist>
<tag><c>never</c></tag>
- <item>Connections are never automatically connected, they
+ <item>Connections are never automatically established, they
must be explicitly connected. See <c>net_kernel(3)</c>.</item>
<tag><c>once</c></tag>
<item>Connections will be established automatically, but only
@@ -231,6 +231,15 @@ MaxT = TickTime + TickTime / 4</code>
<p><em>Note:</em> Normally, a terminating node is detected
immediately.</p>
</item>
+ <tag><c>shutdown_timeout = integer() | infinity</c></tag>
+ <item>
+ <p>Specifies the time <c>application_controller</c> will wait
+ for an application to terminate during node shutdown. If the
+ timer expires, <c>application_controller</c> will brutally
+ kill <c>application_master</c> of the hanging
+ application. If this parameter is undefined, it defaults
+ to <c>infinity</c>.</p>
+ </item>
<tag><c>sync_nodes_mandatory = [NodeName]</c></tag>
<item>
<p>Specifies which other nodes <em>must</em> be alive in order
diff --git a/lib/kernel/doc/src/make.dep b/lib/kernel/doc/src/make.dep
deleted file mode 100644
index f79d1c6367..0000000000
--- a/lib/kernel/doc/src/make.dep
+++ /dev/null
@@ -1,28 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: app.tex application.tex auth.tex book.tex \
- code.tex config.tex disk_log.tex erl_boot_server.tex \
- erl_ddll.tex erl_prim_loader_stub.tex erlang_stub.tex \
- error_handler.tex error_logger.tex file.tex \
- gen_sctp.tex gen_tcp.tex gen_udp.tex global.tex \
- global_group.tex heart.tex inet.tex inet_res.tex \
- init_stub.tex kernel_app.tex net_adm.tex net_kernel.tex \
- os.tex packages.tex pg2.tex ref_man.tex rpc.tex \
- seq_trace.tex user.tex wrap_log_reader.tex \
- zlib_stub.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: ref_man.xml
-
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index 3b7a710664..e54a427ff0 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -210,6 +210,10 @@
<p><c>net_kernel</c> is currently changing
<c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> seconds.</p>
</item>
+ <tag><c>ignored</c></tag>
+ <item>
+ <p>The local node is not alive.</p>
+ </item>
</taglist>
</desc>
</func>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index ec57b03bd9..94624bf512 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,368 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.15.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Driver output has been corrected so output of large
+ binaries (> 4 GiB) now does not silently fail or crash
+ the emulator, but either outputs the binary or fails the
+ call. This means that writing a binary > 4 Gib to file
+ now works but on e.g 64-bit Windows (that has
+ scatter/gather I/O buffer segment lengths of 32 bits)
+ fails. The behaviour may change in the future to always
+ write the binary, in parts if necessary.</p>
+ <p>
+ Own Id: OTP-9820 Aux Id: OTP-9795 </p>
+ </item>
+ <item>
+ <p>
+ erts: minor fix for unnecessary condition erts: change
+ SENDFILE_CHUNK_SIZE from signed to unsigned (Thanks to
+ jovi zhang)</p>
+ <p>
+ Own Id: OTP-9872</p>
+ </item>
+ <item>
+ <p> Two contracts in <c>gen_sctp</c> have been corrected.
+ </p>
+ <p>
+ Own Id: OTP-9874</p>
+ </item>
+ <item>
+ <p>If a process calls a module with an running on_load
+ handler, the process is supposed to be suspended. But if
+ the module with the on_load handler was loading used
+ <c>code:load_binary/3</c>, the call would instead fail
+ with an <c>undef</c> exception.</p>
+ <p>
+ Own Id: OTP-9875</p>
+ </item>
+ <item>
+ <p>
+ File name and error reason is now returned if creation of
+ a cookie fails. (Thanks to Magnus Henoch)</p>
+ <p>
+ Own Id: OTP-9954</p>
+ </item>
+ <item>
+ <p>
+ Fix port leak in <c>zlib</c> when passing invalid data to
+ <c>compress,uncompress,zip,unzip,gzip,gunzip</c>.</p>
+ <p>
+ Own Id: OTP-9981</p>
+ </item>
+ <item>
+ <p>
+ Various typographical errors corrected in documentation
+ for the global, error_logger, etop, lists, ets and
+ supervisor modules and in the c_portdriver and kernel_app
+ documentation. (Thanks to Ricardo Catalinas Jim�nez)</p>
+ <p>
+ Own Id: OTP-9987</p>
+ </item>
+ <item>
+ <p> Fix returned error from gen_tcp:accept/1,2 when
+ running out of ports
+ <p>
+ The {error, enfile} return value is badly misleading and
+ confusing for this case, since the Posix ENFILE errno
+ value has a well-defined meaning that has nothing to do
+ with Erlang ports. The fix changes the return value to
+ {error, system_limit}, which is consistent with e.g.
+ various file(3) functions. inet:format_error/1 has also
+ been updated to support system_limit in the same manner
+ as file:format_error/1. (Thanks to Per Hedeland)</p></p>
+ <p>
+ Own Id: OTP-9990</p>
+ </item>
+ <item>
+ <p><c>erts_debug:size/1</c> has been corrected to take
+ sharing in the environment of funs into account. For funs
+ it used to always give the same result as
+ <c>erts_debug:flat_size/1</c>.</p>
+ <p>
+ Own Id: OTP-9991</p>
+ </item>
+ <item>
+ <p>
+ In some cases when the process doing file:sendfile
+ crashes while sending the file the efile_drv code would
+ not clean up after itself correctly. This has now been
+ fixed.</p>
+ <p>
+ Own Id: OTP-9993</p>
+ </item>
+ <item>
+ <p>
+ On BSD based platforms file:sendfile would sometime go
+ into an infinite loop when sending big files. This has
+ now been fixed.</p>
+ <p>
+ Own Id: OTP-9994</p>
+ </item>
+ <item>
+ <p>While <c>disk_log</c> eagerly collects logged terms
+ for better performance, collecting too much data may
+ choke the system and cause huge binaries to be
+ written.</p>
+ <p>The problem was addressed in OTP-9764, but the
+ situation was not improved in all cases.</p>
+ <p>(Thanks to Richard Carlsson.)</p>
+ <p>
+ Own Id: OTP-9999 Aux Id: OTP-9764 </p>
+ </item>
+ <item>
+ <p>
+ The documentation of .app files incorrectly said that the
+ default value for the <c>mod</c> parameter is
+ <c>undefined</c>. This is now corrected to <c>[]</c>.</p>
+ <p>
+ Own Id: OTP-10002</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.15</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Calls to <c>global:whereis_name/1</c> have been
+ substituted for calls to
+ <c>global:safe_whereis_name/1</c> since the latter is not
+ safe at all.</p>
+ <p>The reason for not doing this earlier is that setting
+ a global lock masked out a bug concerning the restart of
+ supervised children. The bug has now been fixed by a
+ modification of <c>global:whereis_name/1</c>. (Thanks to
+ Ulf Wiger for code contribution.)</p>
+ <p>A minor race conditions in <c>gen_fsm:start*</c> has
+ been fixed: if one of these functions returned <c>{error,
+ Reason}</c> or ignore, the name could still be registered
+ (either locally or in <c>global</c>. (This is the same
+ modification as was done for gen_server in OTP-7669.)</p>
+ <p>The undocumented function
+ <c>global:safe_whereis_name/1</c> has been removed. </p>
+ <p>
+ Own Id: OTP-9212 Aux Id: seq7117, OTP-4174 </p>
+ </item>
+ <item>
+ <p>
+ Honor option <c>packet_size</c> for http packet parsing
+ by both TCP socket and <c>erlang:decode_packet</c>. This
+ gives the ability to accept HTTP headers larger than the
+ default setting, but also avoid DoS attacks by accepting
+ lines only up to whatever length you wish to allow. For
+ consistency, packet type <c>line</c> also honor option
+ <c>packet_size</c>. (Thanks to Steve Vinoski)</p>
+ <p>
+ Own Id: OTP-9389</p>
+ </item>
+ <item>
+ <p> <c>disk_log:reopen/2,3</c> and
+ <c>disk_log:breopen/3</c> could return the error reason
+ from <c>file:rename/2</c> rather than the reason
+ <c>{file_error, Filename, Reason}</c>. This bug has been
+ fixed. </p> <p> The message <c>{disk_log, Node, {error,
+ disk_log_stopped}}</c> which according the documentation
+ is sent upon failure to truncate or reopen a disk log was
+ sometimes turned into a reply. This bug has been fixed.
+ </p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-9508</p>
+ </item>
+ <item>
+ <p>
+ Environment variable 'shutdown_timeout' is added to
+ kernel application. Earlier, application_controller would
+ hang forever if an application top supervisor did not
+ terminate upon a shutdown request. If this new
+ environment variable is set to a positive integer T, then
+ application controller will now give up after T
+ milliseconds and instead brutally kill the application.
+ For backwards compatibility, the default value for
+ shutdown_timeout is 'infinity'.</p>
+ <p>
+ Own Id: OTP-9540</p>
+ </item>
+ <item>
+ <p>
+ Add '-callback' attributes in stdlib's behaviours</p>
+ <p>
+ Replace the behaviour_info(callbacks) export in stdlib's
+ behaviours with -callback' attributes for all the
+ callbacks. Update the documentation with information on
+ the callback attribute Automatically generate
+ 'behaviour_info' function from '-callback' attributes</p>
+ <p>
+ 'behaviour_info(callbacks)' is a special function that is
+ defined in a module which describes a behaviour and
+ returns a list of its callbacks.</p>
+ <p>
+ This function is now automatically generated using the
+ '-callback' specs. An error is returned by lint if user
+ defines both '-callback' attributes and the
+ behaviour_info/1 function. If no type info is needed for
+ a callback use a generic spec for it. Add '-callback'
+ attribute to language syntax</p>
+ <p>
+ Behaviours may define specs for their callbacks using the
+ familiar spec syntax, replacing the '-spec' keyword with
+ '-callback'. Simple lint checks are performed to ensure
+ that no callbacks are defined twice and all types
+ referred are declared.</p>
+ <p>
+ These attributes can be then used by tools to provide
+ documentation to the behaviour or find discrepancies in
+ the callback definitions in the callback module.</p>
+ <p>
+ Add callback specs into 'application' module in kernel
+ Add callback specs to tftp module following internet
+ documentation Add callback specs to inets_service module
+ following possibly deprecated comments</p>
+ <p>
+ Own Id: OTP-9621</p>
+ </item>
+ <item>
+ <p>
+ make tab completion work in remote shells (Thanks to Mats
+ Cronqvist)</p>
+ <p>
+ Own Id: OTP-9673</p>
+ </item>
+ <item>
+ <p>
+ Add missing parenthesis in heart doc.</p>
+ <p>
+ Add missing spaces in the Reference Manual distributed
+ section.</p>
+ <p>
+ In the HTML version of the doc those spaces are necessary
+ to separate those words.</p>
+ <p>
+ Own Id: OTP-9693</p>
+ </item>
+ <item>
+ <p>
+ Fixes net_kernel:get_net_ticktime() doc</p>
+ <p>
+ Adds missing description when `ignored' is returned.
+ (Thanks to Ricardo Catalinas Jim�nez )</p>
+ <p>
+ Own Id: OTP-9713</p>
+ </item>
+ <item>
+ <p> While <c>disk_log</c> eagerly collects logged terms
+ for better performance, collecting too much data may
+ choke the system and cause huge binaries to be written.
+ In order to remedy the situation a (small) limit on the
+ amount of data that is collected before writing to disk
+ has been introduced. </p>
+ <p>
+ Own Id: OTP-9764</p>
+ </item>
+ <item>
+ <p>
+ <list> <item><p>Correct callback spec in application
+ module</p></item> <item><p>Refine warning about callback
+ specs with extra ranges</p></item> <item><p>Cleanup
+ autoimport compiler directives</p></item> <item><p>Fix
+ Dialyzer's warnings in typer</p></item> <item><p>Fix
+ Dialyzer's warning for its own code</p></item>
+ <item><p>Fix bug in Dialyzer's behaviours
+ analysis</p></item> <item><p>Fix crash in
+ Dialyzer</p></item> <item><p>Variable substitution was
+ not generalizing any unknown variables.</p></item>
+ </list></p>
+ <p>
+ Own Id: OTP-9776</p>
+ </item>
+ <item>
+ <p>
+ Fix a crash when file:change_time/2,3 are called with
+ invalid dates</p>
+ <p>
+ Calling file:change_time/2,3 with an invalid date tuple
+ (e.g file:change_time("file.txt", {undefined,
+ undefined})) will cause file_server_2 to crash.
+ error_logger will shutdown and the whole VM will stop.
+ Change behavior to validate given dates on system
+ boundaries. (i.e before issuing a server call).(Thanks to
+ Ahmed Omar)</p>
+ <p>
+ Own Id: OTP-9785</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> An option list argument can now be passed to
+ <c>file:read_file_info/2, file:read_link_info/2</c> and
+ <c>file:write_file_info/3</c> and set time type
+ information in the call. Valid options are <c>{time,
+ local}, {time, universal}</c> and <c>{time, posix}</c>.
+ In the case of <c>posix</c> time no conversions are made
+ which makes the operation a bit faster. </p>
+ <p>
+ Own Id: OTP-7687</p>
+ </item>
+ <item>
+ <p><c>file:list_dir/1,2</c> will now fill an buffer
+ entire with filenames from the efile driver before
+ sending it to an erlang process. This will speed up this
+ file operation in most cases.</p>
+ <p>
+ Own Id: OTP-9023</p>
+ </item>
+ <item>
+ <p>gen_sctp:open/0-2 may now return
+ {error,eprotonosupport} if SCTP is not supported</p>
+ <p>gen_sctp:peeloff/1 has been implemented and creates a
+ one-to-one socket which also are supported now</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-9239</p>
+ </item>
+ <item>
+ <p>
+ Sendfile has been added to the file module's API.
+ sendfile/2 is used to read data from a file and send it
+ to a tcp socket using a zero copying mechanism if
+ available on that OS.</p>
+ <p>
+ Thanks to Tuncer Ayaz and Steve Vinovski for original
+ implementation</p>
+ <p>
+ Own Id: OTP-9240</p>
+ </item>
+ <item>
+ <p>
+ Tuple funs (a two-element tuple with a module name and a
+ function) are now officially deprecated and will be
+ removed in R16. Use '<c>fun M:F/A</c>' instead. To make
+ you aware that your system uses tuple funs, the very
+ first time a tuple fun is applied, a warning will be sent
+ to the error logger.</p>
+ <p>
+ Own Id: OTP-9649</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.14.5</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 e94119845a..09c525b376 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -80,6 +80,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
Each environment variable is given as a single string on
the format <c>"VarName=Value"</c>, where <c>VarName</c> is
the name of the variable and <c>Value</c> its value.</p>
+ <p>If Unicode file name encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings may contain characters with
+ codepoints > 255.</p>
</desc>
</func>
<func>
@@ -93,6 +97,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
<p>Returns the <c>Value</c> of the environment variable
<c>VarName</c>, or <c>false</c> if the environment variable
is undefined.</p>
+ <p>If Unicode file name encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c>VarName</c> and
+ <c>Value</c>) may contain characters with codepoints > 255.</p>
</desc>
</func>
<func>
@@ -123,6 +131,13 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
<desc>
<p>Sets a new <c>Value</c> for the environment variable
<c>VarName</c>.</p>
+ <p>If Unicode filename encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the strings (both <c>VarName</c> and
+ <c>Value</c>) may contain characters with codepoints > 255.</p>
+ <p>On Unix platforms, the environment will be set using UTF-8 encoding
+ if Unicode file name translation is in effect. On Windows the
+ environment is set using wide character interfaces.</p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/packages.xml b/lib/kernel/doc/src/packages.xml
index 80de2e05fc..81b8693baa 100644
--- a/lib/kernel/doc/src/packages.xml
+++ b/lib/kernel/doc/src/packages.xml
@@ -204,11 +204,5 @@ ok
Explicitly
declaring each use of a module makes for safe code.</p>
</description>
- <funcs>
- <func>
- <name>no functions exported</name>
- <fsummary>x</fsummary>
- </func>
- </funcs>
</erlref>
diff --git a/lib/kernel/examples/Makefile b/lib/kernel/examples/Makefile
index fb27f8d438..21dfd0ec0e 100644
--- a/lib/kernel/examples/Makefile
+++ b/lib/kernel/examples/Makefile
@@ -47,8 +47,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(KERNEL_VSN)/examples
EXAMPLES = uds_dist
release_spec:
- $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DIR) "$(RELSYSDIR)"
tar cf - $(EXAMPLES) | \
- (cd $(RELSYSDIR); tar xf - ; chmod -R ug+w $(EXAMPLES) )
+ (cd "$(RELSYSDIR)"; tar xf - ; chmod -R ug+w $(EXAMPLES) )
release_docs_spec:
diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
index 9327ab19dc..9ad6b85a0f 100644
--- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c
+++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
@@ -967,7 +967,7 @@ static void *my_malloc(size_t size)
void *ptr;
if ((ptr = driver_alloc(size)) == NULL) {
- erl_exit(1,"Could not allocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size);
}
return ptr;
}
@@ -977,7 +977,7 @@ static void *my_realloc(void *ptr, size_t size)
void erl_exit(int, char *, ...);
void *nptr;
if ((nptr = driver_realloc(ptr, size)) == NULL) {
- erl_exit(1,"Could not reallocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size);
}
return nptr;
}
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index aea1ab81ba..5b52f6f294 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. 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/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl
index f2b0598532..c3a7f97418 100644
--- a/lib/kernel/include/dist_util.hrl
+++ b/lib/kernel/include/dist_util.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. 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/include/file.hrl b/lib/kernel/include/file.hrl
index 3889bce393..bf97173122 100644
--- a/lib/kernel/include/file.hrl
+++ b/lib/kernel/include/file.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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,10 +25,11 @@
{size :: non_neg_integer(), % Size of file in bytes.
type :: 'device' | 'directory' | 'other' | 'regular' | 'symlink',
access :: 'read' | 'write' | 'read_write' | 'none',
- atime :: file:date_time(), % The local time the file was last read:
- % {{Year, Mon, Day}, {Hour, Min, Sec}}.
- mtime :: file:date_time(), % The local time the file was last written.
- ctime :: file:date_time(), % The interpretation of this time field
+ atime :: file:date_time() | integer(), % The local time the file was last read:
+ % {{Year, Mon, Day}, {Hour, Min, Sec}}.
+ % atime, ctime, mtime may also be unix epochs()
+ mtime :: file:date_time() | integer(), % The local time the file was last written.
+ ctime :: file:date_time() | integer(), % The interpretation of this time field
% is dependent on operating system.
% On Unix it is the last time the file
% or the inode was changed. On Windows,
diff --git a/lib/kernel/include/net_address.hrl b/lib/kernel/include/net_address.hrl
index 5342076507..9b9ea42931 100644
--- a/lib/kernel/include/net_address.hrl
+++ b/lib/kernel/include/net_address.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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/src/Makefile b/lib/kernel/src/Makefile
index 02be6b5036..a39864d6b7 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2011. 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
@@ -198,13 +198,13 @@ $(EBIN)/erl_epmd.beam: $(ESRC)/erl_epmd.erl
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/include
- $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/include"
+ $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
release_docs_spec:
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index caac4d926c..c299fb085c 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -59,7 +59,7 @@
-callback start(StartType :: normal | {takeover, node()} | {failover, node()},
StartArgs :: term()) ->
- {ok, pid()} | {ok, pid(), State :: term()} | {error, Reason :: term}.
+ {'ok', pid()} | {'ok', pid(), State :: term()} | {'error', Reason :: term()}.
-callback stop(State :: term()) ->
term().
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index 42f527f400..ebfe84463a 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -1180,10 +1180,27 @@ terminate(Reason, S) ->
_ ->
ok
end,
+ ShutdownTimeout =
+ case application:get_env(kernel, shutdown_timeout) of
+ undefined -> infinity;
+ {ok,T} -> T
+ end,
foreach(fun({_AppName, Id}) when is_pid(Id) ->
+ Ref = erlang:monitor(process, Id),
+ unlink(Id),
exit(Id, shutdown),
receive
+ %% Proc died before link
{'EXIT', Id, _} -> ok
+ after 0 ->
+ receive
+ {'DOWN', Ref, process, Id, _} -> ok
+ after ShutdownTimeout ->
+ exit(Id, kill),
+ receive
+ {'DOWN', Ref, process, Id, _} -> ok
+ end
+ end
end;
(_) -> ok
end,
diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl
index c329a5652a..6ae786ebd9 100644
--- a/lib/kernel/src/auth.erl
+++ b/lib/kernel/src/auth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -381,13 +381,17 @@ create_cookie(Name) ->
case {R1, R2} of
{ok, ok} ->
ok;
- {{error,_Reason}, _} ->
- {error, "Failed to create cookie file"};
+ {{error,Reason}, _} ->
+ {error,
+ lists:flatten(
+ io_lib:format("Failed to write to cookie file '~s': ~p", [Name, Reason]))};
{ok, {error, Reason}} ->
{error, "Failed to change mode: " ++ atom_to_list(Reason)}
end;
- {error,_Reason} ->
- {error, "Failed to create cookie file"}
+ {error,Reason} ->
+ {error,
+ lists:flatten(
+ io_lib:format("Failed to create cookie file '~s': ~p", [Name, Reason]))}
end.
random_cookie(0, _, Result) ->
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index 882e9625fe..363072951e 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -63,7 +63,7 @@
which/1,
where_is_file/1,
where_is_file/2,
- set_primary_archive/3,
+ set_primary_archive/4,
clash/0]).
-export_type([load_error_rsn/0, load_ret/0]).
@@ -107,7 +107,7 @@
%% unstick_mod(Module) -> true
%% is_sticky(Module) -> boolean()
%% which(Module) -> Filename | loaded_ret_atoms() | non_existing
-%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason}
+%% set_primary_archive((FileName, ArchiveBin, FileInfo, ParserFun) -> ok | {error, Reason}
%% clash() -> ok prints out number of clashes
%%----------------------------------------------------------------------------
@@ -324,15 +324,7 @@ start_link(Flags) ->
%%-----------------------------------------------------------------
do_start(Flags) ->
- %% The following module_info/1 calls are here to ensure
- %% that these modules are loaded prior to their use elsewhere in
- %% the code_server.
- %% Otherwise a deadlock may occur when the code_server is starting.
- code_server = code_server:module_info(module),
- packages = packages:module_info(module),
- catch hipe_unified_loader:load_hipe_modules(),
- Modules2 = [gb_sets, gb_trees, ets, os, binary, unicode, filename, lists],
- lists:foreach(fun (M) -> M = M:module_info(module) end, Modules2),
+ load_code_server_prerequisites(),
Mode = get_mode(Flags),
case init:get_argument(root) of
@@ -360,6 +352,25 @@ do_start(Flags) ->
{error, crash}
end.
+%% Make sure that all modules that the code_server process calls
+%% (directly or indirectly) are loaded. Otherwise the code_server
+%% process will deadlock.
+
+load_code_server_prerequisites() ->
+ %% Please keep the alphabetical order.
+ Needed = [binary,
+ ets,
+ filename,
+ gb_sets,
+ gb_trees,
+ hipe_unified_loader,
+ lists,
+ os,
+ packages,
+ unicode],
+ [M = M:module_info(module) || M <- Needed],
+ ok.
+
do_stick_dirs() ->
do_s(compiler),
do_s(stdlib),
@@ -470,13 +481,16 @@ where_is_file(Path, File) when is_list(Path), is_list(File) ->
-spec set_primary_archive(ArchiveFile :: file:filename(),
ArchiveBin :: binary(),
- FileInfo :: file:file_info())
+ FileInfo :: file:file_info(),
+ ParserFun :: fun())
-> 'ok' | {'error', atom()}.
-set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo)
+set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo,
+ ParserFun)
when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
ArchiveFile = filename:absname(ArchiveFile0),
- case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of
+ case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo,
+ ParserFun}) of
{ok, []} ->
ok;
{ok, _Mode, Ebins} ->
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index e3d22e7999..00ad923466 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. 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
@@ -32,6 +32,8 @@
-import(lists, [foreach/2]).
+-define(ANY_NATIVE_CODE_LOADED, any_native_code_loaded).
+
-record(state, {supervisor,
root,
path,
@@ -97,6 +99,8 @@ init(Ref, Parent, [Root,Mode0]) ->
State0
end,
+ put(?ANY_NATIVE_CODE_LOADED, false),
+
Parent ! {Ref,{ok,self()}},
loop(State#state{supervisor = Parent}).
@@ -390,8 +394,8 @@ handle_call(stop,{_From,_Tag}, S) ->
handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) ->
{reply, no, S};
-handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) ->
- case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of
+handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) ->
+ case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of
{ok, Files} ->
{reply, {ok, Mode, Files}, S};
{error, _Reason} = Error ->
@@ -1278,35 +1282,56 @@ load_native_code(Mod, Bin) ->
%% Therefore we must test for that the loader modules are available
%% before trying to to load native code.
case erlang:module_loaded(hipe_unified_loader) of
- false -> no_native;
- true -> hipe_unified_loader:load_native_code(Mod, Bin)
+ false ->
+ no_native;
+ true ->
+ Result = hipe_unified_loader:load_native_code(Mod, Bin),
+ case Result of
+ {module,_} ->
+ put(?ANY_NATIVE_CODE_LOADED, true);
+ _ ->
+ ok
+ end,
+ Result
end.
hipe_result_to_status(Result) ->
case Result of
- {module,_} -> Result;
- _ -> {error,Result}
+ {module,_} ->
+ put(?ANY_NATIVE_CODE_LOADED, true),
+ Result;
+ _ ->
+ {error,Result}
end.
post_beam_load(Mod) ->
- case erlang:module_loaded(hipe_unified_loader) of
- false -> ok;
- true -> hipe_unified_loader:post_beam_load(Mod)
+ %% post_beam_load/1 can potentially be very expensive because it
+ %% blocks multi-scheduling; thus we want to avoid the call if we
+ %% know that it is not needed.
+ case get(?ANY_NATIVE_CODE_LOADED) of
+ true -> hipe_unified_loader:post_beam_load(Mod);
+ false -> ok
end.
int_list([H|T]) when is_integer(H) -> int_list(T);
int_list([_|_]) -> false;
int_list([]) -> true.
+load_file(Mod0, {From,_}=Caller, St0) ->
+ Mod = to_atom(Mod0),
+ case pending_on_load(Mod, From, St0) of
+ no -> load_file_1(Mod, Caller, St0);
+ {yes,St} -> {noreply,St}
+ end.
-load_file(Mod, Caller, #state{path=Path,cache=no_cache}=St) ->
+load_file_1(Mod, Caller, #state{path=Path,cache=no_cache}=St) ->
case mod_to_bin(Path, Mod) of
error ->
{reply,{error,nofile},St};
{Mod,Binary,File} ->
try_load_module(File, Mod, Binary, Caller, St)
end;
-load_file(Mod, Caller, #state{cache=Cache}=St0) ->
+load_file_1(Mod, Caller, #state{cache=Cache}=St0) ->
Key = {obj,Mod},
case ets:lookup(Cache, Key) of
[] ->
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl
index d6bc23be6d..5b1efcd395 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -64,7 +64,7 @@
%%-define(PROFILE(C), C).
-define(PROFILE(C), void).
--compile({inline,[{log_loop,4},{log_end_sync,2},{replies,2},{rflat,1}]}).
+-compile({inline,[{log_loop,5},{log_end_sync,2},{replies,2},{rflat,1}]}).
%%%----------------------------------------------------------------------
%%% Contract type specifications
@@ -282,7 +282,8 @@ change_notify(Log, Pid, NewNotify) ->
-spec change_header(Log, Header) -> 'ok' | {'error', Reason} when
Log :: log(),
- Header :: {head, dlog_head_opt()} | {head_func, mfa()},
+ Header :: {head, dlog_head_opt()}
+ | {head_func, MFA :: {atom(), atom(), list()}},
Reason :: no_such_log | nonode | {read_only_mode, Log}
| {blocked_log, Log} | {badarg, head}.
change_header(Log, NewHead) ->
@@ -336,7 +337,9 @@ format_error(Error) ->
ok | {blocked, QueueLogRecords :: boolean()}}
| {node, Node :: node()}
| {distributed, Dist :: local | [node()]}
- | {head, Head :: none | {head, term()} | mfa()}
+ | {head, Head :: none
+ | {head, term()}
+ | (MFA :: {atom(), atom(), list()})}
| {no_written_items, NoWrittenItems ::non_neg_integer()}
| {full, Full :: boolean}
| {no_current_bytes, non_neg_integer()}
@@ -685,7 +688,7 @@ handle({From, {log, B}}, S) ->
L when L#log.mode =:= read_only ->
reply(From, {error, {read_only_mode, L#log.name}}, S);
L when L#log.status =:= ok, L#log.format =:= internal ->
- log_loop(S, From, [B], []);
+ log_loop(S, From, [B], [], iolist_size(B));
L when L#log.status =:= ok, L#log.format =:= external ->
reply(From, {error, {format_external, L#log.name}}, S);
L when L#log.status =:= {blocked, false} ->
@@ -700,7 +703,7 @@ handle({From, {blog, B}}, S) ->
L when L#log.mode =:= read_only ->
reply(From, {error, {read_only_mode, L#log.name}}, S);
L when L#log.status =:= ok ->
- log_loop(S, From, [B], []);
+ log_loop(S, From, [B], [], iolist_size(B));
L when L#log.status =:= {blocked, false} ->
reply(From, {error, {blocked_log, L#log.name}}, S);
L when L#log.blocked_by =:= From ->
@@ -714,7 +717,7 @@ handle({alog, B}, S) ->
notify_owners({read_only,B}),
loop(S);
L when L#log.status =:= ok, L#log.format =:= internal ->
- log_loop(S, [], [B], []);
+ log_loop(S, [], [B], [], iolist_size(B));
L when L#log.status =:= ok ->
notify_owners({format_external, B}),
loop(S);
@@ -730,7 +733,7 @@ handle({balog, B}, S) ->
notify_owners({read_only,B}),
loop(S);
L when L#log.status =:= ok ->
- log_loop(S, [], [B], []);
+ log_loop(S, [], [B], [], iolist_size(B));
L when L#log.status =:= {blocked, false} ->
notify_owners({blocked_log, B}),
loop(S);
@@ -1029,38 +1032,40 @@ handle(_, S) ->
loop(S).
sync_loop(From, S) ->
- log_loop(S, [], [], From).
+ log_loop(S, [], [], From, 0).
+
+-define(MAX_LOOK_AHEAD, 64*1024).
%% Inlined.
-log_loop(S, Pids, _Bins, _Sync) when S#state.cache_error =/= ok ->
+log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz) when CE =/= ok ->
loop(cache_error(S, Pids));
-log_loop(S, Pids, Bins, Sync) when S#state.messages =:= [] ->
+log_loop(#state{}=S, Pids, Bins, Sync, Sz) when Sz > ?MAX_LOOK_AHEAD ->
+ loop(log_end(S, Pids, Bins, Sync));
+log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) ->
receive
Message ->
- log_loop(Message, Pids, Bins, Sync, S, get(log))
+ log_loop(Message, Pids, Bins, Sync, Sz, S, get(log))
after 0 ->
loop(log_end(S, Pids, Bins, Sync))
end;
-log_loop(S, Pids, Bins, Sync) ->
- [M | Ms] = S#state.messages,
+log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz) ->
S1 = S#state{messages = Ms},
- log_loop(M, Pids, Bins, Sync, S1, get(log)).
+ log_loop(M, Pids, Bins, Sync, Sz, S1, get(log)).
%% Items logged after the last sync request found are sync:ed as well.
-log_loop({alog,B}, Pids, Bins, Sync, S, L) when L#log.format =:= internal ->
+log_loop({alog,B}, Pids, Bins, Sync, Sz, S, #log{format = internal}) ->
%% {alog, _} allowed for the internal format only.
- log_loop(S, Pids, [B | Bins], Sync);
-log_loop({balog, B}, Pids, Bins, Sync, S, _L) ->
- log_loop(S, Pids, [B | Bins], Sync);
-log_loop({From, {log, B}}, Pids, Bins, Sync, S, L)
- when L#log.format =:= internal ->
+ log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B));
+log_loop({balog, B}, Pids, Bins, Sync, Sz, S, _L) ->
+ log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B));
+log_loop({From, {log, B}}, Pids, Bins, Sync, Sz, S, #log{format = internal}) ->
%% {log, _} allowed for the internal format only.
- log_loop(S, [From | Pids], [B | Bins], Sync);
-log_loop({From, {blog, B}}, Pids, Bins, Sync, S, _L) ->
- log_loop(S, [From | Pids], [B | Bins], Sync);
-log_loop({From, sync}, Pids, Bins, Sync, S, _L) ->
- log_loop(S, Pids, Bins, [From | Sync]);
-log_loop(Message, Pids, Bins, Sync, S, _L) ->
+ log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B));
+log_loop({From, {blog, B}}, Pids, Bins, Sync, Sz, S, _L) ->
+ log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B));
+log_loop({From, sync}, Pids, Bins, Sync, Sz, S, _L) ->
+ log_loop(S, Pids, Bins, [From | Sync], Sz);
+log_loop(Message, Pids, Bins, Sync, _Sz, S, _L) ->
NS = log_end(S, Pids, Bins, Sync),
handle(Message, NS).
diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl
index 259967650f..242a25a7a6 100644
--- a/lib/kernel/src/disk_log.hrl
+++ b/lib/kernel/src/disk_log.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -74,7 +74,7 @@
| {distributed, Nodes :: [node()]}
| {notify, boolean()}
| {head, Head :: dlog_head_opt()}
- | {head_func, mfa()}
+ | {head_func, MFA :: {atom(), atom(), list()}}
| {mode, Mode :: dlog_mode()}.
-type dlog_options() :: [dlog_option()].
-type dlog_repair() :: 'truncate' | boolean().
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index 7d6a5ade94..b8871e0d45 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -53,6 +53,11 @@ size(Tuple, Seen0, Sum0) when is_tuple(Tuple) ->
Sum = Sum0 + 1 + tuple_size(Tuple),
tuple_size(1, tuple_size(Tuple), Tuple, Seen, Sum)
end;
+size(Fun, Seen0, Sum) when is_function(Fun) ->
+ case remember_term(Fun, Seen0) of
+ seen -> {Sum,Seen0};
+ Seen -> fun_size(Fun, Seen, Sum)
+ end;
size(Term, Seen0, Sum) ->
case erts_debug:flat_size(Term) of
0 -> {Sum,Seen0};
@@ -68,6 +73,21 @@ tuple_size(I, Sz, _, Seen, Sum) when I > Sz ->
tuple_size(I, Sz, Tuple, Seen0, Sum0) ->
{Sum,Seen} = size(element(I, Tuple), Seen0, Sum0),
tuple_size(I+1, Sz, Tuple, Seen, Sum).
+
+fun_size(Fun, Seen, Sum) ->
+ case erlang:fun_info(Fun, type) of
+ {type,external} ->
+ {Sum + erts_debug:flat_size(Fun),Seen};
+ {type,local} ->
+ Sz = erts_debug:flat_size(fun() -> ok end),
+ {env,Env} = erlang:fun_info(Fun, env),
+ fun_size_1(Env, Seen, Sum+Sz+length(Env))
+ end.
+
+fun_size_1([H|T], Seen0, Sum0) ->
+ {Sum,Seen} = size(H, Seen0, Sum0),
+ fun_size_1(T, Seen, Sum);
+fun_size_1([], Seen, Sum) -> {Sum,Seen}.
remember_term(Term, Seen) ->
case gb_trees:lookup(Term, Seen) of
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 706c60caaf..cdb984c333 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -28,9 +28,11 @@
%% File system and metadata.
-export([get_cwd/0, get_cwd/1, set_cwd/1, delete/1, rename/2,
make_dir/1, del_dir/1, list_dir/1,
- read_file_info/1, write_file_info/2,
+ read_file_info/1, read_file_info/2,
+ write_file_info/2, write_file_info/3,
altname/1,
- read_link_info/1, read_link/1,
+ read_link_info/1, read_link_info/2,
+ read_link/1,
make_link/2, make_symlink/2,
read_file/1, write_file/2, write_file/3]).
%% Specialized
@@ -51,6 +53,9 @@
-export([pid2name/1]).
+%% Sendfile functions
+-export([sendfile/2,sendfile/5]).
+
%%% Obsolete exported functions
-export([raw_read_file_info/1, raw_write_file_info/2]).
@@ -103,6 +108,10 @@
-type date_time() :: calendar:datetime().
-type posix_file_advise() :: 'normal' | 'sequential' | 'random'
| 'no_reuse' | 'will_need' | 'dont_need'.
+-type sendfile_option() :: {chunk_size, non_neg_integer()}.
+-type file_info_option() :: {'time', 'local'} | {'time', 'universal'}
+ | {'time', 'posix'}.
+
%%%-----------------------------------------------------------------
%%% General functions
@@ -211,6 +220,15 @@ del_dir(Name) ->
read_file_info(Name) ->
check_and_call(read_file_info, [file_name(Name)]).
+-spec read_file_info(Filename, Opts) -> {ok, FileInfo} | {error, Reason} when
+ Filename :: name(),
+ Opts :: [file_info_option()],
+ FileInfo :: file_info(),
+ Reason :: posix() | badarg.
+
+read_file_info(Name, Opts) when is_list(Opts) ->
+ check_and_call(read_file_info, [file_name(Name), Opts]).
+
-spec altname(Name :: name()) -> any().
altname(Name) ->
@@ -224,6 +242,16 @@ altname(Name) ->
read_link_info(Name) ->
check_and_call(read_link_info, [file_name(Name)]).
+-spec read_link_info(Name, Opts) -> {ok, FileInfo} | {error, Reason} when
+ Name :: name(),
+ Opts :: [file_info_option()],
+ FileInfo :: file_info(),
+ Reason :: posix() | badarg.
+
+read_link_info(Name, Opts) when is_list(Opts) ->
+ check_and_call(read_link_info, [file_name(Name),Opts]).
+
+
-spec read_link(Name) -> {ok, Filename} | {error, Reason} when
Name :: name(),
Filename :: filename(),
@@ -240,6 +268,15 @@ read_link(Name) ->
write_file_info(Name, Info = #file_info{}) ->
check_and_call(write_file_info, [file_name(Name), Info]).
+-spec write_file_info(Filename, FileInfo, Opts) -> ok | {error, Reason} when
+ Filename :: name(),
+ Opts :: [file_info_option()],
+ FileInfo :: file_info(),
+ Reason :: posix() | badarg.
+
+write_file_info(Name, Info = #file_info{}, Opts) when is_list(Opts) ->
+ check_and_call(write_file_info, [file_name(Name), Info, Opts]).
+
-spec list_dir(Dir) -> {ok, Filenames} | {error, Reason} when
Dir :: name(),
Filenames :: [filename()],
@@ -264,9 +301,9 @@ read_file(Name) ->
make_link(Old, New) ->
check_and_call(make_link, [file_name(Old), file_name(New)]).
--spec make_symlink(Name1, Name2) -> ok | {error, Reason} when
- Name1 :: name(),
- Name2 :: name(),
+-spec make_symlink(Existing, New) -> ok | {error, Reason} when
+ Existing :: name(),
+ New :: name(),
Reason :: posix() | badarg.
make_symlink(Old, New) ->
@@ -1100,8 +1137,9 @@ change_group(Name, GroupId)
Mtime :: date_time(),
Reason :: posix() | badarg.
-change_time(Name, Time)
- when is_tuple(Time) ->
+change_time(Name, {{Y, M, D}, {H, Min, Sec}}=Time)
+ when is_integer(Y), is_integer(M), is_integer(D),
+ is_integer(H), is_integer(Min), is_integer(Sec)->
write_file_info(Name, #file_info{mtime=Time}).
-spec change_time(Filename, Atime, Mtime) -> ok | {error, Reason} when
@@ -1110,10 +1148,150 @@ change_time(Name, Time)
Mtime :: date_time(),
Reason :: posix() | badarg.
-change_time(Name, Atime, Mtime)
- when is_tuple(Atime), is_tuple(Mtime) ->
+change_time(Name, {{AY, AM, AD}, {AH, AMin, ASec}}=Atime,
+ {{MY, MM, MD}, {MH, MMin, MSec}}=Mtime)
+ when is_integer(AY), is_integer(AM), is_integer(AD),
+ is_integer(AH), is_integer(AMin), is_integer(ASec),
+ is_integer(MY), is_integer(MM), is_integer(MD),
+ is_integer(MH), is_integer(MMin), is_integer(MSec)->
write_file_info(Name, #file_info{atime=Atime, mtime=Mtime}).
+%%
+%% Send data using sendfile
+%%
+
+-define(MAX_CHUNK_SIZE, (1 bsl 20)*20). %% 20 MB, has to fit in primary memory
+
+-spec sendfile(RawFile, Socket, Offset, Bytes, Opts) ->
+ {'ok', non_neg_integer()} | {'error', inet:posix() |
+ closed | badarg | not_owner} when
+ RawFile :: file:fd(),
+ Socket :: inet:socket(),
+ Offset :: non_neg_integer(),
+ Bytes :: non_neg_integer(),
+ Opts :: [sendfile_option()].
+sendfile(File, _Sock, _Offet, _Bytes, _Opts) when is_pid(File) ->
+ {error, badarg};
+sendfile(File, Sock, Offset, Bytes, []) ->
+ sendfile(File, Sock, Offset, Bytes, ?MAX_CHUNK_SIZE, [], [],
+ false, false, false);
+sendfile(File, Sock, Offset, Bytes, Opts) ->
+ ChunkSize0 = proplists:get_value(chunk_size, Opts, ?MAX_CHUNK_SIZE),
+ ChunkSize = if ChunkSize0 > ?MAX_CHUNK_SIZE ->
+ ?MAX_CHUNK_SIZE;
+ true -> ChunkSize0
+ end,
+ %% Support for headers, trailers and options has been removed because the
+ %% Darwin and BSD API for using it does not play nice with
+ %% non-blocking sockets. See unix_efile.c for more info.
+ sendfile(File, Sock, Offset, Bytes, ChunkSize, [], [],
+ false,false,false).
+
+%% sendfile/2
+-spec sendfile(Filename, Socket) ->
+ {'ok', non_neg_integer()} | {'error', inet:posix() |
+ closed | badarg | not_owner}
+ when Filename :: file:name(),
+ Socket :: inet:socket().
+sendfile(Filename, Sock) ->
+ case file:open(Filename, [read, raw, binary]) of
+ {error, Reason} ->
+ {error, Reason};
+ {ok, Fd} ->
+ Res = sendfile(Fd, Sock, 0, 0, []),
+ file:close(Fd),
+ Res
+ end.
+
+%% Internal sendfile functions
+sendfile(#file_descriptor{ module = Mod } = Fd, Sock, Offset, Bytes,
+ ChunkSize, Headers, Trailers, Nodiskio, MNowait, Sync)
+ when is_port(Sock) ->
+ case Mod:sendfile(Fd, Sock, Offset, Bytes, ChunkSize, Headers, Trailers,
+ Nodiskio, MNowait, Sync) of
+ {error, enotsup} ->
+ sendfile_fallback(Fd, Sock, Offset, Bytes, ChunkSize,
+ Headers, Trailers);
+ Else ->
+ Else
+ end;
+sendfile(_,_,_,_,_,_,_,_,_,_) ->
+ {error, badarg}.
+
+%%%
+%% Sendfile Fallback
+%%%
+sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize,
+ Headers, Trailers)
+ when Headers == []; is_integer(Headers) ->
+ case sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize) of
+ {ok, BytesSent} when is_list(Trailers),
+ Trailers =/= [],
+ is_integer(Headers) ->
+ sendfile_send(Sock, Trailers, BytesSent+Headers);
+ {ok, BytesSent} when is_list(Trailers), Trailers =/= [] ->
+ sendfile_send(Sock, Trailers, BytesSent);
+ {ok, BytesSent} when is_integer(Headers) ->
+ {ok, BytesSent + Headers};
+ Else ->
+ Else
+ end;
+sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize, Headers, Trailers) ->
+ case sendfile_send(Sock, Headers, 0) of
+ {ok, BytesSent} ->
+ sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize, BytesSent,
+ Trailers);
+ Else ->
+ Else
+ end.
+
+
+sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize) ->
+ {ok, CurrPos} = file:position(File, {cur, 0}),
+ {ok, _NewPos} = file:position(File, {bof, Offset}),
+ Res = sendfile_fallback_int(File, Sock, Bytes, ChunkSize, 0),
+ file:position(File, {bof, CurrPos}),
+ Res.
+
+
+sendfile_fallback_int(File, Sock, Bytes, ChunkSize, BytesSent)
+ when Bytes > BytesSent; Bytes == 0 ->
+ Size = if Bytes == 0 ->
+ ChunkSize;
+ (Bytes - BytesSent) < ChunkSize ->
+ Bytes - BytesSent;
+ true ->
+ ChunkSize
+ end,
+ case file:read(File, Size) of
+ {ok, Data} ->
+ case sendfile_send(Sock, Data, BytesSent) of
+ {ok,NewBytesSent} ->
+ sendfile_fallback_int(
+ File, Sock, Bytes, ChunkSize,
+ NewBytesSent);
+ Error ->
+ Error
+ end;
+ eof ->
+ {ok, BytesSent};
+ Error ->
+ Error
+ end;
+sendfile_fallback_int(_File, _Sock, BytesSent, _ChunkSize, BytesSent) ->
+ {ok, BytesSent}.
+
+sendfile_send(Sock, Data, Old) ->
+ Len = iolist_size(Data),
+ case gen_tcp:send(Sock, Data) of
+ ok ->
+ {ok, Len+Old};
+ Else ->
+ Else
+ end.
+
+
+
%%%-----------------------------------------------------------------
%%% Helpers
@@ -1238,7 +1416,11 @@ mode_list(_) ->
%% Functions for communicating with the file server
call(Command, Args) when is_list(Args) ->
- gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]), infinity).
+ X = erlang:dt_spread_tag(true),
+ Y = gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]),
+ infinity),
+ erlang:dt_restore_tag(X),
+ Y.
check_and_call(Command, Args) when is_list(Args) ->
case check_args(Args) of
diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl
index 14da9c1a55..0bff56cf46 100644
--- a/lib/kernel/src/file_io_server.erl
+++ b/lib/kernel/src/file_io_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. 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
@@ -57,9 +57,11 @@ start_link(Owner, FileName, ModeList)
do_start(Spawn, Owner, FileName, ModeList) ->
Self = self(),
Ref = make_ref(),
+ Utag = erlang:dt_spread_tag(true),
Pid =
erlang:Spawn(
fun() ->
+ erlang:dt_restore_tag(Utag),
%% process_flag(trap_exit, true),
case parse_options(ModeList) of
{ReadMode, UnicodeMode, Opts} ->
@@ -84,6 +86,7 @@ do_start(Spawn, Owner, FileName, ModeList) ->
exit(Reason1)
end
end),
+ erlang:dt_restore_tag(Utag),
Mref = erlang:monitor(process, Pid),
receive
{Ref, {error, _Reason} = Error} ->
diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl
index 64c61ba3ac..fc6cd823c9 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. 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
@@ -147,15 +147,24 @@ handle_call({get_cwd, Name}, _From, Handle) ->
handle_call({read_file_info, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:read_file_info(Handle, Name), Handle};
+handle_call({read_file_info, Name, Opts}, _From, Handle) ->
+ {reply, ?PRIM_FILE:read_file_info(Handle, Name, Opts), Handle};
+
handle_call({altname, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:altname(Handle, Name), Handle};
handle_call({write_file_info, Name, Info}, _From, Handle) ->
{reply, ?PRIM_FILE:write_file_info(Handle, Name, Info), Handle};
+handle_call({write_file_info, Name, Info, Opts}, _From, Handle) ->
+ {reply, ?PRIM_FILE:write_file_info(Handle, Name, Info, Opts), Handle};
+
handle_call({read_link_info, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:read_link_info(Handle, Name), Handle};
+handle_call({read_link_info, Name, Opts}, _From, Handle) ->
+ {reply, ?PRIM_FILE:read_link_info(Handle, Name, Opts), Handle};
+
handle_call({read_link, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:read_link(Handle, Name), Handle};
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index 6cebb7ab97..8fa963ec78 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -27,7 +27,8 @@
-include("inet_sctp.hrl").
-export([open/0,open/1,open/2,close/1]).
--export([listen/2,connect/4,connect/5,connect_init/4,connect_init/5]).
+-export([listen/2,peeloff/2]).
+-export([connect/4,connect/5,connect_init/4,connect_init/5]).
-export([eof/2,abort/2]).
-export([send/3,send/4,recv/1,recv/2]).
-export([error_string/1]).
@@ -109,9 +110,11 @@ open() ->
| {ifaddr,IP}
| inet:address_family()
| {port,Port}
+ | {type,SockType}
| option(),
IP :: inet:ip_address() | any | loopback,
Port :: inet:port_number(),
+ SockType :: seqpacket | stream,
Socket :: sctp_socket().
open(Opts) when is_list(Opts) ->
@@ -134,9 +137,11 @@ open(X) ->
| {ifaddr,IP}
| inet:address_family()
| {port,Port}
+ | {type,SockType}
| option(),
IP :: inet:ip_address() | any | loopback,
Port :: inet:port_number(),
+ SockType :: seqpacket | stream,
Socket :: sctp_socket().
open(Port, Opts) when is_integer(Port), is_list(Opts) ->
@@ -161,17 +166,38 @@ close(S) ->
-spec listen(Socket, IsServer) -> ok | {error, Reason} when
Socket :: sctp_socket(),
IsServer :: boolean(),
+ Reason :: term();
+ (Socket, Backlog) -> ok | {error, Reason} when
+ Socket :: sctp_socket(),
+ Backlog :: integer(),
Reason :: term().
-listen(S, Flag) when is_port(S), is_boolean(Flag) ->
+listen(S, Backlog)
+ when is_port(S), is_boolean(Backlog);
+ is_port(S), is_integer(Backlog) ->
case inet_db:lookup_socket(S) of
{ok,Mod} ->
- Mod:listen(S, Flag);
+ Mod:listen(S, Backlog);
Error -> Error
end;
listen(S, Flag) ->
erlang:error(badarg, [S,Flag]).
+-spec peeloff(Socket, Assoc) -> {ok, NewSocket} | {error, Reason} when
+ Socket :: sctp_socket(),
+ Assoc :: #sctp_assoc_change{} | assoc_id(),
+ NewSocket :: sctp_socket(),
+ Reason :: term().
+
+peeloff(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
+ peeloff(S, AssocId);
+peeloff(S, AssocId) when is_port(S), is_integer(AssocId) ->
+ case inet_db:lookup_socket(S) of
+ {ok,Mod} ->
+ Mod:peeloff(S, AssocId);
+ Error -> Error
+ end.
+
-spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when
Socket :: sctp_socket(),
Addr :: inet:ip_address() | inet:hostname(),
@@ -288,7 +314,7 @@ eof_or_abort(S, AssocId, Action) ->
-spec send(Socket, SndRcvInfo, Data) -> ok | {error, Reason} when
Socket :: sctp_socket(),
SndRcvInfo :: #sctp_sndrcvinfo{},
- Data :: binary | iolist(),
+ Data :: binary() | iolist(),
Reason :: term().
%% Full-featured send. Rarely needed.
@@ -305,7 +331,7 @@ send(S, SRI, Data) ->
Socket :: sctp_socket(),
Assoc :: #sctp_assoc_change{} | assoc_id(),
Stream :: integer(),
- Data :: binary | iolist(),
+ Data :: binary() | iolist(),
Reason :: term().
send(S, #sctp_assoc_change{assoc_id=AssocId}, Stream, Data)
@@ -399,9 +425,10 @@ error_string(X) ->
erlang:error(badarg, [X]).
--spec controlling_process(Socket, Pid) -> ok when
+-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when
Socket :: sctp_socket(),
- Pid :: pid().
+ Pid :: pid(),
+ Reason :: closed | not_owner | inet:posix().
controlling_process(S, Pid) when is_port(S), is_pid(Pid) ->
inet:udp_controlling_process(S, Pid);
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 8ab18c01b4..e6dfdadb03 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -27,10 +27,10 @@
-export([fdopen/2]).
-include("inet_int.hrl").
+-include("file.hrl").
-type option() ::
{active, true | false | once} |
- {bit8, clear | set | on | off} |
{buffer, non_neg_integer()} |
{delay_send, boolean()} |
{deliver, port | term} |
@@ -60,7 +60,6 @@
{tos, non_neg_integer()}.
-type option_name() ::
active |
- bit8 |
buffer |
delay_send |
deliver |
@@ -174,7 +173,7 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) ->
Port :: inet:port_number(),
Options :: [listen_option()],
ListenSocket :: socket(),
- Reason :: inet:posix().
+ Reason :: system_limit | inet:posix().
listen(Port, Opts) ->
Mod = mod(Opts, undefined),
@@ -193,7 +192,7 @@ listen(Port, Opts) ->
-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when
ListenSocket :: socket(),
Socket :: socket(),
- Reason :: closed | timeout | inet:posix().
+ Reason :: closed | timeout | system_limit | inet:posix().
accept(S) ->
case inet_db:lookup_socket(S) of
@@ -207,7 +206,7 @@ accept(S) ->
ListenSocket :: socket(),
Timeout :: timeout(),
Socket :: socket(),
- Reason :: closed | timeout | inet:posix().
+ Reason :: closed | timeout | system_limit | inet:posix().
accept(S, Time) when is_port(S) ->
case inet_db:lookup_socket(S) of
@@ -302,7 +301,7 @@ unrecv(S, Data) when is_port(S) ->
Mod:unrecv(S, Data);
Error ->
Error
- end.
+ end.
%%
%% Set controlling process
@@ -354,3 +353,4 @@ mod([_|Opts], Address) ->
mod(Opts, Address);
mod([], Address) ->
mod(Address).
+
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 8688799ae9..914854c65c 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -185,9 +185,10 @@ connect(S, Address, Port) when is_port(S) ->
Error
end.
--spec controlling_process(Socket, Pid) -> ok when
+-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when
Socket :: socket(),
- Pid :: pid().
+ Pid :: pid(),
+ Reason :: closed | not_owner | inet:posix().
controlling_process(S, NewOwner) ->
inet:udp_controlling_process(S, NewOwner).
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index 7d15f8bf83..36cb713ee1 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -28,7 +28,7 @@
%% External exports
-export([start/0, start_link/0, stop/0, sync/0, sync/1,
- safe_whereis_name/1, whereis_name/1, register_name/2,
+ whereis_name/1, register_name/2,
register_name/3, register_name_external/2, register_name_external/3,
unregister_name_external/1,re_register_name/2, re_register_name/3,
unregister_name/1, registered_names/0, send/2, node_disconnected/1,
@@ -203,10 +203,6 @@ send(Name, Msg) ->
whereis_name(Name) ->
where(Name).
--spec safe_whereis_name(term()) -> pid() | 'undefined'.
-safe_whereis_name(Name) ->
- gen_server:call(global_name_server, {whereis, Name}, infinity).
-
node_disconnected(Node) ->
global_name_server ! {nodedown, Node}.
@@ -284,13 +280,13 @@ unregister_name(Name) ->
gen_server:call(global_name_server, {registrar, Fun}, infinity)
end.
--spec re_register_name(Name, Pid) -> _ when
+-spec re_register_name(Name, Pid) -> 'yes' when
Name :: term(),
Pid :: pid().
re_register_name(Name, Pid) when is_pid(Pid) ->
re_register_name(Name, Pid, fun random_exit_name/3).
--spec re_register_name(Name, Pid, Resolve) -> _ when
+-spec re_register_name(Name, Pid, Resolve) -> 'yes' when
Name :: term(),
Pid :: pid(),
Resolve :: method().
@@ -510,8 +506,7 @@ init([]) ->
%% delay can sometimes be quite substantial. Global guarantees that
%% the name will eventually be removed, but there is no
%% synchronization between nodes; the name can be removed from some
-%% node(s) long before it is removed from other nodes. Using
-%% safe_whereis_name is no cure.
+%% node(s) long before it is removed from other nodes.
%%
%% - Global cannot handle problems with the distribution very well.
%% Depending on the value of the kernel variable 'net_ticktime' long
@@ -589,10 +584,6 @@ init([]) ->
{'reply', term(), state()} |
{'stop', 'normal', 'stopped', state()}.
-handle_call({whereis, Name}, From, S) ->
- do_whereis(Name, From),
- {noreply, S};
-
handle_call({registrar, Fun}, From, S) ->
S#state.the_registrar ! {trans_all_known, Fun, From},
{noreply, S};
@@ -1235,7 +1226,15 @@ ins_name_ext(Name, Pid, Method, RegNode, FromPidOrNode, ExtraInfo, S0) ->
where(Name) ->
case ets:lookup(global_names, Name) of
- [{_Name, Pid, _Method, _RPid, _Ref}] -> Pid;
+ [{_Name, Pid, _Method, _RPid, _Ref}] ->
+ if node(Pid) == node() ->
+ case is_process_alive(Pid) of
+ true -> Pid;
+ false -> undefined
+ end;
+ true ->
+ Pid
+ end;
[] -> undefined
end.
@@ -1966,7 +1965,7 @@ resolve_it(Method, Name, Pid1, Pid2) ->
minmax(P1,P2) ->
if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end.
--spec random_exit_name(Name, Pid1, Pid2) -> 'none' when
+-spec random_exit_name(Name, Pid1, Pid2) -> pid() when
Name :: term(),
Pid1 :: pid(),
Pid2 :: pid().
@@ -1977,7 +1976,7 @@ random_exit_name(Name, Pid, Pid2) ->
exit(Max, kill),
Min.
--spec random_notify_name(Name, Pid1, Pid2) -> 'none' when
+-spec random_notify_name(Name, Pid1, Pid2) -> pid() when
Name :: term(),
Pid1 :: pid(),
Pid2 :: pid().
@@ -2176,7 +2175,7 @@ get_own_nodes() ->
start_the_registrar() ->
spawn_link(fun() -> loop_the_registrar() end).
-
+
loop_the_registrar() ->
receive
{trans_all_known, Fun, From} ->
diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl
index 255ae4e51b..218be964a0 100644
--- a/lib/kernel/src/heart.erl
+++ b/lib/kernel/src/heart.erl
@@ -18,6 +18,10 @@
%%
-module(heart).
+-compile(no_native).
+% 'no_native' as part of a crude fix to make init:restart/0 work by clearing
+% all hipe inter-module information (hipe_mfa_info's in hipe_bif0.c).
+
%%%--------------------------------------------------------------------
%%% This is a rewrite of pre_heart from BS.3.
%%%
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index 1d3eb926ca..514c002d87 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -34,9 +34,15 @@
-module(hipe_unified_loader).
+-compile(no_native).
+% 'no_native' is a workaround to avoid "The code server called unloaded module"
+% caused by Mod:module_info(exports) in patch_to_emu_step1() called by post_beam_load.
+% Reproducable with hipelibs and asn1_SUITE.
+% I think the real solution would be to let BIF erlang:load_module/2 redirect all
+% hipe calls to the module and thereby remove post_beam_load.
+
-export([chunk_name/1,
%% Only the code and code_server modules may call the entries below!
- load_hipe_modules/0,
load_native_code/2,
post_beam_load/1,
load_module/3,
@@ -78,16 +84,6 @@ chunk_name(Architecture) ->
%%========================================================================
--spec load_hipe_modules() -> 'ok'.
-%% @doc
-%% Ensures HiPE's loader modules are loaded.
-%% Called from code.erl at start-up.
-
-load_hipe_modules() ->
- ok.
-
-%%========================================================================
-
-spec load_native_code(Mod, binary()) -> 'no_native' | {'module', Mod}
when is_subtype(Mod, atom()).
%% @doc
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 48a6f3db65..1a03424f88 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -36,10 +36,14 @@
-export([i/0, i/1, i/2]).
--export([getll/1, getfd/1, open/7, fdopen/5]).
+-export([getll/1, getfd/1, open/8, fdopen/6]).
-export([tcp_controlling_process/2, udp_controlling_process/2,
tcp_close/1, udp_close/1]).
+
+%% used by sendfile
+-export([lock_socket/2]).
+
%% used by socks5
-export([setsockname/2, setpeername/2]).
@@ -115,7 +119,8 @@
'mtu' | 'netmask' | 'flags' |'hwaddr'.
-type address_family() :: 'inet' | 'inet6'.
--type protocol_option() :: 'tcp' | 'udp' | 'sctp'.
+-type socket_protocol() :: 'tcp' | 'udp' | 'sctp'.
+-type socket_type() :: 'stream' | 'dgram' | 'seqpacket'.
-type stat_option() ::
'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' |
'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'.
@@ -748,6 +753,8 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
sctp_opt(Opts, Mod, R#sctp_opts{port=P}, As);
Error -> Error
end;
+ {type,Type} when Type =:= seqpacket; Type =:= stream ->
+ sctp_opt(Opts, Mod, R#sctp_opts{type=Type}, As);
binary -> sctp_opt (Opts, Mod, R, As, mode, binary);
list -> sctp_opt (Opts, Mod, R, As, mode, list);
{sctp_module,_} -> sctp_opt (Opts, Mod, R, As); % Done with
@@ -756,8 +763,12 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
{Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val);
_ -> {error,badarg}
end;
-sctp_opt([], _Mod, R, _SockOpts) ->
- {ok, R}.
+sctp_opt([], _Mod, #sctp_opts{ifaddr=IfAddr}=R, _SockOpts) ->
+ if is_list(IfAddr) ->
+ {ok, R#sctp_opts{ifaddr=lists:reverse(IfAddr)}};
+ true ->
+ {ok, R}
+ end.
sctp_opt(Opts, Mod, R, As, Name, Val) ->
case add_opt(Name, Val, R#sctp_opts.opts, As) of
@@ -996,22 +1007,19 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
Addr :: ip_address(),
Port :: port_number(),
Opts :: [socket_setopt()],
- Protocol :: protocol_option(),
- Family :: 'inet' | 'inet6',
+ Protocol :: socket_protocol(),
+ Family :: address_family(),
+ Type :: socket_type(),
Module :: atom()) ->
{'ok', socket()} | {'error', posix()}.
-open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
- case prim_inet:open(Protocol, Family) of
+open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
+ case prim_inet:open(Protocol, Family, Type) of
{ok,S} ->
case prim_inet:setopts(S, Opts) of
ok ->
case if is_list(Addr) ->
- prim_inet:bind(S, add,
- [case A of
- {_,_} -> A;
- _ -> {A,Port}
- end || A <- Addr]);
+ bindx(S, Addr, Port);
true ->
prim_inet:bind(S, Addr, Port)
end of
@@ -1029,18 +1037,47 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
Error ->
Error
end;
-open(Fd, _Addr, _Port, Opts, Protocol, Family, Module) ->
- fdopen(Fd, Opts, Protocol, Family, Module).
+open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) ->
+ fdopen(Fd, Opts, Protocol, Family, Type, Module).
+
+bindx(S, [Addr], Port0) ->
+ {IP, Port} = set_bindx_port(Addr, Port0),
+ prim_inet:bind(S, IP, Port);
+bindx(S, Addrs, Port0) ->
+ [{IP, Port} | Rest] = [set_bindx_port(Addr, Port0) || Addr <- Addrs],
+ case prim_inet:bind(S, IP, Port) of
+ {ok, AssignedPort} when Port =:= 0 ->
+ %% On newer Linux kernels, Solaris and FreeBSD, calling
+ %% bindx with port 0 is ok, but on SuSE 10, it results in einval
+ Rest2 = [change_bindx_0_port(Addr, AssignedPort) || Addr <- Rest],
+ prim_inet:bind(S, add, Rest2);
+ {ok, _} ->
+ prim_inet:bind(S, add, Rest);
+ Error ->
+ Error
+ end.
+
+set_bindx_port({_IP, _Port}=Addr, _OtherPort) ->
+ Addr;
+set_bindx_port(IP, Port) ->
+ {IP, Port}.
+
+change_bindx_0_port({IP, 0}, AssignedPort) ->
+ {IP, AssignedPort};
+change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) ->
+ Addr.
+
-spec fdopen(Fd :: non_neg_integer(),
Opts :: [socket_setopt()],
- Protocol :: protocol_option(),
+ Protocol :: socket_protocol(),
Family :: address_family(),
+ Type :: socket_type(),
Module :: atom()) ->
{'ok', socket()} | {'error', posix()}.
-fdopen(Fd, Opts, Protocol, Family, Module) ->
- case prim_inet:fdopen(Protocol, Fd, Family) of
+fdopen(Fd, Opts, Protocol, Family, Type, Module) ->
+ case prim_inet:fdopen(Protocol, Family, Type, Fd) of
{ok, S} ->
case prim_inet:setopts(S, Opts) of
ok ->
@@ -1056,18 +1093,24 @@ fdopen(Fd, Opts, Protocol, Family, Module) ->
%% socket stat
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-i() -> i(tcp), i(udp).
+i() -> i(tcp), i(udp), i(sctp).
i(Proto) -> i(Proto, [port, module, recv, sent, owner,
- local_address, foreign_address, state]).
+ local_address, foreign_address, state, type]).
i(tcp, Fs) ->
ii(tcp_sockets(), Fs, tcp);
i(udp, Fs) ->
- ii(udp_sockets(), Fs, udp).
+ ii(udp_sockets(), Fs, udp);
+i(sctp, Fs) ->
+ ii(sctp_sockets(), Fs, sctp).
ii(Ss, Fs, Proto) ->
- LLs = [h_line(Fs) | info_lines(Ss, Fs, Proto)],
+ LLs =
+ case info_lines(Ss, Fs, Proto) of
+ [] -> [];
+ InfoLines -> [h_line(Fs) | InfoLines]
+ end,
Maxs = foldl(
fun(Line,Max0) -> smax(Max0,Line) end,
duplicate(length(Fs),0),LLs),
@@ -1135,6 +1178,7 @@ info(S, F, Proto) ->
case prim_inet:gettype(S) of
{ok,{_,stream}} -> "STREAM";
{ok,{_,dgram}} -> "DGRAM";
+ {ok,{_,seqpacket}} -> "SEQPACKET";
_ -> " "
end;
fd ->
@@ -1186,6 +1230,7 @@ fmt_port(N, Proto) ->
%% Return a list of all tcp sockets
tcp_sockets() -> port_list("tcp_inet").
udp_sockets() -> port_list("udp_inet").
+sctp_sockets() -> port_list("sctp_inet").
%% Return all ports having the name 'Name'
port_list(Name) ->
@@ -1201,11 +1246,13 @@ port_list(Name) ->
%% utils
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec format_error(Posix) -> string() when
- Posix :: posix().
+-spec format_error(Reason) -> string() when
+ Reason :: posix() | system_limit.
format_error(exbadport) -> "invalid port state";
format_error(exbadseq) -> "bad command sequence";
+format_error(system_limit) ->
+ "a system limit was hit, probably not enough ports";
format_error(Tag) ->
erl_posix_msg:message(Tag).
@@ -1227,6 +1274,8 @@ udp_close(S) when is_port(S) ->
%% Set controlling process for TCP socket.
tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
case erlang:port_info(S, connected) of
+ {connected, NewOwner} ->
+ ok;
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
undefined ->
@@ -1278,6 +1327,8 @@ tcp_sync_input(S, Owner, Flag) ->
%% Set controlling process for UDP or SCTP socket.
udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
case erlang:port_info(S, connected) of
+ {connected, NewOwner} ->
+ ok;
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
_ ->
@@ -1340,3 +1391,14 @@ stop_timer(Timer) ->
end;
T -> T
end.
+
+
+lock_socket(S,Val) ->
+ case erlang:port_info(S, connected) of
+ {connected, Pid} when Pid =/= self() ->
+ {error, not_owner};
+ undefined ->
+ {error, einval};
+ _ ->
+ prim_inet:ignorefd(S,Val)
+ end.
diff --git a/lib/kernel/src/inet6_sctp.erl b/lib/kernel/src/inet6_sctp.erl
index 5bf3fca647..c47483bbdd 100644
--- a/lib/kernel/src/inet6_sctp.erl
+++ b/lib/kernel/src/inet6_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. 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
@@ -32,7 +32,8 @@
-define(FAMILY, inet6).
-export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
+-export([open/1,close/1,listen/2,peeloff/2,connect/5]).
+-export([sendmsg/3,send/4,recv/2]).
@@ -54,8 +55,8 @@ translate_ip(IP) ->
open(Opts) ->
case inet:sctp_options(Opts, ?MODULE) of
- {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,opts=SOs}} ->
- inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, ?MODULE);
+ {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} ->
+ inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE);
Error -> Error
end.
@@ -65,6 +66,14 @@ close(S) ->
listen(S, Flag) ->
prim_inet:listen(S, Flag).
+peeloff(S, AssocId) ->
+ case prim_inet:peeloff(S, AssocId) of
+ {ok, NewS}=Result ->
+ inet_db:register_socket(NewS, ?MODULE),
+ Result;
+ Error -> Error
+ end.
+
connect(S, Addr, Port, Opts, Timer) ->
inet_sctp:connect(S, Addr, Port, Opts, Timer).
diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl
index cc45f6c7f6..c714b2bee0 100644
--- a/lib/kernel/src/inet6_tcp.erl
+++ b/lib/kernel/src/inet6_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -93,7 +93,7 @@ do_connect(Addr = {A,B,C,D,E,F,G,H}, Port, Opts, Time) when
port=BPort,
opts=SockOpts}}
when ?ip6(Ab,Bb,Cb,Db,Eb,Fb,Gb,Hb), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of
{ok, S} ->
case prim_inet:connect(S, Addr, Port, Time) of
ok -> {ok,S};
@@ -115,7 +115,7 @@ listen(Port, Opts) ->
port=BPort,
opts=SockOpts}=R}
when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of
{ok, S} ->
case prim_inet:listen(S, R#listen_opts.backlog) of
ok -> {ok, S};
@@ -149,5 +149,5 @@ accept(L,Timeout) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, tcp, inet6, ?MODULE).
+ inet:fdopen(Fd, Opts, tcp, inet6, stream, ?MODULE).
diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl
index e81d417151..ca43c94211 100644
--- a/lib/kernel/src/inet6_udp.erl
+++ b/lib/kernel/src/inet6_udp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -45,7 +45,7 @@ open(Port, Opts) ->
port=BPort,
opts=SockOpts}}
when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) ->
- inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,?MODULE);
+ inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,dgram,?MODULE);
{ok, _} -> exit(badarg)
end.
@@ -84,4 +84,4 @@ controlling_process(Socket, NewOwner) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, udp, inet6, ?MODULE).
+ inet:fdopen(Fd, Opts, udp, inet6, dgram, ?MODULE).
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 6f1688c6a2..cf893c73eb 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -29,7 +29,7 @@
-define(INET_AF_ANY, 3). % Fake for ANY in any address family
-define(INET_AF_LOOPBACK, 4). % Fake for LOOPBACK in any address family
-%% type codes (gettype, INET_REQ_GETTYPE)
+%% type codes to open and gettype - INET_REQ_GETTYPE
-define(INET_TYPE_STREAM, 1).
-define(INET_TYPE_DGRAM, 2).
-define(INET_TYPE_SEQPACKET, 3).
@@ -83,16 +83,21 @@
-define(INET_REQ_IFSET, 23).
-define(INET_REQ_SUBSCRIBE, 24).
-define(INET_REQ_GETIFADDRS, 25).
+-define(INET_REQ_ACCEPT, 26).
+-define(INET_REQ_LISTEN, 27).
+-define(INET_REQ_IGNOREFD, 28).
+
%% TCP requests
--define(TCP_REQ_ACCEPT, 40).
--define(TCP_REQ_LISTEN, 41).
+%%-define(TCP_REQ_ACCEPT, 40). MOVED
+%%-define(TCP_REQ_LISTEN, 41). MERGED
-define(TCP_REQ_RECV, 42).
-define(TCP_REQ_UNRECV, 43).
-define(TCP_REQ_SHUTDOWN, 44).
%% UDP and SCTP requests
-define(PACKET_REQ_RECV, 60).
--define(SCTP_REQ_LISTEN, 61).
+%%-define(SCTP_REQ_LISTEN, 61). MERGED
-define(SCTP_REQ_BINDX, 62). %% Multi-home SCTP bind
+-define(SCTP_REQ_PEELOFF, 63).
%% subscribe codes, INET_REQ_SUBSCRIBE
-define(INET_SUBS_EMPTY_OUT_Q, 1).
@@ -100,7 +105,7 @@
%% reply codes for *_REQ_*
-define(INET_REP_ERROR, 0).
-define(INET_REP_OK, 1).
--define(INET_REP_SCTP, 2).
+-define(INET_REP, 2).
%% INET, TCP and UDP options:
-define(INET_OPT_REUSEADDR, 0).
@@ -399,6 +404,7 @@
ifaddr,
port = 0,
fd = -1,
+ type = seqpacket,
opts = [{mode, binary},
{buffer, ?SCTP_DEF_BUFSZ},
{sndbuf, ?SCTP_DEF_BUFSZ},
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index de74b573bd..2d799d79fa 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. 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
@@ -31,7 +31,8 @@
-define(FAMILY, inet).
-export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
+-export([open/1,close/1,listen/2,peeloff/2,connect/5]).
+-export([sendmsg/3,send/4,recv/2]).
@@ -53,8 +54,8 @@ translate_ip(IP) ->
open(Opts) ->
case inet:sctp_options(Opts, ?MODULE) of
- {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,opts=SOs}} ->
- inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, ?MODULE);
+ {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} ->
+ inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE);
Error -> Error
end.
@@ -64,6 +65,14 @@ close(S) ->
listen(S, Flag) ->
prim_inet:listen(S, Flag).
+peeloff(S, AssocId) ->
+ case prim_inet:peeloff(S, AssocId) of
+ {ok, NewS}=Result ->
+ inet_db:register_socket(NewS, ?MODULE),
+ Result;
+ Error -> Error
+ end.
+
%% A non-blocking connect is implemented when the initial call is to
%% gen_sctp:connect_init which passes the value nowait as the Timer
connect(S, Addr, Port, Opts, Timer) ->
@@ -102,7 +111,7 @@ connect(S, Addr, Port, Opts, Timer) ->
connect_get_assoc(S, Addr, Port, false, Timer) ->
case recv(S, inet:timeout(Timer)) of
- {ok, {Addr, Port, [], #sctp_assoc_change{state=St}=Ev}} ->
+ {ok, {Addr, Port, _, #sctp_assoc_change{state=St}=Ev}} ->
if St =:= comm_up ->
%% Yes, successfully connected, return the whole
%% sctp_assoc_change event (containing, in particular,
@@ -123,7 +132,7 @@ connect_get_assoc(S, Addr, Port, false, Timer) ->
connect_get_assoc(S, Addr, Port, Active, Timer) ->
Timeout = inet:timeout(Timer),
receive
- {sctp,S,Addr,Port,{[],#sctp_assoc_change{state=St}=Ev}} ->
+ {sctp,S,Addr,Port,{_,#sctp_assoc_change{state=St}=Ev}} ->
case Active of
once ->
prim_inet:setopt(S, active, once);
diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl
index 6dadccd6a9..4c2db16ce3 100644
--- a/lib/kernel/src/inet_tcp.erl
+++ b/lib/kernel/src/inet_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -95,7 +95,7 @@ do_connect({A,B,C,D}, Port, Opts, Time) when ?ip(A,B,C,D), ?port(Port) ->
port=BPort,
opts=SockOpts}}
when ?ip(Ab,Bb,Cb,Db), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of
{ok, S} ->
case prim_inet:connect(S, {A,B,C,D}, Port, Time) of
ok -> {ok,S};
@@ -117,7 +117,7 @@ listen(Port, Opts) ->
port=BPort,
opts=SockOpts}=R}
when ?ip(A,B,C,D), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of
{ok, S} ->
case prim_inet:listen(S, R#listen_opts.backlog) of
ok -> {ok, S};
@@ -150,4 +150,4 @@ accept(L,Timeout) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, tcp, inet, ?MODULE).
+ inet:fdopen(Fd, Opts, tcp, inet, stream, ?MODULE).
diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl
index 60bd96f332..80d930fe10 100644
--- a/lib/kernel/src/inet_udp.erl
+++ b/lib/kernel/src/inet_udp.erl
@@ -52,7 +52,7 @@ open(Port, Opts) ->
ifaddr=BAddr={A,B,C,D},
port=BPort,
opts=SockOpts}} when ?ip(A,B,C,D), ?port(BPort) ->
- inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,?MODULE);
+ inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,dgram,?MODULE);
{ok, _} -> exit(badarg)
end.
@@ -93,7 +93,7 @@ controlling_process(Socket, NewOwner) ->
fdopen(Fd, Opts) ->
inet:fdopen(Fd,
optuniquify([{recbuf, ?RECBUF} | Opts]),
- udp, inet, ?MODULE).
+ udp, inet, dgram, ?MODULE).
%% Remove all duplicate options from an option list.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 54a63833e6..bded2408a7 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -1 +1,27 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"%VSN%",
+ %% Up from - max two major revisions back
+ [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ %% Down to - max two major revisions back
+ [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+}.
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index e214ffa404..a3fc57a124 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -286,7 +286,7 @@ call(N,M,F,A) ->
Reason :: term(),
Timeout :: timeout().
-call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call
+call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call
local_call(M,F,A);
call(N,M,F,A,infinity) ->
do_call(N, {call,M,F,A,group_leader()}, infinity);
diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl
index c34f2ddeb0..8f2ca28f56 100644
--- a/lib/kernel/src/user_drv.erl
+++ b/lib/kernel/src/user_drv.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -117,8 +117,9 @@ server1(Iport, Oport, Shell) ->
{Curr,Shell1} =
case init:get_argument(remsh) of
{ok,[[Node]]} ->
- RShell = {list_to_atom(Node),shell,start,[]},
- RGr = group:start(self(), RShell),
+ ANode = list_to_atom(Node),
+ RShell = {ANode,shell,start,[]},
+ RGr = group:start(self(), RShell, rem_sh_opts(ANode)),
{RGr,RShell};
E when E =:= error ; E =:= {ok,[[]]} ->
{group:start(self(), Shell),Shell}
@@ -134,6 +135,9 @@ server1(Iport, Oport, Shell) ->
%% Enter the server loop.
server_loop(Iport, Oport, Curr, User, Gr).
+rem_sh_opts(Node) ->
+ [{expand_fun,fun(B)-> rpc:call(Node,edlin_expand,expand,[B]) end}].
+
%% start_user()
%% Start a group leader process and register it as 'user', unless,
%% of course, a 'user' already exists.
diff --git a/lib/kernel/src/user_sup.erl b/lib/kernel/src/user_sup.erl
index 35b7ff0cfe..cb50d9491d 100644
--- a/lib/kernel/src/user_sup.erl
+++ b/lib/kernel/src/user_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -45,7 +45,7 @@ init([]) ->
Pid = start_slave(Master),
{ok, Pid, Pid};
{M, F, A} ->
- case start_user({M, F}, A) of
+ case start_user(M, F, A) of
{ok, Pid} ->
{ok, Pid, Pid};
Error ->
@@ -95,8 +95,8 @@ terminate(_Reason, UserPid) ->
%% is guaranteed that the user is started.
%%-----------------------------------------------------------------
-start_user(Func,A) ->
- apply(Func, A),
+start_user(Mod, Func, A) ->
+ apply(Mod, Func, A),
wait_for_user_p(100).
wait_for_user_p(0) ->
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 82bc3fc6d1..7bb3e3a365 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -74,7 +74,8 @@ MODULES= \
wrap_log_reader_SUITE \
cleanup \
zlib_SUITE \
- loose_node
+ loose_node \
+ sendfile_SUITE
APP_FILES = \
appinc.app \
@@ -139,12 +140,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
release_tests_spec: make_emakefile
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)
- $(INSTALL_DATA) $(APP_FILES) $(RELSYSDIR)
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) kernel.spec $(EMAKEFILE)\
- $(COVERFILE) $(RELSYSDIR)
- chmod -R u+w $(RELSYSDIR)
- @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+ $(COVERFILE) "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+ @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 2c5b8ccb66..f469a0af98 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -33,7 +33,7 @@
-export([config_change/1,
distr_changed_tc1/1, distr_changed_tc2/1,
- shutdown_func/1, do_shutdown/1]).
+ shutdown_func/1, do_shutdown/1, shutdown_timeout/1]).
-define(TESTCASE, testcase_name).
-define(testcase, ?config(?TESTCASE, Config)).
@@ -50,7 +50,7 @@ all() ->
load_use_cache, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
permit_false_start_dist, get_key,
- {group, distr_changed}, config_change, shutdown_func].
+ {group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
groups() ->
[{reported_bugs, [],
@@ -1915,6 +1915,32 @@ do_shutdown(Reason) ->
+%%%-----------------------------------------------------------------
+%%% Tests the 'shutdown_timeout' kernel config parameter
+%%%-----------------------------------------------------------------
+shutdown_timeout(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_timeout"),
+ wait_for_ready_net(),
+ ok = rpc:call(Cp1, application, set_env, [kernel, shutdown_timeout, 1000]),
+ rpc:call(Cp1, code, add_path, [filename:join([DataDir,deadlock])]),
+ ok = rpc:call(Cp1, application, start, [sasl]),
+ ok = rpc:call(Cp1, application, start, [deadlock]),
+ rpc:call(Cp1, deadlock, restart_and_fail, []),
+
+ ok = net_kernel:monitor_nodes(true),
+ _ = rpc:call(Cp1, init, stop, []),
+ receive
+ {nodedown,Cp1} ->
+ ok
+ after 10000 ->
+ ct:fail("timeout 10 sec: node termination hangs")
+ end,
+ ok.
+
+
+
+
%%-----------------------------------------------------------------
%% Utility functions
%%-----------------------------------------------------------------
diff --git a/lib/kernel/test/application_SUITE_data/Makefile.src b/lib/kernel/test/application_SUITE_data/Makefile.src
index a237f6badb..abc3c82907 100644
--- a/lib/kernel/test/application_SUITE_data/Makefile.src
+++ b/lib/kernel/test/application_SUITE_data/Makefile.src
@@ -2,7 +2,8 @@ EFLAGS=+debug_info
all: app_start_error.@EMULATOR@ trans_abnormal_sup.@EMULATOR@ \
trans_normal_sup.@EMULATOR@ transient.@EMULATOR@ \
- group_leader_sup.@EMULATOR@ group_leader.@EMULATOR@
+ group_leader_sup.@EMULATOR@ group_leader.@EMULATOR@ \
+ deadlock/deadlock.@EMULATOR@
app_start_error.@EMULATOR@: app_start_error.erl
erlc $(EFLAGS) app_start_error.erl
@@ -22,3 +23,5 @@ group_leader.@EMULATOR@: group_leader.erl
group_leader_sup.@EMULATOR@: group_leader_sup.erl
erlc $(EFLAGS) group_leader_sup.erl
+deadlock/deadlock.@EMULATOR@: deadlock/deadlock.erl
+ erlc $(EFLAGS) -o deadlock deadlock/deadlock.erl \ No newline at end of file
diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
new file mode 100644
index 0000000000..0c1001bed6
--- /dev/null
+++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
@@ -0,0 +1,8 @@
+{application, deadlock, [
+ {vsn, "1"},
+ {registered, []},
+ {applications, [kernel, stdlib, sasl]},
+ {modules, [deadlock]},
+ {mod, {deadlock, []}},
+ {env, [{fail_start, false}]}
+]}.
diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
new file mode 100644
index 0000000000..5f68bf9078
--- /dev/null
+++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
@@ -0,0 +1,69 @@
+-module(deadlock).
+-behaviour(application).
+-compile(export_all).
+-define(SUP,deadlock_sup).
+-define(CHILD,deadlock_child).
+
+
+%%%-----------------------------------------------------------------
+%%% application callbacks
+start(_StartType, _StartArgs) ->
+ supervisor:start_link({local, ?SUP}, ?MODULE, [sup]).
+
+stop(_State) ->
+ ok.
+
+
+
+%%%-----------------------------------------------------------------
+%%% supervisor callbacks
+init([sup]) ->
+ {ok, {{one_for_one, 5, 10}, [
+ {
+ sasl_syslog_dm, {?MODULE, start_link, []},
+ permanent, brutal_kill, worker,
+ [deadlock]
+ }
+ ]}};
+
+
+%%%-----------------------------------------------------------------
+%%% gen_server callbacks
+init([child]) ->
+ case application:get_env(deadlock, fail_start) of
+ {ok, false} ->
+ %% we must not fail on the first init, otherwise supervisor
+ %% terminates immediately
+ {ok, []};
+ {ok, true} ->
+ timer:sleep(infinity), % init hangs!!!!
+ {ok, []}
+ end.
+
+handle_call(_Req, _From, State) ->
+ {reply, ok, State}.
+
+handle_cast(restart, State) ->
+ {stop, error, State}.
+
+handle_info(_Msg, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+%%%-----------------------------------------------------------------
+%%% Start child
+start_link() ->
+ gen_server:start_link({local, ?CHILD}, ?MODULE, [child], []).
+
+
+%%%-----------------------------------------------------------------
+%%% Provoke hanging
+restart_and_fail() ->
+ application:set_env(deadlock, fail_start, true), % next init will hang
+ gen_server:cast(?CHILD, restart).
diff --git a/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl
index 6276270d20..a2826f34df 100644
--- a/lib/kernel/test/bif_SUITE.erl
+++ b/lib/kernel/test/bif_SUITE.erl
@@ -260,23 +260,15 @@ spawn_opt2(Config) when is_list(Config) ->
?line P1 = spawn_opt(fun() ->
Parent ! {self(), fetch_proc_vals(self())}
end,
- case heap_type() of
- separate ->
- [{fullsweep_after, 0},{min_heap_size, 1000}];
- shared ->
- []
- end
- ++ [link, {priority, max}]),
+ [{fullsweep_after, 0},{min_heap_size, 1000},
+ link, {priority, max}]),
?line receive
{P1, PV1} ->
?line Node = node(P1),
?line check_proc_vals(true, max, 0, 1000, PV1)
end,
?line P2 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end,
- case heap_type() of
- separate -> [{min_heap_size, 10}];
- shared -> []
- end),
+ [{min_heap_size, 10}]),
?line receive
{P2, PV2} ->
?line Node = node(P2),
@@ -295,13 +287,8 @@ spawn_opt3(Config) when is_list(Config) ->
fun() ->
Parent ! {self(), fetch_proc_vals(self())}
end,
- case heap_type() of
- separate ->
- [{fullsweep_after,0}, {min_heap_size,1000}];
- shared ->
- []
- end
- ++ [link, {priority, max}]),
+ [{fullsweep_after,0}, {min_heap_size,1000},
+ link, {priority, max}]),
?line receive
{P1, PV1} ->
?line Node = node(P1),
@@ -309,10 +296,7 @@ spawn_opt3(Config) when is_list(Config) ->
end,
?line P2 = spawn_opt(Node,
fun() -> Parent ! {self(), fetch_proc_vals(self())} end,
- case heap_type() of
- separate -> [{min_heap_size, 10}];
- shared -> []
- end),
+ [{min_heap_size, 10}]),
?line receive
{P2, PV2} ->
?line Node = node(P2),
@@ -333,13 +317,8 @@ spawn_opt4(Config) when is_list(Config) ->
[fun() ->
Parent ! {self(), fetch_proc_vals(self())}
end],
- case heap_type() of
- separate ->
- [{fullsweep_after,0}, {min_heap_size,1000}];
- shared ->
- []
- end
- ++ [link, {priority, max}]),
+ [{fullsweep_after,0}, {min_heap_size,1000},
+ link, {priority, max}]),
?line receive
{P1, PV1} ->
?line Node = node(P1),
@@ -350,10 +329,7 @@ spawn_opt4(Config) when is_list(Config) ->
[fun() ->
Parent ! {self(), fetch_proc_vals(self())}
end],
- case heap_type() of
- separate -> [{min_heap_size, 10}];
- shared -> []
- end),
+ [{min_heap_size, 10}]),
?line receive
{P2, PV2} ->
?line Node = node(P2),
@@ -374,13 +350,8 @@ spawn_opt5(Config) when is_list(Config) ->
[fun() ->
Parent ! {self(), fetch_proc_vals(self())}
end],
- case heap_type() of
- separate ->
- [{fullsweep_after,0}, {min_heap_size,1000}];
- shared ->
- []
- end
- ++ [link, {priority, max}]),
+ [{fullsweep_after,0}, {min_heap_size,1000},
+ link, {priority, max}]),
?line receive
{P1, PV1} ->
?line Node = node(P1),
@@ -392,10 +363,7 @@ spawn_opt5(Config) when is_list(Config) ->
[fun() ->
Parent ! {self(), fetch_proc_vals(self())}
end],
- case heap_type() of
- separate -> [{min_heap_size, 10}];
- shared -> []
- end),
+ [{min_heap_size, 10}]),
?line receive
{P2, PV2} ->
?line Node = node(P2),
@@ -532,34 +500,19 @@ spawn_failures(Config) when is_list(Config) ->
check_proc_vals(Link, Priority, FullsweepAfter, MinHeapSize, {Ls, P, FA, HS}) ->
?line Link = lists:member(self(), Ls),
?line Priority = P,
- ?line case heap_type() of
- separate ->
- ?line FullsweepAfter = FA,
- ?line true = (HS >= MinHeapSize);
- shared ->
- ?line ok
- end,
+ FullsweepAfter = FA,
+ true = (HS >= MinHeapSize),
?line ok.
fetch_proc_vals(Pid) ->
?line PI = process_info(Pid),
?line {value,{links, Ls}} = lists:keysearch(links, 1, PI),
?line {value,{priority,P}} = lists:keysearch(priority, 1, PI),
- ?line {FA, HS}
- = case heap_type() of
- separate ->
- ?line {value,
- {garbage_collection,
- Gs}} = lists:keysearch(garbage_collection, 1, PI),
- ?line {value,
- {fullsweep_after,
- Fa}} = lists:keysearch(fullsweep_after, 1, Gs),
- ?line {value,
- {heap_size,Hs}} = lists:keysearch(heap_size, 1, PI),
- ?line {Fa, Hs};
- shared ->
- {undefined, undefined}
- end,
+ {value,{garbage_collection,Gs}} =
+ lists:keysearch(garbage_collection, 1, PI),
+ {value,{fullsweep_after,FA}} =
+ lists:keysearch(fullsweep_after, 1, Gs),
+ {value,{heap_size,HS}} = lists:keysearch(heap_size, 1, PI),
?line {Ls, P, FA, HS}.
% This testcase should probably be moved somewhere else
@@ -650,12 +603,3 @@ stop_node(Node) ->
run_fun(Fun) ->
Fun().
-
-heap_type() ->
- case catch erlang:system_info(heap_type) of
- shared -> shared;
- unified -> shared;
- _ -> separate
- end.
-
-
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 10ab3e4370..827208b048 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -30,9 +30,9 @@
load_cached/1, start_node_with_cache/1, add_and_rehash/1,
where_is_file_cached/1, where_is_file_no_cache/1,
purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1,
- code_archive/1, code_archive2/1, on_load/1,
- big_boot_embedded/1,
- on_load_embedded/1, on_load_errors/1, native_early_modules/1]).
+ code_archive/1, code_archive2/1, on_load/1, on_load_binary/1,
+ on_load_embedded/1, on_load_errors/1, big_boot_embedded/1,
+ native_early_modules/1]).
-export([init_per_testcase/2, end_per_testcase/2,
init_per_suite/1, end_per_suite/1,
@@ -55,8 +55,8 @@ all() ->
add_and_rehash, where_is_file_no_cache,
where_is_file_cached, purge_stacktrace, mult_lib_roots,
bad_erl_libs, code_archive, code_archive2, on_load,
- on_load_embedded, big_boot_embedded, on_load_errors,
- native_early_modules].
+ on_load_binary, on_load_embedded, on_load_errors,
+ big_boot_embedded, native_early_modules].
groups() ->
[].
@@ -501,7 +501,7 @@ 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}]),
+ ?line {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]),
case Ret of
@@ -822,7 +822,7 @@ load_cached(Config) when is_list(Config) ->
?line WD = filename:dirname(code:which(?MODULE)),
?line {ok,Node} =
?t:start_node(code_cache_node, peer, [{args,
- "-pa " ++ WD},
+ "-pa \"" ++ WD ++ "\""},
{erl, [this]}]),
CCTabCreated = fun(Tab) ->
case ets:info(Tab, name) of
@@ -907,7 +907,7 @@ add_and_rehash(Config) when is_list(Config) ->
?line WD = filename:dirname(code:which(?MODULE)),
?line {ok,Node} =
?t:start_node(code_cache_node, peer, [{args,
- "-pa " ++ WD},
+ "-pa \"" ++ WD ++ "\""},
{erl, [this]}]),
CCTabCreated = fun(Tab) ->
case ets:info(Tab, name) of
@@ -1286,6 +1286,45 @@ on_load_wait_for_all([Ref|T]) ->
end;
on_load_wait_for_all([]) -> ok.
+on_load_binary(_) ->
+ Master = on_load_binary_test_case_process,
+ register(Master, self()),
+
+ %% Construct, compile and pretty-print.
+ Mod = on_load_binary,
+ File = atom_to_list(Mod) ++ ".erl",
+ Forms = [{attribute,1,file,{File,1}},
+ {attribute,1,module,Mod},
+ {attribute,2,export,[{ok,0}]},
+ {attribute,3,on_load,{init,0}},
+ {function,5,init,0,
+ [{clause,5,[],[],
+ [{op,6,'!',
+ {atom,6,Master},
+ {tuple,6,[{atom,6,Mod},{call,6,{atom,6,self},[]}]}},
+ {'receive',7,[{clause,8,[{atom,8,go}],[],[{atom,8,ok}]}]}]}]},
+ {function,11,ok,0,[{clause,11,[],[],[{atom,11,true}]}]}],
+ {ok,Mod,Bin} = compile:forms(Forms, [report]),
+ [io:put_chars(erl_pp:form(F)) || F <- Forms],
+
+ {Pid1,Ref1} = spawn_monitor(fun() ->
+ code:load_binary(Mod, File, Bin),
+ true = on_load_binary:ok()
+ end),
+ receive {Mod,OnLoadPid} -> ok end,
+ {Pid2,Ref2} = spawn_monitor(fun() ->
+ true = on_load_binary:ok()
+ end),
+ erlang:yield(),
+ OnLoadPid ! go,
+ receive {'DOWN',Ref1,process,Pid1,Exit1} -> ok end,
+ receive {'DOWN',Ref2,process,Pid2,Exit2} -> ok end,
+ normal = Exit1,
+ normal = Exit2,
+ true = code:delete(on_load_binary),
+ false = code:purge(on_load_binary),
+ ok.
+
on_load_embedded(Config) when is_list(Config) ->
try
on_load_embedded_1(Config)
@@ -1402,6 +1441,9 @@ filter_app("netconf",_) ->
% Safe has the same kind of error in the .app file as ic
filter_app("safe",_) ->
false;
+% Comte cannot be started in the "usual" way
+filter_app("comte",_) ->
+ false;
% OS_mon does not find it's port program when running cerl
filter_app("os_mon",true) ->
false;
@@ -1508,7 +1550,8 @@ native_early_modules_1(Architecture) ->
true ->
?line true = lists:all(fun code:is_module_native/1,
[ets,file,filename,gb_sets,gb_trees,
- hipe_unified_loader,lists,os,packages]),
+ %%hipe_unified_loader, no_native as workaround
+ lists,os,packages]),
ok
end.
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index ad987fe7a7..0c3f5c3514 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
@@ -90,7 +90,7 @@
evil/1,
- otp_6278/1]).
+ otp_6278/1, otp_10131/1]).
-export([head_fun/1, hf/0, lserv/1,
measure/0, init_m/1, xx/0, head_exit/0, slow_header/1]).
@@ -124,7 +124,7 @@
[halt_int, wrap_int, halt_ext, wrap_ext, read_mode, head,
notif, new_idx_vsn, reopen, block, unblock, open, close,
error, chunk, truncate, many_users, info, change_size,
- change_attribute, distribution, evil, otp_6278]).
+ change_attribute, distribution, evil, otp_6278, otp_10131]).
%% The following two lists should be mutually exclusive. To skip a case
%% on VxWorks altogether, use the kernel.spec.vxworks file instead.
@@ -153,7 +153,7 @@ all() ->
{group, open}, {group, close}, {group, error}, chunk,
truncate, many_users, {group, info},
{group, change_size}, change_attribute,
- {group, distribution}, evil, otp_6278].
+ {group, distribution}, evil, otp_6278, otp_10131].
groups() ->
[{halt_int, [], [halt_int_inf, {group, halt_int_sz}]},
@@ -4915,6 +4915,22 @@ otp_6278(Conf) when is_list(Conf) ->
end,
?line error_logger:delete_report_handler(?MODULE).
+otp_10131(suite) -> [];
+otp_10131(doc) -> ["OTP-10131. head_func type."];
+otp_10131(Conf) when is_list(Conf) ->
+ Dir = ?privdir(Conf),
+ Log = otp_10131,
+ File = filename:join(Dir, lists:concat([Log, ".LOG"])),
+ HeadFunc = {?MODULE, head_fun, [{ok,"head"}]},
+ {ok, Log} = disk_log:open([{name,Log},{file,File},
+ {head_func, HeadFunc}]),
+ HeadFunc = info(Log, head, undef),
+ HeadFunc2 = {?MODULE, head_fun, [{ok,"head2"}]},
+ ok = disk_log:change_header(Log, {head_func, HeadFunc2}),
+ HeadFunc2 = info(Log, head, undef),
+ ok = disk_log:close(Log),
+ ok.
+
mark(FileName, What) ->
{ok,Fd} = file:open(FileName, [raw, binary, read, write]),
{ok,_} = file:position(Fd, 4),
diff --git a/lib/kernel/test/erl_boot_server_SUITE.erl b/lib/kernel/test/erl_boot_server_SUITE.erl
index cea3715ce4..bb64c01058 100644
--- a/lib/kernel/test/erl_boot_server_SUITE.erl
+++ b/lib/kernel/test/erl_boot_server_SUITE.erl
@@ -346,7 +346,7 @@ good_hosts(_Config) ->
[GoodHost1, GoodHost2, GoodHost3].
open_udp() ->
- ?line {ok, S} = prim_inet:open(udp, inet),
+ ?line {ok, S} = prim_inet:open(udp, inet, dgram),
?line ok = prim_inet:setopts(S, [{mode,list},{active,true},
{deliver,term},{broadcast,true}]),
?line {ok,_} = prim_inet:bind(S, {0,0,0,0}, 0),
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index 7599a89779..72239641e9 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -175,10 +175,10 @@ wait_really_started(Node, N) ->
inet_disconnects(doc) -> ["Start a node using the 'inet' loading method, ",
"then lose the connection."];
inet_disconnects(Config) when is_list(Config) ->
- case os:type() of
- vxworks ->
- {comment, "VxWorks: tested separately"};
- _ ->
+ case test_server:is_native(erl_boot_server) of
+ true ->
+ {skip,"erl_boot_server is native"};
+ false ->
?line Name = erl_prim_test_inet_disconnects,
?line Host = host(),
?line Cookie = atom_to_list(erlang:get_cookie()),
@@ -426,7 +426,9 @@ primary_archive(Config) when is_list(Config) ->
ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"],
io:format("ExpectedEbins: ~p\n", [ExpectedEbins]),
?line {ok, FileInfo} = prim_file:read_file_info(Archive),
- ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo]),
+ ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [Archive, ArchiveBin, FileInfo,
+ fun escript:parse_file/1]),
?line ExpectedEbins = lists:sort(Ebins), % assert
?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]),
@@ -435,7 +437,9 @@ primary_archive(Config) when is_list(Config) ->
?line ok = test_archive(Node, Archive, DictDir, BeamName),
%% Cleanup
- ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined, undefined]),
+ ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [undefined, undefined, undefined,
+ fun escript:parse_file/1]),
?line stop_node(Node),
?line ok = file:delete(Archive),
ok.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 77fc7e73f9..2b6af7e1fb 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -78,7 +78,7 @@
-export([altname/1]).
--export([large_file/1]).
+-export([large_file/1, large_write/1]).
-export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]).
@@ -92,6 +92,8 @@
-export([bytes/2, iterate/3]).
+%% System probe functions that might be handy to check from the shell
+-export([disc_free/1, memsize/0]).
-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
@@ -106,7 +108,7 @@ all() ->
{group, compression}, {group, links}, copy,
delayed_write, read_ahead, segment_read, segment_write,
ipread, pid2name, interleaved_read_write, otp_5814,
- large_file, read_line_1, read_line_2, read_line_3,
+ large_file, large_write, read_line_1, read_line_2, read_line_3,
read_line_4, standard_io].
groups() ->
@@ -143,6 +145,13 @@ end_per_group(_GroupName, Config) ->
init_per_suite(Config) when is_list(Config) ->
+ SaslConfig = case application:start(sasl) of
+ {error, {already_started, sasl}} ->
+ [];
+ ok ->
+ [{sasl,started}]
+ end,
+ ok = application:start(os_mon),
case os:type() of
{win32, _} ->
Priv = ?config(priv_dir, Config),
@@ -154,9 +163,9 @@ init_per_suite(Config) when is_list(Config) ->
{ok, _} ->
[]
end,
- ?FILE_INIT(HasAccessTime++Config);
+ ?FILE_INIT(HasAccessTime++Config++SaslConfig);
_ ->
- ?FILE_INIT(Config)
+ ?FILE_INIT(Config++SaslConfig)
end.
end_per_suite(Config) when is_list(Config) ->
@@ -166,6 +175,13 @@ end_per_suite(Config) when is_list(Config) ->
_ ->
ok
end,
+ application:stop(os_mon),
+ case proplists:get_value(sasl, Config) of
+ started ->
+ application:stop(sasl);
+ _Else ->
+ ok
+ end,
?FILE_FINI(Config).
init_per_testcase(_Func, Config) ->
@@ -394,6 +410,7 @@ make_del_dir(Config) when is_list(Config) ->
%% Don't worry ;-) the parent directory should never be empty, right?
?line case ?FILE_MODULE:del_dir('..') of
{error, eexist} -> ok;
+ {error, eacces} -> ok; %OpenBSD
{error, einval} -> ok %FreeBSD
end,
?line {error, enoent} = ?FILE_MODULE:del_dir(""),
@@ -3144,12 +3161,12 @@ ipread_int(Dir, ModeList) ->
{fun (Bin) when is_binary(Bin) -> Bin;
(List) when is_list(List) -> list_to_binary(List)
end,
- {erlang, size}};
+ fun erlang:byte_size/1};
false ->
{fun (Bin) when is_binary(Bin) -> binary_to_list(Bin);
(List) when is_list(List) -> List
end,
- {erlang, length}}
+ fun erlang:length/1}
end,
?line Pos = 4711,
?line Data = Conv("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG"),
@@ -3287,50 +3304,13 @@ large_file(suite) ->
large_file(doc) ->
["Tests positioning in large files (> 4G)"];
large_file(Config) when is_list(Config) ->
- case {os:type(),os:version()} of
- {{win32,nt},_} ->
- do_large_file(Config);
- {{unix,sunos},{A,B,C}}
- when A == 5, B == 5, C >= 1; A == 5, B >= 6; A >= 6 ->
- do_large_file(Config);
- {{unix,Unix},_} when Unix =/= sunos ->
- N = unix_free(Config),
- io:format("Free: ~w KByte~n", [N]),
- if N < 5 * (1 bsl 20) ->
- %% Less than 5 GByte free
- {skipped,"Less than 5 GByte free"};
- true ->
- do_large_file(Config)
- end;
- _ ->
- {skipped,"Only supported on Win32, Unix or SunOS >= 5.5.1"}
- end.
+ run_large_file_test(Config,
+ fun(Name) -> do_large_file(Name) end,
+ "_large_file").
-unix_free(Config) ->
- Cmd = ["df -k '",?config(priv_dir, Config),"'"],
- DF0 = os:cmd(Cmd),
- io:format("$ ~s~n~s", [Cmd,DF0]),
- [$\n|DF1] = lists:dropwhile(fun ($\n) -> false; (_) -> true end, DF0),
- {ok,[N],_} = io_lib:fread(" ~*s ~d", DF1),
- N.
+do_large_file(Name) ->
+ ?line Watchdog = ?t:timetrap(?t:minutes(20)),
-do_large_file(Config) ->
- ?line Watchdog = ?t:timetrap(?t:minutes(5)),
- %%
- ?line Name = filename:join(?config(priv_dir, Config),
- ?MODULE_STRING ++ "_large_file"),
- ?line Tester = self(),
- Deleter =
- spawn(
- fun() ->
- Mref = erlang:monitor(process, Tester),
- receive
- {'DOWN',Mref,_,_,_} -> ok;
- {Tester,done} -> ok
- end,
- ?FILE_MODULE:delete(Name)
- end),
- %%
?line S = "1234567890",
L = length(S),
R = lists:reverse(S),
@@ -3366,15 +3346,36 @@ do_large_file(Config) ->
?line {ok,R} = ?FILE_MODULE:read(F1, L+1),
?line ok = ?FILE_MODULE:close(F1),
%%
- ?line Mref = erlang:monitor(process, Deleter),
- ?line Deleter ! {Tester,done},
- ?line receive {'DOWN',Mref,_,_,_} -> ok end,
- %%
?line ?t:timetrap_cancel(Watchdog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+large_write(Config) when is_list(Config) ->
+ run_large_file_test(Config,
+ fun(Name) -> do_large_write(Name) end,
+ "_large_write").
+
+do_large_write(Name) ->
+ Memsize = memsize(),
+ io:format("Memsize = ~w Bytes~n", [Memsize]),
+ case {erlang:system_info(wordsize),Memsize} of
+ {4,_} ->
+ {skip,"Needs a 64-bit emulator"};
+ {8,N} when N < 6 bsl 30 ->
+ {skip,
+ "This machine has < 6 GB memory: "
+ ++integer_to_list(N)};
+ {8,_} ->
+ Size = 4*1024*1024*1024+1,
+ Bin = <<0:Size/unit:8>>,
+ ok = file:write_file(Name, Bin),
+ {ok,#file_info{size=Size}} = file:read_file_info(Name),
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
response_analysis(Module, Function, Arguments) ->
@@ -3950,3 +3951,67 @@ flush(Msgs) ->
after 0 ->
lists:reverse(Msgs)
end.
+
+%%%
+%%% Support for testing large files.
+%%%
+
+run_large_file_test(Config, Run, Name) ->
+ case {os:type(),os:version()} of
+ {{win32,nt},_} ->
+ do_run_large_file_test(Config, Run, Name);
+ {{unix,sunos},OsVersion} when OsVersion < {5,5,1} ->
+ {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"};
+ {{unix,_},_} ->
+ N = disc_free(?config(priv_dir, Config)),
+ io:format("Free disk: ~w KByte~n", [N]),
+ if N < 5 * (1 bsl 20) ->
+ %% Less than 5 GByte free
+ {skip,"Less than 5 GByte free"};
+ true ->
+ do_run_large_file_test(Config, Run, Name)
+ end;
+ _ ->
+ {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}
+ end.
+
+
+do_run_large_file_test(Config, Run, Name0) ->
+ Name = filename:join(?config(priv_dir, Config),
+ ?MODULE_STRING ++ Name0),
+
+ %% Set up a process that will delete this file.
+ Tester = self(),
+ Deleter =
+ spawn(
+ fun() ->
+ Mref = erlang:monitor(process, Tester),
+ receive
+ {'DOWN',Mref,_,_,_} -> ok;
+ {Tester,done} -> ok
+ end,
+ ?FILE_MODULE:delete(Name)
+ end),
+
+ %% Run the test case.
+ Res = Run(Name),
+
+ %% Delete file and finish deleter process.
+ Mref = erlang:monitor(process, Deleter),
+ Deleter ! {Tester,done},
+ receive {'DOWN',Mref,_,_,_} -> ok end,
+
+ Res.
+
+disc_free(Path) ->
+ Data = disksup:get_disk_data(),
+ {_,Tot,Perc} = hd(lists:filter(
+ fun({P,_Size,_Full}) ->
+ lists:prefix(filename:nativename(P),
+ filename:nativename(Path))
+ end, lists:reverse(lists:sort(Data)))),
+ round(Tot * (1-(Perc/100))).
+
+memsize() ->
+ {Tot,_Used,_} = memsup:get_memory_data(),
+ Tot.
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
index 53bcb1162d..be33ec2c06 100644
--- a/lib/kernel/test/file_name_SUITE.erl
+++ b/lib/kernel/test/file_name_SUITE.erl
@@ -74,7 +74,7 @@
init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2]).
--export([normal/1,icky/1,very_icky/1,normalize/1]).
+-export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]).
init_per_testcase(_Func, Config) ->
@@ -88,7 +88,7 @@ end_per_testcase(_Func, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [normal, icky, very_icky, normalize].
+ [normal, icky, very_icky, normalize, home_dir].
groups() ->
[].
@@ -105,6 +105,54 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+home_dir(suite) ->
+ [];
+home_dir(doc) ->
+ ["Check that Erlang can be started with unicode named home directory"];
+home_dir(Config) when is_list(Config) ->
+ try
+ Name=[960,945,964,961,953,954],
+ Priv = ?config(priv_dir, Config),
+ UniMode = file:native_name_encoding() =/= latin1,
+ if
+ not UniMode ->
+ throw(need_unicode_mode);
+ true ->
+ ok
+ end,
+ NewHome=filename:join(Priv,Name),
+ file:make_dir(NewHome),
+ {SaveOldName,SaveOldValue} = case os:type() of
+ {win32,nt} ->
+ HomePath=re:replace(filename:nativename(NewHome),"^[a-zA-Z]:","",[{return,list},unicode]),
+ Save = os:getenv("HOMEPATH"),
+ os:putenv("HOMEPATH",HomePath),
+ {"HOMEPATH",Save};
+ {unix,_} ->
+ Save = os:getenv("HOME"),
+ os:putenv("HOME",NewHome),
+ {"HOME",Save};
+ _ ->
+ rm_rf(prim_file,NewHome),
+ throw(unsupported_os)
+ end,
+ try
+ {ok,Node} = test_server:start_node(test_unicode_homedir,slave,[{args,"-setcookie "++atom_to_list(erlang:get_cookie())}]),
+ test_server:stop_node(Node),
+ ok
+ after
+ os:putenv(SaveOldName,SaveOldValue),
+ rm_rf(prim_file,NewHome)
+ end
+ catch
+ throw:need_unicode_mode ->
+ io:format("Sorry, can only run in unicode mode.~n"),
+ {skipped,"VM needs to be started in Unicode filename mode"};
+ throw:unsupported_os ->
+ io:format("Sorry, can only run on Unix/Windows.~n"),
+ {skipped,"Runs only on Unix/Windows"}
+ end.
+
normalize(suite) ->
[];
normalize(doc) ->
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 1b534a5fc4..f4bf6e719e 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -30,33 +30,39 @@
-export(
[basic/1,
api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
- xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1]).
+ xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
+ basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1,
+ open_multihoming_ipv4_socket/1,
+ open_unihoming_ipv6_socket/1,
+ open_multihoming_ipv6_socket/1,
+ open_multihoming_ipv4_and_ipv6_socket/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, api_open_close, api_listen, api_connect_init,
- api_opts, xfer_min, xfer_active, def_sndrcvinfo,
- implicit_inet6].
+ api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
+ basic_stream, xfer_stream_min, peeloff, buffers,
+ open_multihoming_ipv4_socket,
+ open_unihoming_ipv6_socket,
+ open_multihoming_ipv6_socket,
+ open_multihoming_ipv4_and_ipv6_socket].
groups() ->
[].
-init_per_suite(Config) ->
- try gen_sctp:open() of
+init_per_suite(_Config) ->
+ case gen_sctp:open() of
{ok,Socket} ->
gen_sctp:close(Socket),
[];
- _ ->
- []
- catch
- error:badarg ->
- {skip,"SCTP not supported on this machine"};
- _:_ ->
- Config
+ {error,Error}
+ when Error =:= eprotonosupport;
+ Error =:= esocktnosupport ->
+ {skip,"SCTP not supported on this machine"}
end.
-end_per_suite(_Conifig) ->
+end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
@@ -96,7 +102,7 @@ xfer_min(Config) when is_list(Config) ->
?line Stream = 0,
?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
?line Loopback = {127,0,0,1},
- ?line {ok,Sb} = gen_sctp:open(),
+ ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
?line {ok,Pb} = inet:port(Sb),
?line ok = gen_sctp:listen(Sb, true),
@@ -108,29 +114,44 @@ xfer_min(Config) when is_list(Config) ->
inbound_streams=SaInboundStreams,
assoc_id=SaAssocId}=SaAssocChange} =
gen_sctp:connect(Sa, Loopback, Pb, []),
- ?line {ok,{Loopback,
- Pa,[],
+ ?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=SbAssocId}}} =
- gen_sctp:recv(Sb, infinity),
- ?line SaOutboundStreams = SbInboundStreams,
- ?line SbOutboundStreams = SaInboundStreams,
+ 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}} =
+ ?line recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ {AssocId,SbInboundStreams,SbOutboundStreams}
+ end,
+
?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
- ?line case gen_sctp:recv(Sb, infinity) of
- {ok,{Loopback,
- Pa,
- [#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} -> ok;
- {ok,{Loopback,
- Pa,[],
+ ?line case log_ok(gen_sctp:recv(Sb, infinity)) of
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} -> ok;
+ Event1 ->
+ {Loopback,Pa,
#sctp_paddr_change{addr = {Loopback,_},
state = addr_available,
error = 0,
- assoc_id = SbAssocId}}} ->
+ assoc_id = SbAssocId}} =
+ recv_event(Event1),
{ok,{Loopback,
Pa,
[#sctp_sndrcvinfo{stream=Stream,
@@ -138,30 +159,40 @@ xfer_min(Config) when is_list(Config) ->
Data}} = gen_sctp:recv(Sb, infinity)
end,
?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
- ?line {ok,{Loopback,
- Pb,
+ ?line case log_ok(gen_sctp:recv(Sa, infinity)) of
+ {Loopback,Pb,
[#sctp_sndrcvinfo{stream=Stream,
assoc_id=SaAssocId}],
- Data}} =
- gen_sctp:recv(Sa, infinity),
+ Data} ->
+ ok;
+ Event2 ->
+ {Loopback,Pb,
+ #sctp_paddr_change{addr={_,Pb},
+ state=addr_confirmed,
+ error=0,
+ assoc_id=SaAssocId}} =
+ ?line recv_event(Event2),
+ ?line {Loopback,
+ Pb,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SaAssocId}],
+ Data} =
+ log_ok(gen_sctp:recv(Sa, infinity))
+ end,
%%
?line ok = gen_sctp:eof(Sa, SaAssocChange),
- ?line {ok,{Loopback,
- Pa,[],
- #sctp_shutdown_event{assoc_id=SbAssocId}}} =
- gen_sctp:recv(Sb, infinity),
- ?line {ok,{Loopback,
- Pb,[],
- #sctp_assoc_change{state=shutdown_comp,
- error=0,
- assoc_id=SaAssocId}}} =
- gen_sctp:recv(Sa, infinity),
- ?line {ok,{Loopback,
- Pa,[],
- #sctp_assoc_change{state=shutdown_comp,
- error=0,
- assoc_id=SbAssocId}}} =
- gen_sctp:recv(Sb, infinity),
+ ?line {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ ?line {Loopback,Pb,
+ #sctp_assoc_change{state=shutdown_comp,
+ error=0,
+ assoc_id=SaAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity))),
+ ?line {Loopback,Pa,
+ #sctp_assoc_change{state=shutdown_comp,
+ error=0,
+ assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
@@ -186,32 +217,52 @@ xfer_active(Config) when is_list(Config) ->
?line {ok,Sa} = gen_sctp:open([{active,true}]),
?line {ok,Pa} = inet:port(Sa),
- ?line {ok,#sctp_assoc_change{state=comm_up,
- error=0,
- outbound_streams=SaOutboundStreams,
- inbound_streams=SaInboundStreams,
- assoc_id=SaAssocId}=SaAssocChange} =
- gen_sctp:connect(Sa, Loopback, Pb, []),
+ ?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SaOutboundStreams,
+ inbound_streams=SaInboundStreams,
+ assoc_id=SaAssocId} = SaAssocChange =
+ recv_assoc_change(Sa, Loopback, Pb, Timeout),
?line io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, "
"SaOutboundStreams=~p, SaInboundStreams=~p~n",
[Sa,Pa,Sb,Pb,SaAssocId,
SaOutboundStreams,SaInboundStreams]),
- ?line SbAssocId =
- receive
- {sctp,Sb,Loopback,Pa,
- {[],
- #sctp_assoc_change{state=comm_up,
- error=0,
- outbound_streams=SbOutboundStreams,
- inbound_streams=SbInboundStreams,
- assoc_id=SBAI}}} ->
- ?line SaOutboundStreams = SbInboundStreams,
- ?line SaInboundStreams = SbOutboundStreams,
- SBAI
- after Timeout ->
- ?line test_server:fail({unexpected,flush()})
- end,
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SbOutboundStreams,
+ inbound_streams=SbInboundStreams,
+ assoc_id=SbAssocId} =
+ recv_assoc_change(Sb, Loopback, Pa, Timeout),
+ ?line SbOutboundStreams = SaInboundStreams,
+ ?line SbInboundStreams = SaOutboundStreams,
?line io:format("SbAssocId=~p~n", [SbAssocId]),
+
+ ?line case recv_paddr_change(Sa, Loopback, Pb, 314) of
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={_,Pb},
+ error=0,
+ assoc_id=SaAssocId} -> ok;
+ #sctp_paddr_change{state=addr_available,
+ addr={_,Pb},
+ error=0,
+ assoc_id=SaAssocId} -> ok;
+ timeout -> ok
+ end,
+ ?line case recv_paddr_change(Sb, Loopback, Pa, 314) of
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=SbAssocId} -> ok;
+ #sctp_paddr_change{state=addr_available,
+ addr={Loopback,P},
+ error=0,
+ assoc_id=SbAssocId} ->
+ ?line match_unless_solaris(Pa, P);
+ timeout -> ok
+ end,
+ ?line [] = flush(),
+
?line ok =
do_from_other_process(
fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end),
@@ -219,21 +270,9 @@ xfer_active(Config) when is_list(Config) ->
{sctp,Sb,Loopback,Pa,
{[#sctp_sndrcvinfo{stream=Stream,
assoc_id=SbAssocId}],
- Data}} -> ok;
- {sctp,Sb,Loopback,Pa,
- {[],
- #sctp_paddr_change{addr = {Loopback,_},
- state = addr_available,
- error = 0,
- assoc_id = SbAssocId}}} ->
- ?line receive
- {sctp,Sb,Loopback,Pa,
- {[#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} -> ok
- end
+ Data}} -> ok
after Timeout ->
- ?line test_server:fail({unexpected,flush()})
+ ?line test_server:fail({timeout,flush()})
end,
?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
?line receive
@@ -242,31 +281,28 @@ xfer_active(Config) when is_list(Config) ->
assoc_id=SaAssocId}],
Data}} -> ok
after Timeout ->
- ?line test_server:fail({unexpected,flush()})
+ ?line test_server:fail({timeout,flush()})
end,
%%
?line ok = gen_sctp:abort(Sa, SaAssocChange),
- ?line receive
- {sctp,Sb,Loopback,Pa,
- {[],
- #sctp_assoc_change{state=comm_lost,
- assoc_id=SbAssocId}}} -> ok
- after Timeout ->
- ?line test_server:fail({unexpected,flush()})
+ ?line case recv_assoc_change(Sb, Loopback, Pa, Timeout) of
+ #sctp_assoc_change{state=comm_lost,
+ assoc_id=SbAssocId} -> ok;
+ timeout ->
+ ?line test_server:fail({timeout,flush()})
end,
?line ok = gen_sctp:close(Sb),
+ ?line case recv_assoc_change(Sa, Loopback, Pb, Timeout) of
+ #sctp_assoc_change{state=comm_lost,
+ assoc_id=SaAssocId} -> ok;
+ timeout ->
+ ?line io:format("timeout waiting for comm_lost on Sa~n"),
+ ?line match_unless_solaris(ok, {timeout,flush()})
+ end,
?line receive
- {sctp,Sa,Loopback,Pb,
- {[],
- #sctp_assoc_change{state=comm_lost,
- assoc_id=SaAssocId}}} -> ok
- after Timeout ->
- ?line test_server:fail({unexpected,flush()})
- end,
- ?line receive
- {sctp_error,Sa,enotconn} -> ok % Solaris
- after 17 -> ok %% Only happens on Solaris
- end,
+ {sctp_error,Sa,enotconn} -> ok % Solaris
+ after 17 -> ok
+ end,
?line ok = gen_sctp:close(Sa),
%%
?line receive
@@ -275,6 +311,30 @@ xfer_active(Config) when is_list(Config) ->
end,
ok.
+recv_assoc_change(S, Addr, Port, Timeout) ->
+ receive
+ {sctp,S,Addr,Port,{[], #sctp_assoc_change{}=AssocChange}} ->
+ AssocChange;
+ {sctp,S,Addr,Port,
+ {[#sctp_sndrcvinfo{assoc_id=AssocId}],
+ #sctp_assoc_change{assoc_id=AssocId}=AssocChange}} ->
+ AssocChange
+ after Timeout ->
+ timeout
+ end.
+
+recv_paddr_change(S, Addr, Port, Timeout) ->
+ receive
+ {sctp,S,Addr,Port,{[], #sctp_paddr_change{}=PaddrChange}} ->
+ PaddrChange;
+ {sctp,S,Addr,Port,
+ {[#sctp_sndrcvinfo{assoc_id=AssocId}],
+ #sctp_paddr_change{assoc_id=AssocId}=PaddrChange}} ->
+ PaddrChange
+ after Timeout ->
+ timeout
+ end.
+
def_sndrcvinfo(doc) ->
"Test that #sctp_sndrcvinfo{} parameters set on a socket "
"are used by gen_sctp:send/4";
@@ -285,11 +345,11 @@ def_sndrcvinfo(Config) when is_list(Config) ->
?line Data = <<"What goes up, must come down.">>,
%%
?line S1 =
- ok(gen_sctp:open(
+ log_ok(gen_sctp:open(
0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
?LOGVAR(S1),
?line P1 =
- ok(inet:port(S1)),
+ log_ok(inet:port(S1)),
?LOGVAR(P1),
?line #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} =
getopt(S1, sctp_default_send_param),
@@ -297,10 +357,10 @@ def_sndrcvinfo(Config) when is_list(Config) ->
gen_sctp:listen(S1, true),
%%
?line S2 =
- ok(gen_sctp:open()),
+ log_ok(gen_sctp:open()),
?LOGVAR(S2),
?line P2 =
- ok(inet:port(S2)),
+ log_ok(inet:port(S2)),
?LOGVAR(P2),
?line #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} =
getopt(S2, sctp_default_send_param),
@@ -309,32 +369,57 @@ def_sndrcvinfo(Config) when is_list(Config) ->
state=comm_up,
error=0,
assoc_id=S2AssocId} = S2AssocChange =
- ok(gen_sctp:connect(S2, Loopback, P1, [])),
+ log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- ?line case ok(gen_sctp:recv(S1)) of
- {Loopback, P2,[],
+ ?line case recv_event(log_ok(gen_sctp:recv(S1))) of
+ {Loopback,P2,
#sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=S1AssocId}} ->
+ ?LOGVAR(S1AssocId);
+ {Loopback,P2,
+ #sctp_paddr_change{
+ state=addr_confirmed,
+ error=0,
+ assoc_id=S1AssocId}} ->
+ ?LOGVAR(S1AssocId),
+ {Loopback,P2,
+ #sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId)
+ assoc_id=S1AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1)))
end,
+
?line #sctp_sndrcvinfo{
- ppid=17, context=0, timetolive=0, assoc_id=S1AssocId} =
+ ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
getopt(
S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
?line #sctp_sndrcvinfo{
- ppid=0, context=0, timetolive=0, assoc_id=S2AssocId} =
+ ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} =
getopt(
S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}),
%%
?line ok =
gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>),
- ?line case ok(gen_sctp:recv(S2)) of
+ ?line case log_ok(gen_sctp:recv(S2)) of
{Loopback,P1,
[#sctp_sndrcvinfo{
stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
- <<"1: ",Data/binary>>} -> ok
+ <<"1: ",Data/binary>>} -> ok;
+ Event1 ->
+ ?line {Loopback,P1,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={_,P1},
+ error=0,
+ assoc_id=S2AssocId}} =
+ recv_event(Event1),
+ ?line {Loopback,P1,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
+ <<"1: ",Data/binary>>} =
+ log_ok(gen_sctp:recv(S2))
end,
%%
?line ok =
@@ -354,7 +439,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
%%
?line ok =
gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>),
- ?line case ok(gen_sctp:recv(S2)) of
+ ?line case log_ok(gen_sctp:recv(S2)) of
{Loopback,P1,
[#sctp_sndrcvinfo{
stream=0, ppid=19, context=0, assoc_id=S2AssocId}],
@@ -362,16 +447,18 @@ def_sndrcvinfo(Config) when is_list(Config) ->
end,
?line ok =
gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>),
- ?line case ok(gen_sctp:recv(S1)) of
+ ?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;
- {Loopback,P2,[],
- #sctp_paddr_change{
- addr={Loopback,_}, state=addr_available,
- error=0, assoc_id=S1AssocId}} ->
- ?line case ok(gen_sctp:recv(S1)) of
+ 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,
@@ -387,7 +474,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
#sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId},
<<"4: ",Data/binary>>)
end),
- ?line case ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
+ ?line case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
{Loopback,P2,
[#sctp_sndrcvinfo{
stream=0, ppid=20, context=0, assoc_id=S1AssocId}],
@@ -416,8 +503,12 @@ getopt(S, Opt, Param) ->
setopt(S, Opt, Val) ->
inet:setopts(S, [{Opt,Val}]).
-ok({ok,X}) ->
- io:format("OK: ~p~n", [X]),
+log_ok(X) -> log(ok(X)).
+
+ok({ok,X}) -> X.
+
+log(X) ->
+ io:format("LOG[~w]: ~p~n", [self(),X]),
X.
flush() ->
@@ -520,7 +611,10 @@ api_listen(Config) when is_list(Config) ->
#sctp_assoc_change{
state=comm_lost}}} =
gen_sctp:recv(Sa, infinity);
- {error,#sctp_assoc_change{state=cant_assoc}} -> ok
+ {error,#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,
@@ -552,29 +646,41 @@ api_connect_init(Config) when is_list(Config) ->
?line {ok,Sa} = gen_sctp:open(),
?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
{error,econnrefused} ->
- ?line {ok,{Localhost,
- Pb,[],
- #sctp_assoc_change{state=comm_lost}}} =
- gen_sctp:recv(Sa, infinity);
+ ?line {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity)));
ok ->
- ?line {ok,{Localhost,
- Pb,[],
- #sctp_assoc_change{state=cant_assoc}}} =
- gen_sctp:recv(Sa, infinity)
+ ?line {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
end,
?line ok = gen_sctp:listen(Sb, true),
?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
ok ->
- ?line {ok,{Localhost,
- Pb,[],
- #sctp_assoc_change{
- state = comm_up}}} =
- gen_sctp:recv(Sa, infinity)
+ ?line {Localhost,Pb,#sctp_assoc_change{state=comm_up}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
end,
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
ok.
+recv_event({Addr,Port,[],#sctp_assoc_change{}=AssocChange}) ->
+ {Addr,Port,AssocChange};
+recv_event({Addr,Port,
+ [#sctp_sndrcvinfo{assoc_id=Assoc}],
+ #sctp_assoc_change{assoc_id=Assoc}=AssocChange}) ->
+ {Addr,Port,AssocChange};
+recv_event({Addr,Port,[],#sctp_paddr_change{}=PaddrChange}) ->
+ {Addr,Port,PaddrChange};
+recv_event({Addr,Port,
+ [#sctp_sndrcvinfo{assoc_id=Assoc}],
+ #sctp_paddr_change{assoc_id=Assoc}=PaddrChange}) ->
+ {Addr,Port,PaddrChange};
+recv_event({Addr,Port,[],#sctp_shutdown_event{}=ShutdownEvent}) ->
+ {Addr,Port,ShutdownEvent};
+recv_event({Addr,Port,
+ [#sctp_sndrcvinfo{assoc_id=Assoc}],
+ #sctp_shutdown_event{assoc_id=Assoc}=ShutdownEvent}) ->
+ {Addr,Port,ShutdownEvent}.
+
api_opts(doc) ->
"Test socket options";
api_opts(suite) ->
@@ -600,7 +706,7 @@ api_opts(Config) when is_list(Config) ->
end.
implicit_inet6(Config) when is_list(Config) ->
- ?line Hostname = ok(inet:gethostname()),
+ ?line Hostname = log_ok(inet:gethostname()),
?line
case gen_sctp:open(0, [inet6]) of
{ok,S1} ->
@@ -613,16 +719,16 @@ implicit_inet6(Config) when is_list(Config) ->
?line ok = gen_sctp:close(S1),
%%
?line Localhost =
- ok(inet:getaddr("localhost", inet6)),
+ log_ok(inet:getaddr("localhost", inet6)),
?line io:format("~s ~p~n", ["localhost",Localhost]),
?line S2 =
- ok(gen_sctp:open(0, [{ip,Localhost}])),
+ log_ok(gen_sctp:open(0, [{ip,Localhost}])),
?line implicit_inet6(S2, Localhost),
?line ok = gen_sctp:close(S2),
%%
?line io:format("~s ~p~n", [Hostname,Host]),
?line S3 =
- ok(gen_sctp:open(0, [{ifaddr,Host}])),
+ log_ok(gen_sctp:open(0, [{ifaddr,Host}])),
?line implicit_inet6(S3, Host),
?line ok = gen_sctp:close(S1);
{error,eafnosupport} ->
@@ -635,25 +741,159 @@ implicit_inet6(Config) when is_list(Config) ->
implicit_inet6(S1, Addr) ->
?line ok = gen_sctp:listen(S1, true),
- ?line P1 = ok(inet:port(S1)),
- ?line S2 = ok(gen_sctp:open(0, [inet6])),
- ?line P2 = ok(inet:port(S2)),
+ ?line P1 = log_ok(inet:port(S1)),
+ ?line S2 = log_ok(gen_sctp:open(0, [inet6])),
+ ?line P2 = log_ok(inet:port(S2)),
?line #sctp_assoc_change{state=comm_up} =
- ok(gen_sctp:connect(S2, Addr, P1, [])),
- ?line case ok(gen_sctp:recv(S1)) of
- {Addr,P2,[],#sctp_assoc_change{state=comm_up}} ->
- ok
+ log_ok(gen_sctp:connect(S2, Addr, P1, [])),
+ ?line case recv_event(log_ok(gen_sctp:recv(S1))) of
+ {Addr,P2,#sctp_assoc_change{state=comm_up}} ->
+ ok;
+ {Addr,P2,#sctp_paddr_change{state=addr_confirmed,
+ addr={Addr,P2},
+ error=0}} ->
+ {Addr,P2,#sctp_assoc_change{state=comm_up}} =
+ recv_event(log_ok(gen_sctp:recv(S1)))
end,
- ?line case ok(inet:sockname(S1)) of
+ ?line case log_ok(inet:sockname(S1)) of
{Addr,P1} -> ok;
{{0,0,0,0,0,0,0,0},P1} -> ok
end,
- ?line case ok(inet:sockname(S2)) of
+ ?line case log_ok(inet:sockname(S2)) of
{Addr,P2} -> ok;
{{0,0,0,0,0,0,0,0},P2} -> ok
end,
?line ok = gen_sctp:close(S2).
+basic_stream(doc) ->
+ "Hello world stream socket";
+basic_stream(suite) ->
+ [];
+basic_stream(Config) when is_list(Config) ->
+ ?line {ok,S} = gen_sctp:open([{type,stream}]),
+ ?line ok = gen_sctp:listen(S, true),
+ ?line ok =
+ do_from_other_process(
+ fun () -> gen_sctp:listen(S, 10) end),
+ ?line ok = gen_sctp:close(S),
+ ok.
+
+xfer_stream_min(doc) ->
+ "Minimal data transfer";
+xfer_stream_min(suite) ->
+ [];
+xfer_stream_min(Config) when is_list(Config) ->
+ ?line Stream = 0,
+ ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
+ ?line Loopback = {127,0,0,1},
+ ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ ?line ?LOGVAR(Sb),
+ ?line {ok,Pb} = inet:port(Sb),
+ ?line ?LOGVAR(Pb),
+ ?line ok = gen_sctp:listen(Sb, true),
+
+ ?line {ok,Sa} = gen_sctp:open([{type,stream}]),
+ ?line ?LOGVAR(Sa),
+ ?line {ok,Pa} = inet:port(Sa),
+ ?line ?LOGVAR(Pa),
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SaOutboundStreams,
+ inbound_streams=SaInboundStreams,
+ assoc_id=SaAssocId_X} =
+ log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])),
+ ?line ?LOGVAR(SaAssocId_X),
+ ?line [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] =
+ log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info,
+ #sctp_paddrinfo{address={Loopback,Pb}}}])),
+ ?line ?LOGVAR(SaAssocId),
+ ?line match_unless_solaris(SaAssocId_X, SaAssocId),
+
+ ?line {SbOutboundStreams,SbInboundStreams,SbAssocId} =
+ case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
+ {Loopback,Pa,
+ #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=OS,
+ inbound_streams=IS,
+ assoc_id=AI}} ->
+ {OS,IS,AI};
+ {Loopback,Pa,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=AI}} ->
+ {Loopback,Pa,
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=OS,
+ inbound_streams=IS,
+ assoc_id=AI}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ {OS,IS,AI}
+ end,
+ ?line ?LOGVAR(SbAssocId),
+ ?line SaOutboundStreams = SbInboundStreams,
+ ?line ?LOGVAR(SaOutboundStreams),
+ ?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,
+ 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)
+ end,
+ ?line ok =
+ do_from_other_process(
+ fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
+ ?line case log_ok(gen_sctp:recv(Sa, infinity)) of
+ {Loopback,Pb,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SaAssocId}],
+ Data} -> ok;
+ Event1 ->
+ ?line {Loopback,Pb,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={_,Pb},
+ error=0,
+ assoc_id=SaAssocId}} =
+ recv_event(Event1),
+ ?line {Loopback,Pb,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SaAssocId}],
+ Data} =
+ log_ok(gen_sctp:recv(Sa, infinity))
+ end,
+ ?line ok = gen_sctp:close(Sa),
+ ?line {Loopback,Pa,
+ #sctp_shutdown_event{assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ ?line {Loopback,Pa,
+ #sctp_assoc_change{state=shutdown_comp,
+ error=0,
+ assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ ?line ok = gen_sctp:close(Sb),
+
+ ?line receive
+ Msg -> test_server:fail({received,Msg})
+ after 17 -> ok
+ end,
+ ok.
+
do_from_other_process(Fun) ->
@@ -681,3 +921,605 @@ do_from_other_process(Fun) ->
{'DOWN',Mref,_,_,Reason} ->
erlang:exit(Reason)
end.
+
+
+
+peeloff(doc) ->
+ "Peel off an SCTP stream socket";
+peeloff(suite) ->
+ [];
+peeloff(Config) when is_list(Config) ->
+ ?line Addr = {127,0,0,1},
+ ?line Stream = 0,
+ ?line Timeout = 333,
+ ?line S1 = socket_open([{ifaddr,Addr}], Timeout),
+ ?line ?LOGVAR(S1),
+ ?line P1 = socket_call(S1, get_port),
+ ?line ?LOGVAR(P1),
+ ?line Socket1 = socket_call(S1, get_socket),
+ ?line ?LOGVAR(Socket1),
+ ?line socket_call(S1, {listen,true}),
+ ?line S2 = socket_open([{ifaddr,Addr}], Timeout),
+ ?line ?LOGVAR(S2),
+ ?line P2 = socket_call(S2, get_port),
+ ?line ?LOGVAR(P2),
+ ?line Socket2 = socket_call(S2, get_socket),
+ ?line ?LOGVAR(Socket2),
+ %%
+ ?line socket_call(S2, {connect_init,Addr,P1,[]}),
+ ?line S2Ai =
+ receive
+ {S2,{Addr,P1,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId2}}} -> AssocId2
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line ?LOGVAR(S2Ai),
+ ?line S1Ai =
+ receive
+ {S1,{Addr,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId1}}} -> AssocId1
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line ?LOGVAR(S1Ai),
+ %%
+ ?line socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}),
+ ?line
+ receive
+ {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
+ ?line
+ receive
+ {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ %%
+ ?line S3 = socket_peeloff(Socket1, S1Ai, Timeout),
+ ?line ?LOGVAR(S3),
+ ?line P3_X = socket_call(S3, get_port),
+ ?line ?LOGVAR(P3_X),
+ ?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}}}]}),
+ %%?line S3Ai = S1Ai,
+ ?line ?LOGVAR(S3Ai),
+ %%
+ ?line socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}),
+ ?line
+ receive
+ {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2,S3])
+ end,
+ ?line socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
+ ?line
+ receive
+ {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2,S3])
+ end,
+ %%
+ ?line inet:i(sctp),
+ ?line socket_close_verbose(S1),
+ ?line socket_close_verbose(S2),
+ ?line
+ receive
+ {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
+ ?line match_unless_solaris(S3Ai, S3Ai_X)
+ after Timeout ->
+ socket_bailout([S3])
+ end,
+ ?line
+ receive
+ {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
+ assoc_id=S3Ai}}} -> ok
+ after Timeout ->
+ socket_bailout([S3])
+ end,
+ ?line socket_close_verbose(S3),
+ ?line [] = flush(),
+ ok.
+
+
+
+buffers(doc) ->
+ ["Check sndbuf and recbuf behaviour"];
+buffers(suite) ->
+ [];
+buffers(Config) when is_list(Config) ->
+ ?line Limit = 4096,
+ ?line Addr = {127,0,0,1},
+ ?line Stream = 1,
+ ?line Timeout = 3333,
+ ?line S1 = socket_open([{ip,Addr}], Timeout),
+ ?line ?LOGVAR(S1),
+ ?line P1 = socket_call(S1, get_port),
+ ?line ?LOGVAR(P1),
+ ?line ok = socket_call(S1, {listen,true}),
+ ?line S2 = socket_open([{ip,Addr}], Timeout),
+ ?line ?LOGVAR(S2),
+ ?line P2 = socket_call(S2, get_port),
+ ?line ?LOGVAR(P2),
+ %%
+ ?line socket_call(S2, {connect_init,Addr,P1,[]}),
+ ?line S2Ai =
+ receive
+ {S2,{Addr,P1,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId2}}} -> AssocId2
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line S1Ai =
+ receive
+ {S1,{Addr,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId1}}} -> AssocId1
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ %%
+ ?line socket_call(S1, {setopts,[{recbuf,Limit}]}),
+ ?line Recbuf =
+ 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}),
+ ?line
+ receive
+ {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ %%
+ ?line socket_close_verbose(S1),
+ ?line
+ receive
+ {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
+ after Timeout ->
+ socket_bailout([S2])
+ end,
+ ?line
+ receive
+ {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
+ assoc_id=S2Ai}}} -> ok
+ after Timeout ->
+ socket_bailout([S2])
+ end,
+ ?line socket_close_verbose(S2),
+ ?line [] = flush(),
+ ok.
+
+mk_data(Bytes) ->
+ mk_data(0, Bytes, <<>>).
+%%
+mk_data(N, Bytes, Bin) when N < Bytes ->
+ mk_data(N+4, Bytes, <<Bin/binary,N:32>>);
+mk_data(_, _, Bin) ->
+ Bin.
+
+
+
+open_multihoming_ipv4_socket(doc) ->
+ "Test opening a multihoming ipv4 socket";
+open_multihoming_ipv4_socket(suite) ->
+ [];
+open_multihoming_ipv4_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet, 2) of
+ {ok, [Addr1, Addr2]} ->
+ ?line do_open_and_connect([Addr1, Addr2], Addr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+open_unihoming_ipv6_socket(doc) ->
+ %% This test is mostly aimed to indicate
+ %% whether host has a non-working ipv6 setup
+ "Test opening a unihoming (non-multihoming) ipv6 socket";
+open_unihoming_ipv6_socket(suite) ->
+ [];
+open_unihoming_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet6, 1) of
+ {ok, [Addr]} ->
+ ?line do_open_and_connect([Addr], Addr);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
+open_multihoming_ipv6_socket(doc) ->
+ "Test opening a multihoming ipv6 socket";
+open_multihoming_ipv6_socket(suite) ->
+ [];
+open_multihoming_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet6, 2) of
+ {ok, [Addr1, Addr2]} ->
+ ?line do_open_and_connect([Addr1, Addr2], Addr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+open_multihoming_ipv4_and_ipv6_socket(doc) ->
+ "Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses";
+open_multihoming_ipv4_and_ipv6_socket(suite) ->
+ [];
+open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
+ ?line case get_addrs_by_family(inet_and_inet6, 2) of
+ {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} ->
+ %% Connect to the first address to test bind
+ ?line do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2],
+ InetAddr1),
+ ?line do_open_and_connect([Inet6Addr1, InetAddr1],
+ Inet6Addr1),
+
+ %% Connect an address, not the first,
+ %% to test sctp_bindx
+ ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
+ Inet6Addr2),
+ ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
+ InetAddr1);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
+get_addrs_by_family(Family, NumAddrs) ->
+ case os:type() of
+ {unix,linux} ->
+ get_addrs_by_family_aux(Family, NumAddrs);
+ {unix,freebsd} ->
+ get_addrs_by_family_aux(Family, NumAddrs);
+ {unix,sunos} ->
+ case get_addrs_by_family_aux(Family, NumAddrs) of
+ {ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 ->
+ %% Man page for sctp_bindx on Solaris says: "If sock is an
+ %% Internet Protocol Version 6 (IPv6) socket, addrs should
+ %% be an array of sockaddr_in6 structures containing IPv6
+ %% or IPv4-mapped IPv6 addresses."
+ {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]};
+ {ok, Addrs} ->
+ {ok, Addrs};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ Os ->
+ Reason = if Family =:= inet_and_inet6 ->
+ f("Mixing ipv4 and ipv6 addresses for multihoming "
+ " has not been verified on ~p", [Os]);
+ true ->
+ f("Multihoming for ~p has not been verified on ~p",
+ [Family, Os])
+ end,
+ {error, Reason}
+ end.
+
+get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet;
+ Family =:= inet6 ->
+ ?line
+ case inet:getaddr(localhost, Family) of
+ {error,eafnosupport} ->
+ {skip, f("No support for ~p", Family)};
+ {ok, _} ->
+ ?line IfAddrs = ok(inet:getifaddrs()),
+ ?line case filter_addrs_by_family(IfAddrs, Family) of
+ Addrs when length(Addrs) >= NumAddrs ->
+ {ok, lists:sublist(Addrs, NumAddrs)};
+ [] ->
+ {error, f("Need ~p ~p address(es) found none~n",
+ [NumAddrs, Family])};
+ Addrs ->
+ {error,
+ f("Need ~p ~p address(es) found only ~p: ~p~n",
+ [NumAddrs, Family, length(Addrs), Addrs])}
+ end
+ end;
+get_addrs_by_family_aux(inet_and_inet6, NumAddrs) ->
+ ?line catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of
+ {ok, Addrs} -> Addrs;
+ {error, Reason} -> throw({error, Reason})
+ end || Family <- [inet, inet6]]}.
+
+filter_addrs_by_family(IfAddrs, Family) ->
+ lists:flatten([[Addr || {addr, Addr} <- Info,
+ is_good_addr(Addr, Family)]
+ || {_IfName, Info} <- IfAddrs]).
+
+is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 ->
+ true;
+is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) ->
+ false; %% ipv4 mapped
+is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) ->
+ false; %% link-local
+is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 ->
+ true;
+is_good_addr(_Addr, _Family) ->
+ false.
+
+ipv4_map_addrs(InetAddrs) ->
+ [begin
+ <<AB:16>> = <<A,B>>,
+ <<CD:16>> = <<C,D>>,
+ {0, 0, 0, 0, 0, 16#ffff, AB, CD}
+ end || {A,B,C,D} <- InetAddrs].
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
+ ?line ServerFamily = get_family_by_addrs(ServerAddresses),
+ ?line io:format("Serving ~p addresses: ~p~n",
+ [ServerFamily, ServerAddresses]),
+ ?line S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++
+ [ServerFamily])),
+ ?line ok = gen_sctp:listen(S1, true),
+ ?line P1 = ok(inet:port(S1)),
+ ?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])),
+ %% Verify client can connect
+ ?line #sctp_assoc_change{state=comm_up} =
+ ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
+ %% verify server side also receives comm_up from client
+ ?line recv_comm_up_eventually(S1),
+ ?line ok = gen_sctp:close(S2),
+ ?line ok = gen_sctp:close(S1).
+
+%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
+get_family_by_addrs(Addresses) ->
+ ?line case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of
+ [inet, inet6] -> inet6;
+ [inet] -> inet;
+ [inet6] -> inet6
+ end.
+
+get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet;
+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} ->
+ ?line recv_comm_up_eventually(S)
+ end.
+
+%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% socket gen_server ultra light
+
+socket_open(SocketOpts, Timeout) ->
+ Opts = [{type,seqpacket},{active,once},binary|SocketOpts],
+ Starter =
+ fun () ->
+ {ok,Socket} =
+ gen_sctp:open(Opts),
+ Socket
+ end,
+ s_start(Starter, Timeout).
+
+socket_peeloff(Socket, AssocId, Timeout) ->
+ Opts = [{active,once},binary],
+ Starter =
+ fun () ->
+ {ok,NewSocket} =
+ gen_sctp:peeloff(Socket, AssocId),
+ ok = inet:setopts(NewSocket, Opts),
+ NewSocket
+ end,
+ s_start(Starter, Timeout).
+
+socket_close_verbose(S) ->
+ History = socket_history(socket_close(S)),
+ io:format("socket_close ~p:~n ~p.~n", [S,History]),
+ History.
+
+socket_close(S) ->
+ s_req(S, close).
+
+socket_call(S, Request) ->
+ s_req(S, {Request}).
+
+%% socket_get(S, Key) ->
+%% s_req(S, {get,Key}).
+
+socket_bailout([S|Ss]) ->
+ History = socket_history(socket_close(S)),
+ io:format("bailout ~p:~n ~p.~n", [S,History]),
+ socket_bailout(Ss);
+socket_bailout([]) ->
+ io:format("flush: ~p.~n", [flush()]),
+ test_server:fail(socket_bailout).
+
+socket_history({State,Flush}) ->
+ {lists:keysort(
+ 2,
+ lists:flatten(
+ [[{Key,Val} || Val <- Vals]
+ || {Key,Vals} <- gb_trees:to_list(State)])),
+ Flush}.
+
+s_handler(Socket) ->
+ fun ({listen,Listen}) ->
+ ok = gen_sctp:listen(Socket, Listen);
+ (get_port) ->
+ ok(inet:port(Socket));
+ (get_socket) ->
+ Socket;
+ ({connect_init,ConAddr,ConPort,ConOpts}) ->
+ ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts);
+ ({send,AssocId,Stream,Data}) ->
+ ok = gen_sctp:send(Socket, AssocId, Stream, Data);
+ ({send,OtherSocket,AssocId,Stream,Data}) ->
+ ok = gen_sctp:send(OtherSocket, AssocId, Stream, Data);
+ ({setopts,Opts}) ->
+ ok = inet:setopts(Socket, Opts);
+ ({getopts,Optnames}) ->
+ ok(inet:getopts(Socket, Optnames))
+ end.
+
+s_req(S, Req) ->
+ Mref = erlang:monitor(process, S),
+ S ! {self(),Mref,Req},
+ receive
+ {'DOWN',Mref,_,_,Error} ->
+ exit(Error);
+ {S,Mref,Reply} ->
+ erlang:demonitor(Mref),
+ receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
+ Reply
+ end.
+
+s_start(Starter, Timeout) ->
+ Parent = self(),
+ Owner =
+ spawn_link(
+ fun () ->
+ s_start(Starter(), Timeout, Parent)
+ end),
+ Owner.
+
+s_start(Socket, Timeout, Parent) ->
+ Handler = s_handler(Socket),
+ try
+ s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty())
+ catch
+ Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ io:format(?MODULE_STRING":socket exception ~w:~w at~n"
+ "~p.~n", [Class,Reason,Stacktrace]),
+ erlang:raise(Class, Reason, Stacktrace)
+ end.
+
+s_loop(Socket, Timeout, Parent, Handler, State) ->
+ receive
+ {Parent,Ref,close} -> % socket_close()
+ erlang:send_after(Timeout, self(), {Parent,Ref,exit}),
+ s_loop(Socket, Timeout, Parent, Handler, State);
+ {Parent,Ref,exit} ->
+ ok = gen_sctp:close(Socket),
+ Key = exit,
+ Val = {now(),Socket},
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),Ref,{NewState,flush()}};
+ {Parent,Ref,{Msg}} ->
+ Result = Handler(Msg),
+ Key = req,
+ Val = {now(),{Msg,Result}},
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),Ref,Result},
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ %% {Parent,Ref,{get,Key}} ->
+ %% Parent ! {self(),Ref,gb_get(Key, State)},
+ %% s_loop(Socket, Timeout, Parent, Handler, State);
+ {sctp,Socket,Addr,Port,
+ {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
+ when not is_tuple(Data) ->
+ case gb_get({assoc_change,AssocId}, State) of
+ [{_,{Addr,Port,
+ #sctp_assoc_change{
+ state=comm_up,
+ inbound_streams=Is}}}|_]
+ when 0 =< Stream, Stream < Is-> ok;
+ [] -> ok
+ end,
+ Key = {msg,AssocId,Stream},
+ Val = {now(),{Addr,Port,SRI,Data}},
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ {sctp,Socket,Addr,Port,
+ {SRI,#sctp_assoc_change{assoc_id=AssocId,state=St}=SAC}} ->
+ case SRI of
+ [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+ [] -> ok
+ end,
+ Key = {assoc_change,AssocId},
+ Val = {now(),{Addr,Port,SAC}},
+ case {gb_get(Key, State),St} of
+ {[],_} -> ok;
+ {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ when St =:= comm_lost; St =:= shutdown_comp -> ok
+ end,
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),{Addr,Port,SAC}},
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ {sctp,Socket,Addr,Port,
+ {SRI,#sctp_paddr_change{assoc_id=AssocId,
+ addr={_,P},
+ state=St}=SPC}} ->
+ match_unless_solaris(Port, P),
+ case SRI of
+ [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+ [] -> ok
+ end,
+ case {gb_get({assoc_change,AssocId}, State),St} of
+ {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],
+ addr_available} -> ok;
+ {[],addr_confirmed} -> ok
+ end,
+ Key = {paddr_change,AssocId},
+ Val = {now(),{Addr,Port,SPC}},
+ NewState = gb_push(Key, Val, State),
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ {sctp,Socket,Addr,Port,
+ {SRI,#sctp_shutdown_event{assoc_id=AssocId}=SSE}} ->
+ case SRI of
+ [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+ [] -> ok
+ end,
+ case gb_get({assoc_change,AssocId}, State) of
+ [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+ [] -> ok
+ end,
+ Key = {shutdown_event,AssocId},
+ Val = {now(),{Addr,Port}},
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(), {Addr,Port,SSE}},
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ Unexpected ->
+ erlang:error({unexpected,Unexpected})
+ end.
+
+again(Socket) ->
+ inet:setopts(Socket, [{active,once}]).
+
+gb_push(Key, Val, GBT) ->
+ case gb_trees:lookup(Key, GBT) of
+ none ->
+ gb_trees:insert(Key, [Val], GBT);
+ {value,V} ->
+ gb_trees:update(Key, [Val|V], GBT)
+ end.
+
+gb_get(Key, GBT) ->
+ case gb_trees:lookup(Key, GBT) of
+ none ->
+ [];
+ {value,V} ->
+ V
+ end.
+
+match_unless_solaris(A, B) ->
+ case os:type() of
+ {unix,sunos} -> B;
+ _ -> A = B
+ end.
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index cbaec2d6dd..a7af00c12a 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -22,7 +22,7 @@
%% are not tested here, because they are tested indirectly in this and
%% and other test suites.
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/inet.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
@@ -46,6 +46,8 @@ groups() ->
{t_connect, [], [t_connect_timeout, t_connect_bad]},
{t_recv, [], [t_recv_timeout, t_recv_eof]}].
+
+
init_per_suite(Config) ->
Config.
@@ -55,9 +57,8 @@ end_per_suite(_Config) ->
init_per_group(_GroupName, Config) ->
Config.
-end_per_group(_GroupName, Config) ->
- Config.
-
+end_per_group(_,_Config) ->
+ ok.
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(60)),
@@ -237,7 +238,6 @@ implicit_inet6(S, Addr) ->
?line ok = gen_tcp:close(S1).
-
%%% Utilities
%% Calls M:F/length(A), which should return a timeout error, and complete
diff --git a/lib/kernel/test/gen_tcp_echo_SUITE.erl b/lib/kernel/test/gen_tcp_echo_SUITE.erl
index fffaaf4c45..5bbaeb02ad 100644
--- a/lib/kernel/test/gen_tcp_echo_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_echo_SUITE.erl
@@ -167,8 +167,12 @@ echo_test_1(SockOpts, EchoFun, Config0) ->
[{type, {cdr, little}}|Config]),
?line case lists:keymember(packet_size, 1, SockOpts) of
false ->
- ?line echo_packet([{packet, line}|SockOpts],
- EchoFun, Config);
+ % This is cheating, we should test that packet_size
+ % also works for line and http.
+ echo_packet([{packet, line}|SockOpts], EchoFun, Config),
+ echo_packet([{packet, http}|SockOpts], EchoFun, Config),
+ echo_packet([{packet, http_bin}|SockOpts], EchoFun, Config);
+
true -> ok
end,
?line echo_packet([{packet, tpkt}|SockOpts], EchoFun, Config),
@@ -183,9 +187,6 @@ echo_test_1(SockOpts, EchoFun, Config0) ->
[{type, {asn1, short, LongTag}}|Config]),
?line echo_packet([{packet, asn1}|SockOpts], EchoFun,
[{type, {asn1, long, LongTag}}|Config]),
-
- ?line echo_packet([{packet, http}|SockOpts], EchoFun, Config),
- ?line echo_packet([{packet, http_bin}|SockOpts], EchoFun, Config),
ok.
echo_packet(SockOpts, EchoFun, Opts) ->
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index b1ef8826d5..1592399996 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2012. 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,8 @@
-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, no_accept/1, close_with_pending_output/1,
+ controlling_process/1, controlling_process_self/1,
+ no_accept/1, close_with_pending_output/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,
@@ -39,8 +40,10 @@
accept_timeouts_in_order/1,accept_timeouts_in_order2/1,
accept_timeouts_in_order3/1,accept_timeouts_mixed/1,
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
- several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, send_timeout_active/1,
- otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1]).
+ several_accepts_in_one_go/1, accept_system_limit/1,
+ active_once_closed/1, send_timeout/1, send_timeout_active/1,
+ otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1,
+ otp_9389/1]).
%% Internal exports.
-export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1,
@@ -56,7 +59,7 @@ end_per_testcase(_Func, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [controlling_process, no_accept,
+ [controlling_process, controlling_process_self, no_accept,
close_with_pending_output, data_before_close,
iter_max_socks, passive_sockets,
accept_closed_by_other_process, otp_3924, closed_socket,
@@ -70,9 +73,9 @@ all() ->
accept_timeouts_in_order, accept_timeouts_in_order2,
accept_timeouts_in_order3, accept_timeouts_mixed,
killing_acceptor, killing_multi_acceptors,
- killing_multi_acceptors2, several_accepts_in_one_go,
+ killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit,
active_once_closed, send_timeout, send_timeout_active, otp_7731,
- zombie_sockets, otp_7816, otp_8102].
+ zombie_sockets, otp_7816, otp_8102, otp_9389].
groups() ->
[].
@@ -305,6 +308,32 @@ not_owner(S) ->
ok
end.
+controlling_process_self(doc) ->
+ ["Open a listen port and assign the controlling process to "
+ "it self, then exit and make sure the port is closed properly."];
+controlling_process_self(Config) when is_list(Config) ->
+ S = self(),
+ process_flag(trap_exit,true),
+ spawn_link(fun() ->
+ {ok,Sock} = gen_tcp:listen(0,[]),
+ S ! {socket, Sock},
+ ok = gen_tcp:controlling_process(Sock,self()),
+ S ! done
+ end),
+ receive
+ done ->
+ receive
+ {socket,Sock} ->
+ process_flag(trap_exit,false),
+ %% Make sure the port is invalid after process crash
+ {error,einval} = inet:port(Sock)
+ end;
+ Msg when element(1,Msg) /= socket ->
+ process_flag(trap_exit,false),
+ exit({unknown_msg,Msg})
+ end.
+
+
no_accept(doc) ->
["Open a listen port and connect to it, then close the listen port ",
"without doing any accept. The connected socket should receive ",
@@ -1030,6 +1059,7 @@ busy_send_loop(Server, Client, N) ->
{Server,send} ->
?line busy_send_2(Server, Client, N+1)
after 10000 ->
+ %% If this happens, see busy_send_srv
?t:fail({timeout,{server,not_send,flush([])}})
end
end.
@@ -1049,7 +1079,9 @@ busy_send_2(Server, Client, _N) ->
busy_send_srv(L, Master, Msg) ->
%% Server
- %%
+ %% Sometimes this accept does not return, do not really know why
+ %% but is causes the timeout error in busy_send_loop to be
+ %% triggered. Only happens on OS X Leopard?!?
{ok,Socket} = gen_tcp:accept(L),
busy_send_srv_loop(Socket, Master, Msg).
@@ -1833,6 +1865,54 @@ wait_until_accepting(Proc,N) ->
end.
+accept_system_limit(suite) ->
+ [];
+accept_system_limit(doc) ->
+ ["Check that accept returns {error, system_limit} "
+ "(and not {error, enfile}) when running out of ports"];
+accept_system_limit(Config) when is_list(Config) ->
+ ?line {ok, LS} = gen_tcp:listen(0, []),
+ ?line {ok, TcpPort} = inet:port(LS),
+ ?line Connector = spawn_link(fun () -> connector(TcpPort) end),
+ ?line ok = acceptor(LS, false, []),
+ ?line Connector ! stop,
+ ok.
+
+acceptor(LS, GotSL, A) ->
+ case gen_tcp:accept(LS, 1000) of
+ {ok, S} ->
+ acceptor(LS, GotSL, [S|A]);
+ {error, system_limit} ->
+ acceptor(LS, true, A);
+ {error, timeout} when GotSL ->
+ ok;
+ {error, timeout} ->
+ error
+ end.
+
+connector(TcpPort) ->
+ ManyPorts = open_ports([]),
+ ConnF = fun (Port) ->
+ case catch gen_tcp:connect({127,0,0,1}, TcpPort, []) of
+ {ok, Sock} ->
+ Sock;
+ _Error ->
+ port_close(Port)
+ end
+ end,
+ R = [ConnF(Port) || Port <- lists:sublist(ManyPorts, 10)],
+ receive stop -> R end.
+
+open_ports(L) ->
+ case catch open_port({spawn_driver, "ram_file_drv"}, []) of
+ Port when is_port(Port) ->
+ open_ports([Port|L]);
+ {'EXIT', {system_limit, _}} ->
+ {L1, L2} = lists:split(5, L),
+ [port_close(Port) || Port <- L1],
+ L2
+ end.
+
active_once_closed(suite) ->
[];
@@ -1991,7 +2071,7 @@ send_timeout_active(Config) when is_list(Config) ->
?line {error,timeout} =
Loop(fun() ->
receive
- {tcp, Sock, _Data} ->
+ {tcp, _Sock, _Data} ->
inet:setopts(A, [{active, once}]),
Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
%erlang:display(Res),
@@ -2479,4 +2559,63 @@ otp_8102_do(LSocket, PortNum, {Bin,PType}) ->
io:format("Got error msg, ok.\n",[]),
gen_tcp:close(SSocket),
gen_tcp:close(RSocket).
-
+
+otp_9389(doc) -> ["Verify packet_size handles long HTTP header lines"];
+otp_9389(suite) -> [];
+otp_9389(Config) when is_list(Config) ->
+ ?line {ok, LS} = gen_tcp:listen(0, [{active,false}]),
+ ?line {ok, {_, PortNum}} = inet:sockname(LS),
+ io:format("Listening on ~w with port number ~p\n", [LS, PortNum]),
+ OrigLinkHdr = "/" ++ string:chars($S, 8192),
+ _Server = spawn_link(
+ fun() ->
+ ?line {ok, S} = gen_tcp:accept(LS),
+ ?line ok = inet:setopts(S, [{packet_size, 16384}]),
+ ?line ok = otp_9389_loop(S, OrigLinkHdr),
+ ?line ok = gen_tcp:close(S)
+ end),
+ ?line {ok, S} = gen_tcp:connect("localhost", PortNum,
+ [binary, {active, false}]),
+ Req = "GET / HTTP/1.1\r\n"
+ ++ "Host: localhost\r\n"
+ ++ "Link: " ++ OrigLinkHdr ++ "\r\n\r\n",
+ ?line ok = gen_tcp:send(S, Req),
+ ?line ok = inet:setopts(S, [{packet, http}]),
+ ?line {ok, {http_response, {1,1}, 200, "OK"}} = gen_tcp:recv(S, 0),
+ ?line ok = inet:setopts(S, [{packet, httph}, {packet_size, 16384}]),
+ ?line {ok, {http_header, _, 'Content-Length', _, "0"}} = gen_tcp:recv(S, 0),
+ ?line {ok, {http_header, _, "Link", _, LinkHdr}} = gen_tcp:recv(S, 0),
+ ?line true = (LinkHdr == OrigLinkHdr),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(LS),
+ ok.
+
+otp_9389_loop(S, OrigLinkHdr) ->
+ ?line ok = inet:setopts(S, [{active,once},{packet,http}]),
+ receive
+ {http, S, {http_request, 'GET', _, _}} ->
+ ?line ok = otp_9389_loop(S, OrigLinkHdr, undefined)
+ after
+ 3000 ->
+ ?line error({timeout,request_line})
+ end.
+otp_9389_loop(S, OrigLinkHdr, ok) ->
+ ?line Resp = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n" ++
+ "Link: " ++ OrigLinkHdr ++ "\r\n\r\n",
+ ?line ok = gen_tcp:send(S, Resp);
+otp_9389_loop(S, OrigLinkHdr, State) ->
+ ?line ok = inet:setopts(S, [{active,once}, {packet,httph}]),
+ receive
+ {http, S, http_eoh} ->
+ ?line otp_9389_loop(S, OrigLinkHdr, ok);
+ {http, S, {http_header, _, "Link", _, LinkHdr}} ->
+ ?line LinkHdr = OrigLinkHdr,
+ ?line otp_9389_loop(S, OrigLinkHdr, State);
+ {http, S, {http_header, _, _Hdr, _, _Val}} ->
+ ?line otp_9389_loop(S, OrigLinkHdr, State);
+ {http, S, {http_error, Err}} ->
+ ?line error({error, Err})
+ after
+ 3000 ->
+ ?line error({timeout,header})
+ end.
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 514deaf065..2354f8accd 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -99,9 +99,9 @@ buffer_size(Config) when is_list(Config) ->
?line Bin = list_to_binary(lists:seq(0, Len-1)),
?line M = 8192 div Len,
?line Spec0 =
- [{opt,M},{safe,M-1},{long,M+1},
- {opt,2*M},{safe,2*M-1},{long,2*M+1},
- {opt,4*M},{safe,4*M-1},{long,4*M+1}],
+ [{opt,M},{safe,M-3},{long,M+1},
+ {opt,2*M},{safe,2*M-3},{long,2*M+1},
+ {opt,4*M},{safe,4*M-3},{long,4*M+1}],
?line Spec =
[case Tag of
opt ->
@@ -145,16 +145,27 @@ buffer_size_client(_, _, _, _, _, []) ->
?line ok;
buffer_size_client(Server, IP, Port,
Socket, Cnt, [Opts|T]) when is_list(Opts) ->
+ ?line io:format("buffer_size_client Cnt=~w setopts ~p.~n", [Cnt,Opts]),
?line ok = inet:setopts(Socket, Opts),
?line Server ! {self(),setopts,Cnt},
?line receive {Server,setopts,Cnt} -> ok end,
?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T);
buffer_size_client(Server, IP, Port,
- Socket, Cnt, [{B,Replies}|T]) when is_binary(B) ->
- ?line ok = gen_udp:send(Socket, IP, Port, B),
+ Socket, Cnt, [{B,Replies}|T]=Opts) when is_binary(B) ->
+ ?line io:format(
+ "buffer_size_client Cnt=~w send size ~w expecting ~p.~n",
+ [Cnt,size(B),Replies]),
+ ?line ok = gen_udp:send(Socket, IP, Port, <<Cnt,B/binary>>),
?line receive
{Server,Cnt,Reply} ->
- ?line case lists:member(Reply, Replies) of
+ ?line Tag =
+ if
+ is_tuple(Reply) ->
+ element(1, Reply);
+ is_atom(Reply) ->
+ Reply
+ end,
+ ?line case lists:member(Tag, Replies) of
true -> ok;
false ->
?line
@@ -162,34 +173,62 @@ buffer_size_client(Server, IP, Port,
byte_size(B),
inet:getopts(Socket,
[sndbuf,recbuf])})
- end
- end,
- ?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T).
+ end,
+ ?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T)
+ after 1313 ->
+ ?line buffer_size_client(Server, IP, Port, Socket, Cnt, Opts)
+ end.
buffer_size_server(_, _, _, _, _, []) ->
ok;
buffer_size_server(Client, IP, Port,
Socket, Cnt, [Opts|T]) when is_list(Opts) ->
receive {Client,setopts,Cnt} -> ok end,
+ ?line io:format("buffer_size_server Cnt=~w setopts ~p.~n", [Cnt,Opts]),
ok = inet:setopts(Socket, Opts),
Client ! {self(),setopts,Cnt},
buffer_size_server(Client, IP, Port, Socket, Cnt+1, T);
buffer_size_server(Client, IP, Port,
Socket, Cnt, [{B,_}|T]) when is_binary(B) ->
+ ?line io:format(
+ "buffer_size_server Cnt=~w expecting size ~w.~n",
+ [Cnt,size(B)]),
Client !
{self(),Cnt,
- receive
- {udp,Socket,IP,Port,D} when is_binary(D) ->
+ case buffer_size_server_recv(Socket, IP, Port, Cnt) of
+ D when is_binary(D) ->
SizeD = byte_size(D),
+ ?line io:format(
+ "buffer_size_server Cnt=~w received size ~w.~n",
+ [Cnt,SizeD]),
case B of
- D -> correct;
- <<D:SizeD/binary,_/binary>> -> truncated
+ D ->
+ correct;
+ <<D:SizeD/binary,_/binary>> ->
+ truncated;
+ _ ->
+ {unexpected,D}
end;
- {udp_error,Socket,Error} -> Error
- after 5000 -> timeout
+ Error ->
+ ?line io:format(
+ "buffer_size_server Cnt=~w received error ~w.~n",
+ [Cnt,Error]),
+ Error
end},
buffer_size_server(Client, IP, Port, Socket, Cnt+1, T).
+buffer_size_server_recv(Socket, IP, Port, Cnt) ->
+ receive
+ {udp,Socket,IP,Port,<<Cnt,B/binary>>} ->
+ B;
+ {udp,Socket,IP,Port,<<_/binary>>} ->
+ buffer_size_server_recv(Socket, IP, Port, Cnt);
+ {udp_error,Socket,Error} ->
+ Error
+ after 5000 ->
+ {timeout,flush()}
+ end.
+
%%-------------------------------------------------------------
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 1e7bcf1766..60035b50a0 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -436,7 +436,7 @@ lock_global2(Id, Parent) ->
%cp1 - cp3 are started, and the name 'test' registered for a process on
%test_server. Then it is checked that the name is registered on all
-%nodes, using whereis_name and safe_whereis_name. Check that the same
+%nodes, using whereis_name. Check that the same
%name can't be registered with another value. Exit the registered
%process and check that the name disappears. Register a new process
%(Pid2) under the name 'test'. Let another new process (Pid3)
@@ -465,10 +465,6 @@ names(Config) when is_list(Config) ->
% test that it is registered at all nodes
?line
?UNTIL(begin
- (Pid =:= global:safe_whereis_name(test)) and
- (Pid =:= rpc:call(Cp1, global, safe_whereis_name, [test])) and
- (Pid =:= rpc:call(Cp2, global, safe_whereis_name, [test])) and
- (Pid =:= rpc:call(Cp3, global, safe_whereis_name, [test])) and
(Pid =:= global:whereis_name(test)) and
(Pid =:= rpc:call(Cp1, global, whereis_name, [test])) and
(Pid =:= rpc:call(Cp2, global, whereis_name, [test])) and
@@ -566,10 +562,7 @@ names_hidden(Config) when is_list(Config) ->
% Check that it didn't get registered on visible nodes
?line
- ?UNTIL((undefined =:= global:safe_whereis_name(test)) and
- (undefined =:= rpc:call(Cp1, global, safe_whereis_name, [test])) and
- (undefined =:= rpc:call(Cp2, global, safe_whereis_name, [test])) and
- (undefined =:= global:whereis_name(test)) and
+ ?UNTIL((undefined =:= global:whereis_name(test)) and
(undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and
(undefined =:= rpc:call(Cp2, global, whereis_name, [test]))),
@@ -579,11 +572,7 @@ names_hidden(Config) when is_list(Config) ->
% test that it is registered at all nodes
?line
- ?UNTIL((Pid =:= global:safe_whereis_name(test)) and
- (Pid =:= rpc:call(Cp1, global, safe_whereis_name, [test])) and
- (Pid =:= rpc:call(Cp2, global, safe_whereis_name, [test])) and
- (HPid =:= rpc:call(Cp3, global, safe_whereis_name, [test])) and
- (Pid =:= global:whereis_name(test)) and
+ ?UNTIL((Pid =:= global:whereis_name(test)) and
(Pid =:= rpc:call(Cp1, global, whereis_name, [test])) and
(Pid =:= rpc:call(Cp2, global, whereis_name, [test])) and
(HPid =:= rpc:call(Cp3, global, whereis_name, [test])) and
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index aaa20b7398..7241b093d0 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -97,8 +97,12 @@ t_gethostbyaddr() ->
required(v4).
t_gethostbyaddr(doc) -> "Test the inet:gethostbyaddr/1 function.";
t_gethostbyaddr(Config) when is_list(Config) ->
- ?line {Name,FullName,IPStr,IP,Aliases,_,_} =
+ ?line {Name,FullName,IPStr,{A,B,C,D}=IP,Aliases,_,_} =
ct:get_config(test_host_ipv4_only),
+ ?line Rname = integer_to_list(D) ++ "." ++
+ integer_to_list(C) ++ "." ++
+ integer_to_list(B) ++ "." ++
+ integer_to_list(A) ++ ".in-addr.arpa",
?line {ok,HEnt} = inet:gethostbyaddr(IPStr),
?line {ok,HEnt} = inet:gethostbyaddr(IP),
?line {error,Error} = inet:gethostbyaddr(Name),
@@ -116,7 +120,7 @@ t_gethostbyaddr(Config) when is_list(Config) ->
ok;
_ ->
?line check_elems([{HEnt#hostent.h_name,[Name,FullName]},
- {HEnt#hostent.h_aliases,[[],Aliases]}])
+ {HEnt#hostent.h_aliases,[[],Aliases,[Rname]]}])
end,
?line {_DName, _DFullName, DIPStr, DIP, _, _, _} =
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index 8a3d220e46..f3ba28e4f9 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -88,7 +88,7 @@ init_per_testcase(Func, Config) ->
inet_db:ins_alt_ns(IP, Port);
_ -> ok
end,
- Dog = test_server:timetrap(test_server:seconds(10)),
+ Dog = test_server:timetrap(test_server:seconds(20)),
[{nameserver,NsSpec},{res_lookup,Lookup},{watchdog,Dog}|Config]
catch
SkipReason ->
@@ -136,21 +136,22 @@ ns_init(ZoneDir, PrivDir, DataDir) ->
atom_to_list(ZoneDir)]},
stderr_to_stdout,
eof]),
- ns_start(ZoneDir, NS, P);
+ ns_start(ZoneDir, PrivDir, NS, P);
_ ->
throw("Only run on Unix")
end.
-ns_start(ZoneDir, NS, P) ->
+ns_start(ZoneDir, PrivDir, NS, P) ->
case ns_collect(P) of
eof ->
erlang:error(eof);
"Running: "++_ ->
{ZoneDir,NS,P};
"Error: "++Error ->
+ ns_printlog(filename:join([PrivDir,ZoneDir,"named.log"])),
throw(Error);
_ ->
- ns_start(ZoneDir, NS, P)
+ ns_start(ZoneDir, PrivDir, NS, P)
end.
ns_end(undefined, _PrivDir) -> undefined;
@@ -302,7 +303,7 @@ basic(Config) when is_list(Config) ->
{ok,Msg2} = inet_dns:decode(Bin2),
%%
%% lookup
- [IP] = inet_res:lookup(Name, in, a, [{nameservers,[NS]}]),
+ [IP] = inet_res:lookup(Name, in, a, [{nameservers,[NS]},verbose]),
%%
%% gethostbyname
{ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(Name),
@@ -409,7 +410,7 @@ edns0(Config) when is_list(Config) ->
false = inet_db:res_option(edns), % ASSERT
true = inet_db:res_option(udp_payload_size) >= 1280, % ASSERT
%% These will fall back to TCP
- MXs = lists:sort(inet_res:lookup(Domain, in, mx, [{nameservers,[NS]}])),
+ MXs = lists:sort(inet_res:lookup(Domain, in, mx, [{nameservers,[NS]},verbose])),
%%
{ok,#hostent{h_addr_list=As}} = inet_res:getbyname(Domain++".", mx),
MXs = lists:sort(As),
diff --git a/lib/kernel/test/inet_res_SUITE_data/run-named b/lib/kernel/test/inet_res_SUITE_data/run-named
index 7caa3756ef..211d2c7af7 100755
--- a/lib/kernel/test/inet_res_SUITE_data/run-named
+++ b/lib/kernel/test/inet_res_SUITE_data/run-named
@@ -2,7 +2,7 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2009-2011. All Rights Reserved.
+## Copyright Ericsson AB 2009-2012. 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
@@ -71,10 +71,14 @@ test -d "$SRCDIR" || \
test -f "$SRCDIR/$INC_FILE" || \
error "Missing file: $SRCDIR/$INC_FILE !"
-# Locate named and check version
+# Locate named and check version.
+# The bind-named name is used for tricking Apparmor and such
+# by copying/hardlinking the real named to that name.
NAMED=named
-for n in /usr/sbin/named /usr/sbin/in.named; do
- test -x "$n" && NAMED="$n"
+for n in /usr/local/bin/bind-named /usr/local/bin/named \
+ /usr/sbin/bind-named /usr/sbin/named /usr/sbin/in.named
+do
+ test -x "$n" && NAMED="$n" && break
done
NAMED_VER="`"$NAMED" -v 2>&1`" || \
error "Name server not found!"
@@ -151,13 +155,21 @@ echo "Cwd: `pwd`"
echo "Nameserver: $NAMED_VER"
echo "Port: $2"
echo "ZoneDir: $3"
+echo "Command: $NAMED $NAMED_FG -c $CONF_FILE"
$NAMED $NAMED_FG -c "$CONF_FILE" >"$LOG_FILE" 2>&1 </dev/null &
-NAMED=$!
-trap "kill -TERM $NAMED >/dev/null 2>&1; wait $NAMED >/dev/null 2>&1" \
+NAMED_PID=$!
+echo "Pid: $NAMED_PID"
+trap "kill $NAMED_PID >/dev/null 2>&1; wait $NAMED_PID >/dev/null 2>&1" \
0 1 2 3 15
-sleep 2 # Give name server time to load its zone files
-echo "Running: Enter \`\`quit'' to terminate nameserver[$NAMED]..."
-while read LINE; do
- test :"$LINE" = :'quit' && break
-done
-echo "Closing: Terminating nameserver..."
+
+sleep 5 # Give name server time to load its zone files
+
+if ps -p $NAMED_PID >/dev/null 2>&1 || ps p $NAMED_PID >/dev/null 2>&1; then
+ echo "Running: Enter \`\`quit'' to terminate nameserver[$NAMED_PID]..."
+ while read LINE; do
+ test :"$LINE" = :'quit' && break
+ done
+ echo "Closing: Terminating nameserver..."
+else
+ error "$NAMED failed to start"
+fi
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index b39fadd65f..e3fa4642b7 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -608,7 +608,7 @@ boot2(Config) when is_list(Config) ->
%% Absolute boot file name
Boot = filename:join([code:root_dir(), "bin", "start_sasl"]),
- Args = args() ++ " -boot " ++ Boot,
+ Args = args() ++ " -boot \"" ++ Boot++"\"",
?line {ok, Node} = start_node(init_test, Args),
?line stop_node(Node),
@@ -618,7 +618,7 @@ boot2(Config) when is_list(Config) ->
%% converted to backslashes.
Win_boot = lists:map(fun($/) -> $\\; (C) -> C end,
Boot),
- Args2 = args() ++ " -boot " ++ Win_boot,
+ Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"",
?line {ok, Node2} = start_node(init_test, Args2),
?line stop_node(Node2);
_ ->
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index b2308dd321..96e45cc23f 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -251,7 +251,7 @@ rtnode(Commands,Nodename,ErlPrefix) ->
?line {skip, Reason2};
Tempdir ->
?line SPid =
- start_runerl_node(RunErl,ErlPrefix++Erl,
+ start_runerl_node(RunErl,ErlPrefix++"\\\""++Erl++"\\\"",
Tempdir,Nodename),
?line CPid = start_toerl_server(ToErl,Tempdir),
?line erase(getline_skipped),
@@ -487,7 +487,7 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename) ->
" -setcookie "++atom_to_list(erlang:get_cookie())
end,
spawn(fun() ->
- os:cmd(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++
+ os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++
Erl++XArg++"\"")
end).
@@ -518,7 +518,7 @@ try_to_erl(Command, N) ->
end.
toerl_server(Parent,ToErl,Tempdir) ->
- Port = try_to_erl(ToErl++" "++Tempdir++"/ 2>/dev/null",8),
+ Port = try_to_erl("\""++ToErl++"\" "++Tempdir++"/ 2>/dev/null",8),
case Port of
P when is_port(P) ->
Parent ! {self(),started};
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 16b6c54939..0f29d895e5 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -32,7 +32,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
--export([app_test/1]).
+-export([app_test/1, appup_test/1]).
%%
%% all/1
@@ -40,7 +40,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test].
+ [app_test, appup_test].
groups() ->
[].
@@ -76,3 +76,63 @@ app_test(suite) ->
app_test(Config) when is_list(Config) ->
?line ok=?t:app_test(kernel),
ok.
+
+
+%% Test that appup allows upgrade from/downgrade to a maximum of two
+%% major releases back.
+appup_test(_Config) ->
+ application:load(kernel),
+ {_,_,Vsn} = lists:keyfind(kernel,1,application:loaded_applications()),
+ AppupFile = filename:join([code:lib_dir(kernel),ebin,"kernel.appup"]),
+ {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
+ ct:log("~p~n",[AppupScript]),
+ {OkVsns,NokVsns} = create_test_vsns(Vsn),
+ check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
+ check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
+ check_appup(NokVsns,UpFrom,error),
+ check_appup(NokVsns,DownTo,error),
+ ok.
+
+create_test_vsns(Current) ->
+ [XStr,YStr|Rest] = string:tokens(Current,"."),
+ X = list_to_integer(XStr),
+ Y = list_to_integer(YStr),
+ SecondMajor = vsn(X,Y-2),
+ SecondMinor = SecondMajor ++ ".1.3",
+ FirstMajor = vsn(X,Y-1),
+ FirstMinor = FirstMajor ++ ".57",
+ ThisMajor = vsn(X,Y),
+ This =
+ case Rest of
+ [] ->
+ [];
+ ["1"] ->
+ [ThisMajor];
+ _ ->
+ ThisMinor = ThisMajor ++ ".1",
+ [ThisMajor,ThisMinor]
+ end,
+ OkVsns = This ++ [FirstMajor, FirstMinor, SecondMajor, SecondMinor],
+
+ ThirdMajor = vsn(X,Y-3),
+ ThirdMinor = ThirdMajor ++ ".10.12",
+ Illegal = ThisMajor ++ ",1",
+ Newer1Major = vsn(X,Y+1),
+ Newer1Minor = Newer1Major ++ ".1",
+ Newer2Major = ThisMajor ++ "1",
+ NokVsns = [ThirdMajor,ThirdMinor,
+ Illegal,
+ Newer1Major,Newer1Minor,
+ Newer2Major],
+ {OkVsns,NokVsns}.
+
+vsn(X,Y) ->
+ integer_to_list(X) ++ "." ++ integer_to_list(Y).
+
+check_appup([Vsn|Vsns],Instrs,Expected) ->
+ case systools_relup:appup_search_for_version(Vsn, Instrs) of
+ Expected -> check_appup(Vsns,Instrs,Expected);
+ Other -> ct:fail({unexpected_result_for_vsn,Vsn,Other})
+ end;
+check_appup([],_,_) ->
+ ok.
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index b08b12c978..ae3410d13f 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -117,9 +117,21 @@ space_in_name(Config) when is_list(Config) ->
?line ok = file:change_mode(Echo, 8#777), % Make it executable on Unix.
%% Run the echo program.
-
- ?line comp("", os:cmd("\"" ++ Echo ++ "\"")),
- ?line comp("a::b::c", os:cmd("\"" ++ Echo ++ "\" a b c")),
+ %% Quoting on windows depends on if the full path of the executable
+ %% contains special characters. Paths when running common_tests always
+ %% include @, why Windows would always fail if we do not double the
+ %% quotes (this is the behaviour of cmd.exe, not Erlang's idea).
+ Quote = case os:type() of
+ {win32,_} ->
+ case (Echo -- "&<>()@^|") =:= Echo of
+ true -> "\"";
+ false -> "\"\""
+ end;
+ _ ->
+ "\""
+ end,
+ ?line comp("", os:cmd(Quote ++ Echo ++ Quote)),
+ ?line comp("a::b::c", os:cmd(Quote ++ Echo ++ Quote ++ " a b c")),
?t:sleep(5),
?line [] = receive_all(),
ok.
diff --git a/lib/kernel/test/pg2_SUITE.erl b/lib/kernel/test/pg2_SUITE.erl
index 0ac34e735c..520b53b4e4 100644
--- a/lib/kernel/test/pg2_SUITE.erl
+++ b/lib/kernel/test/pg2_SUITE.erl
@@ -47,6 +47,7 @@ init_per_testcase(Case, Config) ->
[{?TESTCASE, Case}, {watchdog, Dog} | Config].
end_per_testcase(_Case, _Config) ->
+ test_server_ctrl:kill_slavenodes(),
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 00eda6292f..3e2202922c 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -32,7 +32,10 @@
file_info_basic_directory_a/1, file_info_basic_directory_b/1,
file_info_bad_a/1, file_info_bad_b/1,
file_info_times_a/1, file_info_times_b/1,
- file_write_file_info_a/1, file_write_file_info_b/1]).
+ file_write_file_info_a/1, file_write_file_info_b/1,
+ file_read_file_info_opts/1, file_write_file_info_opts/1,
+ file_write_read_file_info_opts/1
+ ]).
-export([rename_a/1, rename_b/1,
access/1, truncate/1, datasync/1, sync/1,
read_write/1, pread_write/1, append/1, exclusive/1]).
@@ -49,6 +52,10 @@
list_dir_limit/1]).
-export([advise/1]).
+-export([large_write/1]).
+
+%% System probe functions that might be handy to check from the shell
+-export([unix_free/1]).
-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
@@ -80,7 +87,7 @@ groups() ->
cur_dir_1a, cur_dir_1b]},
{files, [],
[{group, open}, {group, pos}, {group, file_info},
- truncate, sync, datasync, advise]},
+ truncate, sync, datasync, advise, large_write]},
{open, [],
[open1, modes, close, access, read_write, pread_write,
append, exclusive]},
@@ -90,7 +97,10 @@ groups() ->
file_info_basic_directory_a,
file_info_basic_directory_b, file_info_bad_a,
file_info_bad_b, file_info_times_a, file_info_times_b,
- file_write_file_info_a, file_write_file_info_b]},
+ file_write_file_info_a, file_write_file_info_b,
+ file_read_file_info_opts, file_write_file_info_opts,
+ file_write_read_file_info_opts
+ ]},
{errors, [],
[e_delete, e_rename, e_make_dir, e_del_dir]},
{compression, [],
@@ -284,6 +294,7 @@ make_del_dir(Config, Handle, Suffix) ->
%% Don't worry ;-) the parent directory should never be empty, right?
?line case ?PRIM_FILE_call(del_dir, Handle, [".."]) of
{error, eexist} -> ok;
+ {error, eacces} -> ok; %OpenBSD
{error, einval} -> ok %FreeBSD
end,
?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]),
@@ -1074,6 +1085,104 @@ file_write_file_info(Config, Handle, Suffix) ->
?line test_server:timetrap_cancel(Dog),
ok.
+%% Test the write_file_info/3 function.
+
+file_write_file_info_opts(suite) -> [];
+file_write_file_info_opts(doc) -> [];
+file_write_file_info_opts(Config) when is_list(Config) ->
+ {ok, Handle} = ?PRIM_FILE:start(),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = get_good_directory(Config),
+ test_server:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_write_file_info_opts"),
+ ok = ?PRIM_FILE:write_file(Name, "hello_opts"),
+
+ lists:foreach(fun
+ ({FI, Opts}) ->
+ ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts])
+ end, [
+ {#file_info{ mode=8#600, atime = Time, mtime = Time, ctime = Time}, Opts} ||
+ Opts <- [[{time, posix}]],
+ Time <- [ 0,1,-1,100,-100,1000,-1000,10000,-10000 ]
+ ]),
+
+ % REM: determine date range dependent on time_t = Uint32 | Sint32 | Sint64
+ % Determine time_t on os:type()?
+ lists:foreach(fun
+ ({FI, Opts}) ->
+ ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts])
+ end, [
+ {#file_info{ mode=8#400, atime = Time, mtime = Time, ctime = Time}, Opts} ||
+ Opts <- [[{time, universal}],[{time, local}]],
+ Time <- [
+ {{1970,1,1},{0,0,0}},
+ {{1970,1,1},{0,0,1}},
+ {{1969,12,31},{23,59,59}},
+ {{1908,2,3},{23,59,59}},
+ {{2012,2,3},{23,59,59}},
+ {{2037,2,3},{23,59,59}},
+ erlang:localtime()
+ ]]),
+ ok = ?PRIM_FILE:stop(Handle),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+file_read_file_info_opts(suite) -> [];
+file_read_file_info_opts(doc) -> [];
+file_read_file_info_opts(Config) when is_list(Config) ->
+ {ok, Handle} = ?PRIM_FILE:start(),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = get_good_directory(Config),
+ test_server:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_read_file_info_opts"),
+ ok = ?PRIM_FILE:write_file(Name, "hello_opts"),
+
+ lists:foreach(fun
+ (Opts) ->
+ {ok,_} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts])
+ end, [[{time, Type}] || Type <- [local, universal, posix]]),
+ ok = ?PRIM_FILE:stop(Handle),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%% Test the write and read back *_file_info/3 functions.
+
+file_write_read_file_info_opts(suite) -> [];
+file_write_read_file_info_opts(doc) -> [];
+file_write_read_file_info_opts(Config) when is_list(Config) ->
+ {ok, Handle} = ?PRIM_FILE:start(),
+ Dog = test_server:timetrap(test_server:seconds(10)),
+ RootDir = get_good_directory(Config),
+ test_server:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_read_write_file_info_opts"),
+ ok = ?PRIM_FILE:write_file(Name, "hello_opts2"),
+
+ ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, local}]),
+ ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, universal}]),
+ ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]),
+ ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]),
+ ok = file_write_read_file_info_opts(Handle, Name, 1, [{time, posix}]),
+ ok = file_write_read_file_info_opts(Handle, Name, -1, [{time, posix}]),
+ ok = file_write_read_file_info_opts(Handle, Name, 300000, [{time, posix}]),
+ ok = file_write_read_file_info_opts(Handle, Name, -300000, [{time, posix}]),
+ ok = file_write_read_file_info_opts(Handle, Name, 0, [{time, posix}]),
+
+ ok = ?PRIM_FILE:stop(Handle),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+file_write_read_file_info_opts(Handle, Name, Mtime, Opts) ->
+ {ok, FI} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]),
+ FI2 = FI#file_info{ mtime = Mtime },
+ ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI2, Opts]),
+ {ok, FI2} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]),
+ ok.
+
+
+
%% Returns a directory on a file system that has correct file times.
get_good_directory(Config) ->
@@ -1218,6 +1327,41 @@ advise(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+large_write(Config) when is_list(Config) ->
+ run_large_file_test(Config,
+ fun(Name) -> do_large_write(Name) end,
+ "_large_write").
+
+do_large_write(Name) ->
+ Dog = test_server:timetrap(test_server:minutes(60)),
+ ChunkSize = (256 bsl 20) + 1, % 256 M + 1
+ Chunks = 16, % times 16 -> 4 G + 16
+ Base = 100,
+ Interleave = lists:seq(Base+1, Base+Chunks),
+ Chunk = <<0:ChunkSize/unit:8>>,
+ Data = zip_data(lists:duplicate(Chunks, Chunk), Interleave),
+ Size = Chunks * ChunkSize + Chunks, % 4 G + 32
+ Wordsize = erlang:system_info(wordsize),
+ case prim_file:write_file(Name, Data) of
+ ok when Wordsize =:= 8 ->
+ {ok,#file_info{size=Size}} = file:read_file_info(Name),
+ {ok,Fd} = prim_file:open(Name, [read]),
+ check_large_write(Dog, Fd, ChunkSize, 0, Interleave);
+ {error,einval} when Wordsize =:= 4 ->
+ ok
+ end.
+
+check_large_write(Dog, Fd, ChunkSize, Pos, [X|Interleave]) ->
+ Pos1 = Pos + ChunkSize,
+ {ok,Pos1} = prim_file:position(Fd, {cur,ChunkSize}),
+ {ok,[X]} = prim_file:read(Fd, 1),
+ check_large_write(Dog, Fd, ChunkSize, Pos1+1, Interleave);
+check_large_write(Dog, Fd, _, _, []) ->
+ eof = prim_file:read(Fd, 1),
+ test_server:timetrap_cancel(Dog),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1940,3 +2084,70 @@ list_dir_limit_cleanup(Dir, Handle, N, Cnt) ->
?PRIM_FILE:delete(Handle, filename:join(Dir, Name)),
list_dir_limit_cleanup(Dir, Handle, N, Cnt+1).
+%%%
+%%% Support for testing large files.
+%%%
+
+run_large_file_test(Config, Run, Name) ->
+ case {os:type(),os:version()} of
+ {{win32,nt},_} ->
+ do_run_large_file_test(Config, Run, Name);
+ {{unix,sunos},OsVersion} when OsVersion < {5,5,1} ->
+ {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"};
+ {{unix,_},_} ->
+ N = unix_free(?config(priv_dir, Config)),
+ io:format("Free disk: ~w KByte~n", [N]),
+ if N < 5 bsl 20 ->
+ %% Less than 5 GByte free
+ {skip,"Less than 5 GByte free disk"};
+ true ->
+ do_run_large_file_test(Config, Run, Name)
+ end;
+ _ ->
+ {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}
+ end.
+
+
+do_run_large_file_test(Config, Run, Name0) ->
+ Name = filename:join(?config(priv_dir, Config),
+ ?MODULE_STRING ++ Name0),
+
+ %% Set up a process that will delete this file.
+ Tester = self(),
+ Deleter =
+ spawn(
+ fun() ->
+ Mref = erlang:monitor(process, Tester),
+ receive
+ {'DOWN',Mref,_,_,_} -> ok;
+ {Tester,done} -> ok
+ end,
+ prim_file:delete(Name)
+ end),
+
+ %% Run the test case.
+ Res = Run(Name),
+
+ %% Delete file and finish deleter process.
+ Mref = erlang:monitor(process, Deleter),
+ Deleter ! {Tester,done},
+ receive {'DOWN',Mref,_,_,_} -> ok end,
+
+ Res.
+
+unix_free(Path) ->
+ Cmd = ["df -k '",Path,"'"],
+ DF0 = os:cmd(Cmd),
+ io:format("$ ~s~n~s", [Cmd,DF0]),
+ Lines = re:split(DF0, "\n", [trim,{return,list}]),
+ Last = lists:last(Lines),
+ RE = "^[^\\s]*\\s+\\d+\\s+\\d+\\s+(\\d+)",
+ {match,[Avail]} = re:run(Last, RE, [{capture,all_but_first,list}]),
+ list_to_integer(Avail).
+
+zip_data([A|As], [B|Bs]) ->
+ [[A,B]|zip_data(As, Bs)];
+zip_data([], Bs) ->
+ Bs;
+zip_data(As, []) ->
+ As.
diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl
new file mode 100644
index 0000000000..4cf4c6489d
--- /dev/null
+++ b/lib/kernel/test/sendfile_SUITE.erl
@@ -0,0 +1,407 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(sendfile_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-compile(export_all).
+
+all() ->
+ [t_sendfile_small
+ ,t_sendfile_big_all
+ ,t_sendfile_big_size
+ ,t_sendfile_many_small
+ ,t_sendfile_partial
+ ,t_sendfile_offset
+ ,t_sendfile_sendafter
+ ,t_sendfile_recvafter
+ ,t_sendfile_sendduring
+ ,t_sendfile_recvduring
+ ,t_sendfile_closeduring
+ ,t_sendfile_crashduring
+ ].
+
+init_per_suite(Config) ->
+ case {os:type(),os:version()} of
+ {{unix,sunos}, {5,8,_}} ->
+ {skip, "Solaris 8 not supported for now"};
+ _ ->
+ Priv = ?config(priv_dir, Config),
+ SFilename = filename:join(Priv, "sendfile_small.html"),
+ {ok, DS} = file:open(SFilename,[write,raw]),
+ file:write(DS,"yo baby yo"),
+ file:sync(DS),
+ file:close(DS),
+ BFilename = filename:join(Priv, "sendfile_big.html"),
+ {ok, DB} = file:open(BFilename,[write,raw]),
+ [file:write(DB,[<<0:(10*8*1024*1024)>>]) || _I <- lists:seq(1,51)],
+ file:sync(DB),
+ file:close(DB),
+ [{small_file, SFilename},
+ {file_opts,[raw,binary]},
+ {big_file, BFilename}|Config]
+ end.
+
+end_per_suite(Config) ->
+ file:delete(proplists:get_value(big_file, Config)).
+
+init_per_testcase(TC,Config) when TC == t_sendfile_recvduring;
+ TC == t_sendfile_sendduring ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {_Size, Data} = sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename, [raw,binary,read]),
+ prim_file:sendfile(D, Sock, 0, 0, 0,
+ [],[],false,false,false),
+ Data
+ end,
+
+ %% Check if sendfile is supported on this platform
+ case catch sendfile_send(Send) of
+ ok ->
+ Config;
+ Error ->
+ ct:log("Error: ~p",[Error]),
+ {skip,"Not supported"}
+ end;
+init_per_testcase(_Tc,Config) ->
+ Config.
+
+
+t_sendfile_small(Config) when is_list(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ Data
+ end,
+
+ ok = sendfile_send(Send).
+
+t_sendfile_many_small(Config) when is_list(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ error_logger:add_report_handler(?MODULE,[self()]),
+
+ Send = fun(Sock) ->
+ {Size,_} = sendfile_file_info(Filename),
+ N = 10000,
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ [begin
+ {ok,Size} = file:sendfile(D,Sock,0,0,[])
+ end || _I <- lists:seq(1,N)],
+ file:close(D),
+ Size*N
+ end,
+
+ ok = sendfile_send({127,0,0,1}, Send, 0),
+
+ receive
+ {stolen,Reason} ->
+ exit(Reason)
+ after 200 ->
+ ok
+ end.
+
+
+t_sendfile_big_all(Config) when is_list(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ Size
+ end,
+
+ ok = sendfile_send({127,0,0,1}, Send, 0).
+
+t_sendfile_big_size(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ SendAll = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok, Size} = file:sendfile(D, Sock,0,Size,[]),
+ Size
+ end,
+
+ ok = sendfile_send({127,0,0,1}, SendAll, 0).
+
+t_sendfile_partial(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ SendSingle = fun(Sock) ->
+ {_Size, <<Data:5/binary,_/binary>>} =
+ sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok,5} = file:sendfile(D,Sock,0,5,[]),
+ file:close(D),
+ Data
+ end,
+ ok = sendfile_send(SendSingle),
+
+ {_Size, <<FData:5/binary,SData:3/binary,_/binary>>} =
+ sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok, <<FData/binary>>} = file:read(D,5),
+ FSend = fun(Sock) ->
+ {ok,5} = file:sendfile(D,Sock,0,5,[]),
+ FData
+ end,
+
+ ok = sendfile_send(FSend),
+
+ SSend = fun(Sock) ->
+ {ok,3} = file:sendfile(D,Sock,5,3,[]),
+ SData
+ end,
+
+ ok = sendfile_send(SSend),
+
+ {ok, <<SData/binary>>} = file:read(D,3),
+
+ file:close(D).
+
+t_sendfile_offset(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ Send = fun(Sock) ->
+ {_Size, <<_:5/binary,Data:3/binary,_/binary>> = AllData} =
+ sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok,3} = file:sendfile(D,Sock,5,3,[]),
+ {ok, AllData} = file:read(D,100),
+ file:close(D),
+ Data
+ end,
+ ok = sendfile_send(Send).
+
+
+t_sendfile_sendafter(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ ok = gen_tcp:send(Sock, <<2>>),
+ <<Data/binary,2>>
+ end,
+
+ ok = sendfile_send(Send).
+
+t_sendfile_recvafter(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ ok = gen_tcp:send(Sock, <<1>>),
+ {ok,<<1>>} = gen_tcp:recv(Sock, 1),
+ <<Data/binary,1>>
+ end,
+
+ ok = sendfile_send(Send).
+
+t_sendfile_sendduring(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ spawn_link(fun() ->
+ timer:sleep(50),
+ ok = gen_tcp:send(Sock, <<2>>)
+ end),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ Size+1
+ end,
+
+ ok = sendfile_send({127,0,0,1}, Send, 0).
+
+t_sendfile_recvduring(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ spawn_link(fun() ->
+ timer:sleep(50),
+ ok = gen_tcp:send(Sock, <<1>>),
+ {ok,<<1>>} = gen_tcp:recv(Sock, 1)
+ end),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ timer:sleep(1000),
+ Size+1
+ end,
+
+ ok = sendfile_send({127,0,0,1}, Send, 0).
+
+t_sendfile_closeduring(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock,SFServPid) ->
+ spawn_link(fun() ->
+ timer:sleep(50),
+ SFServPid ! stop
+ end),
+ case erlang:system_info(thread_pool_size) of
+ 0 ->
+ {error, closed} = file:sendfile(Filename, Sock);
+ _Else ->
+ %% This can return how much has been sent or
+ %% {error,closed} depending on OS.
+ %% How much is sent impossible to know as
+ %% the socket was closed mid sendfile
+ case file:sendfile(Filename, Sock) of
+ {error, closed} ->
+ ok;
+ {ok, Size} when is_integer(Size) ->
+ ok
+ end
+ end,
+ -1
+ end,
+
+ ok = sendfile_send({127,0,0,1}, Send, 0).
+
+t_sendfile_crashduring(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ error_logger:add_report_handler(?MODULE,[self()]),
+
+ Send = fun(Sock) ->
+ spawn_link(fun() ->
+ timer:sleep(50),
+ exit(die)
+ end),
+ {error, closed} = file:sendfile(Filename, Sock),
+ -1
+ end,
+ process_flag(trap_exit,true),
+ spawn_link(fun() ->
+ ok = sendfile_send({127,0,0,1}, Send, 0)
+ end),
+ receive
+ {stolen,Reason} ->
+ process_flag(trap_exit,false),
+ ct:fail(Reason)
+ after 200 ->
+ receive
+ {'EXIT',_,Reason} ->
+ process_flag(trap_exit,false),
+ die = Reason
+ end
+ end.
+
+%% Generic sendfile server code
+sendfile_send(Send) ->
+ sendfile_send({127,0,0,1},Send).
+sendfile_send(Host, Send) ->
+ sendfile_send(Host, Send, []).
+sendfile_send(Host, Send, Orig) ->
+ SFServer = spawn_link(?MODULE, sendfile_server, [self(), Orig]),
+ receive
+ {server, Port} ->
+ {ok, Sock} = gen_tcp:connect(Host, Port,
+ [binary,{packet,0},
+ {active,false}]),
+ Data = case proplists:get_value(arity,erlang:fun_info(Send)) of
+ 1 ->
+ Send(Sock);
+ 2 ->
+ Send(Sock, SFServer)
+ end,
+ ok = gen_tcp:close(Sock),
+ receive
+ {ok, Bin} ->
+ Data = Bin,
+ ok
+ end
+ end.
+
+sendfile_server(ClientPid, Orig) ->
+ {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 0},
+ {active, true},
+ {reuseaddr, true}]),
+ {ok, Port} = inet:port(LSock),
+ ClientPid ! {server, Port},
+ {ok, Sock} = gen_tcp:accept(LSock),
+ {ok, Bin} = sendfile_do_recv(Sock, Orig),
+ ClientPid ! {ok, Bin},
+ gen_tcp:send(Sock, <<1>>).
+
+-define(SENDFILE_TIMEOUT, 10000).
+sendfile_do_recv(Sock, Bs) ->
+ TimeoutMul = case os:type() of
+ {win32, _} -> 6;
+ _ -> 1
+ end,
+ receive
+ stop when Bs /= 0,is_integer(Bs) ->
+ gen_tcp:close(Sock),
+ {ok, -1};
+ {tcp, Sock, B} ->
+ case binary:match(B,<<1>>) of
+ nomatch when is_list(Bs) ->
+ sendfile_do_recv(Sock, [B|Bs]);
+ nomatch when is_integer(Bs) ->
+ sendfile_do_recv(Sock, byte_size(B) + Bs);
+ _ when is_list(Bs) ->
+ ct:log("Stopped due to a 1"),
+ {ok, iolist_to_binary(lists:reverse([B|Bs]))};
+ _ when is_integer(Bs) ->
+ ct:log("Stopped due to a 1"),
+ {ok, byte_size(B) + Bs}
+ end;
+ {tcp_closed, Sock} when is_list(Bs) ->
+ ct:log("Stopped due to close"),
+ {ok, iolist_to_binary(lists:reverse(Bs))};
+ {tcp_closed, Sock} when is_integer(Bs) ->
+ ct:log("Stopped due to close"),
+ {ok, Bs}
+ after ?SENDFILE_TIMEOUT * TimeoutMul ->
+ ct:log("Sendfile timeout"),
+ timeout
+ end.
+
+sendfile_file_info(File) ->
+ {ok, #file_info{size = Size}} = file:read_file_info(File),
+ {ok, Data} = file:read_file(File),
+ {Size, Data}.
+
+
+%% Error handler
+
+init([Proc]) -> {ok,Proc}.
+
+handle_event({error,noproc,{emulator,Format,Args}}, Proc) ->
+ Proc ! {stolen,lists:flatten(io_lib:format(Format,Args))},
+ {ok,Proc};
+handle_event(_, Proc) ->
+ {ok,Proc}.
diff --git a/lib/kernel/test/seq_trace_SUITE_data/echo_drv.c b/lib/kernel/test/seq_trace_SUITE_data/echo_drv.c
index dcbb3348d8..aa182b0877 100644
--- a/lib/kernel/test/seq_trace_SUITE_data/echo_drv.c
+++ b/lib/kernel/test/seq_trace_SUITE_data/echo_drv.c
@@ -3,7 +3,7 @@
static ErlDrvPort erlang_port;
static ErlDrvData echo_start(ErlDrvPort, char *);
-static void echo_stop(ErlDrvData), echo_read(ErlDrvData, char*, int);
+static void echo_stop(ErlDrvData), echo_read(ErlDrvData, char*, ErlDrvSizeT);
static ErlDrvEntry echo_driver_entry = {
NULL,
@@ -13,7 +13,22 @@ static ErlDrvEntry echo_driver_entry = {
NULL,
NULL,
"echo_drv",
- NULL
+ NULL,
+ NULL, /* handle */
+ NULL, /* control */
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL,
};
DRIVER_INIT(echo_drv)
@@ -31,7 +46,7 @@ static ErlDrvData echo_start(ErlDrvPort port,char *buf)
return (ErlDrvData)port;
}
-static void echo_read(ErlDrvData data, char *buf, int count)
+static void echo_read(ErlDrvData data, char *buf, ErlDrvSizeT count)
{
driver_output(erlang_port, buf, count);
}
diff --git a/lib/kernel/test/wrap_log_reader_SUITE.erl b/lib/kernel/test/wrap_log_reader_SUITE.erl
index ffc8def626..96dc3e6d33 100644
--- a/lib/kernel/test/wrap_log_reader_SUITE.erl
+++ b/lib/kernel/test/wrap_log_reader_SUITE.erl
@@ -561,4 +561,4 @@ rec(M, Where) ->
end.
pps() ->
- {erlang:ports(), lists:filter({erlang, is_process_alive}, processes())}.
+ {erlang:ports(), lists:filter(fun erlang:is_process_alive/1, processes())}.
diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl
index 74bafe8935..bd237cb513 100644
--- a/lib/kernel/test/zlib_SUITE.erl
+++ b/lib/kernel/test/zlib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. 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
@@ -73,6 +73,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, api}, {group, examples}, {group, func}, smp,
+ otp_9981,
otp_7359].
groups() ->
@@ -964,6 +965,24 @@ otp_7359_def_inf(Data,{DefSize,InfSize}) ->
?line ok = zlib:close(ZInf),
ok.
+otp_9981(Config) when is_list(Config) ->
+ Ports = lists:sort(erlang:ports()),
+ Invalid = <<"My invalid data">>,
+ catch zlib:compress(invalid),
+ Ports = lists:sort(erlang:ports()),
+ catch zlib:uncompress(Invalid),
+ Ports = lists:sort(erlang:ports()),
+ catch zlib:zip(invalid),
+ Ports = lists:sort(erlang:ports()),
+ catch zlib:unzip(Invalid),
+ Ports = lists:sort(erlang:ports()),
+ catch zlib:gzip(invalid),
+ Ports = lists:sort(erlang:ports()),
+ catch zlib:gunzip(Invalid),
+ Ports = lists:sort(erlang:ports()),
+ ok.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Helps with testing directly %%%%%%%%%%%%%
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 76c62ece67..c494f8a864 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 2.15
+KERNEL_VSN = 2.15.2