aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile.in8
-rw-r--r--OTP_VERSION2
-rw-r--r--erts/configure.in3
-rw-r--r--erts/doc/src/notes.xml40
-rw-r--r--erts/emulator/beam/utils.c2
-rw-r--r--erts/emulator/drivers/common/inet_drv.c113
-rw-r--r--erts/etc/unix/to_erl.c1
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54496 -> 54576 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin51536 -> 51584 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin28328 -> 28428 bytes
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl7
-rw-r--r--erts/preloaded/src/init.erl7
-rw-r--r--erts/preloaded/src/prim_file.erl7
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/debugger/doc/src/Makefile3
-rw-r--r--lib/erl_docgen/priv/css/otp_doc.css10
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl76
-rw-r--r--lib/ftp/src/ftp.erl287
-rw-r--r--lib/ftp/test/ftp_SUITE.erl67
-rw-r--r--lib/jinterface/doc/src/Makefile10
-rw-r--r--lib/kernel/doc/src/application.xml35
-rw-r--r--lib/kernel/doc/src/logger.xml24
-rw-r--r--lib/kernel/doc/src/notes.xml19
-rw-r--r--lib/kernel/src/application.erl22
-rw-r--r--lib/kernel/src/application_controller.erl104
-rw-r--r--lib/kernel/src/code_server.erl10
-rw-r--r--lib/kernel/src/inet_db.erl7
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/src/kernel.appup.src8
-rw-r--r--lib/kernel/src/logger.erl10
-rw-r--r--lib/kernel/src/logger_h_common.erl2
-rw-r--r--lib/kernel/src/logger_olp.erl5
-rw-r--r--lib/kernel/src/logger_olp.hrl7
-rw-r--r--lib/kernel/src/logger_simple_h.erl4
-rw-r--r--lib/kernel/test/application_SUITE.erl97
-rw-r--r--lib/kernel/test/inet_SUITE.erl31
-rw-r--r--lib/kernel/test/kernel.spec1
-rw-r--r--lib/kernel/test/logger_SUITE.erl4
-rw-r--r--lib/kernel/test/logger_formatter_SUITE.erl2
-rw-r--r--lib/kernel/test/logger_stress_SUITE.erl94
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/ssl/doc/src/ssl.xml21
-rw-r--r--lib/ssl/src/dtls_packet_demux.erl3
-rw-r--r--lib/ssl/src/dtls_socket.erl49
-rw-r--r--lib/ssl/src/ssl.erl2
-rw-r--r--lib/ssl/src/ssl_connection.erl49
-rw-r--r--lib/ssl/src/tls_socket.erl56
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl176
-rw-r--r--lib/stdlib/src/calendar.erl55
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl46
-rw-r--r--lib/stdlib/test/calendar_SUITE.erl2
-rw-r--r--lib/stdlib/test/io_SUITE.erl39
-rw-r--r--make/install_dir_data.sh.in70
-rw-r--r--make/otp.mk.in3
-rw-r--r--make/otp_version_tickets_in_merge5
-rwxr-xr-xotp_patch_apply6
-rw-r--r--otp_versions.table1
-rw-r--r--system/doc/design_principles/Makefile5
-rw-r--r--system/doc/efficiency_guide/Makefile5
-rw-r--r--system/doc/embedded/Makefile5
-rw-r--r--system/doc/getting_started/Makefile5
-rw-r--r--system/doc/html/design_principles/.gitignore0
-rw-r--r--system/doc/html/efficiency_guide/.gitignore0
-rw-r--r--system/doc/html/embedded/.gitignore0
-rw-r--r--system/doc/html/getting_started/.gitignore0
-rw-r--r--system/doc/html/installation_guide/.gitignore0
-rw-r--r--system/doc/html/installation_guide/source/.gitignore0
-rw-r--r--system/doc/html/js/.gitignore0
-rw-r--r--system/doc/html/oam/.gitignore0
-rw-r--r--system/doc/html/programming_examples/.gitignore0
-rw-r--r--system/doc/html/reference_manual/.gitignore0
-rw-r--r--system/doc/html/system_architecture_intro/.gitignore0
-rw-r--r--system/doc/html/system_principles/.gitignore0
-rw-r--r--system/doc/html/tutorial/.gitignore0
-rw-r--r--system/doc/installation_guide/Makefile5
-rw-r--r--system/doc/oam/Makefile7
-rw-r--r--system/doc/programming_examples/Makefile5
-rw-r--r--system/doc/reference_manual/Makefile5
-rw-r--r--system/doc/system_architecture_intro/Makefile5
-rw-r--r--system/doc/system_principles/Makefile5
-rw-r--r--system/doc/top/Makefile93
-rw-r--r--system/doc/top/src/erl_html_tools.erl4
-rw-r--r--system/doc/top/src/erlresolvelinks.erl90
-rw-r--r--system/doc/tutorial/Makefile7
-rw-r--r--system/doc/xml/design_principles/.gitignore0
-rw-r--r--system/doc/xml/efficiency_guide/.gitignore0
-rw-r--r--system/doc/xml/embedded/.gitignore0
-rw-r--r--system/doc/xml/getting_started/.gitignore0
-rw-r--r--system/doc/xml/installation_guide/.gitignore0
-rw-r--r--system/doc/xml/oam/.gitignore0
-rw-r--r--system/doc/xml/programming_examples/.gitignore0
-rw-r--r--system/doc/xml/reference_manual/.gitignore0
-rw-r--r--system/doc/xml/system_architecture_intro/.gitignore0
-rw-r--r--system/doc/xml/system_principles/.gitignore0
-rw-r--r--system/doc/xml/tutorial/.gitignore0
96 files changed, 1449 insertions, 518 deletions
diff --git a/.gitignore b/.gitignore
index 0e9d07757f..b90bda1763 100644
--- a/.gitignore
+++ b/.gitignore
@@ -139,6 +139,7 @@ JAVADOC-GENERATED
/make/output.mk
/make/emd2exml
/make/make_emakefile
+/make/install_dir_data.sh
# Created by "out_build update_primary"
/bootstrap/primary_compiler/
@@ -307,6 +308,7 @@ JAVADOC-GENERATED
/lib/jinterface/doc/html/java
/lib/jinterface/pom.xml
/lib/jinterface/target
+/lib/jinterface/doc/src/jdoc
# kernel
@@ -361,7 +363,6 @@ JAVADOC-GENERATED
/system/doc/html
/system/doc/xml
/system/doc/top/PR.template
-/system/doc/top/erlresolvelinks.js
# test_server
diff --git a/Makefile.in b/Makefile.in
index fa7c128379..749cd27f9b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -434,7 +434,7 @@ endif
PATH=$(BOOT_PREFIX)"$${PATH}" \
ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT="$(RELEASE_ROOT)" DOCGEN=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen $@
ifneq ($(OTP_SMALL_BUILD),true)
- echo "OTP doc built" > $(ERL_TOP)/make/otp_doc_built
+ test -f $(ERL_TOP)/make/otp_doc_built || echo "OTP doc built" > $(ERL_TOP)/make/otp_doc_built
endif
xmllint: docs
@@ -450,7 +450,9 @@ else
$(MAKE) -C system/doc $@
endif
-mod2app:
+mod2app: $(ERL_TOP)/make/$(TARGET)/mod2app.xml
+
+$(ERL_TOP)/make/$(TARGET)/mod2app.xml: erts/doc/src/Makefile lib/*/doc/src/Makefile
PATH=$(BOOT_PREFIX)"$${PATH}" escript $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml
# ----------------------------------------------------------------------
@@ -488,7 +490,7 @@ else
$(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
- $(V_at)echo "OTP built" > $(ERL_TOP)/make/otp_built
+ $(V_at)test -f $(ERL_TOP)/make/otp_built || echo "OTP built" > $(ERL_TOP)/make/otp_built
endif
kernel:
$(make_verbose)cd lib/kernel && \
diff --git a/OTP_VERSION b/OTP_VERSION
index cd23cb3bd7..54a8840717 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-21.2.6
+21.2.7
diff --git a/erts/configure.in b/erts/configure.in
index 9245e4dc90..53e8bd683a 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -4326,7 +4326,6 @@ AC_CONFIG_FILES([../make/make_emakefile:../make/make_emakefile.in],
dnl
dnl The ones below should be moved to their respective lib
dnl
-dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in
AC_CONFIG_FILES([
../lib/os_mon/c_src/$host/Makefile:../lib/os_mon/c_src/Makefile.in
../lib/crypto/c_src/$host/Makefile:../lib/crypto/c_src/Makefile.in
@@ -4334,4 +4333,6 @@ AC_CONFIG_FILES([
../lib/tools/c_src/$host/Makefile:../lib/tools/c_src/Makefile.in
])
+AC_CONFIG_FILES([../make/install_dir_data.sh:../make/install_dir_data.sh.in], [chmod +x ../make/install_dir_data.sh])
+
AC_OUTPUT
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 9ea1beb887..f9720c74de 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,46 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.2.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixes of install/release phase in build system.</p>
+ <list> <item>The source tree was modified when
+ installing/releasing and/or applying a patch.</item>
+ <item>Some files were installed with wrong access
+ rights.</item> <item>If applying a patch (using
+ <c>otp_patch_apply</c>) as another user (except root)
+ than the user that built the source, the documentation
+ was not properly updated.</item> </list>
+ <p>
+ Own Id: OTP-15551</p>
+ </item>
+ <item>
+ <p>
+ Setting the <c>recbuf</c> size of an inet socket the
+ <c>buffer</c> is also automatically increased. Fix a bug
+ where the auto adjustment of inet buffer size would be
+ triggered even if an explicit inet buffer size had
+ already been set.</p>
+ <p>
+ Own Id: OTP-15651 Aux Id: ERIERL-304 </p>
+ </item>
+ <item>
+ <p>
+ Reading from UDP using active <c>true</c> or active
+ <c>N</c> mode has been optimized when more packets than
+ specified by <c>read_packets</c> are available on the
+ socket.</p>
+ <p>
+ Own Id: OTP-15652 Aux Id: ERIERL-304 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.2.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index a231638b50..c5deed38ad 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1946,7 +1946,7 @@ do_allocate_logger_message(Eterm gleader, ErtsMonotonicTime *ts, Eterm *pid,
else
sz += MAP4_SZ /* metadata map w gl w pid*/;
- *ts = ERTS_MONOTONIC_TO_USEC(erts_get_monotonic_time(NULL) + erts_get_time_offset());
+ *ts = ERTS_MONOTONIC_TO_USEC(erts_os_system_time());
erts_bld_sint64(NULL, &sz, *ts);
*bp = new_message_buffer(sz);
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index a6a5c20266..b71ce0389d 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -574,7 +574,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
driver_select(port, e, mode | (on?ERL_DRV_USE:0), on)
#define sock_select(d, flags, onoff) do { \
- ASSERT(!(d)->is_ignored); \
+ ASSERT(!INET_IGNORED(d)); \
(d)->event_mask = (onoff) ? \
((d)->event_mask | (flags)) : \
((d)->event_mask & ~(flags)); \
@@ -898,6 +898,15 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#define INET_CMSG_RECVTCLASS (1 << 1) /* am_recvtclass, am_tclass */
#define INET_CMSG_RECVTTL (1 << 2) /* am_recvttl, am_ttl */
+/* Inet flags */
+#define INET_FLG_BUFFER_SET (1 << 0) /* am_buffer has been set by user */
+#define INET_FLG_IS_IGNORED (1 << 1) /* If a fd is ignored by the inet_drv.
+ This flag should be set to true when
+ the fd is used outside of inet_drv. */
+#define INET_FLG_IS_IGNORED_RD (1 << 2)
+#define INET_FLG_IS_IGNORED_WR (1 << 3)
+#define INET_FLG_IS_IGNORED_PASS (1 << 4)
+
/*
** End of interface constants.
**--------------------------------------------------------------------------*/
@@ -943,10 +952,11 @@ static size_t my_strnlen(const char *s, size_t maxlen)
#define INET_IFNAMSIZ 16
/* INET Ignore states */
-#define INET_IGNORE_NONE 0
-#define INET_IGNORE_READ (1 << 0)
-#define INET_IGNORE_WRITE (1 << 1)
-#define INET_IGNORE_PASSIVE (1 << 2)
+#define INET_IGNORE_CLEAR(desc) ((desc)->flags & ~(INET_IGNORE_READ|INET_IGNORE_WRITE|INET_IGNORE_PASSIVE))
+#define INET_IGNORED(desc) ((desc)->flags & INET_FLG_IS_IGNORED)
+#define INET_IGNORE_READ (INET_FLG_IS_IGNORED|INET_FLG_IS_IGNORED_RD)
+#define INET_IGNORE_WRITE (INET_FLG_IS_IGNORED|INET_FLG_IS_IGNORED_WR)
+#define INET_IGNORE_PASSIVE (INET_FLG_IS_IGNORED|INET_FLG_IS_IGNORED_PASS)
/* Max length of Erlang Term Buffer (for outputting structured terms): */
#ifdef HAVE_SCTP
@@ -1128,9 +1138,7 @@ typedef struct {
double send_avg; /* average packet size sent */
subs_list empty_out_q_subs; /* Empty out queue subscribers */
- int is_ignored; /* if a fd is ignored by the inet_drv.
- This flag should be set to true when
- the fd is used outside of inet_drv. */
+ int flags;
#ifdef HAVE_SETNS
char *netns; /* Socket network namespace name
as full file path */
@@ -4551,7 +4559,7 @@ static void desc_close(inet_descriptor* desc)
* We should close the fd here, but the other driver might still
* be selecting on it.
*/
- if (!desc->is_ignored)
+ if (!INET_IGNORED(desc))
driver_select(desc->port,(ErlDrvEvent)(long)desc->event,
ERL_DRV_USE, 0);
else
@@ -6312,6 +6320,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
(long)desc->port, desc->s, ival));
if (ival < INET_MIN_BUFFER) ival = INET_MIN_BUFFER;
desc->bufsz = ival;
+ desc->flags |= INET_FLG_BUFFER_SET;
continue;
case INET_LOPT_ACTIVE:
@@ -6507,6 +6516,16 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
case INET_OPT_RCVBUF: type = SO_RCVBUF;
DEBUGF(("inet_set_opts(%ld): s=%d, SO_RCVBUF=%d\r\n",
(long)desc->port, desc->s, ival));
+ if (!(desc->flags & INET_FLG_BUFFER_SET)) {
+ /* make sure we have desc->bufsz >= SO_RCVBUF */
+ if (ival > (1 << 16) && desc->stype == SOCK_DGRAM && !IS_SCTP(desc))
+ /* For UDP we don't want to automatically
+ set the buffer size to be larger than
+ the theoretical max MTU */
+ desc->bufsz = 1 << 16;
+ else if (ival > desc->bufsz)
+ desc->bufsz = ival;
+ }
break;
case INET_OPT_LINGER: type = SO_LINGER;
if (len < 4)
@@ -6752,23 +6771,17 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
}
DEBUGF(("inet_set_opts(%ld): s=%d returned %d\r\n",
(long)desc->port, desc->s, res));
- if (type == SO_RCVBUF) {
- /* make sure we have desc->bufsz >= SO_RCVBUF */
- if (ival > (1 << 16) && desc->stype == SOCK_DGRAM && !IS_SCTP(desc))
- /* For UDP we don't want to automatically
- set the buffer size to be larger than
- the theoretical max MTU */
- desc->bufsz = 1 << 16;
- else if (ival > desc->bufsz)
- desc->bufsz = ival;
- }
}
if ( ((desc->stype == SOCK_STREAM) && IS_CONNECTED(desc)) ||
((desc->stype == SOCK_DGRAM) && IS_OPEN(desc))) {
- if (desc->active != old_active)
+ if (desc->active != old_active) {
+ /* Need to cancel the read_packet timer if we go from active to passive. */
+ if (desc->active == INET_PASSIVE && desc->stype == SOCK_DGRAM)
+ driver_cancel_timer(desc->port);
sock_select(desc, (FD_READ|FD_CLOSE), (desc->active>0));
+ }
/* XXX: UDP sockets could also trigger immediate read here NIY */
if ((desc->stype==SOCK_STREAM) && desc->active) {
@@ -6910,6 +6923,7 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
if (desc->bufsz < INET_MIN_BUFFER)
desc->bufsz = INET_MIN_BUFFER;
+ desc->flags |= INET_FLG_BUFFER_SET;
res = 0; /* This does not affect the kernel buffer size */
continue;
@@ -7031,6 +7045,7 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
smaller than the kernel one: */
if (desc->bufsz <= arg.ival)
desc->bufsz = arg.ival;
+ desc->flags |= INET_FLG_BUFFER_SET;
break;
}
case INET_OPT_SNDBUF:
@@ -7041,10 +7056,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
arg_ptr = (char*) (&arg.ival);
arg_sz = sizeof ( arg.ival);
- /* Adjust the size of the user-level recv buffer, so it's not
- smaller than the kernel one: */
- if (desc->bufsz <= arg.ival)
- desc->bufsz = arg.ival;
break;
}
case INET_OPT_REUSEADDR:
@@ -9101,7 +9112,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
sys_memzero((char *)&desc->remote,sizeof(desc->remote));
- desc->is_ignored = 0;
+ desc->flags = 0;
#ifdef HAVE_SETNS
desc->netns = NULL;
@@ -9503,19 +9514,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (desc->stype != SOCK_STREAM)
return ctl_error(EINVAL, rbuf, rsize);
- if (*buf == 1 && !desc->is_ignored) {
+ if (*buf == 1 && !INET_IGNORED(desc)) {
sock_select(desc, (FD_READ|FD_WRITE|FD_CLOSE|ERL_DRV_USE_NO_CALLBACK), 0);
if (desc->active)
- desc->is_ignored = INET_IGNORE_READ;
+ desc->flags |= INET_IGNORE_READ;
else
- desc->is_ignored = INET_IGNORE_PASSIVE;
- } else if (*buf == 0 && desc->is_ignored) {
+ desc->flags |= INET_IGNORE_PASSIVE;
+ } else if (*buf == 0 && INET_IGNORED(desc)) {
int flags = FD_CLOSE;
- if (desc->is_ignored & INET_IGNORE_READ)
+ if (desc->flags & INET_IGNORE_READ)
flags |= FD_READ;
- if (desc->is_ignored & INET_IGNORE_WRITE)
+ if (desc->flags & INET_IGNORE_WRITE)
flags |= FD_WRITE;
- desc->is_ignored = INET_IGNORE_NONE;
+ desc->flags = INET_IGNORE_CLEAR(desc);
if (flags != FD_CLOSE)
sock_select(desc, flags, 1);
} else
@@ -10231,17 +10242,17 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
if (enq_async(INETP(desc), tbuf, TCP_REQ_RECV) < 0)
return ctl_error(EALREADY, rbuf, rsize);
- if (INETP(desc)->is_ignored || tcp_recv(desc, n) == 0) {
+ if (INET_IGNORED(INETP(desc)) || tcp_recv(desc, n) == 0) {
if (timeout == 0)
async_error_am(INETP(desc), am_timeout);
else {
if (timeout != INET_INFINITY)
add_multi_timer(desc, INETP(desc)->port, 0,
timeout, &tcp_inet_recv_timeout);
- if (!INETP(desc)->is_ignored)
+ if (!INET_IGNORED(INETP(desc)))
sock_select(INETP(desc),(FD_READ|FD_CLOSE),1);
else
- INETP(desc)->is_ignored |= INET_IGNORE_READ;
+ INETP(desc)->flags |= INET_IGNORE_READ;
}
}
return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize);
@@ -11108,7 +11119,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
#ifdef DEBUG
long port = (long) desc->inet.port; /* Used after driver_exit() */
#endif
- ASSERT(!INETP(desc)->is_ignored);
+ ASSERT(!INET_IGNORED(INETP(desc)));
DEBUGF(("tcp_inet_input(%ld) {s=%d\r\n", port, desc->inet.s));
/* XXX fprintf(stderr,"tcp_inet_input(%ld) {s=%d}\r\n",(long) desc->inet.port, desc->inet.s); */
if (desc->inet.state == INET_STATE_ACCEPTING) {
@@ -11443,8 +11454,8 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev)
DEBUGF(("tcp_sendv(%ld): s=%d, about to send "LLU","LLU" bytes\r\n",
(long)desc->inet.port, desc->inet.s, (llu_t)h_len, (llu_t)len));
- if (INETP(desc)->is_ignored) {
- INETP(desc)->is_ignored |= INET_IGNORE_WRITE;
+ if (INET_IGNORED(INETP(desc))) {
+ INETP(desc)->flags |= INET_IGNORE_WRITE;
n = 0;
} else if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) {
driver_enqv(ix, ev, 0);
@@ -11479,7 +11490,7 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev)
DEBUGF(("tcp_sendv(%ld): s=%d, Send failed, queuing\r\n",
(long)desc->inet.port, desc->inet.s));
driver_enqv(ix, ev, n);
- if (!INETP(desc)->is_ignored)
+ if (!INET_IGNORED(INETP(desc)))
sock_select(INETP(desc),(FD_WRITE|FD_CLOSE), 1);
}
return 0;
@@ -11548,8 +11559,8 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len)
DEBUGF(("tcp_send(%ld): s=%d, about to send "LLU","LLU" bytes\r\n",
(long)desc->inet.port, desc->inet.s, (llu_t)h_len, (llu_t)len));
- if (INETP(desc)->is_ignored) {
- INETP(desc)->is_ignored |= INET_IGNORE_WRITE;
+ if (INET_IGNORED(INETP(desc))) {
+ INETP(desc)->flags |= INET_IGNORE_WRITE;
n = 0;
} else if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) {
sock_send(desc->inet.s, buf, 0, 0);
@@ -11582,7 +11593,7 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len)
n -= h_len;
driver_enq(ix, ptr+n, len-n);
}
- if (!INETP(desc)->is_ignored)
+ if (!INET_IGNORED(INETP(desc)))
sock_select(INETP(desc),(FD_WRITE|FD_CLOSE), 1);
}
return 0;
@@ -11863,7 +11874,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
int ret = 0;
ErlDrvPort ix = desc->inet.port;
- ASSERT(!INETP(desc)->is_ignored);
+ ASSERT(!INET_IGNORED(INETP(desc)));
DEBUGF(("tcp_inet_output(%ld) {s=%d\r\n",
(long)desc->inet.port, desc->inet.s));
if (desc->inet.state == INET_STATE_CONNECTING) {
@@ -12538,9 +12549,12 @@ static void packet_inet_timeout(ErlDrvData e)
{
udp_descriptor * udesc = (udp_descriptor*) e;
inet_descriptor * desc = INETP(udesc);
- if (!(desc->active))
+ if (!(desc->active)) {
sock_select(desc, FD_READ, 0);
- async_error_am (desc, am_timeout);
+ async_error_am (desc, am_timeout);
+ } else {
+ (void)packet_inet_input(udesc, desc->s);
+ }
}
@@ -12896,6 +12910,15 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event)
sock_select(desc, FD_READ, 1);
}
#endif
+
+ /* We set a timer on the port to trigger now.
+ This emulates a "yield" operation as that is
+ what we want to do here. We do *NOT* do a deselect
+ as that is expensive, instead we check if the
+ socket it still active when the timeout triggers
+ and if it is not, then we just ignore the timeout */
+ driver_set_timer(desc->port, 0);
+
return count;
}
diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c
index afff8f7e54..ed4fe12e8b 100644
--- a/erts/etc/unix/to_erl.c
+++ b/erts/etc/unix/to_erl.c
@@ -245,7 +245,6 @@ int main(int argc, char **argv)
tty_smode.c_iflag =
1*BRKINT |/*Signal interrupt on break.*/
1*IGNPAR |/*Ignore characters with parity errors.*/
- 1*ISTRIP |/*Strip character.*/
0;
#if 0
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 7cb34c2d58..6f5a75a2db 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 7d848ba790..d8c3a26674 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index ffb349cdf3..d3d4a75a11 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index fefdd34292..1605c20f2c 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -297,12 +297,13 @@ check_file_result(Func, Target, {error,Reason}) ->
"Target: " ++ TargetStr ++ ". " ++
"Function: " ++ atom_to_list(Func) ++ ". " ++ Process
end,
- %% this is equal to calling error_logger:error_report/1 which
- %% we don't want to do from code_server during system boot
+ %% This is equal to calling logger:error/2 which
+ %% we don't want to do from code_server during system boot.
+ %% We don't want to call logger:timestamp() either.
logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
#{pid=>self(),
gl=>group_leader(),
- time=>erlang:system_time(microsecond),
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>error_report,
type=>std_error}}},
error
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index bdcdf72c2f..86b4f35ae5 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -483,13 +483,16 @@ do_handle_msg(Msg,State) ->
{From, {ensure_loaded, _}} ->
From ! {init, not_allowed};
X ->
+ %% This is equal to calling logger:info/3 which we don't
+ %% want to do from the init process, at least not during
+ %% system boot. We don't want to call logger:timestamp()
+ %% either.
case whereis(user) of
undefined ->
- Time = erlang:system_time(microsecond),
catch logger ! {log, info, "init got unexpected: ~p", [X],
#{pid=>self(),
gl=>self(),
- time=>Time,
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>info_msg}}};
User ->
User ! X,
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 0994e2a9f4..1aa5d85c64 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -572,12 +572,13 @@ list_dir_convert([RawName | Rest], SkipInvalid, Result) ->
{error, ignore} ->
list_dir_convert(Rest, SkipInvalid, Result);
{error, warning} ->
- %% this is equal to calling error_logger:warning_msg/2 which
- %% we don't want to do from code_server during system boot
+ %% This is equal to calling logger:warning/3 which
+ %% we don't want to do from code_server during system boot.
+ %% We don't want to call logger:timestamp() either.
logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName],
#{pid=>self(),
gl=>group_leader(),
- time=>erlang:system_time(microsecond),
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>warning_msg}}},
list_dir_convert(Rest, SkipInvalid, Result);
{error, _} ->
diff --git a/erts/vsn.mk b/erts/vsn.mk
index e4bdb1a8eb..bab5c805eb 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.2.4
+VSN = 10.2.5
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 56d6085e9c..49b5a4be57 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -114,8 +114,7 @@ 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"
- ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ $(INSTALL_DIR_DATA) $(HTMLDIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/erl_docgen/priv/css/otp_doc.css b/lib/erl_docgen/priv/css/otp_doc.css
index 89b278215c..17d9f8dd56 100644
--- a/lib/erl_docgen/priv/css/otp_doc.css
+++ b/lib/erl_docgen/priv/css/otp_doc.css
@@ -74,7 +74,7 @@ a:visited { color: #1b6ec2; text-decoration: none }
/* Invisible table for function specs,
* just to get since-version out in right margin */
-.func-table, .func-tr, .func-td, .func-since-td {
+.func-table, .func-tr, .func-td, .cfunc-td, .func-since-td {
width: 200%;
border: 0;
padding: 0;
@@ -86,7 +86,13 @@ a:visited { color: #1b6ec2; text-decoration: none }
}
.func-td {
- width: 50%
+ width: 50%;
+}
+
+.cfunc-td {
+ width: 50%;
+ padding-left: 100px;
+ text-indent: -100px;
}
.func-since-td {
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index c5150d447c..c9be926e1e 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -2119,17 +2119,10 @@
<xsl:when test="ancestor::cref">
<table class="func-table">
<tr class="func-tr">
- <td class="func-td">
+ <td class="cfunc-td">
<span class="bold_code bc-7">
<xsl:call-template name="title_link">
<xsl:with-param name="link" select="substring-before(nametext, '(')"/>
- <xsl:with-param name="title">
- <xsl:value-of select="ret"/>
- <xsl:call-template name="maybe-space-after-ret">
- <xsl:with-param name="s" select="ret"/>
- </xsl:call-template>
- <xsl:value-of select="nametext"/>
- </xsl:with-param>
</xsl:call-template>
</span>
</td>
@@ -2199,18 +2192,6 @@
</xsl:template>
- <xsl:template name="maybe-space-after-ret">
- <xsl:param name="s"/>
- <xsl:variable name="last_char"
- select="substring($s, string-length($s), 1)"/>
- <xsl:choose>
- <xsl:when test="$last_char != '*'">
- <xsl:text> </xsl:text>
- </xsl:when>
- </xsl:choose>
- </xsl:template>
-
-
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
@@ -2264,7 +2245,7 @@
</xsl:template>
<xsl:template name="title_link">
- <xsl:param name="title"/>
+ <xsl:param name="title" select="'APPLY'"/>
<xsl:param name="link" select="erl:to-link(title)"/>
<xsl:param name="ghlink" select="ancestor-or-self::*[@ghlink][position() = 1]/@ghlink"/>
<xsl:variable name="id" select="concat(concat($link,'-'), generate-id(.))"/>
@@ -2274,7 +2255,16 @@
<xsl:with-param name="id" select="$id"/>
<xsl:with-param name="ghlink" select="$ghlink"/>
</xsl:call-template>
- <a class="title_link" name="{$link}" href="#{$link}"><xsl:value-of select="$title"/></a>
+ <a class="title_link" name="{$link}" href="#{$link}">
+ <xsl:choose>
+ <xsl:when test="$title = 'APPLY'">
+ <xsl:apply-templates/> <!-- like <ret> and <nametext> -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$title"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </a>
</span>
</xsl:template>
@@ -2704,4 +2694,46 @@
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
+ <xsl:template match="ret">
+ <xsl:value-of select="."/>
+ <xsl:variable name="last_char" select="substring(., string-length(.), 1)"/>
+ <xsl:if test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="nametext">
+ <xsl:value-of select="substring-before(.,'(')"/>
+ <xsl:text>(</xsl:text>
+ <xsl:variable name="arglist" select="substring-after(.,'(')"/>
+ <xsl:choose>
+ <xsl:when test="$arglist = ')' or $arglist = 'void)'">
+ <xsl:value-of select="$arglist"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <br/>
+ <xsl:call-template name="cfunc-arglist">
+ <xsl:with-param name="text" select="$arglist"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Format C function argument list with <br> after comma -->
+ <xsl:template name="cfunc-arglist">
+ <xsl:param name="text"/>
+ <xsl:variable name="line" select="normalize-space($text)"/>
+ <xsl:choose>
+ <xsl:when test="contains($line,',')">
+ <xsl:value-of select="substring-before($line,',')"/>,<br/>
+ <xsl:call-template name="cfunc-arglist">
+ <xsl:with-param name="text" select="substring-after($line,',')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$line"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
</xsl:stylesheet>
diff --git a/lib/ftp/src/ftp.erl b/lib/ftp/src/ftp.erl
index 40f6b53fa3..18cd8c7524 100644
--- a/lib/ftp/src/ftp.erl
+++ b/lib/ftp/src/ftp.erl
@@ -1065,9 +1065,9 @@ handle_call({_, {open, ip_comm, Opts, {CtrlOpts, DataPassOpts, DataActOpts}}}, F
end
end;
-handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) ->
- _ = send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
- activate_ctrl_connection(State),
+handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("AUTH TLS", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From, caller = open, tls_options = TLSOptions}};
handle_call({_, {user, User, Password}}, From,
@@ -1081,17 +1081,17 @@ handle_call({_, {user, User, Password, Acc}}, From,
handle_call({_, {account, Acc}}, From, State)->
handle_user_account(Acc, State#state{client = From});
-handle_call({_, pwd}, From, #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("PWD", [])),
- activate_ctrl_connection(State),
+handle_call({_, pwd}, From, #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("PWD", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From, caller = pwd}};
handle_call({_, lpwd}, From, #state{ldir = LDir} = State) ->
{reply, {ok, LDir}, State#state{client = From}};
-handle_call({_, {cd, Dir}}, From, #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
- activate_ctrl_connection(State),
+handle_call({_, {cd, Dir}}, From, #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("CWD ~s", [Dir])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From, caller = cd}};
handle_call({_,{lcd, Dir}}, _From, #state{ldir = LDir0} = State) ->
@@ -1108,41 +1108,41 @@ handle_call({_, {dir, Len, Dir}}, {_Pid, _} = From,
setup_data_connection(State#state{caller = {dir, Dir, Len},
client = From});
handle_call({_, {rename, CurrFile, NewFile}}, From,
- #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("RNFR ~s", [CurrFile])),
- activate_ctrl_connection(State),
+ #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("RNFR ~s", [CurrFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {rename, NewFile}, client = From}};
handle_call({_, {delete, File}}, {_Pid, _} = From,
- #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("DELE ~s", [File])),
- activate_ctrl_connection(State),
+ #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("DELE ~s", [File])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From}};
-handle_call({_, {mkdir, Dir}}, From, #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("MKD ~s", [Dir])),
- activate_ctrl_connection(State),
+handle_call({_, {mkdir, Dir}}, From, #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("MKD ~s", [Dir])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From}};
-handle_call({_,{rmdir, Dir}}, From, #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("RMD ~s", [Dir])),
- activate_ctrl_connection(State),
+handle_call({_,{rmdir, Dir}}, From, #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("RMD ~s", [Dir])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From}};
-handle_call({_,{type, Type}}, From, #state{chunk = false} = State) ->
+handle_call({_,{type, Type}}, From, #state{chunk = false} = State0) ->
case Type of
ascii ->
- _ = send_ctrl_message(State, mk_cmd("TYPE A", [])),
- activate_ctrl_connection(State),
+ _ = send_ctrl_message(State0, mk_cmd("TYPE A", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = type, type = ascii,
client = From}};
binary ->
- _ = send_ctrl_message(State, mk_cmd("TYPE I", [])),
- activate_ctrl_connection(State),
+ _ = send_ctrl_message(State0, mk_cmd("TYPE I", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = type, type = binary,
client = From}};
_ ->
- {reply, {error, etype}, State}
+ {reply, {error, etype}, State0}
end;
handle_call({_,{recv, RemoteFile, LocalFile}}, From,
@@ -1181,8 +1181,8 @@ handle_call({_, recv_chunk}, _From, #state{chunk = true,
} = State0) ->
%% The ftp:recv_chunk call was the last event we waited for, finnish and clean up
?DBG("recv_chunk_closing ftp:recv_chunk, last event",[]),
- activate_ctrl_connection(State0),
- {reply, ok, State0#state{caller = undefined,
+ State = activate_ctrl_connection(State0),
+ {reply, ok, State#state{caller = undefined,
chunk = false,
client = undefined}};
@@ -1238,18 +1238,18 @@ handle_call({_, {transfer_chunk, Bin}}, _, #state{chunk = true} = State) ->
handle_call({_, {transfer_chunk, _}}, _, #state{chunk = false} = State) ->
{reply, {error, echunk}, State};
-handle_call({_, chunk_end}, From, #state{chunk = true} = State) ->
- close_data_connection(State),
- activate_ctrl_connection(State),
+handle_call({_, chunk_end}, From, #state{chunk = true} = State0) ->
+ close_data_connection(State0),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From, dsock = undefined,
caller = end_chunk_transfer, chunk = false}};
handle_call({_, chunk_end}, _, #state{chunk = false} = State) ->
{reply, {error, echunk}, State};
-handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd(Cmd, [])),
- activate_ctrl_connection(State),
+handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd(Cmd, [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{client = From, caller = quote}};
handle_call({_, _Req}, _From, #state{csock = CSock} = State)
@@ -1325,38 +1325,38 @@ handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when T
Data/binary>>}};
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
- caller = {recv_file, Fd}} = State)
+ caller = {recv_file, Fd}} = State0)
when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
file_close(Fd),
- progress_report({transfer_size, 0}, State),
- activate_ctrl_connection(State),
+ progress_report({transfer_size, 0}, State0),
+ State = activate_ctrl_connection(State0),
?DBG("Data channel close",[]),
{noreply, State#state{dsock = undefined, data = <<>>}};
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
client = Client,
- caller = recv_chunk} = State)
+ caller = recv_chunk} = State0)
when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
?DBG("Data channel close recv_chunk",[]),
- activate_ctrl_connection(State),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{dsock = undefined,
caller = #recv_chunk_closing{dconn_closed = true,
client_called_us = Client =/= undefined}
}};
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin,
- data = Data} = State)
+ data = Data} = State0)
when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
?DBG("Data channel close",[]),
- activate_ctrl_connection(State),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{dsock = undefined, data = <<>>,
caller = {recv_bin, Data}}};
handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data,
- caller = {handle_dir_result, Dir}}
- = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
+ caller = {handle_dir_result, Dir}}
+ = State0) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
?DBG("Data channel close",[]),
- activate_ctrl_connection(State),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{dsock = undefined,
caller = {handle_dir_result, Dir, Data},
% data = <<?CR,?LF>>}};
@@ -1377,7 +1377,7 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
client = From,
ctrl_data = {CtrlData, AccLines,
LineStatus}}
- = State) ->
+ = State0) ->
?DBG('--ctrl ~p ----> ~s~p~n',[Socket,<<CtrlData/binary, Data/binary>>,State]),
case ftp_response:parse_lines(<<CtrlData/binary, Data/binary>>,
AccLines, LineStatus) of
@@ -1387,21 +1387,21 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
case Caller of
quote ->
gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])),
- {noreply, State#state{client = undefined,
- caller = undefined,
- latest_ctrl_response = Lines,
- ctrl_data = {NextMsgData, [],
- start}}};
+ {noreply, State0#state{client = undefined,
+ caller = undefined,
+ latest_ctrl_response = Lines,
+ ctrl_data = {NextMsgData, [],
+ start}}};
_ ->
?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]),
handle_ctrl_result(CtrlResult,
- State#state{latest_ctrl_response = Lines,
- ctrl_data =
- {NextMsgData, [], start}})
+ State0#state{latest_ctrl_response = Lines,
+ ctrl_data =
+ {NextMsgData, [], start}})
end;
{continue, NewCtrlData} ->
?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
- activate_ctrl_connection(State),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{ctrl_data = NewCtrlData}}
end;
@@ -1567,19 +1567,19 @@ start_link(Opts, GenServerOptions) ->
%%% Help functions to handle_call and/or handle_ctrl_result
%%--------------------------------------------------------------------------
%% User handling
-handle_user(User, Password, Acc, State) ->
- _ = send_ctrl_message(State, mk_cmd("USER ~s", [User])),
- activate_ctrl_connection(State),
+handle_user(User, Password, Acc, State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("USER ~s", [User])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {handle_user, Password, Acc}}}.
-handle_user_passwd(Password, Acc, State) ->
- _ = send_ctrl_message(State, mk_cmd("PASS ~s", [Password])),
- activate_ctrl_connection(State),
+handle_user_passwd(Password, Acc, State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("PASS ~s", [Password])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {handle_user_passwd, Acc}}}.
-handle_user_account(Acc, State) ->
- _ = send_ctrl_message(State, mk_cmd("ACCT ~s", [Acc])),
- activate_ctrl_connection(State),
+handle_user_account(Acc, State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("ACCT ~s", [Acc])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = handle_user_account}}.
@@ -1594,9 +1594,9 @@ handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
case ssl:connect(Socket, TLSOptions, Timeout) of
{ok, TLSSocket} ->
- State = State0#state{csock = {ssl,TLSSocket}},
- _ = send_ctrl_message(State, mk_cmd("PBSZ 0", [])),
- activate_ctrl_connection(State),
+ State1 = State0#state{csock = {ssl,TLSSocket}},
+ _ = send_ctrl_message(State1, mk_cmd("PBSZ 0", [])),
+ State = activate_ctrl_connection(State1),
{noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} };
{error, _} = Error ->
gen_server:reply(From, {Error, self()}),
@@ -1605,9 +1605,9 @@ handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
tls_upgrading_data_connection = false}}
end;
-handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) ->
- _ = send_ctrl_message(State, mk_cmd("PROT P", [])),
- activate_ctrl_connection(State),
+handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("PROT P", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{tls_upgrading_data_connection = {true, prot}}};
handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot},
@@ -1801,10 +1801,10 @@ handle_ctrl_result({pos_compl, _}, #state{caller = {handle_dir_result, Dir,
handle_ctrl_result({pos_compl, Lines},
#state{caller = {handle_dir_data, Dir, DirData}} =
- State) ->
+ State0) ->
OldDir = pwd_result(Lines),
- _ = send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),
- activate_ctrl_connection(State),
+ _ = send_ctrl_message(State0, mk_cmd("CWD ~s", [Dir])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {handle_dir_data_second_phase, OldDir,
DirData}}};
handle_ctrl_result({Status, _},
@@ -1818,9 +1818,9 @@ handle_ctrl_result(S={_Status, _},
handle_ctrl_result({pos_compl, _},
#state{caller = {handle_dir_data_second_phase, OldDir,
- DirData}} = State) ->
- _ = send_ctrl_message(State, mk_cmd("CWD ~s", [OldDir])),
- activate_ctrl_connection(State),
+ DirData}} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("CWD ~s", [OldDir])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {handle_dir_data_third_phase, DirData}}};
handle_ctrl_result({Status, _},
#state{caller = {handle_dir_data_second_phase, _, _}}
@@ -1840,9 +1840,9 @@ handle_ctrl_result(Status={epath, _}, #state{caller = {dir,_}} = State) ->
%%--------------------------------------------------------------------------
%% File renaming
handle_ctrl_result({pos_interm, _}, #state{caller = {rename, NewFile}}
- = State) ->
- _ = send_ctrl_message(State, mk_cmd("RNTO ~s", [NewFile])),
- activate_ctrl_connection(State),
+ = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("RNTO ~s", [NewFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = rename_second_phase}};
handle_ctrl_result({Status, _},
@@ -1916,10 +1916,10 @@ handle_ctrl_result({pos_compl, _}, #state{client = From,
%% The pos_compl was the last event we waited for, finnish and clean up
?DBG("recv_chunk_closing pos_compl, last event",[]),
gen_server:reply(From, ok),
- activate_ctrl_connection(State0),
- {noreply, State0#state{caller = undefined,
- chunk = false,
- client = undefined}};
+ State = activate_ctrl_connection(State0),
+ {noreply, State#state{caller = undefined,
+ chunk = false,
+ client = undefined}};
handle_ctrl_result({pos_compl, _}, #state{caller = #recv_chunk_closing{}=R}
= State0) ->
@@ -2013,71 +2013,71 @@ ctrl_result_response(_, #state{client = From} = State, ErrorMsg) ->
{noreply, State#state{client = undefined, caller = undefined}}.
%%--------------------------------------------------------------------------
-handle_caller(#state{caller = {dir, Dir, Len}} = State) ->
+handle_caller(#state{caller = {dir, Dir, Len}} = State0) ->
Cmd = case Len of
short -> "NLST";
long -> "LIST"
end,
_ = case Dir of
"" ->
- send_ctrl_message(State, mk_cmd(Cmd, ""));
+ send_ctrl_message(State0, mk_cmd(Cmd, ""));
_ ->
- send_ctrl_message(State, mk_cmd(Cmd ++ " ~s", [Dir]))
+ send_ctrl_message(State0, mk_cmd(Cmd ++ " ~s", [Dir]))
end,
- activate_ctrl_connection(State),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {dir, Dir}}};
-handle_caller(#state{caller = {recv_bin, RemoteFile}} = State) ->
- _ = send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
- activate_ctrl_connection(State),
+handle_caller(#state{caller = {recv_bin, RemoteFile}} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("RETR ~s", [RemoteFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = recv_bin}};
handle_caller(#state{caller = {start_chunk_transfer, Cmd, RemoteFile}} =
- State) ->
- _ = send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
- activate_ctrl_connection(State),
+ State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("~s ~s", [Cmd, RemoteFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = start_chunk_transfer}};
-handle_caller(#state{caller = {recv_file, RemoteFile, Fd}} = State) ->
- _ = send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),
- activate_ctrl_connection(State),
+handle_caller(#state{caller = {recv_file, RemoteFile, Fd}} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("RETR ~s", [RemoteFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {recv_file, Fd}}};
handle_caller(#state{caller = {transfer_file, {Cmd, LocalFile, RemoteFile}},
- ldir = LocalDir, client = From} = State) ->
+ ldir = LocalDir, client = From} = State0) ->
case file_open(filename:absname(LocalFile, LocalDir), read) of
{ok, Fd} ->
- _ = send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
- activate_ctrl_connection(State),
+ _ = send_ctrl_message(State0, mk_cmd("~s ~s", [Cmd, RemoteFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {transfer_file, Fd}}};
{error, _} ->
gen_server:reply(From, {error, epath}),
- {noreply, State#state{client = undefined, caller = undefined,
- dsock = undefined}}
+ {noreply, State0#state{client = undefined, caller = undefined,
+ dsock = undefined}}
end;
handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} =
- State) ->
- _ = send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
- activate_ctrl_connection(State),
+ State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("~s ~s", [Cmd, RemoteFile])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {transfer_data, Bin}}}.
%% ----------- FTP SERVER COMMUNICATION -------------------------
%% Connect to FTP server at Host (default is TCP port 21)
%% in order to establish a control connection.
-setup_ctrl_connection(Host, Port, Timeout, #state{sockopts_ctrl = SockOpts} = State) ->
+setup_ctrl_connection(Host, Port, Timeout, #state{sockopts_ctrl = SockOpts} = State0) ->
MsTime = erlang:monotonic_time(),
- case connect(Host, Port, SockOpts, Timeout, State) of
+ case connect(Host, Port, SockOpts, Timeout, State0) of
{ok, IpFam, CSock} ->
- NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam},
- activate_ctrl_connection(NewState),
+ State1 = State0#state{csock = {tcp, CSock}, ipfamily = IpFam},
+ State = activate_ctrl_connection(State1),
case Timeout - millisec_passed(MsTime) of
Timeout2 when (Timeout2 >= 0) ->
- {ok, NewState#state{caller = open}, Timeout2};
+ {ok, State#state{caller = open}, Timeout2};
_ ->
%% Oups: Simulate timeout
- {ok, NewState#state{caller = open}, 0}
+ {ok, State#state{caller = open}, 0}
end;
Error ->
Error
@@ -2087,7 +2087,7 @@ setup_data_connection(#state{mode = active,
caller = Caller,
csock = CSock,
sockopts_data_active = SockOpts,
- ftp_extension = FtpExt} = State) ->
+ ftp_extension = FtpExt} = State0) ->
case (catch sockname(CSock)) of
{ok, {{_, _, _, _, _, _, _, _} = IP0, _}} ->
IP = proplists:get_value(ip, SockOpts, IP0),
@@ -2098,8 +2098,8 @@ setup_data_connection(#state{mode = active,
{ok, {_, Port}} = sockname({tcp,LSock}),
IpAddress = inet_parse:ntoa(IP),
Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]),
- _ = send_ctrl_message(State, Cmd),
- activate_ctrl_connection(State),
+ _ = send_ctrl_message(State0, Cmd),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}};
{ok, {{_,_,_,_} = IP0, _}} ->
@@ -2112,37 +2112,37 @@ setup_data_connection(#state{mode = active,
false ->
{IP1, IP2, IP3, IP4} = IP,
{Port1, Port2} = {Port div 256, Port rem 256},
- send_ctrl_message(State,
+ send_ctrl_message(State0,
mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
[IP1, IP2, IP3, IP4, Port1, Port2]));
true ->
IpAddress = inet_parse:ntoa(IP),
Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
- send_ctrl_message(State, Cmd)
+ send_ctrl_message(State0, Cmd)
end,
- activate_ctrl_connection(State),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}}
end;
setup_data_connection(#state{mode = passive, ipfamily = inet6,
- caller = Caller} = State) ->
- _ = send_ctrl_message(State, mk_cmd("EPSV", [])),
- activate_ctrl_connection(State),
+ caller = Caller} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("EPSV", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {setup_data_connection, Caller}}};
setup_data_connection(#state{mode = passive, ipfamily = inet,
caller = Caller,
- ftp_extension = false} = State) ->
- _ = send_ctrl_message(State, mk_cmd("PASV", [])),
- activate_ctrl_connection(State),
+ ftp_extension = false} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("PASV", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {setup_data_connection, Caller}}};
setup_data_connection(#state{mode = passive, ipfamily = inet,
caller = Caller,
- ftp_extension = true} = State) ->
- _ = send_ctrl_message(State, mk_cmd("EPSV", [])),
- activate_ctrl_connection(State),
+ ftp_extension = true} = State0) ->
+ _ = send_ctrl_message(State0, mk_cmd("EPSV", [])),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = {setup_data_connection, Caller}}}.
connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet = IpFam}) ->
@@ -2248,14 +2248,15 @@ send_message({tcp, Socket}, Message) ->
send_message({ssl, Socket}, Message) ->
ssl:send(Socket, Message).
-activate_ctrl_connection(#state{csock = CSock, ctrl_data = {<<>>, _, _}}) ->
- activate_connection(CSock);
-activate_ctrl_connection(#state{csock = CSock}) ->
+activate_ctrl_connection(#state{csock = CSock, ctrl_data = {<<>>, _, _}} = State) ->
+ activate_connection(CSock),
+ State;
+activate_ctrl_connection(#state{csock = CSock} = State0) ->
activate_connection(CSock),
%% We have already received at least part of the next control message,
%% that has been saved in ctrl_data, process this first.
- self() ! {socket_type(CSock), unwrap_socket(CSock), <<>>},
- ok.
+ {noreply, State} = handle_info({socket_type(CSock), unwrap_socket(CSock), <<>>}, State0),
+ State.
activate_data_connection(#state{dsock = DSock} = State) ->
activate_connection(DSock),
@@ -2290,22 +2291,22 @@ close_connection({ssl, Socket}) -> ignore_return_value( ssl:close(Socket) ).
%% ------------ FILE HANDLING ----------------------------------------
send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) ->
{noreply, State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_file, Fd}}};
-send_file(State, Fd) ->
+send_file(State0, Fd) ->
case file_read(Fd) of
{ok, N, Bin} when N > 0 ->
- send_data_message(State, Bin),
- progress_report({binary, Bin}, State),
- send_file(State, Fd);
+ send_data_message(State0, Bin),
+ progress_report({binary, Bin}, State0),
+ send_file(State0, Fd);
{ok, _, _} ->
file_close(Fd),
- close_data_connection(State),
- progress_report({transfer_size, 0}, State),
- activate_ctrl_connection(State),
+ close_data_connection(State0),
+ progress_report({transfer_size, 0}, State0),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = transfer_file_second_phase,
dsock = undefined}};
{error, Reason} ->
- gen_server:reply(State#state.client, {error, Reason}),
- {stop, normal, State#state{client = undefined}}
+ gen_server:reply(State0#state.client, {error, Reason}),
+ {stop, normal, State0#state{client = undefined}}
end.
file_open(File, Option) ->
@@ -2347,10 +2348,10 @@ cast(GenServer, Msg) ->
send_bin(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Bin) ->
State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_bin, Bin}};
-send_bin(State, Bin) ->
- send_data_message(State, Bin),
- close_data_connection(State),
- activate_ctrl_connection(State),
+send_bin(State0, Bin) ->
+ send_data_message(State0, Bin),
+ close_data_connection(State0),
+ State = activate_ctrl_connection(State0),
{noreply, State#state{caller = transfer_data_second_phase,
dsock = undefined}}.
diff --git a/lib/ftp/test/ftp_SUITE.erl b/lib/ftp/test/ftp_SUITE.erl
index 7c87d5cbdb..81147b5821 100644
--- a/lib/ftp/test/ftp_SUITE.erl
+++ b/lib/ftp/test/ftp_SUITE.erl
@@ -669,9 +669,9 @@ recv_chunk(Config0) ->
Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
Config = set_state([reset, {mkfile,File,Contents}], Config0),
Pid = proplists:get_value(ftp, Config),
- {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+ {error, "ftp:recv_chunk_start/2 not called"} = do_recv_chunk(Pid),
ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)),
- {ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>),
+ {ok, ReceivedContents} = do_recv_chunk(Pid),
find_diff(ReceivedContents, Contents).
recv_chunk_twice() ->
@@ -683,11 +683,11 @@ recv_chunk_twice(Config0) ->
Contents2 = crypto:strong_rand_bytes(1200),
Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}], Config0),
Pid = proplists:get_value(ftp, Config),
- {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+ {error, "ftp:recv_chunk_start/2 not called"} = do_recv_chunk(Pid),
ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)),
- {ok, ReceivedContents1, _Ncunks1} = recv_chunk(Pid, <<>>),
+ {ok, ReceivedContents1} = do_recv_chunk(Pid),
ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)),
- {ok, ReceivedContents2, _Ncunks2} = recv_chunk(Pid, <<>>),
+ {ok, ReceivedContents2} = do_recv_chunk(Pid),
find_diff(ReceivedContents1, Contents1),
find_diff(ReceivedContents2, Contents2).
@@ -704,47 +704,50 @@ recv_chunk_three_times(Config0) ->
Config = set_state([reset, {mkfile,File1,Contents1}, {mkfile,File2,Contents2}, {mkfile,File3,Contents3}], Config0),
Pid = proplists:get_value(ftp, Config),
- {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+ {error, "ftp:recv_chunk_start/2 not called"} = do_recv_chunk(Pid),
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File3,Config)),
+ {ok, ReceivedContents3} = do_recv_chunk(Pid),
+
ok = ftp:recv_chunk_start(Pid, id2ftp(File1,Config)),
- {ok, ReceivedContents1, Nchunks1} = recv_chunk(Pid, <<>>),
+ {ok, ReceivedContents1} = do_recv_chunk(Pid),
ok = ftp:recv_chunk_start(Pid, id2ftp(File2,Config)),
- {ok, ReceivedContents2, _Nchunks2} = recv_chunk(Pid, <<>>),
-
- ok = ftp:recv_chunk_start(Pid, id2ftp(File3,Config)),
- {ok, ReceivedContents3, _Nchunks3} = recv_chunk(Pid, <<>>, 10000, 0, Nchunks1),
+ {ok, ReceivedContents2} = do_recv_chunk(Pid),
find_diff(ReceivedContents1, Contents1),
find_diff(ReceivedContents2, Contents2),
find_diff(ReceivedContents3, Contents3).
-
+do_recv_chunk(Pid) ->
+ recv_chunk(Pid, <<>>).
recv_chunk(Pid, Acc) ->
- recv_chunk(Pid, Acc, 0, 0, undefined).
-
-
-
-%% ExpectNchunks :: integer() | undefined
-recv_chunk(Pid, Acc, DelayMilliSec, N, ExpectNchunks) when N+1 < ExpectNchunks ->
- %% for all I in integer(), I < undefined
- recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks);
-
-recv_chunk(Pid, Acc, DelayMilliSec, N, ExpectNchunks) ->
- %% N >= ExpectNchunks-1
- timer:sleep(DelayMilliSec),
- recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks).
-
-
-recv_chunk1(Pid, Acc, DelayMilliSec, N, ExpectNchunks) ->
- ct:log("Call ftp:recv_chunk",[]),
case ftp:recv_chunk(Pid) of
- ok -> {ok, Acc, N};
- {ok, Bin} -> recv_chunk(Pid, <<Acc/binary, Bin/binary>>, DelayMilliSec, N+1, ExpectNchunks);
- Error -> {Error, N}
+ ok ->
+ {ok, Acc};
+ {ok, Bin} ->
+ recv_chunk(Pid, <<Acc/binary, Bin/binary>>);
+ Error ->
+ Error
end.
+%% Make new test case that uses this or new code
+%% only test one thing at the time
+%% delay_recv_chunk(Pid) ->
+%% delay_recv_chunk(Pid, <<>>).
+%% delay_recv_chunk(Pid, Acc) ->
+%% ct:pal("FOO ~p", [byte_size(Acc)]),
+%% case ftp:recv_chunk(Pid) of
+%% ok ->
+%% {ok, Acc};
+%% {ok, Bin} ->
+%% ct:sleep(100),
+%% delay_recv_chunk(Pid, <<Acc/binary, Bin/binary>>);
+%% Error ->
+%% Error
+%% end.
+
%%-------------------------------------------------------------------------
type() ->
[{doc,"Test that we can change btween ASCCI and binary transfer mode"}].
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index d80bb38ec1..f5cba9d074 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -137,6 +137,7 @@ clean clean_docs:
jdoc:$(JAVA_SRC_FILES)
(cd ../../java_src;$(JAVADOC) -sourcepath . -d $(JAVADOC_DEST) \
-windowtitle $(JAVADOC_TITLE) $(JAVADOC_PKGS))
+ touch jdoc
man:
@@ -152,15 +153,8 @@ 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/html/java/$(JAVA_PKG_PATH)"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- ($(CP) -rf ../html "$(RELSYSDIR)/doc")
-
-# $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
-# "$(RELSYSDIR)/doc/html"
-# $(INSTALL_DATA) $(JAVA_EXTRA_FILES) "$(RELSYSDIR)/doc/html/java"
-# $(INSTALL_DATA) $(TOP_HTML_FILES) "$(RELSYSDIR)/doc"
+ $(INSTALL_DIR_DATA) $(HTMLDIR) "$(RELSYSDIR)/doc/html"
release_spec:
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 4e32c1a3a5..5170502581 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -238,6 +238,41 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
</desc>
</func>
<func>
+ <name name="set_env" arity="1" since="OTP @OTP-15642@"/>
+ <name name="set_env" arity="2" since="OTP @OTP-15642@"/>
+ <fsummary>Sets the configuration parameters of multiple applications.</fsummary>
+ <desc>
+ <p>Sets the configuration <c><anno>Config</anno></c> for multiple
+ applications. It is equivalent to calling <c>set_env/4</c> on
+ each application individially, except it is more efficient.
+ The given <c><anno>Config</anno></c> is validated before the
+ configuration is set.</p>
+ <p><c>set_env/2</c> uses the standard <c>gen_server</c> time-out
+ value (5000 ms). Option <c>timeout</c> can be specified
+ if another time-out value is useful, for example, in situations
+ where the application controller is heavily loaded.</p>
+ <p>Option <c>persistent</c> can be set to <c>true</c>
+ to guarantee that parameters set with <c>set_env/2</c>
+ are not overridden by those defined in the application resource
+ file on load. This means that persistent values will stick after the application
+ is loaded and also on application reload.</p>
+ <p>If an application is given more than once or if an application
+ has the same key given more than once, the behaviour is undefined
+ and a warning message will be logged. In future releases, an error
+ will be raised.</p>
+ <p><c>set_env/1</c> is equivalent to <c>set_env(Config, [])</c>.</p>
+ <warning>
+ <p>Use this function only if you know what you are doing,
+ that is, on your own applications. It is very
+ application-dependent and
+ configuration parameter-dependent when and how often
+ the value is read by the application. Careless use
+ of this function can put the application in a
+ weird, inconsistent, and malfunctioning state.</p>
+ </warning>
+ </desc>
+ </func>
+ <func>
<name name="permit" arity="2" since=""/>
<fsummary>Change the permission for an application to run at a node.</fsummary>
<desc>
diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml
index df2d081d76..e6448e144e 100644
--- a/lib/kernel/doc/src/logger.xml
+++ b/lib/kernel/doc/src/logger.xml
@@ -190,7 +190,7 @@ logger:error("error happened because: ~p", [Reason]). % Without macro
<list>
<item><c>pid => self()</c></item>
<item><c>gl => group_leader()</c></item>
- <item><c>time => erlang:system_time(microsecond)</c></item>
+ <item><c>time => logger:timestamp()</c></item>
</list>
<p>When a log macro is used, Logger also inserts location
information:</p>
@@ -288,8 +288,8 @@ logger:error("error happened because: ~p", [Reason]). % Without macro
<name name="timestamp"/>
<desc>
<p>A timestamp produced
- with <seealso marker="erts:erlang#system_time-1">
- <c>erlang:system_time(microsecond)</c></seealso>.</p>
+ with <seealso marker="#timestamp-0">
+ <c>logger:timestamp()</c></seealso>.</p>
</desc>
</datatype>
</datatypes>
@@ -1117,6 +1117,24 @@ logger:set_proxy_config(maps:merge(Old, Config)).
a key-value list before formatting as such.</p>
</desc>
</func>
+
+ <func>
+ <name name="timestamp" arity="0" since="OTP @OTP-15625@"/>
+ <fsummary>Return a timestamp to insert in meta data for a log
+ event.</fsummary>
+ <desc>
+ <p>Return a timestamp that can be inserted as the <c>time</c>
+ field in the meta data for a log event. It is produced with
+ <seealso marker="kernel:os#system_time-1">
+ <c>os:system_time(microsecond)</c></seealso>.</p>
+ <p>Notice that Logger automatically inserts a timestamp in the
+ meta data unless it already exists. This function is
+ exported for the rare case when the timestamp must be taken
+ at a different point in time than when the log event is
+ issued.</p>
+ </desc>
+ </func>
+
</funcs>
<section>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 021ecfa40d..1f4d9be1f5 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,25 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 6.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Setting the <c>recbuf</c> size of an inet socket the
+ <c>buffer</c> is also automatically increased. Fix a bug
+ where the auto adjustment of inet buffer size would be
+ triggered even if an explicit inet buffer size had
+ already been set.</p>
+ <p>
+ Own Id: OTP-15651 Aux Id: ERIERL-304 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 6.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index bc6be2f8f5..5c2e981e4b 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -25,7 +25,7 @@
which_applications/0, which_applications/1,
loaded_applications/0, permit/2]).
-export([ensure_started/1, ensure_started/2]).
--export([set_env/3, set_env/4, unset_env/2, unset_env/3]).
+-export([set_env/1, set_env/2, set_env/3, set_env/4, unset_env/2, unset_env/3]).
-export([get_env/1, get_env/2, get_env/3, get_all_env/0, get_all_env/1]).
-export([get_key/1, get_key/2, get_all_key/0, get_all_key/1]).
-export([get_application/0, get_application/1, info/0]).
@@ -279,6 +279,26 @@ loaded_applications() ->
info() ->
application_controller:info().
+-spec set_env(Config) -> 'ok' when
+ Config :: [{Application, Env}],
+ Application :: atom(),
+ Env :: [{Par :: atom(), Val :: term()}].
+
+set_env(Config) when is_list(Config) ->
+ set_env(Config, []).
+
+-spec set_env(Config, Opts) -> 'ok' when
+ Config :: [{Application, Env}],
+ Application :: atom(),
+ Env :: [{Par :: atom(), Val :: term()}],
+ Opts :: [{timeout, timeout()} | {persistent, boolean()}].
+
+set_env(Config, Opts) when is_list(Config), is_list(Opts) ->
+ case application_controller:set_env(Config, Opts) of
+ ok -> ok;
+ {error, Msg} -> erlang:error({badarg, Msg}, [Config, Opts])
+ end.
+
-spec set_env(Application, Par, Val) -> 'ok' when
Application :: atom(),
Par :: atom(),
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index a074d2e74b..9a8091fb2e 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -26,7 +26,7 @@
control_application/1,
change_application_data/2, prep_config_change/0, config_change/1,
which_applications/0, which_applications/1,
- loaded_applications/0, info/0,
+ loaded_applications/0, info/0, set_env/2,
get_pid_env/2, get_env/2, get_pid_all_env/1, get_all_env/1,
get_pid_key/2, get_key/2, get_pid_all_key/1, get_all_key/1,
get_master/1, get_application/1, get_application_module/1,
@@ -345,9 +345,6 @@ get_all_env(AppName) ->
map(fun([Key, Val]) -> {Key, Val} end,
ets:match(ac_tab, {{env, AppName, '$1'}, '$2'})).
-
-
-
get_pid_key(Master, Key) ->
case ets:match(ac_tab, {{application_master, '$1'}, Master}) of
[[AppName]] -> get_key(AppName, Key);
@@ -461,6 +458,15 @@ permit_application(ApplName, Flag) ->
{permit_application, ApplName, Flag},
infinity).
+set_env(Config, Opts) ->
+ case check_conf_data(Config) of
+ ok ->
+ Timeout = proplists:get_value(timeout, Opts, 5000),
+ gen_server:call(?AC, {set_env, Config, Opts}, Timeout);
+
+ {error, _} = Error ->
+ Error
+ end.
set_env(AppName, Key, Val) ->
gen_server:call(?AC, {set_env, AppName, Key, Val, []}).
@@ -528,19 +534,17 @@ check_conf_data([]) ->
check_conf_data(ConfData) when is_list(ConfData) ->
[Application | ConfDataRem] = ConfData,
case Application of
- {kernel, List} when is_list(List) ->
- case check_para_kernel(List) of
- ok ->
- check_conf_data(ConfDataRem);
- Error1 ->
- Error1
- end;
{AppName, List} when is_atom(AppName), is_list(List) ->
- case check_para(List, atom_to_list(AppName)) of
- ok ->
- check_conf_data(ConfDataRem);
- Error2 ->
- Error2
+ case lists:keymember(AppName, 1, ConfDataRem) of
+ true ->
+ ?LOG_WARNING("duplicate application config: " ++ atom_to_list(AppName));
+ false ->
+ ok
+ end,
+
+ case check_para(List, AppName) of
+ ok -> check_conf_data(ConfDataRem);
+ Error -> Error
end;
{AppName, List} when is_list(List) ->
ErrMsg = "application: "
@@ -553,36 +557,40 @@ check_conf_data(ConfData) when is_list(ConfData) ->
++ "; parameters must be a list",
{error, ErrMsg};
Else ->
- ErrMsg = "invalid application name: " ++
- lists:flatten(io_lib:format(" ~tp",[Else])),
+ ErrMsg = "invalid application config: "
+ ++ lists:flatten(io_lib:format("~tp",[Else])),
{error, ErrMsg}
end;
check_conf_data(_ConfData) ->
- {error, 'configuration must be a list ended by <dot><whitespace>'}.
-
+ {error, "configuration must be a list ended by <dot><whitespace>"}.
-%% Special check of distributed parameter for kernel
-check_para_kernel([]) ->
+
+check_para([], _AppName) ->
ok;
-check_para_kernel([{distributed, Apps} | ParaList]) when is_list(Apps) ->
- case check_distributed(Apps) of
- {error, _ErrorMsg} = Error ->
- Error;
- _ ->
- check_para_kernel(ParaList)
+check_para([{Para, Val} | ParaList], AppName) when is_atom(Para) ->
+ case lists:keymember(Para, 1, ParaList) of
+ true ->
+ ?LOG_WARNING("application: " ++ atom_to_list(AppName) ++
+ "; duplicate parameter: " ++ atom_to_list(Para));
+ false ->
+ ok
+ end,
+
+ case check_para_value(Para, Val, AppName) of
+ ok -> check_para(ParaList, AppName);
+ {error, _} = Error -> Error
end;
-check_para_kernel([{distributed, _Apps} | _ParaList]) ->
- {error, "application: kernel; erroneous parameter: distributed"};
-check_para_kernel([{Para, _Val} | ParaList]) when is_atom(Para) ->
- check_para_kernel(ParaList);
-check_para_kernel([{Para, _Val} | _ParaList]) ->
- {error, "application: kernel; invalid parameter: " ++
+check_para([{Para, _Val} | _ParaList], AppName) ->
+ {error, "application: " ++ atom_to_list(AppName) ++ "; invalid parameter name: " ++
lists:flatten(io_lib:format("~tp",[Para]))};
-check_para_kernel(Else) ->
- {error, "application: kernel; invalid parameter list: " ++
+check_para([Else | _ParaList], AppName) ->
+ {error, "application: " ++ atom_to_list(AppName) ++ "; invalid parameter: " ++
lists:flatten(io_lib:format("~tp",[Else]))}.
-
+check_para_value(distributed, Apps, kernel) -> check_distributed(Apps);
+check_para_value(_Para, _Val, _AppName) -> ok.
+
+%% Special check of distributed parameter for kernel
check_distributed([]) ->
ok;
check_distributed([{App, List} | Apps]) when is_atom(App), is_list(List) ->
@@ -595,18 +603,6 @@ check_distributed(_Else) ->
{error, "application: kernel; erroneous parameter: distributed"}.
-check_para([], _AppName) ->
- ok;
-check_para([{Para, _Val} | ParaList], AppName) when is_atom(Para) ->
- check_para(ParaList, AppName);
-check_para([{Para, _Val} | _ParaList], AppName) ->
- {error, "application: " ++ AppName ++ "; invalid parameter: " ++
- lists:flatten(io_lib:format("~tp",[Para]))};
-check_para([Else | _ParaList], AppName) ->
- {error, "application: " ++ AppName ++ "; invalid parameter: " ++
- lists:flatten(io_lib:format("~tp",[Else]))}.
-
-
-type calls() :: 'info' | 'prep_config_change' | 'which_applications'
| {'config_change' | 'control_application' |
'load_application' | 'start_type' | 'stop_application' |
@@ -863,6 +859,16 @@ handle_call(which_applications, _From, S) ->
end, S#state.running),
{reply, Reply, S};
+handle_call({set_env, Config, Opts}, _From, S) ->
+ _ = [add_env(AppName, Env) || {AppName, Env} <- Config],
+
+ case proplists:get_value(persistent, Opts, false) of
+ true ->
+ {reply, ok, S#state{conf_data = merge_env(S#state.conf_data, Config)}};
+ false ->
+ {reply, ok, S}
+ end;
+
handle_call({set_env, AppName, Key, Val, Opts}, _From, S) ->
ets:insert(ac_tab, {{env, AppName, Key}, Val}),
case proplists:get_value(persistent, Opts, false) of
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 1b4a67ecb7..68e1205301 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -1434,19 +1434,25 @@ all_loaded(Db) ->
-spec error_msg(io:format(), [term()]) -> 'ok'.
error_msg(Format, Args) ->
+ %% This is equal to calling logger:error/3 which we don't want to
+ %% do from code_server. We don't want to call logger:timestamp()
+ %% either.
logger ! {log,error,Format,Args,
#{pid=>self(),
gl=>group_leader(),
- time=>erlang:system_time(microsecond),
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>error}}},
ok.
-spec info_msg(io:format(), [term()]) -> 'ok'.
info_msg(Format, Args) ->
+ %% This is equal to calling logger:info/3 which we don't want to
+ %% do from code_server. We don't want to call logger:timestamp()
+ %% either.
logger ! {log,info,Format,Args,
#{pid=>self(),
gl=>group_leader(),
- time=>erlang:system_time(microsecond),
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>info_msg}}},
ok.
diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl
index 6cbb6ac2da..3f5a2ea5ee 100644
--- a/lib/kernel/src/inet_db.erl
+++ b/lib/kernel/src/inet_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1223,7 +1223,10 @@ handle_set_file(Option, Fname, TagTm, TagInfo, ParseFun, From,
{ok, B, _} -> B;
_ -> <<>>
end;
- _ -> <<>>
+ _ ->
+ ets:insert(Db, {TagInfo, undefined}),
+ TimeZero = - (?RES_FILE_UPDATE_TM + 1), % Early enough
+ ets:insert(Db, {TagTm, TimeZero})
end,
handle_set_file(ParseFun, Bin, From, State);
false -> {reply,error,State}
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index a1d9e8e215..6bd5518a37 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -148,6 +148,6 @@
{logger_sasl_compatible, false}
]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-10.1", "stdlib-3.5", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-10.2.5", "stdlib-3.5", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index ccf0a82ced..66fbcbf78d 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -40,7 +40,9 @@
{<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^6\\.1$">>,[restart_new_emulator]},
{<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
- {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
+ {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^6\\.2$">>,[restart_new_emulator]},
+ {<<"^6\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
[{<<"^5\\.3$">>,[restart_new_emulator]},
{<<"^5\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^5\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -54,4 +56,6 @@
{<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^6\\.1$">>,[restart_new_emulator]},
{<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
- {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
+ {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^6\\.2$">>,[restart_new_emulator]},
+ {<<"^6\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index 7d36640f52..38bd2f481c 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -61,6 +61,7 @@
-export([set_process_metadata/1, update_process_metadata/1,
unset_process_metadata/0, get_process_metadata/0]).
-export([i/0, i/1]).
+-export([timestamp/0]).
%% Basic report formatting
-export([format_report/1, format_otp_report/1]).
@@ -154,7 +155,8 @@
filter_return/0,
config_handler/0,
formatter_config/0,
- olp_config/0]).
+ olp_config/0,
+ timestamp/0]).
%%%-----------------------------------------------------------------
%%% API
@@ -354,6 +356,10 @@ internal_log(Level,Term) when is_atom(Level) ->
erlang:display_string("Logger - "++ atom_to_list(Level) ++ ": "),
erlang:display(Term).
+-spec timestamp() -> timestamp().
+timestamp() ->
+ os:system_time(microsecond).
+
%%%-----------------------------------------------------------------
%%% Configuration
-spec add_primary_filter(FilterId,Filter) -> ok | {error,term()} when
@@ -1129,7 +1135,7 @@ proc_meta() ->
default(pid) -> self();
default(gl) -> group_leader();
-default(time) -> erlang:system_time(microsecond).
+default(time) -> timestamp().
%% Remove everything upto and including this module from the stacktrace
filter_stacktrace(Module,[{Module,_,_,_}|_]) ->
diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl
index e69f6de38d..4b5e0a7dd0 100644
--- a/lib/kernel/src/logger_h_common.erl
+++ b/lib/kernel/src/logger_h_common.erl
@@ -351,7 +351,7 @@ log_handler_info(Name, Format, Args, #{module:=Module,
{ok,Conf} -> Conf;
_ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}
end,
- Meta = #{time=>erlang:system_time(microsecond)},
+ Meta = #{time=>logger:timestamp()},
Bin = log_to_binary(#{level => notice,
msg => {Format,Args},
meta => Meta}, Config),
diff --git a/lib/kernel/src/logger_olp.erl b/lib/kernel/src/logger_olp.erl
index 009280a9c9..8365383fe2 100644
--- a/lib/kernel/src/logger_olp.erl
+++ b/lib/kernel/src/logger_olp.erl
@@ -515,10 +515,11 @@ check_load(State = #{id:=_Name, mode_ref := ModeRef, mode := Mode,
end,
State1 = ?update_other(drops,DROPS,_NewDrops,State),
State2 = ?update_max_qlen(QLen,State1),
- State3 = maybe_notify_mode_change(Mode1,State2),
+ State3 = ?update_max_mem(Mem,State2),
+ State4 = maybe_notify_mode_change(Mode1,State3),
{Mode1, QLen, Mem,
?update_other(flushes,FLUSHES,_NewFlushes,
- State3#{last_qlen => QLen})}.
+ State4#{last_qlen => QLen})}.
limit_burst(#{burst_limit_enable := false}=State) ->
{true,State};
diff --git a/lib/kernel/src/logger_olp.hrl b/lib/kernel/src/logger_olp.hrl
index 9b4f5ebf27..d68b5c048d 100644
--- a/lib/kernel/src/logger_olp.hrl
+++ b/lib/kernel/src/logger_olp.hrl
@@ -114,12 +114,16 @@
flushes => 0, flushed => 0, drops => 0,
burst_drops => 0, casts => 0, calls => 0,
writes => 0, max_qlen => 0, max_time => 0,
- freq => {TIME,0,0}} end).
+ max_mem => 0, freq => {TIME,0,0}} end).
-define(update_max_qlen(QLEN, STATE),
begin #{max_qlen := QLEN0} = STATE,
STATE#{max_qlen => ?max(QLEN0,QLEN)} end).
+ -define(update_max_mem(MEM, STATE),
+ begin #{max_mem := MEM0} = STATE,
+ STATE#{max_mem => ?max(MEM0,MEM)} end).
+
-define(update_calls_or_casts(CALL_OR_CAST, INC, STATE),
case CALL_OR_CAST of
cast ->
@@ -154,6 +158,7 @@
-else. % DEFAULT!
-define(merge_with_stats(STATE), STATE).
-define(update_max_qlen(_QLEN, STATE), STATE).
+ -define(update_max_mem(_MEM, STATE), STATE).
-define(update_calls_or_casts(_CALL_OR_CAST, _INC, STATE), STATE).
-define(update_max_time(_TIME, STATE), STATE).
-define(update_other(_OTHER, _VAR, _INCVAL, STATE), STATE).
diff --git a/lib/kernel/src/logger_simple_h.erl b/lib/kernel/src/logger_simple_h.erl
index fe181722f3..a0d51dba25 100644
--- a/lib/kernel/src/logger_simple_h.erl
+++ b/lib/kernel/src/logger_simple_h.erl
@@ -69,7 +69,7 @@ log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) ->
do_log(
#{level=>error,
msg=>{report,{error,simple_handler_process_dead}},
- meta=>#{time=>erlang:system_time(microsecond)}}),
+ meta=>#{time=>logger:timestamp()}}),
do_log(Log);
_ ->
?MODULE ! {log,Log}
@@ -129,7 +129,7 @@ drop_msg(0) ->
drop_msg(N) ->
[#{level=>info,
msg=>{"Simple handler buffer full, dropped ~w messages",[N]},
- meta=>#{time=>erlang:system_time(microsecond)}}].
+ meta=>#{time=>logger:timestamp()}}].
%%%-----------------------------------------------------------------
%%% Internal
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 5c35b82207..94d7c17712 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -31,6 +31,7 @@
otp_3002/1, otp_3184/1, otp_4066/1, otp_4227/1, otp_5363/1,
otp_5606/1,
start_phases/1, get_key/1, get_env/1,
+ set_env/1, set_env_persistent/1, set_env_errors/1,
permit_false_start_local/1, permit_false_start_dist/1, script_start/1,
nodedown_start/1, init2973/0, loop2973/0, loop5606/1]).
@@ -55,6 +56,7 @@ all() ->
load_use_cache, ensure_started, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
permit_false_start_dist, get_key, get_env, ensure_all_started,
+ set_env, set_env_persistent, set_env_errors,
{group, distr_changed}, config_change, shutdown_func, shutdown_timeout,
shutdown_deadlock, config_relative_paths,
persistent_env].
@@ -1944,6 +1946,101 @@ get_appls([_ | T], Res) ->
get_appls([], Res) ->
Res.
+%% Test set_env/1.
+set_env(Conf) when is_list(Conf) ->
+ ok = application:set_env([{appinc, [{own2, persist}, {not_in_app, persist}]},
+ {unknown_app, [{key, persist}]}]),
+
+ %% own_env1 and own2 are set in appinc
+ undefined = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, not_in_app),
+ {ok, persist} = application:get_env(unknown_app, key),
+
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, val2} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, not_in_app),
+ {ok, persist} = application:get_env(unknown_app, key),
+
+ %% On reload, values are lost
+ ok = application:unload(appinc),
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, val2} = application:get_env(appinc, own2),
+ undefined = application:get_env(appinc, not_in_app),
+
+ %% Clean up
+ ok = application:unload(appinc).
+
+%% Test set_env/2 with persistent true.
+set_env_persistent(Conf) when is_list(Conf) ->
+ Opts = [{persistent, true}],
+ ok = application:set_env([{appinc, [{own2, persist}, {not_in_app, persist}]},
+ {unknown_app, [{key, persist}]}], Opts),
+
+ %% own_env1 and own2 are set in appinc
+ undefined = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, not_in_app),
+ {ok, persist} = application:get_env(unknown_app, key),
+
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, not_in_app),
+ {ok, persist} = application:get_env(unknown_app, key),
+
+ %% On reload, values are not lost
+ ok = application:unload(appinc),
+ ok = application:load(appinc()),
+ {ok, value1} = application:get_env(appinc, own_env1),
+ {ok, persist} = application:get_env(appinc, own2),
+ {ok, persist} = application:get_env(appinc, not_in_app),
+
+ %% Clean up
+ ok = application:unload(appinc).
+
+set_env_errors(Conf) when is_list(Conf) ->
+ "application: 1; application name must be an atom" =
+ badarg_msg(fun() -> application:set_env([{1, []}]) end),
+
+ "application: foo; parameters must be a list" =
+ badarg_msg(fun() -> application:set_env([{foo, bar}]) end),
+
+ "invalid application config: foo_bar" =
+ badarg_msg(fun() -> application:set_env([foo_bar]) end),
+
+ "application: foo; invalid parameter name: 1" =
+ badarg_msg(fun() -> application:set_env([{foo, [{1, 2}]}]) end),
+
+ "application: foo; invalid parameter: config" =
+ badarg_msg(fun() -> application:set_env([{foo, [config]}]) end),
+
+ "application: kernel; erroneous parameter: distributed" =
+ badarg_msg(fun() -> application:set_env([{kernel, [{distributed, config}]}]) end),
+
+ %% This will raise in the future
+ ct:capture_start(),
+ _ = application:set_env([{foo, []}, {foo, []}]),
+ timer:sleep(100),
+ ct:capture_stop(),
+ [_ | _] = string:find(ct:capture_get(), "duplicate application config: foo"),
+
+ ct:capture_start(),
+ _ = application:set_env([{foo, [{bar, baz}, {bar, bat}]}]),
+ timer:sleep(100),
+ ct:capture_stop(),
+ [_ | _] = string:find(ct:capture_get(), "application: foo; duplicate parameter: bar"),
+
+ ok.
+
+badarg_msg(Fun) ->
+ try Fun() of
+ _ -> ct:fail(try_succeeded)
+ catch
+ error:{badarg, Msg} -> Msg
+ end.
%% Test set_env/4 and unset_env/3 with persistent true.
persistent_env(Conf) when is_list(Conf) ->
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index f436eafad3..5a2d809aa4 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -21,6 +21,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/inet.hrl").
+-include_lib("kernel/src/inet_res.hrl").
-include_lib("kernel/src/inet_dns.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
@@ -34,7 +35,7 @@
ipv4_to_ipv6/0, ipv4_to_ipv6/1,
host_and_addr/0, host_and_addr/1,
t_gethostnative/1,
- gethostnative_parallell/1, cname_loop/1,
+ gethostnative_parallell/1, cname_loop/1, missing_hosts_reload/1,
gethostnative_soft_restart/0, gethostnative_soft_restart/1,
gethostnative_debug_level/0, gethostnative_debug_level/1,
lookup_bad_search_option/1,
@@ -56,7 +57,7 @@ all() ->
[t_gethostbyaddr, t_gethostbyname, t_getaddr,
t_gethostbyaddr_v6, t_gethostbyname_v6, t_getaddr_v6,
ipv4_to_ipv6, host_and_addr, {group, parse},
- t_gethostnative, gethostnative_parallell, cname_loop,
+ t_gethostnative, gethostnative_parallell, cname_loop, missing_hosts_reload,
gethostnative_debug_level, gethostnative_soft_restart,
lookup_bad_search_option,
getif, getif_ifr_name_overflow, getservbyname_overflow,
@@ -840,6 +841,32 @@ cname_loop(Config) when is_list(Config) ->
ok.
+%% Test that hosts file gets reloaded correctly in case when it
+% was missing during initial startup
+missing_hosts_reload(Config) when is_list(Config) ->
+ RootDir = proplists:get_value(priv_dir,Config),
+ HostsFile = filename:join(RootDir, atom_to_list(?MODULE) ++ ".hosts"),
+ InetRc = filename:join(RootDir, "inetrc"),
+ ok = file:write_file(InetRc, "{hosts_file, \"" ++ HostsFile ++ "\"}.\n"),
+ {error, enoent} = file:read_file_info(HostsFile),
+ % start a node
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok, TestNode} = test_server:start_node(?MODULE, slave,
+ [{args, "-pa " ++ Pa ++ " -kernel inetrc '\"" ++ InetRc ++ "\"'"}]),
+ % ensure it has our RC
+ Rc = rpc:call(TestNode, inet_db, get_rc, []),
+ {hosts_file, HostsFile} = lists:keyfind(hosts_file, 1, Rc),
+ % ensure it does not resolve
+ {error, nxdomain} = rpc:call(TestNode, inet_hosts, gethostbyname, ["somehost"]),
+ % write hosts file
+ ok = file:write_file(HostsFile, "1.2.3.4 somehost"),
+ % wait for cached timestamp to expire
+ timer:sleep(?RES_FILE_UPDATE_TM * 1000 + 100),
+ % ensure it DOES resolve
+ {ok,{hostent,"somehost",[],inet,4,[{1,2,3,4}]}} =
+ rpc:call(TestNode, inet_hosts, gethostbyname, ["somehost"]),
+ % cleanup
+ true = test_server:stop_node(TestNode).
%% These must be run in the whole suite since they need
%% the host list and require inet_gethost_native to be started.
diff --git a/lib/kernel/test/kernel.spec b/lib/kernel/test/kernel.spec
index 62afc9f97b..eaa17f3a59 100644
--- a/lib/kernel/test/kernel.spec
+++ b/lib/kernel/test/kernel.spec
@@ -2,3 +2,4 @@
{config, "../test_server/ts.unix.config"}.
{suites,"../kernel_test", all}.
+{skip_suites,"../kernel_test",[logger_stress_SUITE],"Benchmarks only"}.
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index 2dad651f9c..70bb775db8 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -899,14 +899,14 @@ process_metadata(_Config) ->
undefined = logger:get_process_metadata(),
{error,badarg} = ?TRY(logger:set_process_metadata(bad)),
ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}),
- Time = erlang:system_time(microsecond),
+ Time = logger:timestamp(),
ProcMeta = #{time=>Time,line=>0,custom=>proc},
ok = logger:set_process_metadata(ProcMeta),
S1 = ?str,
?LOG_NOTICE(S1,#{custom=>macro}),
check_logged(notice,S1,#{time=>Time,line=>0,custom=>macro}),
- Time2 = erlang:system_time(microsecond),
+ Time2 = logger:timestamp(),
S2 = ?str,
?LOG_NOTICE(S2,#{time=>Time2,line=>1,custom=>macro}),
check_logged(notice,S2,#{time=>Time2,line=>1,custom=>macro}),
diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl
index 8c13f0f908..83e3e6c40a 100644
--- a/lib/kernel/test/logger_formatter_SUITE.erl
+++ b/lib/kernel/test/logger_formatter_SUITE.erl
@@ -867,7 +867,7 @@ my_try(Fun) ->
try Fun() catch C:R:S -> {C,R,hd(S)} end.
timestamp() ->
- erlang:system_time(microsecond).
+ logger:timestamp().
%% necessary?
add_time(#{time:=_}=Meta) ->
diff --git a/lib/kernel/test/logger_stress_SUITE.erl b/lib/kernel/test/logger_stress_SUITE.erl
index 4072e8c86a..1a278fb1b2 100644
--- a/lib/kernel/test/logger_stress_SUITE.erl
+++ b/lib/kernel/test/logger_stress_SUITE.erl
@@ -67,6 +67,10 @@ all() ->
reject_events,
std_handler,
disk_log_handler,
+ std_handler_time,
+ std_handler_time_big,
+ disk_log_handler_time,
+ disk_log_handler_time_big,
emulator_events,
remote_events,
remote_to_disk_log,
@@ -123,6 +127,20 @@ std_handler(cleanup,_Config) ->
_ = file:delete("default.log"),
ok.
+%% Disable overload protection and just print a lot - measure time.
+%% The IOPS reported is the number of log events written per millisecond.
+std_handler_time(Config) ->
+ measure_handler_time(logger_std_h,#{type=>{file,"default.log"}},Config).
+std_handler_time(cleanup,_Config) ->
+ _ = file:delete("default.log"),
+ ok.
+
+std_handler_time_big(Config) ->
+ measure_handler_time_big(logger_std_h,#{type=>{file,"default.log"}},Config).
+std_handler_time_big(cleanup,_Config) ->
+ _ = file:delete("default.log"),
+ ok.
+
%% Cascading failure that produce gen_server and proc_lib reports -
%% how many of the produced log events are actually written to a log
%% with logger_disk_log_h wrap file handler.
@@ -140,6 +158,20 @@ disk_log_handler(cleanup,_Config) ->
[_ = file:delete(F) || F <- Files],
ok.
+%% Disable overload protection and just print a lot - measure time.
+%% The IOPS reported is the number of log events written per millisecond.
+disk_log_handler_time(Config) ->
+ measure_handler_time(logger_disk_log_h,#{type=>halt},Config).
+disk_log_handler_time(cleanup,_Config) ->
+ _ = file:delete("default"),
+ ok.
+
+disk_log_handler_time_big(Config) ->
+ measure_handler_time_big(logger_disk_log_h,#{type=>halt},Config).
+disk_log_handler_time_big(cleanup,_Config) ->
+ _ = file:delete("default"),
+ ok.
+
%% Cascading failure that produce log events from the emulator - how
%% many of the produced log events pass through the proxy.
emulator_events(Config) ->
@@ -221,6 +253,68 @@ remote_emulator_to_disk_log(cleanup,_Config) ->
%%%-----------------------------------------------------------------
%%% Internal functions
+measure_handler_time(Module,HCfg,Config) ->
+ measure_handler_time(Module,100000,small_fa(),millisecond,HCfg,#{},Config).
+
+measure_handler_time_big(Module,HCfg,Config) ->
+ FCfg = #{chars_limit=>4096, max_size=>1024},
+ measure_handler_time(Module,100,big_fa(),second,HCfg,FCfg,Config).
+
+measure_handler_time(Module,N,FA,Unit,HCfg,FCfg,Config) ->
+ {ok,_,Node} =
+ logger_test_lib:setup(
+ Config,
+ [{logger,
+ [{handler,default,Module,
+ #{formatter => {logger_formatter,
+ maps:merge(#{legacy_header=>false,
+ single_line=>true},FCfg)},
+ config=>maps:merge(#{sync_mode_qlen => N+1,
+ drop_mode_qlen => N+1,
+ flush_qlen => N+1,
+ burst_limit_enable => false,
+ filesync_repeat_interval => no_repeat},
+ HCfg)}}]}]),
+ %% HPid = rpc:call(Node,erlang,whereis,[?name_to_reg_name(Module,default)]),
+ %% {links,[_,FCPid]} = rpc:call(Node,erlang,process_info,[HPid,links]),
+ T0 = erlang:monotonic_time(millisecond),
+ ok = rpc:call(Node,?MODULE,nlogs_wait,[N,FA,Module]),
+ %% ok = rpc:call(Node,fprof,apply,[fun ?MODULE:nlogs_wait/2,[N div 10,FA,Module],[{procs,[HPid,FCPid]}]]),
+ T1 = erlang:monotonic_time(millisecond),
+ T = T1-T0,
+ M = case Unit of
+ millisecond -> 1;
+ second -> 1000
+ end,
+ IOPS = M*N/T,
+ ct:pal("N: ~p~nT: ~p~nIOPS: ~.2f events pr ~w",[N,T,IOPS,Unit]),
+ %% Stats = rpc:call(Node,logger_olp,info,[?name_to_reg_name(Module,default)]),
+ %% ct:pal("Stats: ~p",[Stats]),
+ ct_event:notify(#event{name = benchmark_data,
+ data = [{value,IOPS}]}),
+ {comment,io_lib:format("~.2f events written pr ~w",[IOPS,Unit])}.
+
+nlogs_wait(N,{F,A},Module) ->
+ group_leader(whereis(user),self()),
+ [?LOG_NOTICE(F,A) || _ <- lists:seq(1,N)],
+ wait(Module).
+
+wait(Module) ->
+ case Module:filesync(default) of
+ {error,handler_busy} ->
+ wait(Module);
+ ok ->
+ ok
+ end.
+
+small_fa() ->
+ Str = "\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "[\\]^_`abcdefghijklmnopqr",
+ {"~ts",[Str]}.
+
+big_fa() ->
+ {"~p",[lists:duplicate(1048576,"a")]}.
+
nlogs(N) ->
group_leader(whereis(user),self()),
Str = "\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 4b43c6ae9d..6e0eea7824 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 6.2
+KERNEL_VSN = 6.2.1
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 67ec93809f..90a9181ede 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -89,9 +89,12 @@
<datatype>
<name name="active_msgs"/>
<desc>
- <p>When an TLS/DTLS socket is in active mode (the default), data from the
+ <p>When a TLS/DTLS socket is in active mode (the default), data from the
socket is delivered to the owner of the socket in the form of
messages as described above.</p>
+ <p>The <c>ssl_passive</c> message is sent only when the socket is in
+ <c>{active, N}</c> mode and the counter dropped to 0. It indicates
+ that the socket has transitioned to passive (<c>{active, false}</c>) mode.</p>
</desc>
</datatype>
@@ -1171,8 +1174,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
</p>
- <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
- process owning the sslsocket will receive messages of type
+ <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value,
+ the process owning the sslsocket will receive messages of type
<seealso marker="#type-active_msgs"> active_msgs() </seealso>
</p>
</desc>
@@ -1219,8 +1222,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
</p>
- <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
- process owning the sslsocket will receive messages of type
+ <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value,
+ the process owning the sslsocket will receive messages of type
<seealso marker="#type-active_msgs"> active_msgs() </seealso>
</p>
</desc>
@@ -1381,8 +1384,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p>Performs the SSL/TLS/DTLS server-side handshake.</p>
<p>Returns a new TLS/DTLS socket if the handshake is successful.</p>
- <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
- process owning the sslsocket will receive messages of type
+ <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value,
+ the process owning the sslsocket will receive messages of type
<seealso marker="#type-active_msgs"> active_msgs() </seealso>
</p>
</desc>
@@ -1426,8 +1429,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
marker="#handshake_cancel-1"><c>handshake_cancel/1</c></seealso>.
</p>
- <p> If the option <c>active</c> is set to <c>once</c> or <c>true</c> the
- process owning the sslsocket will receive messages of type
+ <p> If the option <c>active</c> is set to <c>once</c>, <c>true</c> or an integer value,
+ the process owning the sslsocket will receive messages of type
<seealso marker="#type-active_msgs"> active_msgs() </seealso>
</p>
diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl
index dccc22a448..092366b7c0 100644
--- a/lib/ssl/src/dtls_packet_demux.erl
+++ b/lib/ssl/src/dtls_packet_demux.erl
@@ -297,6 +297,9 @@ do_set_emulated_opts([], Opts) ->
Opts;
do_set_emulated_opts([{mode, Value} | Rest], Opts) ->
do_set_emulated_opts(Rest, Opts#socket_options{mode = Value});
+do_set_emulated_opts([{active, N0} | Rest], Opts=#socket_options{active = Active}) when is_integer(N0) ->
+ N = tls_socket:update_active_n(N0, Active),
+ do_set_emulated_opts(Rest, Opts#socket_options{active = N});
do_set_emulated_opts([{active, Value} | Rest], Opts) ->
do_set_emulated_opts(Rest, Opts#socket_options{active = Value}).
diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl
index 2001afd02f..4d07372e31 100644
--- a/lib/ssl/src/dtls_socket.erl
+++ b/lib/ssl/src/dtls_socket.erl
@@ -38,7 +38,9 @@ listen(Port, #config{transport_info = TransportInfo,
case dtls_listener_sup:start_child([Port, TransportInfo, emulated_socket_options(EmOpts, #socket_options{}),
Options ++ internal_inet_values(), SslOpts]) of
{ok, Pid} ->
- {ok, #sslsocket{pid = {dtls, Config#config{dtls_handler = {Pid, Port}}}}};
+ Socket = #sslsocket{pid = {dtls, Config#config{dtls_handler = {Pid, Port}}}},
+ check_active_n(EmOpts, Socket),
+ {ok, Socket};
Err = {error, _} ->
Err
end.
@@ -81,8 +83,9 @@ socket(Pids, Transport, Socket, ConnectionCb) ->
#sslsocket{pid = Pids,
%% "The name "fd" is keept for backwards compatibility
fd = {Transport, Socket, ConnectionCb}}.
-setopts(_, #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) ->
- SplitOpts = tls_socket:split_options(Options),
+setopts(_, Socket = #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) ->
+ SplitOpts = {_, EmOpts} = tls_socket:split_options(Options),
+ check_active_n(EmOpts, Socket),
dtls_packet_demux:set_sock_opts(ListenPid, SplitOpts);
%%% Following clauses will not be called for emulated options, they are handled in the connection process
setopts(gen_udp, Socket, Options) ->
@@ -90,6 +93,32 @@ setopts(gen_udp, Socket, Options) ->
setopts(Transport, Socket, Options) ->
Transport:setopts(Socket, Options).
+check_active_n(EmulatedOpts, Socket = #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}) ->
+ %% We check the resulting options to send an ssl_passive message if necessary.
+ case proplists:lookup(active, EmulatedOpts) of
+ %% The provided value is out of bound.
+ {_, N} when is_integer(N), N < -32768 ->
+ throw(einval);
+ {_, N} when is_integer(N), N > 32767 ->
+ throw(einval);
+ {_, N} when is_integer(N) ->
+ {ok, #socket_options{active = Active}, _} = dtls_packet_demux:get_all_opts(ListenPid),
+ case Active of
+ Atom when is_atom(Atom), N =< 0 ->
+ self() ! {ssl_passive, Socket};
+ %% The result of the addition is out of bound.
+ %% We do not need to check < -32768 because Active can't be below 1.
+ A when is_integer(A), A + N > 32767 ->
+ throw(einval);
+ A when is_integer(A), A + N =< 0 ->
+ self() ! {ssl_passive, Socket};
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
getopts(_, #sslsocket{pid = {dtls, #config{dtls_handler = {ListenPid, _}}}}, Options) ->
SplitOpts = tls_socket:split_options(Options),
dtls_packet_demux:get_sock_opts(ListenPid, SplitOpts);
@@ -161,9 +190,18 @@ emulated_socket_options(InetValues, #socket_options{
mode = proplists:get_value(mode, InetValues, Mode),
packet = proplists:get_value(packet, InetValues, Packet),
packet_size = proplists:get_value(packet_size, InetValues, PacketSize),
- active = proplists:get_value(active, InetValues, Active)
+ active = emulated_active_option(InetValues, Active)
}.
+emulated_active_option([], Active) ->
+ Active;
+emulated_active_option([{active, Active} | _], _) when Active =< 0 ->
+ false;
+emulated_active_option([{active, Active} | _], _) ->
+ Active;
+emulated_active_option([_|Tail], Active) ->
+ emulated_active_option(Tail, Active).
+
emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
validate_inet_option(mode, Value),
emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
@@ -185,6 +223,9 @@ validate_inet_option(mode, Value)
when Value =/= list, Value =/= binary ->
throw({error, {options, {mode,Value}}});
validate_inet_option(active, Value)
+ when Value >= -32768, Value =< 32767 ->
+ ok;
+validate_inet_option(active, Value)
when Value =/= true, Value =/= false, Value =/= once ->
throw({error, {options, {active,Value}}});
validate_inet_option(_, _) ->
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 50e74d5eb7..fb6b7ba8e8 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -92,7 +92,7 @@
-type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option().
-type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option().
-type active_msgs() :: {ssl, sslsocket(), Data::binary() | list()} | {ssl_closed, sslsocket()} |
- {ssl_error, sslsocket(), Reason::term()}.
+ {ssl_error, sslsocket(), Reason::term()} | {ssl_passive, sslsocket()}.
-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(),
ClosedTag::atom(), ErrTag::atom()}}.
-type host() :: hostname() | ip_address().
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 9e037313bb..24fb091ddf 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1381,10 +1381,22 @@ handle_call({get_opts, OptTags}, From, _,
{keep_state_and_data, [{reply, From, OptsReply}]};
handle_call({set_opts, Opts0}, From, StateName,
#state{static_env = #static_env{socket = Socket,
- transport_cb = Transport},
+ transport_cb = Transport,
+ tracker = Tracker},
+ connection_env =
+ #connection_env{user_application = {_Mon, Pid}},
socket_options = Opts1
} = State0, Connection) ->
{Reply, Opts} = set_socket_opts(Connection, Transport, Socket, Opts0, Opts1, []),
+ case {proplists:lookup(active, Opts0), Opts} of
+ {{_, N}, #socket_options{active=false}} when is_integer(N) ->
+ send_user(
+ Pid,
+ format_passive(
+ Connection:pids(State0), Transport, Socket, Tracker, Connection));
+ _ ->
+ ok
+ end,
State = State0#state{socket_options = Opts},
handle_active_option(Opts#socket_options.active, StateName, From, Reply, State);
@@ -2455,6 +2467,30 @@ set_socket_opts(ConnectionCb, Transport, Socket, [{active, Active}| Opts], SockO
Active == false ->
set_socket_opts(ConnectionCb, Transport, Socket, Opts,
SockOpts#socket_options{active = Active}, Other);
+set_socket_opts(ConnectionCb, Transport, Socket, [{active, Active1} = Opt| Opts],
+ SockOpts=#socket_options{active = Active0}, Other)
+ when Active1 >= -32768, Active1 =< 32767 ->
+ Active = if
+ is_integer(Active0), Active0 + Active1 < -32768 ->
+ error;
+ is_integer(Active0), Active0 + Active1 =< 0 ->
+ false;
+ is_integer(Active0), Active0 + Active1 > 32767 ->
+ error;
+ Active1 =< 0 ->
+ false;
+ is_integer(Active0) ->
+ Active0 + Active1;
+ true ->
+ Active1
+ end,
+ case Active of
+ error ->
+ {{error, {options, {socket_options, Opt}} }, SockOpts};
+ _ ->
+ set_socket_opts(ConnectionCb, Transport, Socket, Opts,
+ SockOpts#socket_options{active = Active}, Other)
+ end;
set_socket_opts(_,_, _, [{active, _} = Opt| _], SockOpts, _) ->
{{error, {options, {socket_options, Opt}} }, SockOpts};
set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other) ->
@@ -2750,6 +2786,14 @@ deliver_app_data(
case Active of
once ->
SO#socket_options{active=false};
+ 1 ->
+ send_user(
+ Pid,
+ format_passive(
+ CPids, Transport, Socket, Tracker, Connection)),
+ SO#socket_options{active=false};
+ N when is_integer(N) ->
+ SO#socket_options{active=N - 1};
_ ->
SO
end.
@@ -2786,6 +2830,9 @@ do_format_reply(list, Packet, _, Data)
do_format_reply(list, _,_, Data) ->
binary_to_list(Data).
+format_passive(CPids, Transport, Socket, Tracker, Connection) ->
+ {ssl_passive, Connection:socket(CPids, Transport, Socket, Tracker)}.
+
header(0, <<>>) ->
<<>>;
header(_, <<>>) ->
diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl
index a391bc53de..c3c41d3e12 100644
--- a/lib/ssl/src/tls_socket.erl
+++ b/lib/ssl/src/tls_socket.erl
@@ -32,6 +32,7 @@
emulated_socket_options/2, get_emulated_opts/1,
set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2,
handle_info/2, code_change/3]).
+-export([update_active_n/2]).
-record(state, {
emulated_opts,
@@ -51,7 +52,9 @@ listen(Transport, Port, #config{transport_info = {Transport, _, _, _},
case Transport:listen(Port, Options ++ internal_inet_values()) of
{ok, ListenSocket} ->
{ok, Tracker} = inherit_tracker(ListenSocket, EmOpts, SslOpts),
- {ok, #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}}};
+ Socket = #sslsocket{pid = {ListenSocket, Config#config{emulated = Tracker}}},
+ check_active_n(EmOpts, Socket),
+ {ok, Socket};
Err = {error, _} ->
Err
end.
@@ -117,14 +120,16 @@ socket(Pids, Transport, Socket, ConnectionCb, Tracker) ->
#sslsocket{pid = Pids,
%% "The name "fd" is keept for backwards compatibility
fd = {Transport, Socket, ConnectionCb, Tracker}}.
-setopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+setopts(gen_tcp, Socket = #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
{SockOpts, EmulatedOpts} = split_options(Options),
ok = set_emulated_opts(Tracker, EmulatedOpts),
+ check_active_n(EmulatedOpts, Socket),
inet:setopts(ListenSocket, SockOpts);
-setopts(_, #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_},
+setopts(_, Socket = #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_},
emulated = Tracker}}}, Options) ->
{SockOpts, EmulatedOpts} = split_options(Options),
ok = set_emulated_opts(Tracker, EmulatedOpts),
+ check_active_n(EmulatedOpts, Socket),
Transport:setopts(ListenSocket, SockOpts);
%%% Following clauses will not be called for emulated options, they are handled in the connection process
setopts(gen_tcp, Socket, Options) ->
@@ -132,6 +137,31 @@ setopts(gen_tcp, Socket, Options) ->
setopts(Transport, Socket, Options) ->
Transport:setopts(Socket, Options).
+check_active_n(EmulatedOpts, Socket = #sslsocket{pid = {_, #config{emulated = Tracker}}}) ->
+ %% We check the resulting options to send an ssl_passive message if necessary.
+ case proplists:lookup(active, EmulatedOpts) of
+ %% The provided value is out of bound.
+ {_, N} when is_integer(N), N < -32768 ->
+ throw(einval);
+ {_, N} when is_integer(N), N > 32767 ->
+ throw(einval);
+ {_, N} when is_integer(N) ->
+ case get_emulated_opts(Tracker, [active]) of
+ [{_, false}] ->
+ self() ! {ssl_passive, Socket},
+ ok;
+ %% The result of the addition is out of bound.
+ [{_, A}] when is_integer(A), A < -32768 ->
+ throw(einval);
+ [{_, A}] when is_integer(A), A > 32767 ->
+ throw(einval);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
getopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
{SockOptNames, EmulatedOptNames} = split_options(Options),
EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
@@ -209,7 +239,7 @@ start_link(Port, SockOpts, SslOpts) ->
init([Port, Opts, SslOpts]) ->
process_flag(trap_exit, true),
true = link(Port),
- {ok, #state{emulated_opts = Opts, port = Port, ssl_opts = SslOpts}}.
+ {ok, #state{emulated_opts = do_set_emulated_opts(Opts, []), port = Port, ssl_opts = SslOpts}}.
%%--------------------------------------------------------------------
-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}.
@@ -304,9 +334,24 @@ split_options([Name | Opts], Emu, SocketOptNames, EmuOptNames) ->
do_set_emulated_opts([], Opts) ->
Opts;
+do_set_emulated_opts([{active, N0} | Rest], Opts) when is_integer(N0) ->
+ N = update_active_n(N0, proplists:get_value(active, Opts, false)),
+ do_set_emulated_opts(Rest, [{active, N} | proplists:delete(active, Opts)]);
do_set_emulated_opts([{Name,_} = Opt | Rest], Opts) ->
do_set_emulated_opts(Rest, [Opt | proplists:delete(Name, Opts)]).
+update_active_n(New, Current) ->
+ if
+ is_integer(Current), New + Current =< 0 ->
+ false;
+ is_integer(Current) ->
+ New + Current;
+ New =< 0 ->
+ false;
+ true ->
+ New
+ end.
+
get_socket_opts(_, [], _) ->
[];
get_socket_opts(ListenSocket, SockOptNames, Cb) ->
@@ -366,6 +411,9 @@ validate_inet_option(header, Value)
when not is_integer(Value) ->
throw({error, {options, {header,Value}}});
validate_inet_option(active, Value)
+ when Value >= -32768, Value =< 32767 ->
+ ok;
+validate_inet_option(active, Value)
when Value =/= true, Value =/= false, Value =/= once ->
throw({error, {options, {active,Value}}});
validate_inet_option(_, _) ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 0a66dfa0b1..e86d2eaf9e 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -164,6 +164,7 @@ api_tests() ->
accept_pool,
prf,
socket_options,
+ active_n,
cipher_suites,
handshake_continue,
handshake_continue_timeout,
@@ -243,6 +244,7 @@ error_handling_tests()->
[close_transport_accept,
recv_active,
recv_active_once,
+ recv_active_n,
recv_error_handling,
call_in_error_state,
close_in_error_state,
@@ -1978,7 +1980,7 @@ recv_active(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
recv_active_once() ->
- [{doc,"Test recv on active socket"}].
+ [{doc,"Test recv on active (once) socket"}].
recv_active_once(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
@@ -2003,6 +2005,178 @@ recv_active_once(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+recv_active_n() ->
+ [{doc,"Test recv on active (n) socket"}].
+
+recv_active_n(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, 1} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, 1} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+%% Test case adapted from gen_tcp_misc_SUITE.
+active_n() ->
+ [{doc,"Test {active,N} option"}].
+
+active_n(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ Port = ssl_test_lib:inet_port(node()),
+ N = 3,
+ LS = ok(ssl:listen(Port, [{active,N}|ServerOpts])),
+ [{active,N}] = ok(ssl:getopts(LS, [active])),
+ active_n_common(LS, N),
+ Self = self(),
+ spawn_link(fun() ->
+ S0 = ok(ssl:transport_accept(LS)),
+ {ok, S} = ssl:handshake(S0),
+ ok = ssl:setopts(S, [{active,N}]),
+ [{active,N}] = ok(ssl:getopts(S, [active])),
+ ssl:controlling_process(S, Self),
+ Self ! {server, S}
+ end),
+ C = ok(ssl:connect("localhost", Port, [{active,N}|ClientOpts])),
+ [{active,N}] = ok(ssl:getopts(C, [active])),
+ S = receive
+ {server, S0} -> S0
+ after
+ 1000 ->
+ exit({error, connect})
+ end,
+ active_n_common(C, N),
+ active_n_common(S, N),
+ ok = ssl:setopts(C, [{active,N}]),
+ ok = ssl:setopts(S, [{active,N}]),
+ ReceiveMsg = fun(Socket, Msg) ->
+ receive
+ {ssl,Socket,Msg} ->
+ ok;
+ {ssl,Socket,Begin} ->
+ receive
+ {ssl,Socket,End} ->
+ Msg = Begin ++ End,
+ ok
+ after 1000 ->
+ exit(timeout)
+ end
+ after 1000 ->
+ exit(timeout)
+ end
+ end,
+ repeat(3, fun(I) ->
+ Msg = "message "++integer_to_list(I),
+ ok = ssl:send(C, Msg),
+ ReceiveMsg(S, Msg),
+ ok = ssl:send(S, Msg),
+ ReceiveMsg(C, Msg)
+ end),
+ receive
+ {ssl_passive,S} ->
+ [{active,false}] = ok(ssl:getopts(S, [active]))
+ after
+ 1000 ->
+ exit({error,ssl_passive})
+ end,
+ receive
+ {ssl_passive,C} ->
+ [{active,false}] = ok(ssl:getopts(C, [active]))
+ after
+ 1000 ->
+ exit({error,ssl_passive})
+ end,
+ LS2 = ok(ssl:listen(0, [{active,0}])),
+ receive
+ {ssl_passive,LS2} ->
+ [{active,false}] = ok(ssl:getopts(LS2, [active]))
+ after
+ 1000 ->
+ exit({error,ssl_passive})
+ end,
+ ok = ssl:close(LS2),
+ ok = ssl:close(C),
+ ok = ssl:close(S),
+ ok = ssl:close(LS),
+ ok.
+
+active_n_common(S, N) ->
+ ok = ssl:setopts(S, [{active,-N}]),
+ receive
+ {ssl_passive, S} -> ok
+ after
+ 1000 ->
+ error({error,ssl_passive_failure})
+ end,
+ [{active,false}] = ok(ssl:getopts(S, [active])),
+ ok = ssl:setopts(S, [{active,0}]),
+ receive
+ {ssl_passive, S} -> ok
+ after
+ 1000 ->
+ error({error,ssl_passive_failure})
+ end,
+ ok = ssl:setopts(S, [{active,32767}]),
+ {error,{options,_}} = ssl:setopts(S, [{active,1}]),
+ {error,{options,_}} = ssl:setopts(S, [{active,-32769}]),
+ ok = ssl:setopts(S, [{active,-32768}]),
+ receive
+ {ssl_passive, S} -> ok
+ after
+ 1000 ->
+ error({error,ssl_passive_failure})
+ end,
+ [{active,false}] = ok(ssl:getopts(S, [active])),
+ ok = ssl:setopts(S, [{active,N}]),
+ ok = ssl:setopts(S, [{active,true}]),
+ [{active,true}] = ok(ssl:getopts(S, [active])),
+ receive
+ _ -> error({error,active_n})
+ after
+ 0 ->
+ ok
+ end,
+ ok = ssl:setopts(S, [{active,N}]),
+ ok = ssl:setopts(S, [{active,once}]),
+ [{active,once}] = ok(ssl:getopts(S, [active])),
+ receive
+ _ -> error({error,active_n})
+ after
+ 0 ->
+ ok
+ end,
+ {error,{options,_}} = ssl:setopts(S, [{active,32768}]),
+ ok = ssl:setopts(S, [{active,false}]),
+ [{active,false}] = ok(ssl:getopts(S, [active])),
+ ok.
+
+ok({ok,V}) -> V.
+
+repeat(N, Fun) ->
+ repeat(N, N, Fun).
+
+repeat(N, T, Fun) when is_integer(N), N > 0 ->
+ Fun(T-N),
+ repeat(N-1, T, Fun);
+repeat(_, _, _) ->
+ ok.
+
+%%--------------------------------------------------------------------
dh_params() ->
[{doc,"Test to specify DH-params file in server."}].
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index 3a083d9fda..3a8fe2211b 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -357,13 +357,17 @@ rfc3339_to_system_time(DateTimeString) ->
rfc3339_to_system_time(DateTimeString, Options) ->
Unit = proplists:get_value(unit, Options, second),
%% _T is the character separating the date and the time:
- {DateStr, [_T|TimeStr]} = lists:split(10, DateTimeString),
- {TimeStr2, TimeStr3} = lists:split(8, TimeStr),
- {ok, [Hour, Min, Sec], []} = io_lib:fread("~d:~d:~d", TimeStr2),
- {ok, [Year, Month, Day], []} = io_lib:fread("~d-~d-~d", DateStr),
+ [Y1, Y2, Y3, Y4, $-, Mon1, Mon2, $-, D1, D2, _T,
+ H1, H2, $:, Min1, Min2, $:, S1, S2 | TimeStr] = DateTimeString,
+ Hour = list_to_integer([H1, H2]),
+ Min = list_to_integer([Min1, Min2]),
+ Sec = list_to_integer([S1, S2]),
+ Year = list_to_integer([Y1, Y2, Y3, Y4]),
+ Month = list_to_integer([Mon1, Mon2]),
+ Day = list_to_integer([D1, D2]),
DateTime = {{Year, Month, Day}, {Hour, Min, Sec}},
IsFractionChar = fun(C) -> C >= $0 andalso C =< $9 orelse C =:= $. end,
- {FractionStr, UtcOffset} = lists:splitwith(IsFractionChar, TimeStr3),
+ {FractionStr, UtcOffset} = lists:splitwith(IsFractionChar, TimeStr),
Time = datetime_to_system_time(DateTime),
Secs = Time - offset_adjustment(Time, second, UtcOffset),
check(DateTimeString, Options, Secs),
@@ -451,8 +455,9 @@ system_time_to_rfc3339(Time, Options) ->
DateTime = system_time_to_datetime(Secs),
{{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
FractionStr = fraction_str(Factor, AdjustedTime),
- flat_fwrite("~4.10.0B-~2.10.0B-~2.10.0B~c~2.10.0B:~2.10.0B:~2.10.0B~s~s",
- [Year, Month, Day, T, Hour, Min, Sec, FractionStr, Offset]).
+ L = [pad4(Year), "-", pad2(Month), "-", pad2(Day), [T],
+ pad2(Hour), ":", pad2(Min), ":", pad2(Sec), FractionStr, Offset],
+ lists:append(L).
%% time_difference(T1, T2) = Tdiff
%%
@@ -680,7 +685,7 @@ offset(OffsetOption, Secs0) when OffsetOption =:= "";
Secs = abs(Secs0),
Hour = Secs div 3600,
Min = (Secs rem 3600) div 60,
- io_lib:fwrite("~c~2.10.0B:~2.10.0B", [Sign, Hour, Min]);
+ [Sign | lists:append([pad2(Hour), ":", pad2(Min)])];
offset(OffsetOption, _Secs) ->
OffsetOption.
@@ -695,8 +700,10 @@ offset_string_adjustment(_Time, _Unit, "Z") ->
0;
offset_string_adjustment(_Time, _Unit, "z") ->
0;
-offset_string_adjustment(_Time, _Unit, [Sign|Tz]) ->
- {ok, [Hour, Min], []} = io_lib:fread("~d:~d", Tz),
+offset_string_adjustment(_Time, _Unit, Tz) ->
+ [Sign, H1, H2, $:, M1, M2] = Tz,
+ Hour = list_to_integer([H1, H2]),
+ Min = list_to_integer([M1, M2]),
Adjustment = 3600 * Hour + 60 * Min,
case Sign of
$- -> -Adjustment;
@@ -704,8 +711,9 @@ offset_string_adjustment(_Time, _Unit, [Sign|Tz]) ->
end.
local_offset(SystemTime, Unit) ->
- LocalTime = system_time_to_local_time(SystemTime, Unit),
+ %% Not optimized for special cases.
UniversalTime = system_time_to_universal_time(SystemTime, Unit),
+ LocalTime = erlang:universaltime_to_localtime(UniversalTime),
LocalSecs = datetime_to_gregorian_seconds(LocalTime),
UniversalSecs = datetime_to_gregorian_seconds(UniversalTime),
LocalSecs - UniversalSecs.
@@ -714,7 +722,8 @@ fraction_str(1, _Time) ->
"";
fraction_str(Factor, Time) ->
Fraction = Time rem Factor,
- io_lib:fwrite(".~*..0B", [log10(Factor), abs(Fraction)]).
+ S = integer_to_list(abs(Fraction)),
+ [$. | pad(log10(Factor) - length(S), S)].
fraction(second, _) ->
0;
@@ -735,5 +744,21 @@ log10(1000) -> 3;
log10(1000000) -> 6;
log10(1000000000) -> 9.
-flat_fwrite(F, S) ->
- lists:flatten(io_lib:fwrite(F, S)).
+pad(0, S) ->
+ S;
+pad(I, S) ->
+ [$0 | pad(I - 1, S)].
+
+pad2(N) when N < 10 ->
+ [$0 | integer_to_list(N)];
+pad2(N) ->
+ integer_to_list(N).
+
+pad4(N) when N < 10 ->
+ [$0, $0, $0 | integer_to_list(N)];
+pad4(N) when N < 100 ->
+ [$0, $0 | integer_to_list(N)];
+pad4(N) when N < 1000 ->
+ [$0 | integer_to_list(N)];
+pad4(N) ->
+ integer_to_list(N).
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 5483ea87b5..8f2fd7ea8f 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -721,7 +721,7 @@ printable_list(_L, 1, _T, _Enc) ->
printable_list(L, _D, T, latin1) when T < 0 ->
io_lib:printable_latin1_list(L);
printable_list(L, _D, T, Enc) when T >= 0 ->
- case slice(L, tsub(T, 2)) of
+ case slice(L, tsub(T, 2), Enc) of
false ->
false;
{prefix, Prefix} when Enc =:= latin1 ->
@@ -737,20 +737,46 @@ printable_list(L, _D, T, Enc) when T >= 0 ->
printable_list(L, _D, T, _Uni) when T < 0->
io_lib:printable_list(L).
-slice(L, N) ->
- try io_lib:chars_length(L) =< N of
- true ->
+slice(L, N, latin1) ->
+ try lists:split(N, L) of
+ {_, []} ->
all;
- false ->
- case string:slice(L, 0, N) of
- "" ->
- false;
- Prefix ->
- {prefix, Prefix}
+ {[], _} ->
+ false;
+ {L1, _} ->
+ {prefix, L1}
+ catch
+ _:_ ->
+ all
+ end;
+slice(L, N, _Uni) ->
+ %% Be careful not to traverse more of L than necessary.
+ try string:slice(L, 0, N) of
+ "" ->
+ false;
+ Prefix ->
+ %% Assume no binaries are introduced by string:slice().
+ case is_flat(L, lists:flatlength(Prefix)) of
+ true ->
+ case string:equal(Prefix, L) of
+ true ->
+ all;
+ false ->
+ {prefix, Prefix}
+ end;
+ false ->
+ false
end
catch _:_ -> false
end.
+is_flat(_L, 0) ->
+ true;
+is_flat([C|Cs], N) when is_integer(C) ->
+ is_flat(Cs, N - 1);
+is_flat(_, _N) ->
+ false.
+
printable_bin0(Bin, D, T, Enc) ->
Len = case D >= 0 of
true ->
diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl
index c6d9dbca4a..224c0d5625 100644
--- a/lib/stdlib/test/calendar_SUITE.erl
+++ b/lib/stdlib/test/calendar_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index f097552e8c..824f5d19f2 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@
io_with_huge_message_queue/1, format_string/1,
maps/1, coverage/1, otp_14178_unicode_atoms/1, otp_14175/1,
otp_14285/1, limit_term/1, otp_14983/1, otp_15103/1, otp_15076/1,
- otp_15159/1]).
+ otp_15159/1, otp_15639/1]).
-export([pretty/2, trf/3]).
@@ -64,7 +64,8 @@ all() ->
io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836,
io_lib_width_too_small, io_with_huge_message_queue,
format_string, maps, coverage, otp_14178_unicode_atoms, otp_14175,
- otp_14285, limit_term, otp_14983, otp_15103, otp_15076, otp_15159].
+ otp_14285, limit_term, otp_14983, otp_15103, otp_15076, otp_15159,
+ otp_15639].
%% Error cases for output.
error_1(Config) when is_list(Config) ->
@@ -2647,3 +2648,35 @@ otp_15076(_Config) ->
{'EXIT', {badarg, _}} = (catch io_lib:build_text(L)),
{'EXIT', {badarg, _}} = (catch io_lib:build_text(L, [])),
ok.
+
+otp_15639(_Config) ->
+ L = lists:duplicate(10, "a"),
+ LOpts = [{encoding, latin1}, {chars_limit, 10}],
+ UOpts = [{encoding, unicode}, {chars_limit, 10}],
+ "[[...]|...]" = pretty(L, LOpts),
+ "[[...]|...]" = pretty(L, UOpts),
+ "[\"a\",[...]|...]" =
+ pretty(L, [{chars_limit, 12}, {encoding, latin1}]),
+ "[\"a\",[...]|...]" =
+ pretty(L, [{chars_limit, 12}, {encoding, unicode}]),
+
+ %% Latin-1
+ "\"12345678\"" = pretty("12345678", LOpts),
+ "\"12345678\"..." = pretty("123456789", LOpts),
+ "\"\\r\\n123456\"..." = pretty("\r\n1234567", LOpts),
+ "\"\\r1234567\"..." = pretty("\r12345678", LOpts),
+ "\"\\r\\n123456\"..." = pretty("\r\n12345678", LOpts),
+ "\"12345678\"..." = pretty("12345678"++[x], LOpts),
+ "[49,50|...]" = pretty("1234567"++[x], LOpts),
+ "[49,x]" = pretty("1"++[x], LOpts),
+ "[[...]|...]" = pretty(["1","2","3","4","5","6","7","8"], LOpts),
+ %% Unicode
+ "\"12345678\"" = pretty("12345678", UOpts),
+ "\"12345678\"..." = pretty("123456789", UOpts),
+ "\"\\r\\n1234567\"" = pretty("\r\n1234567", UOpts),
+ "\"\\r1234567\"..." = pretty("\r12345678", UOpts),
+ "\"\\r\\n1234567\"..." = pretty("\r\n12345678", UOpts),
+ "[49,50|...]" = pretty("12345678"++[x], UOpts),
+ "\"12345678\"..." = pretty("123456789"++[x], UOpts),
+ "[[...]|...]" = pretty(["1","2","3","4","5","6","7","8"], UOpts),
+ ok.
diff --git a/make/install_dir_data.sh.in b/make/install_dir_data.sh.in
new file mode 100644
index 0000000000..8c1dc3d889
--- /dev/null
+++ b/make/install_dir_data.sh.in
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2019. 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%
+#
+
+#
+# install_dir_data.sh <SourceDir> <DestDir>
+#
+# Install all content in <SourceDir> including subdirectories
+# into <DestDir>.
+#
+
+INSTALL="@INSTALL@"
+INSTALL_DIR="@INSTALL_DIR@"
+INSTALL_DATA="@INSTALL_DATA@"
+
+debug=yes
+
+error () {
+ echo "ERROR: $1" 1>&2
+ exit 1
+}
+
+usage () {
+ error "$1\n Usage $progname <SourceDir> <DestDir>"
+}
+
+cmd () {
+ [ $debug = no ] || echo "$@"
+ "$@" || exit 1
+}
+
+progname="$0"
+
+[ $# -eq 2 ] || usage "Invalid amount of arguments"
+
+src="$1"
+dest="$2"
+
+cmd cd "$src"
+
+for dir in `find . -type d`; do
+ destdir="$dest"
+ [ "$dir" = "." ] || destdir="$dest/$dir"
+ cmd $INSTALL_DIR "$destdir"
+done
+
+for file in `find . -type f`; do
+ subdir=`dirname "$file"`
+ destdir="$dest"
+ [ "$subdir" = "." ] || destdir="$dest/$subdir"
+ cmd $INSTALL_DATA "$file" "$destdir"
+done
+
+exit 0
diff --git a/make/otp.mk.in b/make/otp.mk.in
index df29d26833..fb573680c8 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -73,6 +73,7 @@ INSTALL_DIR = @INSTALL_DIR@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_DATA = @INSTALL_DATA@
+INSTALL_DIR_DATA = $(ERL_TOP)/make/install_dir_data.sh
CC = @CC@
GCC = @GCC@
@@ -273,7 +274,9 @@ DEFAULT_GIF_FILES = $(HTMLDIR)/min_head.gif
XSLTPROC = @XSLTPROC@
FOP = @FOP@
XMLLINT = @XMLLINT@
+RM = @RM@
CP = @CP@
+MKDIR = @MKDIR@
DOCGEN=$(ERL_TOP)/lib/erl_docgen
FOP_CONFIG = $(DOCGEN)/priv/fop.xconf
diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge
index 32e6923ce9..ff967634a2 100644
--- a/make/otp_version_tickets_in_merge
+++ b/make/otp_version_tickets_in_merge
@@ -1,2 +1,3 @@
-OTP-14728
-OTP-15573
+OTP-15551
+OTP-15651
+OTP-15652
diff --git a/otp_patch_apply b/otp_patch_apply
index 0127b96a73..3ff929ccbb 100755
--- a/otp_patch_apply
+++ b/otp_patch_apply
@@ -19,7 +19,7 @@
# %CopyrightEnd%
#
-version="1.0.1"
+version="1.0.2"
force=
lib_path=
@@ -388,9 +388,7 @@ if [ $install_docs = yes ]; then
TESTROOT="$idir" release_docs) || exit 1
done
- (cd "$sdir/system/doc/top" && $MAKE clean)
-
- (cd "$sdir/system/doc/top" && \
+ (cd "$sdir/system/doc" && \
$MAKE MAKE="$MAKE" RELEASE_ROOT="$idir" RELEASE_PATH="$idir" \
TESTROOT="$idir" release_docs) || exit 1
diff --git a/otp_versions.table b/otp_versions.table
index aea1e8d5cd..0f79284903 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-21.2.7 : erts-10.2.5 kernel-6.2.1 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.5 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7.1 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
OTP-21.2.6 : erts-10.2.4 stdlib-3.7.1 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 inets-7.0.5 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
OTP-21.2.5 : inets-7.0.5 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 erts-10.2.3 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
OTP-21.2.4 : erts-10.2.3 inets-7.0.4 # asn1-5.0.8 common_test-1.16.1 compiler-7.3.1 crypto-4.4 debugger-4.2.6 dialyzer-3.3.1 diameter-2.1.6 edoc-0.9.4 eldap-1.2.6 erl_docgen-0.8.1 erl_interface-3.10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.1 hipe-3.18.2 jinterface-1.9.1 kernel-6.2 megaco-3.18.4 mnesia-4.15.5 observer-2.8.2 odbc-2.12.2 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.4 reltool-0.7.8 runtime_tools-1.13.1 sasl-3.3 snmp-5.2.12 ssh-4.7.3 ssl-9.1.2 stdlib-3.7 syntax_tools-2.1.6 tftp-1.0.1 tools-3.0.2 wx-1.8.6 xmerl-1.3.19 :
diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile
index 242bf1c9a4..2fbd7d087f 100644
--- a/system/doc/design_principles/Makefile
+++ b/system/doc/design_principles/Makefile
@@ -88,7 +88,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
@@ -107,8 +106,8 @@ images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile
index 72bcd2ee73..a2742a1354 100644
--- a/system/doc/efficiency_guide/Makefile
+++ b/system/doc/efficiency_guide/Makefile
@@ -87,7 +87,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -98,8 +97,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/embedded/Makefile b/system/doc/embedded/Makefile
index 396aef276b..1604075312 100644
--- a/system/doc/embedded/Makefile
+++ b/system/doc/embedded/Makefile
@@ -75,7 +75,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -86,8 +85,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile
index cdf1e121c2..1c917895d5 100644
--- a/system/doc/getting_started/Makefile
+++ b/system/doc/getting_started/Makefile
@@ -74,7 +74,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -85,8 +84,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/html/design_principles/.gitignore b/system/doc/html/design_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/design_principles/.gitignore
diff --git a/system/doc/html/efficiency_guide/.gitignore b/system/doc/html/efficiency_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/efficiency_guide/.gitignore
diff --git a/system/doc/html/embedded/.gitignore b/system/doc/html/embedded/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/embedded/.gitignore
diff --git a/system/doc/html/getting_started/.gitignore b/system/doc/html/getting_started/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/getting_started/.gitignore
diff --git a/system/doc/html/installation_guide/.gitignore b/system/doc/html/installation_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/installation_guide/.gitignore
diff --git a/system/doc/html/installation_guide/source/.gitignore b/system/doc/html/installation_guide/source/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/installation_guide/source/.gitignore
diff --git a/system/doc/html/js/.gitignore b/system/doc/html/js/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/js/.gitignore
diff --git a/system/doc/html/oam/.gitignore b/system/doc/html/oam/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/oam/.gitignore
diff --git a/system/doc/html/programming_examples/.gitignore b/system/doc/html/programming_examples/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/programming_examples/.gitignore
diff --git a/system/doc/html/reference_manual/.gitignore b/system/doc/html/reference_manual/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/reference_manual/.gitignore
diff --git a/system/doc/html/system_architecture_intro/.gitignore b/system/doc/html/system_architecture_intro/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/system_architecture_intro/.gitignore
diff --git a/system/doc/html/system_principles/.gitignore b/system/doc/html/system_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/system_principles/.gitignore
diff --git a/system/doc/html/tutorial/.gitignore b/system/doc/html/tutorial/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/tutorial/.gitignore
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 4a1335cf31..38252757d6 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -91,7 +91,6 @@ $(XMLDIR)/%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
$(REDIRECT_HTML_DIR)/%.html: Makefile
- test -d $(REDIRECT_HTML_DIR) || $(INSTALL_DIR) $(REDIRECT_HTML_DIR)
echo "<html><head><meta HTTP-EQUIV=\"REFRESH\"" > $@
echo " content=\"5; url=../"$(notdir $@)"\">" >> $@
echo "<title>This page has moved</title></head><body>" >> $@
@@ -112,8 +111,8 @@ debug opt:
clean clean_docs:
rm -f $(GENERATED_XML_FILES)
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/oam/Makefile b/system/doc/oam/Makefile
index 147f56f885..2eb429e04d 100644
--- a/system/doc/oam/Makefile
+++ b/system/doc/oam/Makefile
@@ -71,10 +71,9 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+ $(CP) $< $@
docs: html
@@ -87,8 +86,8 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile
index e4737ba069..9c67c24b64 100644
--- a/system/doc/programming_examples/Makefile
+++ b/system/doc/programming_examples/Makefile
@@ -74,7 +74,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
local_docs: PDFDIR=../../pdf
@@ -84,8 +83,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/reference_manual/Makefile b/system/doc/reference_manual/Makefile
index d034ad2ff8..809eb2c979 100644
--- a/system/doc/reference_manual/Makefile
+++ b/system/doc/reference_manual/Makefile
@@ -84,7 +84,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -95,8 +94,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile
index eb885a744d..ea9ee85105 100644
--- a/system/doc/system_architecture_intro/Makefile
+++ b/system/doc/system_architecture_intro/Makefile
@@ -69,7 +69,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -80,8 +79,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile
index 1979deda4c..5110b73373 100644
--- a/system/doc/system_principles/Makefile
+++ b/system/doc/system_principles/Makefile
@@ -70,7 +70,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -81,8 +80,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index 0703b821f1..2f50ff1905 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -98,6 +98,25 @@ PDFREFDIR= pdf
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
TOPDOC=true
+ifdef RELEASE_PATH
+INST_TYPE=rel
+INST_TYPE_SRC_DIR=$(RELEASE_PATH)
+# We build to the 'temporary' dir in order to be able to install
+# results using INSTALL_DATA (in order to get correct access
+# rights on installed files)
+INST_TYPE_DEST_DIR=$(RELSYSDIR)/temporary
+INST_TYPE_DEST_DIR_DEP=$(INST_TYPE_DEST_DIR)
+INST_TYPE_JS_DEST_DIR=$(INST_TYPE_DEST_DIR)
+INST_TYPE_VSN_FILE=$(INST_TYPE_DEST_DIR)/OTP_VERSION
+else
+INST_TYPE=src
+INST_TYPE_SRC_DIR=$(ERL_TOP)
+INST_TYPE_DEST_DIR=$(HTMLDIR)
+INST_TYPE_DEST_DIR_DEP=
+INST_TYPE_JS_DEST_DIR=$(INST_TYPE_DEST_DIR)/js
+INST_TYPE_VSN_FILE=$(ERL_TOP)/OTP_VERSION
+endif
+
#--------------------------------------------------------------------------
# We generate the index page from the installed system. This make
# it important that this is done last. The file index.html.src
@@ -107,17 +126,18 @@ EBIN = ebin
INDEX_SCRIPT = $(EBIN)/erl_html_tools.$(EMULATOR)
INDEX_SRC = src/erl_html_tools.erl
-INDEX_FILES = \
- $(HTMLDIR)/index.html \
- $(HTMLDIR)/applications.html
-JAVASCRIPT = $(HTMLDIR)/js/erlresolvelinks.js
+INDEX_HTML=$(INST_TYPE_DEST_DIR)/index.html
+APPLICATIONS_HTML=$(INST_TYPE_DEST_DIR)/applications.html
+INDEX_FILES = $(INDEX_HTML) $(APPLICATIONS_HTML)
+
+JAVASCRIPT = $(INST_TYPE_JS_DEST_DIR)/erlresolvelinks.js
JAVASCRIPT_BUILD_SCRIPT = $(EBIN)/erlresolvelinks.$(EMULATOR)
JAVASCRIPT_BUILD_SCRIPT_SRC = src/erlresolvelinks.erl
MAN_INDEX_SCRIPT = $(EBIN)/otp_man_index.$(EMULATOR)
MAN_INDEX_SRC = src/otp_man_index.erl
-MAN_INDEX = $(HTMLDIR)/man_index.html
+MAN_INDEX = $(INST_TYPE_DEST_DIR)/man_index.html
GLOSSARY = $(HTMLDIR)/glossary.html
GLOSSARY_SRC = $(ERL_TOP)/system/internal_tools/doctools/src/glossary.erl
@@ -132,45 +152,38 @@ TEMPLATES = \
$(INDEX_SCRIPT): $(INDEX_SRC)
$(ERLC) -o$(EBIN) +warn_unused_vars $<
-# We don't list toc_*.html as targets because we don't know
-$(HTMLDIR)/index.html + $(HTMLDIR)/applications.html: $(INDEX_SCRIPT) $(TEMPLATES)
- echo "Generating index $@"
-# Check if we are building the index from source or an installed release
- if test "$$RELEASE_ROOT" = "" ; then \
- $(ERL) -noshell -pa $(EBIN) -s erl_html_tools top_index src $(ERL_TOP) \
- $(HTMLDIR) `cat "$(ERL_TOP)/OTP_VERSION"` -s erlang halt ;\
+$(INST_TYPE_DEST_DIR)/OTP_VERSION: $(INST_TYPE_DEST_DIR_DEP)
+ if test -f "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/OTP_VERSION"; then \
+ $(CP) "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/OTP_VERSION" $@; \
else \
- $(ERL) -noshell -pa $(EBIN) -s erl_html_tools top_index rel $(RELEASE_ROOT) \
- $(HTMLDIR) `cat "$(RELEASE_ROOT)/releases/$(SYSTEM_VSN)/OTP_VERSION"` \
- -s erlang halt ;\
+ $(CP) $(ERL_TOP)/OTP_VERSION $@; \
fi
+# We don't list toc_*.html as targets because we don't know
+$(INDEX_HTML) + $(APPLICATIONS_HTML): $(INST_TYPE_DEST_DIR_DEP) $(INDEX_SCRIPT) $(TEMPLATES) $(INST_TYPE_VSN_FILE)
+ echo "Generating index $@"
+ $(ERL) -noshell -pa $(EBIN) -s erl_html_tools top_index $(INST_TYPE) \
+ $(INST_TYPE_SRC_DIR) $(INST_TYPE_DEST_DIR) \
+ `cat "$(INST_TYPE_VSN_FILE)"` -s erlang halt
+
#--------------------------------------------------------------------------
$(JAVASCRIPT_BUILD_SCRIPT): $(JAVASCRIPT_BUILD_SCRIPT_SRC)
$(ERLC) -o$(EBIN) +warn_unused_vars $<
-$(JAVASCRIPT): $(JAVASCRIPT_BUILD_SCRIPT)
- erl -noshell -pa $(EBIN) -s erlresolvelinks make -s erlang halt
- $(INSTALL_DIR) $(HTMLDIR)/js
- $(INSTALL_DATA) erlresolvelinks.js $(JAVASCRIPT)
+$(JAVASCRIPT): $(INST_TYPE_DEST_DIR_DEP) $(JAVASCRIPT_BUILD_SCRIPT)
+ erl -noshell -pa $(EBIN) -run erlresolvelinks make $(ERL_TOP) \
+ $(INST_TYPE_SRC_DIR) $(INST_TYPE_JS_DEST_DIR) -s erlang halt
#--------------------------------------------------------------------------
$(MAN_INDEX_SCRIPT): $(MAN_INDEX_SRC)
$(ERLC) -o$(EBIN) +warn_unused_vars $<
-$(MAN_INDEX): $(MAN_INDEX_SCRIPT)
-# Check if we are building the index from source or an installed release
- if test "$$RELEASE_ROOT" = "" ; then \
- $(ERL) -noshell -pa $(EBIN) -s otp_man_index gen src $(ERL_TOP) $@ \
- -s erlang halt ;\
- else \
- $(ERL) -noshell -pa $(EBIN) -s otp_man_index gen rel $(RELEASE_ROOT) $@ \
- -s erlang halt ;\
- fi
-
+$(MAN_INDEX): $(INST_TYPE_DEST_DIR_DEP) $(MAN_INDEX_SCRIPT)
+ $(ERL) -noshell -pa $(EBIN) -s otp_man_index gen $(INST_TYPE) \
+ $(INST_TYPE_SRC_DIR) $@ -s erlang halt
#--------------------------------------------------------------------------
@@ -248,18 +261,22 @@ html: $(INDEX_FILES) \
debug opt:
clean:
- rm -rf ../html/js
- rm -f PR.template
- rm -f $(INDEX_FILES) $(MAN_INDEX)
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(INDEX_SCRIPT) $(GLOSSARY_SCRIPT) \
- $(JAVASCRIPT_BUILD_SCRIPT)
- rm -f erl_crash.dump errs core *~
+ $(RM) -f ../html/js/*.js
+ $(RM) -f PR.template
+ $(RM) -f $(XMLDIR)/*.xml
+ $(RM) -f $(INDEX_FILES) $(MAN_INDEX)
+ $(RM) -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ $(RM) -f $(INDEX_SCRIPT) $(GLOSSARY_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT)
+ $(RM) -f erl_crash.dump errs core *~
+
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
+$(RELSYSDIR)/temporary:
+ $(INSTALL_DIR) $(RELSYSDIR)/temporary
+
release_docs_spec: docs
$(INSTALL_DIR) "$(RELEASE_PATH)"
$(INSTALL_DATA) $(INFO_FILES) "$(RELEASE_PATH)"
@@ -268,13 +285,13 @@ release_docs_spec: docs
$(INSTALL_DATA) \
$(TOP_PDF_FILE) $(RELSYSDIR)/pdf
$(INSTALL_DIR) $(RELSYSDIR)/js
- $(INSTALL_DATA) \
- $(JAVASCRIPT) $(RELSYSDIR)/js
+ $(INSTALL_DATA) $(JAVASCRIPT) $(RELSYSDIR)/js
$(INSTALL_DATA) $(INDEX_FILES) $(MAN_INDEX) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/docbuild
$(INSTALL_DATA) $(INDEX_SCRIPT) $(MAN_INDEX_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT) \
$(INDEX_SRC) $(MAN_INDEX_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \
$(TEMPLATES) $(RELSYSDIR)/docbuild
+ $(RM) -rf $(RELSYSDIR)/temporary
release_spec:
diff --git a/system/doc/top/src/erl_html_tools.erl b/system/doc/top/src/erl_html_tools.erl
index 28a0649658..dee1871342 100644
--- a/system/doc/top/src/erl_html_tools.erl
+++ b/system/doc/top/src/erl_html_tools.erl
@@ -508,7 +508,7 @@ subst_app(App, [{VSN,_Path,Link,Text} | VerInfos]) ->
" <a href=\"",Link,"\" target=\"_top\">",uc(App),
"</a>\n",
" <a href=\"",Link,"\" target=\"_top\">",VSN,"</a>\n",
- " <td class=appnums>\n",
+ " <br/>\n",
subst_vsn(VerInfos),
" </td>\n",
" <td>\n",
@@ -522,7 +522,7 @@ subst_vsn([{VSN,_Path,Link,_Text} | VSNs]) ->
[
" <font size=\"2\"><a class=anum href=\"",Link,"\" target=\"_top\">",
VSN,
- "</a></font><br>\n",
+ "</a></font><br/>\n",
subst_vsn(VSNs)
];
subst_vsn([]) ->
diff --git a/system/doc/top/src/erlresolvelinks.erl b/system/doc/top/src/erlresolvelinks.erl
index cfe8d0fa0b..c1285fa1c3 100644
--- a/system/doc/top/src/erlresolvelinks.erl
+++ b/system/doc/top/src/erlresolvelinks.erl
@@ -27,40 +27,28 @@
%%-----------------------------------------------------------------
-module(erlresolvelinks).
--export([make/0, make/1]).
+-export([make/1]).
-include_lib("kernel/include/file.hrl").
-define(JAVASCRIPT_NAME, "erlresolvelinks.js").
-make() ->
- case os:getenv("ERL_TOP") of
- false ->
- io:format("Variable ERL_TOP is required\n",[]);
- Value ->
- make_from_src(Value, ".")
- end.
-
-make([RootDir, DestDir]) ->
- do_make(RootDir, DestDir);
-make(RootDir) when is_atom(RootDir) ->
- DestDir = filename:join(RootDir, "doc"),
- do_make(RootDir, DestDir).
-
-do_make(_RootDir, _DestDir) ->
- ok.
+make([ErlTop, RootDir, DestDir]) ->
+ make(ErlTop, RootDir, DestDir).
-make_from_src(RootDir, DestDir) ->
+make(ErlTop, RootDir, DestDir) ->
%% doc/Dir
%% erts-Vsn
%% lib/App-Vsn
Name = ?JAVASCRIPT_NAME,
- DocDirs0 = get_dirs(filename:join([RootDir, "system/doc"])),
+ DocDirs0 = get_dirs(filename:join([ErlTop, "system/doc"])),
DocDirs = lists:map(fun({Dir, _DirPath}) ->
D = filename:join(["doc", Dir]),
{D, D} end, DocDirs0),
- ErtsDirs = latest_app_dirs(RootDir, ""),
- AppDirs = latest_app_dirs(RootDir, "lib"),
+ Released = ErlTop /= RootDir,
+
+ ErtsDirs = latest_app_dirs(Released, RootDir, ""),
+ AppDirs = latest_app_dirs(Released, RootDir, "lib"),
AllAppDirs =
lists:map(
@@ -106,30 +94,46 @@ is_dir({File, AFile}) ->
false
end.
-latest_app_dirs(RootDir, Dir) ->
+released_app_vsns([]) ->
+ [];
+released_app_vsns([{AppVsn, Dir} | AVDirs]) ->
+ try
+ {ok, _} = file:read_file_info(filename:join([Dir, "doc", "html"])),
+ [App, Vsn] = string:tokens(AppVsn, "-"),
+ VsnNumList = vsnstr_to_numlist(Vsn),
+ [_Maj, _Min | _] = VsnNumList,
+ [{{App, VsnNumList}, AppVsn} | released_app_vsns(AVDirs)]
+ catch
+ _:_ -> released_app_vsns(AVDirs)
+ end.
+
+latest_app_dirs(Release, RootDir, Dir) ->
ADir = filename:join(RootDir, Dir),
RDirs0 = get_dirs(ADir),
- RDirs1 = lists:filter(fun is_app_dir/1, RDirs0),
-
- SDirs0 =
- lists:map(fun({App, Dir1}) ->
- File = filename:join(Dir1, "vsn.mk"),
- case file:read_file(File) of
- {ok, Bin} ->
- case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
- {match, [VsnStr]} ->
- VsnNumList = vsnstr_to_numlist(VsnStr),
- {{App, VsnNumList}, App++"-"++VsnStr};
- nomatch ->
- io:format("No VSN variable found in ~s\n", [File]),
- error
- end;
- {error, Reason} ->
- io:format("~p : ~s\n", [Reason, File]),
- error
- end
- end,
- RDirs1),
+ SDirs0 = case Release of
+ true ->
+ released_app_vsns(RDirs0);
+ false ->
+ lists:map(fun({App, Dir1}) ->
+ File = filename:join(Dir1, "vsn.mk"),
+ case file:read_file(File) of
+ {ok, Bin} ->
+ case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
+ {match, [VsnStr]} ->
+ VsnNumList = vsnstr_to_numlist(VsnStr),
+ {{App, VsnNumList}, App++"-"++VsnStr};
+ nomatch ->
+ io:format("No VSN variable found in ~s\n", [File]),
+ error
+ end;
+ {error, Reason} ->
+ io:format("~p : ~s\n", [Reason, File]),
+ error
+ end
+ end,
+ lists:filter(fun is_app_dir/1, RDirs0))
+ end,
+
SDirs1 = lists:keysort(1, SDirs0),
App2Dirs = lists:foldr(fun({{App, _VsnNumList}, AppVsn}, Acc) ->
case lists:keymember(App, 1, Acc) of
diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile
index 5867096fc8..4c62deeffd 100644
--- a/system/doc/tutorial/Makefile
+++ b/system/doc/tutorial/Makefile
@@ -93,10 +93,9 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+ $(CP) $< $@
docs: html
@@ -109,8 +108,8 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
- rm -rf $(XMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/system/doc/xml/design_principles/.gitignore b/system/doc/xml/design_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/design_principles/.gitignore
diff --git a/system/doc/xml/efficiency_guide/.gitignore b/system/doc/xml/efficiency_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/efficiency_guide/.gitignore
diff --git a/system/doc/xml/embedded/.gitignore b/system/doc/xml/embedded/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/embedded/.gitignore
diff --git a/system/doc/xml/getting_started/.gitignore b/system/doc/xml/getting_started/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/getting_started/.gitignore
diff --git a/system/doc/xml/installation_guide/.gitignore b/system/doc/xml/installation_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/installation_guide/.gitignore
diff --git a/system/doc/xml/oam/.gitignore b/system/doc/xml/oam/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/oam/.gitignore
diff --git a/system/doc/xml/programming_examples/.gitignore b/system/doc/xml/programming_examples/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/programming_examples/.gitignore
diff --git a/system/doc/xml/reference_manual/.gitignore b/system/doc/xml/reference_manual/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/reference_manual/.gitignore
diff --git a/system/doc/xml/system_architecture_intro/.gitignore b/system/doc/xml/system_architecture_intro/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/system_architecture_intro/.gitignore
diff --git a/system/doc/xml/system_principles/.gitignore b/system/doc/xml/system_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/system_principles/.gitignore
diff --git a/system/doc/xml/tutorial/.gitignore b/system/doc/xml/tutorial/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/tutorial/.gitignore