aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.in14
-rw-r--r--erts/configure.in12
-rw-r--r--erts/doc/src/erlang.xml2
-rw-r--r--erts/emulator/beam/beam_load.c8
-rw-r--r--erts/emulator/beam/beam_ranges.c6
-rw-r--r--erts/emulator/beam/big.c15
-rw-r--r--erts/emulator/beam/binary.c63
-rw-r--r--erts/emulator/beam/erl_bif_info.c13
-rw-r--r--erts/emulator/beam/erl_unicode.c152
-rw-r--r--erts/emulator/beam/erl_unicode.h5
-rw-r--r--erts/emulator/beam/global.h1
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c104
-rw-r--r--erts/emulator/test/code_SUITE.erl55
-rw-r--r--erts/emulator/test/code_SUITE_data/erl_544.erl35
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl73
-rw-r--r--erts/lib_src/common/erl_printf_format.c2
-rw-r--r--erts/preloaded/src/erlang.erl2
-rw-r--r--lib/asn1/test/asn1_SUITE.erl4
-rw-r--r--lib/compiler/src/beam_type.erl2
-rw-r--r--lib/compiler/src/beam_validator.erl7
-rw-r--r--lib/crypto/c_src/crypto.c26
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/abs.erl9
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/bsL.erl13
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl14
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl9
-rw-r--r--lib/hipe/cerl/erl_types.erl4
-rw-r--r--lib/hipe/llvm/hipe_llvm.erl2
-rw-r--r--lib/hipe/test/bs_SUITE_data/bs_construct.erl13
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/c_client.c8
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c8
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c8
-rw-r--r--lib/kernel/doc/src/kernel_app.xml20
-rw-r--r--lib/kernel/src/disk_log.erl4
-rw-r--r--lib/kernel/src/erl_boot_server.erl37
-rw-r--r--lib/kernel/src/group_history.erl4
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl2
-rw-r--r--lib/kernel/test/global_SUITE.erl17
-rw-r--r--lib/observer/include/etop.hrl2
-rw-r--r--lib/observer/src/cdv_detail_wx.erl11
-rw-r--r--lib/observer/src/cdv_info_wx.erl4
-rw-r--r--lib/observer/src/crashdump_viewer.erl140
-rw-r--r--lib/observer/src/observer_lib.erl67
-rw-r--r--lib/observer/src/observer_port_wx.erl4
-rw-r--r--lib/observer/src/observer_pro_wx.erl5
-rw-r--r--lib/observer/src/observer_procinfo.erl6
-rw-r--r--lib/observer/src/observer_tv_wx.erl355
-rw-r--r--lib/observer/test/crashdump_helper.erl1
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl46
-rw-r--r--lib/observer/test/observer_SUITE.erl7
-rw-r--r--lib/runtime_tools/src/system_information.erl232
-rw-r--r--lib/ssh/src/ssh_transport.erl2
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl814
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image38
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image71
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image61
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all87
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub1
-rw-r--r--lib/ssl/src/ssl_cipher.erl12
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl23
-rw-r--r--lib/ssl/test/ssl_test_lib.erl124
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl4
-rw-r--r--lib/stdlib/doc/src/filelib.xml4
-rw-r--r--lib/stdlib/doc/src/filename.xml19
-rw-r--r--lib/stdlib/src/erl_tar.erl2
-rw-r--r--lib/stdlib/src/filelib.erl21
-rw-r--r--lib/stdlib/src/gen_event.erl5
-rw-r--r--lib/stdlib/src/gen_fsm.erl4
-rw-r--r--lib/stdlib/src/io_lib.erl4
-rw-r--r--lib/stdlib/src/io_lib_fread.erl4
-rw-r--r--lib/stdlib/src/lib.erl6
-rw-r--r--lib/stdlib/src/shell.erl6
-rw-r--r--lib/stdlib/src/sys.erl6
-rw-r--r--lib/stdlib/test/epp_SUITE.erl6
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl7
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl6
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode12
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/unicode22
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl35
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl8
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl14
-rw-r--r--lib/stdlib/test/io_SUITE.erl6
-rw-r--r--lib/stdlib/test/shell_SUITE.erl25
105 files changed, 2504 insertions, 713 deletions
diff --git a/.gitignore b/.gitignore
index 7bc051278c..f77d771256 100644
--- a/.gitignore
+++ b/.gitignore
@@ -131,6 +131,7 @@ JAVADOC-GENERATED
/bootstrap/lib/ic
/bootstrap/lib/orber
/bootstrap/lib/parsetools
+/bootstrap/lib/runtime_tools
/bootstrap/lib/sasl
/bootstrap/lib/snmp
/bootstrap/lib/syntax_tools
diff --git a/Makefile.in b/Makefile.in
index 3dce72ba78..67eec53ca0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -679,6 +679,8 @@ tertiary_bootstrap_copy:
$(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/wx/include ; fi
$(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test ; fi
$(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/common_test/include ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/runtime_tools ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/runtime_tools ; fi
+ $(V_at)if test ! -d $(BOOTSTRAP_ROOT)/bootstrap/lib/runtime_tools/include ; then mkdir $(BOOTSTRAP_ROOT)/bootstrap/lib/runtime_tools/include ; fi
$(V_at)for x in lib/ic/ebin/*.beam; do \
BN=`basename $$x`; \
TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/ic/ebin/$$BN; \
@@ -767,6 +769,18 @@ tertiary_bootstrap_copy:
cp $$x $$TF; \
true; \
done
+
+# copy runtime_tool includes to be able to compile with include_lib
+ $(V_at)for x in lib/runtime_tools/include/*.hrl; do \
+ BN=`basename $$x`; \
+ TF=$(BOOTSTRAP_ROOT)/bootstrap/lib/runtime_tools/include/$$BN; \
+ test -f $$TF && \
+ test '!' -z "`find $$x -newer $$TF -print`" && \
+ cp $$x $$TF; \
+ test '!' -f $$TF && \
+ cp $$x $$TF; \
+ true; \
+ done
# $(V_at)cp lib/syntax_tools/ebin/*.beam $(BOOTSTRAP_ROOT)/bootstrap/lib/syntax_tools/ebin
doc_bootstrap_build:
diff --git a/erts/configure.in b/erts/configure.in
index 913315e402..cbebca97a3 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1507,12 +1507,16 @@ error
],[
AC_MSG_RESULT(no)
])
+
+if test "$Z_LIB" != ""; then
+ AC_MSG_CHECKING(for zlib inflateGetDictionary presence)
+ AC_SEARCH_LIBS(inflateGetDictionary, [z],
+ AC_DEFINE(HAVE_ZLIB_INFLATEGETDICTIONARY, 1,
+ [Define if your zlib version defines inflateGetDictionary.]))
+fi
+
LIBS=$zlib_save_LIBS
-AC_MSG_CHECKING(for zlib inflateGetDictionary presence)
-AC_SEARCH_LIBS(inflateGetDictionary, [z],
- AC_DEFINE(HAVE_ZLIB_INFLATEGETDICTIONARY, 1,
- [Define if your zlib version defines inflateGetDictionary.]))
fi
AC_SUBST(Z_LIB)
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index b04f2b008e..69af7430f1 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -8799,7 +8799,7 @@ ok
<c>erlang:now()</c>. This is also the default if no
time stamp flag is specified. If <c>cpu_timestamp</c> has
been enabled through
- <seealso marker="erlang:trace/3"><c>erlang:trace/3</c></seealso>,
+ <seealso marker="#trace/3"><c>erlang:trace/3</c></seealso>,
this also effects the time stamp produced in profiling messages
when flag <c>timestamp</c> is enabled.</p>
</item>
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 5b1aa6f128..a4e226d459 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
#include "erl_zlib.h"
#include "erl_map.h"
#include "erl_process_dict.h"
+#include "erl_unicode.h"
#ifdef HIPE
#include "hipe_bif0.h"
@@ -1792,7 +1793,7 @@ read_line_table(LoaderState* stp)
GetInt(stp, 2, n);
GetString(stp, fname, n);
- stp->fname[i] = erts_atom_put(fname, n, ERTS_ATOM_ENC_LATIN1, 1);
+ stp->fname[i] = erts_atom_put(fname, n, ERTS_ATOM_ENC_UTF8, 1);
}
}
@@ -5891,8 +5892,7 @@ erts_build_mfa_item(FunctionInfo* fi, Eterm* hp, Eterm args, Eterm* mfa_p)
file_term = buf_to_intlist(&hp, ".erl", 4, NIL);
file_term = buf_to_intlist(&hp, (char*)ap->name, ap->len, file_term);
} else {
- Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
- file_term = buf_to_intlist(&hp, (char*)ap->name, ap->len, NIL);
+ file_term = erts_atom_to_string(&hp, (fi->fname_ptr)[file-1]);
}
tuple = TUPLE2(hp, am_line, make_small(line));
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
index fac4289271..c7b17d58f3 100644
--- a/erts/emulator/beam/beam_ranges.c
+++ b/erts/emulator/beam/beam_ranges.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2012-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2012-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
#include "erl_vm.h"
#include "global.h"
#include "beam_load.h"
+#include "erl_unicode.h"
typedef struct {
BeamInstr* start; /* Pointer to start of module. */
@@ -341,8 +342,7 @@ lookup_loc(FunctionInfo* fi, const BeamInstr* pc,
Atom* mod_atom = atom_tab(atom_val(fi->mfa->module));
fi->needed += 2*(mod_atom->len+4);
} else {
- Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
- fi->needed += 2*ap->len;
+ fi->needed += 2*erts_atom_to_string_length((fi->fname_ptr)[file-1]);
}
return;
} else {
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 5eaf262cd8..c5cb268f09 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2549,12 +2549,17 @@ int term_equals_2pow32(Eterm x)
}
}
+static ERTS_INLINE int c2int_is_valid_char(byte ch, int base) {
+ if (base <= 10)
+ return (ch >= '0' && ch < ('0' + base));
+ else
+ return (ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch < ('A' + base - 10))
+ || (ch >= 'a' && ch < ('a' + base - 10));
+}
+
static ERTS_INLINE int c2int_is_invalid_char(byte ch, int base) {
- return (ch < '0'
- || (ch > ('0' + base - 1)
- && !(base > 10
- && ((ch >= 'a' && ch < ('a' + base - 10))
- || (ch >= 'A' && ch < ('A' + base - 10))))));
+ return !c2int_is_valid_char(ch, base);
}
static ERTS_INLINE byte c2int_digit_from_base(byte ch) {
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index ca3e48e205..d4db1a4188 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -60,14 +60,36 @@ erts_init_binary(void)
}
+static ERTS_INLINE
+Eterm build_proc_bin(ErlOffHeap* ohp, Eterm* hp, Binary* bptr)
+{
+ ProcBin* pb = (ProcBin *) hp;
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = bptr->orig_size;
+ pb->next = ohp->first;
+ ohp->first = (struct erl_off_heap_header*)pb;
+ pb->val = bptr;
+ pb->bytes = (byte*) bptr->orig_bytes;
+ pb->flags = 0;
+ OH_OVERHEAD(ohp, pb->size / sizeof(Eterm));
+
+ return make_binary(pb);
+}
+
+/** @brief Initiate a ProcBin for a full Binary.
+ * @param hp must point to PROC_BIN_SIZE available heap words.
+ */
+Eterm erts_build_proc_bin(ErlOffHeap* ohp, Eterm* hp, Binary* bptr)
+{
+ return build_proc_bin(ohp, hp, bptr);
+}
+
/*
* Create a brand new binary from scratch.
*/
-
Eterm
new_binary(Process *p, byte *buf, Uint len)
{
- ProcBin* pb;
Binary* bptr;
if (len <= ERL_ONHEAP_BIN_LIMIT) {
@@ -88,23 +110,7 @@ new_binary(Process *p, byte *buf, Uint len)
sys_memcpy(bptr->orig_bytes, buf, len);
}
- /*
- * Now allocate the ProcBin on the heap.
- */
- pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = len;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = bptr;
- pb->bytes = (byte*) bptr->orig_bytes;
- pb->flags = 0;
-
- /*
- * Miscellaneous updates. Return the tagged binary.
- */
- OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
- return make_binary(pb);
+ return build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE), bptr);
}
/*
@@ -113,7 +119,6 @@ new_binary(Process *p, byte *buf, Uint len)
Eterm erts_new_mso_binary(Process *p, byte *buf, Uint len)
{
- ProcBin* pb;
Binary* bptr;
/*
@@ -124,23 +129,7 @@ Eterm erts_new_mso_binary(Process *p, byte *buf, Uint len)
sys_memcpy(bptr->orig_bytes, buf, len);
}
- /*
- * Now allocate the ProcBin on the heap.
- */
- pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = len;
- pb->next = MSO(p).first;
- MSO(p).first = (struct erl_off_heap_header*)pb;
- pb->val = bptr;
- pb->bytes = (byte*) bptr->orig_bytes;
- pb->flags = 0;
-
- /*
- * Miscellaneous updates. Return the tagged binary.
- */
- OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
- return make_binary(pb);
+ return build_proc_bin(&MSO(p), HAlloc(p, PROC_BIN_SIZE), bptr);
}
/*
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 80adca0072..4050fb6146 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -4447,6 +4447,19 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(res);
}
}
+ else if (ERTS_IS_ATOM_STR("binary", BIF_ARG_1)) {
+ Sint64 size;
+ if (term_to_Sint64(BIF_ARG_2, &size)) {
+ Binary* refbin = erts_bin_drv_alloc_fnf(size);
+ if (!refbin)
+ BIF_RET(am_false);
+ sys_memset(refbin->orig_bytes, 0, size);
+ BIF_RET(erts_build_proc_bin(&MSO(BIF_P),
+ HAlloc(BIF_P, PROC_BIN_SIZE),
+ refbin));
+ }
+ }
+
}
BIF_ERROR(BIF_P, BADARG);
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 2d1d1443a7..c5fc6f9815 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1158,14 +1158,15 @@ static ERTS_INLINE int
analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left,
Sint *num_latin1_chars, Uint max_chars)
{
+ int res = ERTS_UTF8_OK;
Uint latin1_count;
int is_latin1;
+ Uint nchars = 0;
*err_pos = source;
if (num_latin1_chars) {
is_latin1 = 1;
latin1_count = 0;
}
- *num_chars = 0;
while (size) {
if (((*source) & ((byte) 0x80)) == 0) {
source++;
@@ -1174,11 +1175,13 @@ analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left
latin1_count++;
} else if (((*source) & ((byte) 0xE0)) == 0xC0) {
if (size < 2) {
- return ERTS_UTF8_INCOMPLETE;
+ res = ERTS_UTF8_INCOMPLETE;
+ break;
}
if (((source[1] & ((byte) 0xC0)) != 0x80) ||
((*source) < 0xC2) /* overlong */) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
if (num_latin1_chars) {
latin1_count++;
@@ -1189,16 +1192,19 @@ analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left
size -= 2;
} else if (((*source) & ((byte) 0xF0)) == 0xE0) {
if (size < 3) {
- return ERTS_UTF8_INCOMPLETE;
+ res = ERTS_UTF8_INCOMPLETE;
+ break;
}
if (((source[1] & ((byte) 0xC0)) != 0x80) ||
((source[2] & ((byte) 0xC0)) != 0x80) ||
(((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
if ((((*source) & ((byte) 0xF)) == 0xD) &&
((source[1] & 0x20) != 0)) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
source += 3;
size -= 3;
@@ -1206,37 +1212,47 @@ analyze_utf8(byte *source, Uint size, byte **err_pos, Uint *num_chars, int *left
is_latin1 = 0;
} else if (((*source) & ((byte) 0xF8)) == 0xF0) {
if (size < 4) {
- return ERTS_UTF8_INCOMPLETE;
+ res = ERTS_UTF8_INCOMPLETE;
+ break;
}
if (((source[1] & ((byte) 0xC0)) != 0x80) ||
((source[2] & ((byte) 0xC0)) != 0x80) ||
((source[3] & ((byte) 0xC0)) != 0x80) ||
(((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
if ((((*source) & ((byte)0x7)) > 0x4U) ||
((((*source) & ((byte)0x7)) == 0x4U) &&
((source[1] & ((byte)0x3F)) > 0xFU))) {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
source += 4;
size -= 4;
if (num_latin1_chars)
is_latin1 = 0;
} else {
- return ERTS_UTF8_ERROR;
+ res = ERTS_UTF8_ERROR;
+ break;
}
- ++(*num_chars);
+ ++nchars;
*err_pos = source;
- if (max_chars && size > 0 && *num_chars == max_chars)
- return ERTS_UTF8_OK_MAX_CHARS;
+ if (max_chars && size > 0 && nchars == max_chars) {
+ res = ERTS_UTF8_OK_MAX_CHARS;
+ break;
+ }
if (left && --(*left) <= 0 && size) {
- return ERTS_UTF8_ANALYZE_MORE;
+ res = ERTS_UTF8_ANALYZE_MORE;
+ break;
}
}
+
+ *num_chars = nchars;
if (num_latin1_chars)
*num_latin1_chars = is_latin1 ? latin1_count : -1;
- return ERTS_UTF8_OK;
+
+ return res;
}
int erts_analyze_utf8(byte *source, Uint size,
@@ -1252,29 +1268,18 @@ int erts_analyze_utf8_x(byte *source, Uint size,
return analyze_utf8(source, size, err_pos, num_chars, left, num_latin1_chars, max_chars);
}
-/*
- * No errors should be able to occur - no overlongs, no malformed, no nothing
- */
-static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
- Uint left,
- Uint *num_built, Uint *num_eaten, Eterm tail)
+static ERTS_INLINE Eterm
+make_list_from_utf8_buf(Eterm **hpp, Uint num,
+ byte *bytes, Uint sz,
+ Uint *num_built, Uint *num_eaten,
+ Eterm tail)
{
Eterm *hp;
Eterm ret;
+ Uint left = num;
byte *source, *ssource;
Uint unipoint;
-
- ASSERT(num > 0);
- if (left < num) {
- if (left > 0)
- num = left;
- else
- num = 1;
- }
-
- *num_built = num; /* Always */
-
- hp = HAlloc(p,num * 2);
+ hp = *hpp;
ret = tail;
source = bytes + sz;
ssource = source;
@@ -1302,20 +1307,97 @@ static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
}
ret = CONS(hp,make_small(unipoint),ret);
hp += 2;
- if (--num <= 0) {
+ if (--left <= 0) {
break;
}
}
+ *hpp = hp;
+ *num_built = num; /* Always */
*num_eaten = (ssource - source);
return ret;
}
+/*
+ * No errors should be able to occur - no overlongs, no malformed, no nothing
+ */
+static Eterm do_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz,
+ Uint left,
+ Uint *num_built, Uint *num_eaten, Eterm tail)
+{
+ Eterm *hp;
+
+ ASSERT(num > 0);
+ if (left < num) {
+ if (left > 0)
+ num = left;
+ else
+ num = 1;
+ }
+
+ hp = HAlloc(p,num * 2);
+
+ return make_list_from_utf8_buf(&hp, num, bytes, sz,
+ num_built, num_eaten,
+ tail);
+}
Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left,
Uint *num_built, Uint *num_eaten, Eterm tail)
{
return do_utf8_to_list(p, num, bytes, sz, left, num_built, num_eaten, tail);
}
+Uint erts_atom_to_string_length(Eterm atom)
+{
+ Atom *ap;
+
+ ASSERT(is_atom(atom));
+ ap = atom_tab(atom_val(atom));
+
+ if (ap->latin1_chars >= 0)
+ return (Uint) ap->len;
+ else {
+ byte* err_pos;
+ Uint num_chars;
+#ifdef DEBUG
+ int ares =
+#endif
+ erts_analyze_utf8(ap->name, ap->len, &err_pos, &num_chars, NULL);
+ ASSERT(ares == ERTS_UTF8_OK);
+
+ return num_chars;
+ }
+}
+
+Eterm erts_atom_to_string(Eterm **hpp, Eterm atom)
+{
+ Atom *ap;
+
+ ASSERT(is_atom(atom));
+ ap = atom_tab(atom_val(atom));
+ if (ap->latin1_chars >= 0)
+ return buf_to_intlist(hpp, (char*)ap->name, ap->len, NIL);
+ else {
+ Eterm res;
+ byte* err_pos;
+ Uint num_chars, num_built, num_eaten;
+#ifdef DEBUG
+ Eterm *hp_start = *hpp;
+ int ares =
+#endif
+ erts_analyze_utf8(ap->name, ap->len, &err_pos, &num_chars, NULL);
+ ASSERT(ares == ERTS_UTF8_OK);
+
+ res = make_list_from_utf8_buf(hpp, num_chars, ap->name, ap->len,
+ &num_built, &num_eaten, NIL);
+
+ ASSERT(num_built == num_chars);
+ ASSERT(num_eaten == ap->len);
+ ASSERT(*hpp - hp_start == 2*num_chars);
+
+ return res;
+ }
+}
+
static int is_candidate(Uint cp)
{
int index,pos;
diff --git a/erts/emulator/beam/erl_unicode.h b/erts/emulator/beam/erl_unicode.h
index e01eaa787e..31369fc8f9 100644
--- a/erts/emulator/beam/erl_unicode.h
+++ b/erts/emulator/beam/erl_unicode.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2016. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,4 +21,7 @@
#ifndef _ERL_UNICODE_H
#define _ERL_UNICODE_H
+Uint erts_atom_to_string_length(Eterm atom);
+Eterm erts_atom_to_string(Eterm **hpp, Eterm atom);
+
#endif /* _ERL_UNICODE_H */
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 9505942307..87777d14e9 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -867,6 +867,7 @@ Eterm erts_new_heap_binary(Process *p, byte *buf, int len, byte** datap);
Eterm erts_new_mso_binary(Process*, byte*, Uint);
Eterm new_binary(Process*, byte*, Uint);
Eterm erts_realloc_binary(Eterm bin, size_t size);
+Eterm erts_build_proc_bin(ErlOffHeap*, Eterm*, Binary*);
/* erl_bif_info.c */
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 79f87eb3a9..ba03a11405 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -158,59 +158,50 @@ int
sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
int compact)
{
- /* Note that some C compilers don't support "static const" propagation
- * so we use a defines */
- #define SYS_DOUBLE_RND_CONST 0.55555555555555555
+ #define SYS_DOUBLE_RND_CONST 0.5
#define FRAC_SIZE 52
#define EXP_SIZE 11
- #define EXP_MASK ((1ll << EXP_SIZE) - 1)
+ #define EXP_MASK (((Uint64)1 << EXP_SIZE) - 1)
#define MAX_DECIMALS (sizeof(cs_sys_double_pow10) \
/ sizeof(cs_sys_double_pow10[0]))
- #define FRAC_MASK ((1ll << FRAC_SIZE) - 1)
- #define FRAC_MASK2 ((1ll << (FRAC_SIZE + 1)) - 1)
- #define MAX_FLOAT (1ll << (FRAC_SIZE+1))
+ #define FRAC_MASK (((Uint64)1 << FRAC_SIZE) - 1)
+ #define FRAC_MASK2 (((Uint64)1 << (FRAC_SIZE + 1)) - 1)
+ #define MAX_FLOAT ((Uint64)1 << (FRAC_SIZE+1))
static const double cs_sys_double_pow10[] = {
- SYS_DOUBLE_RND_CONST / 1ll,
- SYS_DOUBLE_RND_CONST / 10ll,
- SYS_DOUBLE_RND_CONST / 100ll,
- SYS_DOUBLE_RND_CONST / 1000ll,
- SYS_DOUBLE_RND_CONST / 10000ll,
- SYS_DOUBLE_RND_CONST / 100000ll,
- SYS_DOUBLE_RND_CONST / 1000000ll,
- SYS_DOUBLE_RND_CONST / 10000000ll,
- SYS_DOUBLE_RND_CONST / 100000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000ll,
- SYS_DOUBLE_RND_CONST / 10000000000ll,
- SYS_DOUBLE_RND_CONST / 100000000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000000ll,
- SYS_DOUBLE_RND_CONST / 10000000000000ll,
- SYS_DOUBLE_RND_CONST / 100000000000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000000000ll,
- SYS_DOUBLE_RND_CONST / 10000000000000000ll,
- SYS_DOUBLE_RND_CONST / 100000000000000000ll,
- SYS_DOUBLE_RND_CONST / 1000000000000000000ll
+ SYS_DOUBLE_RND_CONST / 1e0,
+ SYS_DOUBLE_RND_CONST / 1e1,
+ SYS_DOUBLE_RND_CONST / 1e2,
+ SYS_DOUBLE_RND_CONST / 1e3,
+ SYS_DOUBLE_RND_CONST / 1e4,
+ SYS_DOUBLE_RND_CONST / 1e5,
+ SYS_DOUBLE_RND_CONST / 1e6,
+ SYS_DOUBLE_RND_CONST / 1e7,
+ SYS_DOUBLE_RND_CONST / 1e8,
+ SYS_DOUBLE_RND_CONST / 1e9,
+ SYS_DOUBLE_RND_CONST / 1e10,
+ SYS_DOUBLE_RND_CONST / 1e11,
+ SYS_DOUBLE_RND_CONST / 1e12,
+ SYS_DOUBLE_RND_CONST / 1e13,
+ SYS_DOUBLE_RND_CONST / 1e14,
+ SYS_DOUBLE_RND_CONST / 1e15,
+ SYS_DOUBLE_RND_CONST / 1e16,
+ SYS_DOUBLE_RND_CONST / 1e17,
+ SYS_DOUBLE_RND_CONST / 1e18
};
- long long mantissa, int_part = 0, frac_part = 0;
- short exp;
+ Uint64 mantissa, int_part, frac_part;
+ int exp;
+ int fbits;
int max;
int neg;
double fr;
- union { long long L; double F; } x;
+ union { Uint64 L; double F; } x;
char *p = buffer;
if (decimals < 0)
return -1;
- /* Round the number to given decimal places. The number of 5's in the
- * SYS_DOUBLE_RND_CONST constant is chosen such that adding any more 5's doesn't
- * change the double precision of the number, i.e.:
- * 1> term_to_binary(0.55555555555555555, [{minor_version, 1}]).
- * <<131,70,63,225,199,28,113,199,28,114>>
- * 2> term_to_binary(0.5555555555555555555, [{minor_version, 1}]).
- * <<131,70,63,225,199,28,113,199,28,114>>
- */
if (f >= 0) {
neg = 0;
fr = decimals < MAX_DECIMALS ? (f + cs_sys_double_pow10[decimals]) : f;
@@ -241,7 +232,7 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
}
exp -= EXP_MASK >> 1;
- mantissa |= (1ll << FRAC_SIZE);
+ mantissa |= ((Uint64)1 << FRAC_SIZE);
/* Don't bother with optimizing too large numbers or too large precision */
if (x.F > MAX_FLOAT || decimals >= MAX_DECIMALS) {
@@ -256,11 +247,16 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
return p - buffer;
} else if (exp >= FRAC_SIZE) {
int_part = mantissa << (exp - FRAC_SIZE);
+ frac_part = 0;
+ fbits = FRAC_SIZE; /* not important as frac_part==0 */
} else if (exp >= 0) {
- int_part = mantissa >> (FRAC_SIZE - exp);
- frac_part = (mantissa << (exp + 1)) & FRAC_MASK2;
+ fbits = FRAC_SIZE - exp;
+ int_part = mantissa >> fbits;
+ frac_part = mantissa & (((Uint64)1 << fbits) -1);
} else /* if (exp < 0) */ {
- frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1);
+ int_part = 0;
+ frac_part = mantissa;
+ fbits = FRAC_SIZE - exp;
}
if (!int_part) {
@@ -270,9 +266,8 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
} else {
int ret, i, n;
while (int_part != 0) {
- long long j = int_part / 10;
- *p++ = (char)(int_part - ((j << 3) + (j << 1)) + '0');
- int_part = j;
+ *p++ = (char)((int_part % 10) + '0');
+ int_part /= 10;
}
if (neg)
*p++ = '-';
@@ -298,11 +293,22 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
max = decimals;
for (i = 0; i < max; i++) {
- /* frac_part *= 10; */
- frac_part = (frac_part << 3) + (frac_part << 1);
-
- *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
- frac_part &= FRAC_MASK2;
+ if (frac_part > (ERTS_UINT64_MAX/5)) {
+ frac_part >>= 3;
+ fbits -= 3;
+ }
+
+ /* Multiply by 10 (5*2) to extract decimal digit as integer part */
+ frac_part *= 5;
+ fbits--;
+
+ if (fbits >= 64) {
+ *p++ = '0';
+ }
+ else {
+ *p++ = (char)((frac_part >> fbits) + '0');
+ frac_part &= ((Uint64)1 << fbits) - 1;
+ }
}
/* Delete trailing zeroes */
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index dca600bc7b..661a2ee6c9 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,7 +27,8 @@
constant_pools/1,constant_refc_binaries/1,
fake_literals/1,
false_dependency/1,coverage/1,fun_confusion/1,
- t_copy_literals/1, t_copy_literals_frags/1]).
+ t_copy_literals/1, t_copy_literals_frags/1,
+ erl_544/1]).
-define(line_trace, 1).
-include_lib("common_test/include/ct.hrl").
@@ -41,7 +42,8 @@ all() ->
module_md5,
constant_pools, constant_refc_binaries, fake_literals,
false_dependency,
- coverage, fun_confusion, t_copy_literals, t_copy_literals_frags].
+ coverage, fun_confusion, t_copy_literals, t_copy_literals_frags,
+ erl_544].
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -918,6 +920,53 @@ reloader(Mod,Code,Time) ->
reloader(Mod,Code,Time)
end.
+erl_544(Config) when is_list(Config) ->
+ case file:native_name_encoding() of
+ utf8 ->
+ {ok, CWD} = file:get_cwd(),
+ try
+ Mod = erl_544,
+ FileName = atom_to_list(Mod) ++ ".erl",
+ Priv = proplists:get_value(priv_dir, Config),
+ Data = proplists:get_value(data_dir, Config),
+ {ok, FileContent} = file:read_file(filename:join(Data,
+ FileName)),
+ Dir = filename:join(Priv, [16#2620,16#2620,16#2620]),
+ File = filename:join(Dir, FileName),
+ io:format("~ts~n", [File]),
+ ok = file:make_dir(Dir),
+ ok = file:set_cwd(Dir),
+ ok = file:write_file(File, [FileContent]),
+ {ok, Mod} = compile:file(File),
+ Res1 = (catch Mod:err()),
+ io:format("~p~n", [Res1]),
+ {'EXIT', {err, [{Mod, err, 0, Info1}|_]}} = Res1,
+ File = proplists:get_value(file, Info1),
+ Me = self(),
+ Go = make_ref(),
+ Tester = spawn_link(fun () ->
+ Mod:wait(Me, Go),
+ Mod:err()
+ end),
+ receive Go -> ok end,
+ Res2 = process_info(Tester, current_stacktrace),
+ io:format("~p~n", [Res2]),
+ {current_stacktrace, Stack} = Res2,
+ [{Mod, wait, 2, Info2}|_] = Stack,
+ File = proplists:get_value(file, Info2),
+ StackFun = fun(_, _, _) -> false end,
+ FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
+ Formated =
+ lib:format_stacktrace(1, Stack, StackFun, FormatFun),
+ true = is_list(Formated),
+ ok
+ after
+ ok = file:set_cwd(CWD)
+ end,
+ ok;
+ _Enc ->
+ {skipped, "Only run when native file name encoding is utf8"}
+ end.
%% Utilities.
diff --git a/erts/emulator/test/code_SUITE_data/erl_544.erl b/erts/emulator/test/code_SUITE_data/erl_544.erl
new file mode 100644
index 0000000000..c93f3ef5bc
--- /dev/null
+++ b/erts/emulator/test/code_SUITE_data/erl_544.erl
@@ -0,0 +1,35 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erl_544).
+
+-export([err/0, wait/2]).
+
+err() ->
+ erlang:error(err).
+
+wait(Pid, Msg) ->
+ erlang:yield(),
+ Pid ! Msg,
+ receive
+ after infinity ->
+ ok
+ end,
+ err().
diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl
index 1c76eb8019..d950179882 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -118,6 +118,7 @@ t_float(Config) when is_list(Config) ->
%% Tests float_to_list/1, float_to_list/2, float_to_binary/1, float_to_binary/2
t_float_to_string(Config) when is_list(Config) ->
+ rand_seed(),
test_fts("0.00000000000000000000e+00", 0.0),
test_fts("2.50000000000000000000e+01", 25.0),
test_fts("2.50000000000000000000e+00", 2.5),
@@ -167,8 +168,8 @@ t_float_to_string(Config) when is_list(Config) ->
test_fts("1.12300",1.123, [{decimals, 5}]),
test_fts("1.123",1.123, [{decimals, 5}, compact]),
test_fts("1.1234",1.1234,[{decimals, 6}, compact]),
- test_fts("1.01",1.005, [{decimals, 2}]),
- test_fts("-1.01",-1.005,[{decimals, 2}]),
+ test_fts("1.00",1.005, [{decimals, 2}]), %% 1.005 is really 1.0049999999...
+ test_fts("-1.00",-1.005,[{decimals, 2}]),
test_fts("0.999",0.999, [{decimals, 3}]),
test_fts("-0.999",-0.999,[{decimals, 3}]),
test_fts("1.0",0.999, [{decimals, 2}, compact]),
@@ -184,6 +185,9 @@ t_float_to_string(Config) when is_list(Config) ->
test_fts("123000000000000000000.0",1.23e20, [{decimals, 10}, compact]),
test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]),
test_fts("1.23000000000000000000e+20",1.23e20, []),
+
+ fts_rand_float_decimals(1000),
+
ok.
test_fts(Expect, Float) ->
@@ -197,6 +201,49 @@ test_fts(Expect, Float, Args) ->
BinExpect = float_to_binary(Float,Args).
+rand_float_reasonable() ->
+ F = rand_float(),
+ case abs(F) > 1.0e238 of
+ true -> rand_float_reasonable();
+ false -> F
+ end.
+
+fts_rand_float_decimals(0) -> ok;
+fts_rand_float_decimals(N) ->
+ [begin
+ F0 = rand_float_reasonable(),
+ L0 = float_to_list(F0, [{decimals, D}]),
+ L1 = case D of
+ 0 -> L0 ++ ".0";
+ _ -> L0
+ end,
+ F1 = list_to_float(L1),
+ Diff = abs(F0-F1),
+ MaxDiff = max_diff_decimals(F0, D),
+ ok = case Diff =< MaxDiff of
+ true -> ok;
+ false ->
+ io:format("F0 = ~w ~w\n", [F0, <<F0/float>>]),
+ io:format("L1 = ~s\n", [L1]),
+ io:format("F1 = ~w ~w\n", [F1, <<F1/float>>]),
+ io:format("Diff = ~w, MaxDiff = ~w\n", [Diff, MaxDiff]),
+ error
+ end
+ end
+ || D <- lists:seq(0,15)],
+
+ fts_rand_float_decimals(N-1).
+
+max_diff_decimals(F, D) ->
+ IntBits = floor(math:log2(abs(F))) + 1,
+ FracBits = (52 - IntBits),
+ Log10_2 = 0.3010299956639812, % math:log10(2)
+ MaxDec = floor(FracBits * Log10_2),
+
+ Resolution = math:pow(2, IntBits - 53),
+
+ (math:pow(10, -min(D,MaxDec)) / 2) + Resolution.
+
%% Tests list_to_float/1.
t_string_to_float_safe(Config) when is_list(Config) ->
@@ -331,18 +378,26 @@ t_trunc_and_friends(_Config) ->
-18446744073709551616 = trunc_and_friends(-float(1 bsl 64)),
%% Random.
+ rand_seed(),
t_trunc_and_friends_rand(100),
ok.
+rand_seed() ->
+ rand:seed(exrop),
+ io:format("\n*** rand:export_seed() = ~w\n\n", [rand:export_seed()]),
+ ok.
+
+rand_float() ->
+ F0 = rand:uniform() * math:pow(10, 50*rand:normal()),
+ case rand:uniform() of
+ U when U < 0.5 -> -F0;
+ _ -> F0
+ end.
+
t_trunc_and_friends_rand(0) ->
ok;
t_trunc_and_friends_rand(N) ->
- F0 = rand:uniform() * math:pow(10, 50*rand:normal()),
- F = case rand:uniform() of
- U when U < 0.5 -> -F0;
- _ -> F0
- end,
- _ = trunc_and_friends(F),
+ _ = trunc_and_friends(rand_float()),
t_trunc_and_friends_rand(N-1).
trunc_and_friends(F) ->
@@ -491,7 +546,7 @@ t_string_to_integer(Config) when is_list(Config) ->
list_to_binary(Value),Base)),
{'EXIT', {badarg, _}} =
(catch erlang:list_to_integer(Value,Base))
- end,[{" 1",1},{" 1",37},{"2",2},{"C",11},
+ end,[{" 1",1},{" 1",37},{"2",2},{"B",11},{"b",11},{":", 16},
{"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16},
{"1z111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",16},
{"111z11111111",16}]),
diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c
index 3daa066fd3..3302083288 100644
--- a/erts/lib_src/common/erl_printf_format.c
+++ b/erts/lib_src/common/erl_printf_format.c
@@ -331,7 +331,7 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
char *bufp = sbuf;
double dexp;
int exp;
- size_t max_size = 1;
+ size_t max_size = 2; /* including possible sign */
int size;
int new_fmt = fmt;
int fpe_was_unmasked;
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index f796ea64d3..109d3313c7 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -2452,7 +2452,7 @@ term_to_binary(_Term) ->
Term :: term(),
Options :: [compressed |
{compressed, Level :: 0..9} |
- {minor_version, Version :: 0..1} ].
+ {minor_version, Version :: 0..2} ].
term_to_binary(_Term, _Options) ->
erlang:nif_error(undefined).
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index b98a704e28..bfeffa969f 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -1355,8 +1355,8 @@ xref_export_all(_Config) ->
[] ->
ok;
[_|_] ->
- S = [io_lib:format("~p:~p/~p\n", [M,F,A]) || {M,F,A} <- Unused],
- io:format("There are unused functions:\n\n~s\n", [S]),
+ Msg = [io_lib:format("~p:~p/~p\n", [M,F,A]) || {M,F,A} <- Unused],
+ io:format("There are unused functions:\n\n~s\n", [Msg]),
?t:fail(unused_functions)
end.
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 6bf3f2795b..c8abfa524f 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -351,6 +351,8 @@ flt_need_heap_2({set,_,_,get_list}, H, Fl) ->
{[],H,Fl};
flt_need_heap_2({set,_,_,{try_catch,_,_}}, H, Fl) ->
{[],H,Fl};
+flt_need_heap_2({set,_,_,init}, H, Fl) ->
+ {[],H,Fl};
%% All other instructions should cause the insertion of an allocation
%% instruction if needed.
flt_need_heap_2(_, H, Fl) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index be8908dd6b..6219bc6894 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -529,9 +529,10 @@ valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
Type = bif_type(Op, Src, Vst),
set_type_reg(Type, Dst, Vst);
valfun_4({gc_bif,Op,{f,Fail},Live,Src,Dst}, #vst{current=St0}=Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
St = kill_heap_allocation(St0),
Vst1 = Vst0#vst{current=St},
- verify_live(Live, Vst1),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
validate_src(Src, Vst),
@@ -685,6 +686,7 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -697,6 +699,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -709,6 +712,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
assert_term(Bits, Vst0),
assert_term(Bin, Vst0),
Vst1 = heap_alloc(Heap, Vst0),
@@ -944,6 +948,7 @@ deallocate(#vst{current=St}=Vst) ->
test_heap(Heap, Live, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
heap_alloc(Heap, Vst).
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 6957d25774..9a3ea07c97 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -179,6 +179,12 @@
# define HAVE_ECB_IVEC_BUG
#endif
+#define HAVE_RSA_SSLV23_PADDING
+#if defined(HAS_LIBRESSL) \
+ && LIBRESSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(2,6,1)
+# undef HAVE_RSA_SSLV23_PADDING
+#endif
+
#if defined(HAVE_CMAC)
#include <openssl/cmac.h>
#endif
@@ -659,7 +665,9 @@ static ERL_NIF_TERM atom_rsa_oaep_md;
static ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */
static ERL_NIF_TERM atom_rsa_padding;
static ERL_NIF_TERM atom_rsa_pkcs1_pss_padding;
+#ifdef HAVE_RSA_SSLV23_PADDING
static ERL_NIF_TERM atom_rsa_sslv23_padding;
+#endif
static ERL_NIF_TERM atom_rsa_x931_padding;
static ERL_NIF_TERM atom_rsa_pss_saltlen;
static ERL_NIF_TERM atom_sha224;
@@ -1064,7 +1072,9 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
atom_rsa_pad = enif_make_atom(env,"rsa_pad"); /* backwards compatibility */
atom_rsa_padding = enif_make_atom(env,"rsa_padding");
atom_rsa_pkcs1_pss_padding = enif_make_atom(env,"rsa_pkcs1_pss_padding");
+#ifdef HAVE_RSA_SSLV23_PADDING
atom_rsa_sslv23_padding = enif_make_atom(env,"rsa_sslv23_padding");
+#endif
atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding");
atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen");
atom_sha224 = enif_make_atom(env,"sha224");
@@ -4449,8 +4459,10 @@ static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NI
opt->rsa_padding = RSA_PKCS1_PADDING;
} else if (tpl_terms[1] == atom_rsa_pkcs1_oaep_padding) {
opt->rsa_padding = RSA_PKCS1_OAEP_PADDING;
+#ifdef HAVE_RSA_SSLV23_PADDING
} else if (tpl_terms[1] == atom_rsa_sslv23_padding) {
opt->rsa_padding = RSA_SSLV23_PADDING;
+#endif
} else if (tpl_terms[1] == atom_rsa_x931_padding) {
opt->rsa_padding = RSA_X931_PADDING;
} else if (tpl_terms[1] == atom_rsa_no_padding) {
@@ -4516,7 +4528,10 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
#endif
PKeyCryptOptions crypt_opt;
ErlNifBinary in_bin, out_bin, tmp_bin;
- size_t outlen, tmplen;
+ size_t outlen;
+#ifdef HAVE_RSA_SSLV23_PADDING
+ size_t tmplen;
+#endif
int is_private = (argv[4] == atom_true),
is_encrypt = (argv[5] == atom_true);
int algo_init = 0;
@@ -4596,6 +4611,7 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
if (crypt_opt.signature_md != NULL
&& EVP_PKEY_CTX_set_signature_md(ctx, crypt_opt.signature_md) <= 0)
goto badarg;
+#ifdef HAVE_RSA_SSLV23_PADDING
if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) {
if (is_encrypt) {
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
@@ -4607,9 +4623,11 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
in_bin = tmp_bin;
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) goto badarg;
- } else {
+ } else
+#endif
+ {
if (EVP_PKEY_CTX_set_rsa_padding(ctx, crypt_opt.rsa_padding) <= 0) goto badarg;
- }
+ }
#ifdef HAVE_RSA_OAEP_MD
if (crypt_opt.rsa_padding == RSA_PKCS1_OAEP_PADDING) {
if (crypt_opt.rsa_oaep_md != NULL
@@ -4728,6 +4746,7 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
#endif
if ((i > 0) && argv[0] == atom_rsa && !is_encrypt) {
+#ifdef HAVE_RSA_SSLV23_PADDING
if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) {
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
unsigned char *p;
@@ -4745,6 +4764,7 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
i = 1;
}
}
+#endif
}
if (tmp_bin.data != NULL) {
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 8367432ac5..f6433a807d 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -3116,7 +3116,10 @@ state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
state__remove_added_warnings(OldState, NewState) ->
#state{warnings = OldWarnings} = OldState,
#state{warnings = NewWarnings} = NewState,
- {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}.
+ case NewWarnings =:= OldWarnings of
+ true -> {[], NewState};
+ false -> {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}
+ end.
state__add_warnings(Warns, #state{warnings = Warnings} = State) ->
State#state{warnings = Warns ++ Warnings}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/abs.erl b/lib/dialyzer/test/small_SUITE_data/src/abs.erl
index 251e24cdfc..0e38c3dbb7 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/abs.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/abs.erl
@@ -5,7 +5,7 @@
-export([t/0]).
t() ->
- Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0],
+ Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0, fun erl_551/0],
_ = [catch F() || F <- Fs],
ok.
@@ -60,6 +60,13 @@ f1() ->
f1(A) ->
abs(A).
+erl_551() ->
+ accept(9),
+ accept(-3).
+
+accept(Number) when abs(Number) >= 8 -> first;
+accept(_Number) -> second.
+
-spec int() -> integer().
int() ->
diff --git a/lib/dialyzer/test/small_SUITE_data/src/bsL.erl b/lib/dialyzer/test/small_SUITE_data/src/bsL.erl
new file mode 100644
index 0000000000..b2fdc16324
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/bsL.erl
@@ -0,0 +1,13 @@
+-module(bsL).
+
+-export([t/0]).
+
+%% Found in lib/observer/test/crashdump_helper.erl.
+
+t() ->
+ Size = 60,
+ <<H:16/unit:8>> = erlang:md5(<<Size:32>>),
+ true = H < 20,
+ true = H > 2,
+ Data = ((H bsl (8*150)) div (H+7919)),
+ <<Data:Size/unit:8>>.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl b/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl
new file mode 100644
index 0000000000..2dc00d272a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl
@@ -0,0 +1,14 @@
+-module(erl_tar_table).
+
+%% OTP-14860, PR 1670.
+
+-export([t/0, v/0, x/0]).
+
+t() ->
+ {ok, ["file"]} = erl_tar:table("table.tar").
+
+v() ->
+ {ok, [{_,_,_,_,_,_,_}]} = erl_tar:table("table.tar", [verbose]).
+
+x() ->
+ {ok, ["file"]} = erl_tar:table("table.tar", []).
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index a3a936322a..518f67ee1b 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1903,7 +1903,8 @@ infinity_div(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
infinity_bsl(pos_inf, _) -> pos_inf;
infinity_bsl(neg_inf, _) -> neg_inf;
-infinity_bsl(Number, pos_inf) when is_integer(Number), Number >= 0 -> pos_inf;
+infinity_bsl(0, pos_inf) -> 0;
+infinity_bsl(Number, pos_inf) when is_integer(Number), Number > 0 -> pos_inf;
infinity_bsl(Number, pos_inf) when is_integer(Number) -> neg_inf;
infinity_bsl(Number, neg_inf) when is_integer(Number), Number >= 0 -> 0;
infinity_bsl(Number, neg_inf) when is_integer(Number) -> -1;
@@ -1992,9 +1993,11 @@ arith_abs(X1, Opaques) ->
case infinity_geq(Min1, 0) of
true -> {Min1, Max1};
false ->
+ NegMin1 = infinity_inv(Min1),
+ NegMax1 = infinity_inv(Max1),
case infinity_geq(Max1, 0) of
- true -> {0, infinity_inv(Min1)};
- false -> {infinity_inv(Max1), infinity_inv(Min1)}
+ true -> {0, max(NegMin1, Max1)};
+ false -> {NegMax1, NegMin1}
end
end,
t_from_range(NewMin, NewMax)
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 4e0f93212d..2b290b2f23 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -2351,6 +2351,8 @@ t_from_range(X, Y) ->
-else.
+t_from_range(pos_inf, pos_inf) -> ?integer_pos;
+t_from_range(neg_inf, neg_inf) -> ?integer_neg;
t_from_range(neg_inf, pos_inf) -> t_integer();
t_from_range(neg_inf, Y) when is_integer(Y), Y < 0 -> ?integer_neg;
t_from_range(neg_inf, Y) when is_integer(Y), Y >= 0 -> t_integer();
@@ -2383,6 +2385,8 @@ t_from_range(pos_inf, neg_inf) -> t_none().
-spec t_from_range_unsafe(rng_elem(), rng_elem()) -> erl_type().
+t_from_range_unsafe(pos_inf, pos_inf) -> ?integer_pos;
+t_from_range_unsafe(neg_inf, neg_inf) -> ?integer_neg;
t_from_range_unsafe(neg_inf, pos_inf) -> t_integer();
t_from_range_unsafe(neg_inf, Y) -> ?int_range(neg_inf, Y);
t_from_range_unsafe(X, pos_inf) -> ?int_range(X, pos_inf);
diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl
index 641d3fda0a..e04b171194 100644
--- a/lib/hipe/llvm/hipe_llvm.erl
+++ b/lib/hipe/llvm/hipe_llvm.erl
@@ -934,7 +934,7 @@ pp_ins(Dev, Ver, I) ->
end,
case call_is_tail(I) of
true -> write(Dev, "tail ");
- false -> ok
+ false -> write(Dev, "notail ")
end,
write(Dev, ["call ", call_cconv(I), " "]),
pp_options(Dev, call_ret_attrs(I)),
diff --git a/lib/hipe/test/bs_SUITE_data/bs_construct.erl b/lib/hipe/test/bs_SUITE_data/bs_construct.erl
index b9e7d93570..aa85626857 100644
--- a/lib/hipe/test/bs_SUITE_data/bs_construct.erl
+++ b/lib/hipe/test/bs_SUITE_data/bs_construct.erl
@@ -279,13 +279,22 @@ bad_floats() ->
%% (incorrectly) signed.
huge_binaries() ->
- AlmostIllegal = id(<<0:(id((1 bsl 32)-8))>>),
case erlang:system_info(wordsize) of
- 4 -> huge_binaries_32(AlmostIllegal);
+ 4 ->
+ Old = erts_debug:set_internal_state(available_internal_state, true),
+ case erts_debug:set_internal_state(binary, (1 bsl 29)-1) of
+ false ->
+ io:format("\nNot enough memory to create 512Mb binary\n",[]);
+ Bin->
+ huge_binaries_32(Bin)
+ end,
+ erts_debug:set_internal_state(available_internal_state, Old);
+
8 -> ok
end,
garbage_collect(),
id(<<0:(id((1 bsl 31)-1))>>),
+ garbage_collect(),
id(<<0:(id((1 bsl 30)-1))>>),
garbage_collect(),
ok.
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
index b3a18e03d4..446b46ad82 100644
--- a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
@@ -1365,8 +1365,8 @@ static int cmp_strRec(m_strRec *b1, m_strRec *b2)
return 0;
if (!cmp_str(b1->str6,b2->str6))
return 0;
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
if (b1->str7[i][j] != b2->str7[i][j])
return 0;
for (j = 0; j < 3; j++)
@@ -1579,8 +1579,8 @@ static void print_strRec(m_strRec* sr)
fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
fprintf(stdout, "string str4 : %s\n",sr->str4);
fprintf(stdout, "str7[2][3] :\n");
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
for (j = 0; j < sr->str5._length; j++)
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
index 40c7328f03..d6a78d2481 100644
--- a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
@@ -1369,8 +1369,8 @@ static int cmp_strRec(m_strRec *b1, m_strRec *b2)
return 0;
if (!cmp_str(b1->str6,b2->str6))
return 0;
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
if (b1->str7[i][j] != b2->str7[i][j])
return 0;
for (j = 0; j < 3; j++)
@@ -1583,8 +1583,8 @@ static void print_strRec(m_strRec* sr)
fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
fprintf(stdout, "string str4 : %s\n",sr->str4);
fprintf(stdout, "str7[2][3] :\n");
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
for (j = 0; j < sr->str5._length; j++)
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
index 33cfe71322..17ef21f4f4 100644
--- a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
@@ -1369,8 +1369,8 @@ static int cmp_strRec(m_strRec *b1, m_strRec *b2)
return 0;
if (!cmp_str(b1->str6,b2->str6))
return 0;
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
if (b1->str7[i][j] != b2->str7[i][j])
return 0;
for (j = 0; j < 3; j++)
@@ -1583,8 +1583,8 @@ static void print_strRec(m_strRec* sr)
fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
fprintf(stdout, "string str4 : %s\n",sr->str4);
fprintf(stdout, "str7[2][3] :\n");
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
for (j = 0; j < sr->str5._length; j++)
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index e5ac031539..0762cebc94 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>1996</year><year>2017</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -469,8 +469,12 @@ MaxT = TickTime + TickTime / 4</code>
<item><c>ObjSuffix = string()</c></item>
<item><c>SrcSuffix = string()</c></item>
</list>
- <p>Specifies a list of rules for use by <c>filelib:find_file/2</c> and
- <c>filelib:find_source/2</c>. If this is set to some other value
+ <p>Specifies a list of rules for use by
+ <seealso marker="stdlib:filelib#find_file/2">
+ <c>filelib:find_file/2</c></seealso>
+ <seealso marker="stdlib:filelib#find_source/2">
+ <c>filelib:find_source/2</c></seealso>
+ If this is set to some other value
than the empty list, it replaces the default rules. Rules can be
simple pairs of directory suffixes, such as <c>{"ebin",
"src"}</c>, which are used by <c>filelib:find_file/2</c>, or
@@ -478,6 +482,16 @@ MaxT = TickTime + TickTime / 4</code>
file name extensions, for example <c>[{".beam", ".erl", [{"ebin",
"src"}]}</c>, which are used by <c>filelib:find_source/2</c>. Both
kinds of rules can be mixed in the list.</p>
+ <p>The interpretation of <c>ObjDirSuffix</c> and <c>SrcDirSuffix</c>
+ is as follows: if the end of the directory name where an
+ object is located matches <c>ObjDirSuffix</c>, then the
+ name created by replacing <c>ObjDirSuffix</c> with
+ <c>SrcDirSuffix</c> is expanded by calling
+ <seealso marker="stdlib:filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seealso>, and the first regular
+ file found among the matches is the source file.
+ </p>
+
</item>
</taglist>
</section>
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl
index 70cbf1c87c..99ea8dc384 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -266,7 +266,7 @@ inc_wrap_file(Log) ->
Size :: dlog_size(),
Reason :: no_such_log | nonode | {read_only_mode, Log}
| {blocked_log, Log}
- | {new_size_too_small, CurrentSize :: pos_integer()}
+ | {new_size_too_small, Log, CurrentSize :: pos_integer()}
| {badarg, size}
| {file_error, file:filename(), file_error()}.
change_size(Log, NewSize) ->
diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl
index ac81cc9689..2578b74428 100644
--- a/lib/kernel/src/erl_boot_server.erl
+++ b/lib/kernel/src/erl_boot_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -58,13 +58,11 @@
-define(single_addr_mask, {255, 255, 255, 255}).
--type ip4_address() :: {0..255,0..255,0..255,0..255}.
-
--spec start(Slaves) -> {'ok', Pid} | {'error', What} when
+-spec start(Slaves) -> {'ok', Pid} | {'error', Reason} when
Slaves :: [Host],
- Host :: atom(),
+ Host :: inet:ip_address() | inet:hostname(),
Pid :: pid(),
- What :: any().
+ Reason :: {'badarg', Slaves}.
start(Slaves) ->
case check_arg(Slaves) of
@@ -74,11 +72,11 @@ start(Slaves) ->
{error, {badarg, Slaves}}
end.
--spec start_link(Slaves) -> {'ok', Pid} | {'error', What} when
+-spec start_link(Slaves) -> {'ok', Pid} | {'error', Reason} when
Slaves :: [Host],
- Host :: atom(),
+ Host :: inet:ip_address() | inet:hostname(),
Pid :: pid(),
- What :: any().
+ Reason :: {'badarg', Slaves}.
start_link(Slaves) ->
case check_arg(Slaves) of
@@ -104,10 +102,10 @@ check_arg([], Result) ->
check_arg(_, _Result) ->
error.
--spec add_slave(Slave) -> 'ok' | {'error', What} when
+-spec add_slave(Slave) -> 'ok' | {'error', Reason} when
Slave :: Host,
- Host :: atom(),
- What :: any().
+ Host :: inet:ip_address() | inet:hostname(),
+ Reason :: {'badarg', Slave}.
add_slave(Slave) ->
case inet:getaddr(Slave, inet) of
@@ -117,10 +115,10 @@ add_slave(Slave) ->
{error, {badarg, Slave}}
end.
--spec delete_slave(Slave) -> 'ok' | {'error', What} when
+-spec delete_slave(Slave) -> 'ok' | {'error', Reason} when
Slave :: Host,
- Host :: atom(),
- What :: any().
+ Host :: inet:ip_address() | inet:hostname(),
+ Reason :: {'badarg', Slave}.
delete_slave(Slave) ->
case inet:getaddr(Slave, inet) of
@@ -130,7 +128,7 @@ delete_slave(Slave) ->
{error, {badarg, Slave}}
end.
--spec add_subnet(Mask :: ip4_address(), Addr :: ip4_address()) ->
+-spec add_subnet(Netmask :: inet:ip_address(), Addr :: inet:ip_address()) ->
'ok' | {'error', any()}.
add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) ->
@@ -141,14 +139,15 @@ add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) ->
{error, empty_subnet}
end.
--spec delete_subnet(Mask :: ip4_address(), Addr :: ip4_address()) -> 'ok'.
+-spec delete_subnet(Netmask :: inet:ip_address(),
+ Addr :: inet:ip_address()) -> 'ok'.
delete_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) ->
gen_server:call(boot_server, {delete, {Mask, Addr}}).
-spec which_slaves() -> Slaves when
- Slaves :: [Host],
- Host :: atom().
+ Slaves :: [Slave],
+ Slave :: {Netmask :: inet:ip_address(), Address :: inet:ip_address()}.
which_slaves() ->
gen_server:call(boot_server, which).
diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl
index 91f3663cc5..9745848992 100644
--- a/lib/kernel/src/group_history.erl
+++ b/lib/kernel/src/group_history.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -260,7 +260,7 @@ resize_log(Name, _OldSize, NewSize) ->
ok ->
show('$#erlang-history-resize-result',
"ok~n", []);
- {error, {new_size_too_small, _}} ->
+ {error, {new_size_too_small, _, _}} ->
show('$#erlang-history-resize-result',
"failed (new size is too small)~n", []),
disable_history();
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index fe2fc778f2..12e2521939 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 0a7f73c344..0e7b7adc47 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -3470,8 +3470,8 @@ start_procs(Parent, N1, N2, N3, Config) ->
Pid6 = rpc:call(N3, ?MODULE, start_proc3, [test4]),
assert_pid(Pid6),
yes = global:register_name(test1, Pid3),
- yes = global:register_name(test2, Pid4, {global, notify_all_name}),
- yes = global:register_name(test3, Pid5, {global, random_notify_name}),
+ yes = global:register_name(test2, Pid4, fun global:notify_all_name/3),
+ yes = global:register_name(test3, Pid5, fun global:random_notify_name/3),
Resolve = fun(Name, Pid1, Pid2) ->
Parent ! {resolve_called, Name, node()},
{Min, Max} = minmax(Pid1, Pid2),
@@ -3546,7 +3546,7 @@ start_proc_basic(Name) ->
end.
init_proc_basic(Parent, Name) ->
- X = global:register_name(Name, self(), {?MODULE, fix_basic_name}),
+ X = global:register_name(Name, self(), fun ?MODULE:fix_basic_name/3),
Parent ! {self(),X},
loop().
@@ -3791,15 +3791,6 @@ stop() ->
test_server:stop_node(Node)
end, nodes()).
-dbg_logs(Name) -> dbg_logs(Name, ?NODES).
-
-dbg_logs(Name, Nodes) ->
- lists:foreach(fun(N) ->
- F = lists:concat([Name, ".log.", N, ".txt"]),
- ok = sys:log_to_file({global_name_server, N}, F)
- end, Nodes).
-
-
%% Tests that locally loaded nodes do not loose contact with other nodes.
global_lost_nodes(Config) when is_list(Config) ->
Timeout = 60,
diff --git a/lib/observer/include/etop.hrl b/lib/observer/include/etop.hrl
index 002937e522..f8d370450b 100644
--- a/lib/observer/include/etop.hrl
+++ b/lib/observer/include/etop.hrl
@@ -18,4 +18,4 @@
%% %CopyrightEnd%
%%
--include("../../runtime_tools/include/observer_backend.hrl").
+-include_lib("runtime_tools/include/observer_backend.hrl").
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index f6d282638a..6c4739042b 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -48,6 +48,7 @@ init([Id, Data, ParentFrame, Callback, App, Parent]) ->
display_progress(ParentFrame,App),
case Callback:get_details(Id, Data) of
{ok,Details} ->
+ display_progress_pulse(Callback,Id),
init(Id,ParentFrame,Callback,App,Parent,Details);
{yes_no, Info, Fun} ->
destroy_progress(App),
@@ -69,8 +70,16 @@ display_progress(ParentFrame,cdv) ->
"Reading data");
display_progress(_,_) ->
ok.
+
+%% Display pulse while creating process detail page with much data
+display_progress_pulse(cdv_proc_cb,Pid) ->
+ observer_lib:report_progress({ok,"Displaying data for "++Pid}),
+ observer_lib:report_progress({ok,start_pulse});
+display_progress_pulse(_,_) ->
+ ok.
+
destroy_progress(cdv) ->
- observer_lib:destroy_progress_dialog();
+ observer_lib:sync_destroy_progress_dialog();
destroy_progress(_) ->
ok.
diff --git a/lib/observer/src/cdv_info_wx.erl b/lib/observer/src/cdv_info_wx.erl
index 7e416dd11a..07c28610e2 100644
--- a/lib/observer/src/cdv_info_wx.erl
+++ b/lib/observer/src/cdv_info_wx.erl
@@ -95,6 +95,10 @@ handle_cast(Msg, State) ->
io:format("~p~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
+handle_event(#wx{obj=MoreEntry,event=#wxMouse{type=left_down},userData={more,More}}, State) ->
+ observer_lib:add_scroll_entries(MoreEntry,More),
+ {noreply, State};
+
handle_event(#wx{event=#wxMouse{type=left_down},userData=Target}, State) ->
cdv_virtual_list_wx:start_detail_win(Target),
{noreply, State};
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index feaec5c678..861b592439 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1240,7 +1240,7 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"Last calls" ->
get_procinfo(Fd,Fun,Proc#proc{last_calls=get_last_calls(Fd)},WS);
"Link list" ->
- {Links,Monitors,MonitoredBy} = parse_link_list(bytes(Fd),[],[],[]),
+ {Links,Monitors,MonitoredBy} = get_link_list(Fd),
get_procinfo(Fd,Fun,Proc#proc{links=Links,
monitors=Monitors,
mon_by=MonitoredBy},WS);
@@ -1322,86 +1322,64 @@ get_last_calls(Fd,<<>>,Acc,Lines) ->
lists:reverse(Lines,[byte_list_to_string(lists:reverse(Acc))])
end.
-parse_link_list([SB|Str],Links,Monitors,MonitoredBy) when SB==$[; SB==$] ->
- parse_link_list(Str,Links,Monitors,MonitoredBy);
-parse_link_list("#Port"++_=Str,Links,Monitors,MonitoredBy) ->
- {Link,Rest} = parse_port(Str),
- parse_link_list(Rest,[Link|Links],Monitors,MonitoredBy);
-parse_link_list("<"++_=Str,Links,Monitors,MonitoredBy) ->
- {Link,Rest} = parse_pid(Str),
- parse_link_list(Rest,[Link|Links],Monitors,MonitoredBy);
-parse_link_list("{to,"++Str,Links,Monitors,MonitoredBy) ->
- {Mon,Rest} = parse_monitor(Str),
- parse_link_list(Rest,Links,[Mon|Monitors],MonitoredBy);
-parse_link_list("{from,"++Str,Links,Monitors,MonitoredBy) ->
- {Mon,Rest} = parse_monitor(Str),
- parse_link_list(Rest,Links,Monitors,[Mon|MonitoredBy]);
-parse_link_list(", "++Rest,Links,Monitors,MonitoredBy) ->
- parse_link_list(Rest,Links,Monitors,MonitoredBy);
-parse_link_list([],Links,Monitors,MonitoredBy) ->
- {lists:reverse(Links),lists:reverse(Monitors),lists:reverse(MonitoredBy)};
-parse_link_list(Unexpected,Links,Monitors,MonitoredBy) ->
- io:format("WARNING: found unexpected data in link list:~n~ts~n",[Unexpected]),
- parse_link_list([],Links,Monitors,MonitoredBy).
-
-
-parse_port(Str) ->
- {Port,Rest} = parse_link(Str,[]),
- {{Port,Port},Rest}.
-
-parse_pid(Str) ->
- {Pid,Rest} = parse_link(Str,[]),
- {{Pid,Pid},Rest}.
-
-parse_monitor("{"++Str) ->
- %% Named process
- {Name,Node,Rest1} = parse_name_node(Str,[]),
- Pid = get_pid_from_name(Name,Node),
- case parse_link(string:strip(Rest1,left,$,),[]) of
- {Ref,"}"++Rest2} ->
- %% Bug in break.c - prints an extra "}" for remote
- %% nodes... thus the strip
- {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},
- string:strip(Rest2,left,$})};
- {Ref,[]} ->
- {{Pid,"{"++Name++","++Node++"} ("++Ref++")"},[]}
- end;
-parse_monitor(Str) ->
- case parse_link(Str,[]) of
- {Pid,","++Rest1} ->
- case parse_link(Rest1,[]) of
- {Ref,"}"++Rest2} ->
- {{Pid,Pid++" ("++Ref++")"},Rest2};
- {Ref,[]} ->
- {{Pid,Pid++" ("++Ref++")"},[]}
- end;
- {Pid,[]} ->
- {{Pid,Pid++" (unknown_ref)"},[]}
+get_link_list(Fd) ->
+ case get_chunk(Fd) of
+ {ok,<<"[",Bin/binary>>} ->
+ #{links:=Links,
+ mons:=Monitors,
+ mon_by:=MonitoredBy} =
+ get_link_list(Fd,Bin,#{links=>[],mons=>[],mon_by=>[]}),
+ {lists:reverse(Links),
+ lists:reverse(Monitors),
+ lists:reverse(MonitoredBy)};
+ eof ->
+ {[],[],[]}
end.
-parse_link(">"++Rest,Acc) ->
- {lists:reverse(Acc,">"),Rest};
-parse_link([H|T],Acc) ->
- parse_link(T,[H|Acc]);
-parse_link([],Acc) ->
- %% truncated
- {lists:reverse(Acc),[]}.
+get_link_list(Fd,<<NL:8,_/binary>>=Bin,Acc) when NL=:=$\r; NL=:=$\n->
+ skip(Fd,Bin),
+ Acc;
+get_link_list(Fd,Bin,Acc) ->
+ case binary:split(Bin,[<<", ">>,<<"]">>]) of
+ [Link,Rest] ->
+ get_link_list(Fd,Rest,get_link(Link,Acc));
+ [Incomplete] ->
+ case get_chunk(Fd) of
+ {ok,More} ->
+ get_link_list(Fd,<<Incomplete/binary,More/binary>>,Acc);
+ eof ->
+ Acc
+ end
+ end.
-parse_name_node(","++Rest,Name) ->
- parse_name_node(Rest,Name,[]);
-parse_name_node([H|T],Name) ->
- parse_name_node(T,[H|Name]);
-parse_name_node([],Name) ->
- %% truncated
- {lists:reverse(Name),[],[]}.
-
-parse_name_node("}"++Rest,Name,Node) ->
- {lists:reverse(Name),lists:reverse(Node),Rest};
-parse_name_node([H|T],Name,Node) ->
- parse_name_node(T,Name,[H|Node]);
-parse_name_node([],Name,Node) ->
- %% truncated
- {lists:reverse(Name),lists:reverse(Node),[]}.
+get_link(<<"#Port",_/binary>>=PortBin,#{links:=Links}=Acc) ->
+ PortStr = binary_to_list(PortBin),
+ Acc#{links=>[{PortStr,PortStr}|Links]};
+get_link(<<"<",_/binary>>=PidBin,#{links:=Links}=Acc) ->
+ PidStr = binary_to_list(PidBin),
+ Acc#{links=>[{PidStr,PidStr}|Links]};
+get_link(<<"{to,",Bin/binary>>,#{mons:=Monitors}=Acc) ->
+ Acc#{mons=>[parse_monitor(Bin)|Monitors]};
+get_link(<<"{from,",Bin/binary>>,#{mon_by:=MonitoredBy}=Acc) ->
+ Acc#{mon_by=>[parse_monitor(Bin)|MonitoredBy]};
+get_link(Unexpected,Acc) ->
+ io:format("WARNING: found unexpected data in link list:~n~ts~n",[Unexpected]),
+ Acc.
+
+parse_monitor(MonBin) ->
+ case binary:split(MonBin,[<<",">>,<<"{">>,<<"}">>],[global]) of
+ [PidBin,RefBin,<<>>] ->
+ PidStr = binary_to_list(PidBin),
+ RefStr = binary_to_list(RefBin),
+ {PidStr,PidStr++" ("++RefStr++")"};
+ [<<>>,NameBin,NodeBin,<<>>,RefBin,<<>>] ->
+ %% Named process
+ NameStr = binary_to_list(NameBin),
+ NodeStr = binary_to_list(NodeBin),
+ PidStr = get_pid_from_name(NameStr,NodeStr),
+ RefStr = binary_to_list(RefBin),
+ {PidStr,"{"++NameStr++","++NodeStr++"} ("++RefStr++")"}
+ end.
get_pid_from_name(Name,Node) ->
case ets:lookup(cdv_reg_proc_table,cdv_dump_node_name) of
@@ -2027,12 +2005,16 @@ all_modinfo(Fd,LM,LineHead,DecodeOpts) ->
end.
get_attribute(Fd, DecodeOpts) ->
+ Term = do_get_attribute(Fd, DecodeOpts),
+ io_lib:format("~tp~n",[Term]).
+
+do_get_attribute(Fd, DecodeOpts) ->
Bytes = bytes(Fd, ""),
try get_binary(Bytes, DecodeOpts) of
{Bin,_} ->
try binary_to_term(Bin) of
Term ->
- io_lib:format("~tp~n",[Term])
+ Term
catch
_:_ ->
{"WARNING: The term is probably truncated!",
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 94d199e688..0470e785d9 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -21,7 +21,8 @@
-export([get_wx_parent/1,
display_info_dialog/2, display_yes_no_dialog/1,
- display_progress_dialog/3, destroy_progress_dialog/0,
+ display_progress_dialog/3,
+ destroy_progress_dialog/0, sync_destroy_progress_dialog/0,
wait_for_progress/0, report_progress/1,
user_term/3, user_term_multiline/3,
interval_dialog/4, start_timer/1, start_timer/2, stop_timer/1, timer_config/1,
@@ -31,7 +32,8 @@
set_listctrl_col_size/2,
create_status_bar/1,
html_window/1, html_window/2,
- make_obsbin/2
+ make_obsbin/2,
+ add_scroll_entries/2
]).
-include_lib("wx/include/wx.hrl").
@@ -40,6 +42,8 @@
-define(SINGLE_LINE_STYLE, ?wxBORDER_NONE bor ?wxTE_READONLY bor ?wxTE_RICH2).
-define(MULTI_LINE_STYLE, ?SINGLE_LINE_STYLE bor ?wxTE_MULTILINE).
+-define(NUM_SCROLL_ITEMS,8).
+
-define(pulse_timeout,50).
get_wx_parent(Window) ->
@@ -397,17 +401,18 @@ get_box_info({Title, left, List}) -> {Title, ?wxALIGN_LEFT, List};
get_box_info({Title, right, List}) -> {Title, ?wxALIGN_RIGHT, List}.
add_box(Panel, OuterBox, Cursor, Title, Proportion, {Format, List}) ->
- Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title}]),
+ NumStr = " ("++integer_to_list(length(List))++")",
+ Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title ++ NumStr}]),
Scroll = wxScrolledWindow:new(Panel),
wxScrolledWindow:enableScrolling(Scroll,true,true),
wxScrolledWindow:setScrollbars(Scroll,1,1,0,0),
ScrollSizer = wxBoxSizer:new(?wxVERTICAL),
wxScrolledWindow:setSizer(Scroll, ScrollSizer),
wxWindow:setBackgroundStyle(Scroll, ?wxBG_STYLE_SYSTEM),
- add_entries(Format, List, Scroll, ScrollSizer, Cursor),
+ Entries = add_entries(Format, List, Scroll, ScrollSizer, Cursor),
wxSizer:add(Box,Scroll,[{proportion,1},{flag,?wxEXPAND}]),
wxSizer:add(OuterBox,Box,[{proportion,Proportion},{flag,?wxEXPAND}]),
- {Scroll,ScrollSizer,length(List)}.
+ {Scroll,ScrollSizer,length(Entries)}.
add_entries(click, List, Scroll, ScrollSizer, Cursor) ->
Add = fun(Link) ->
@@ -415,7 +420,20 @@ add_entries(click, List, Scroll, ScrollSizer, Cursor) ->
wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}])
end,
- [Add(Link) || Link <- List];
+ if length(List) > ?NUM_SCROLL_ITEMS ->
+ {List1,Rest} = lists:split(?NUM_SCROLL_ITEMS,List),
+ LinkEntries = [Add(Link) || Link <- List1],
+ NStr = integer_to_list(length(Rest)),
+ TC = link_entry2(Scroll,
+ {{more,{Rest,Scroll,ScrollSizer}},"more..."},
+ Cursor,
+ "Click to see " ++ NStr ++ " more entries"),
+ wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
+ E = wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}]),
+ LinkEntries ++ [E];
+ true ->
+ [Add(Link) || Link <- List]
+ end;
add_entries(plain, List, Scroll, ScrollSizer, _) ->
Add = fun(String) ->
TC = wxStaticText:new(Scroll, ?wxID_ANY, String),
@@ -423,6 +441,23 @@ add_entries(plain, List, Scroll, ScrollSizer, _) ->
end,
[Add(String) || String <- List].
+add_scroll_entries(MoreEntry,{List, Scroll, ScrollSizer}) ->
+ wx:batch(
+ fun() ->
+ wxSizer:remove(ScrollSizer,?NUM_SCROLL_ITEMS),
+ wxStaticText:destroy(MoreEntry),
+ Cursor = wxCursor:new(?wxCURSOR_HAND),
+ Add = fun(Link) ->
+ TC = link_entry(Scroll, Link, Cursor),
+ wxWindow:setBackgroundStyle(TC, ?wxBG_STYLE_SYSTEM),
+ wxSizer:add(ScrollSizer,TC, [{flag,?wxEXPAND}])
+ end,
+ Entries = [Add(Link) || Link <- List],
+ wxCursor:destroy(Cursor),
+ wxSizer:layout(ScrollSizer),
+ wxSizer:setVirtualSizeHints(ScrollSizer,Scroll),
+ Entries
+ end).
create_box(_Panel, {scroll_boxes,[]}) ->
undefined;
@@ -449,7 +484,7 @@ create_box(Panel, {scroll_boxes,Data}) ->
{_,H} = wxWindow:getSize(Dummy),
wxTextCtrl:destroy(Dummy),
- MaxH = if MaxL > 8 -> 8*H;
+ MaxH = if MaxL > ?NUM_SCROLL_ITEMS -> ?NUM_SCROLL_ITEMS*H;
true -> MaxL*H
end,
[wxWindow:setMinSize(B,{0,MaxH}) || {B,_,_} <- Boxes],
@@ -504,20 +539,22 @@ create_box(Parent, Data) ->
link_entry(Panel, Link) ->
Cursor = wxCursor:new(?wxCURSOR_HAND),
- TC = link_entry2(Panel, to_link(Link), Cursor),
+ TC = link_entry(Panel, Link, Cursor),
wxCursor:destroy(Cursor),
TC.
link_entry(Panel, Link, Cursor) ->
- link_entry2(Panel, to_link(Link), Cursor).
+ link_entry2(Panel,to_link(Link),Cursor).
link_entry2(Panel,{Target,Str},Cursor) ->
+ link_entry2(Panel,{Target,Str},Cursor,"Click to see properties for " ++ Str).
+link_entry2(Panel,{Target,Str},Cursor,ToolTipText) ->
TC = wxStaticText:new(Panel, ?wxID_ANY, Str),
wxWindow:setForegroundColour(TC,?wxBLUE),
wxWindow:setCursor(TC, Cursor),
wxWindow:connect(TC, left_down, [{userData,Target}]),
wxWindow:connect(TC, enter_window),
wxWindow:connect(TC, leave_window),
- ToolTip = wxToolTip:new("Click to see properties for " ++ Str),
+ ToolTip = wxToolTip:new(ToolTipText),
wxWindow:setToolTip(TC, ToolTip),
TC.
@@ -708,6 +745,11 @@ wait_for_progress() ->
destroy_progress_dialog() ->
report_progress(finish).
+sync_destroy_progress_dialog() ->
+ Ref = erlang:monitor(process,?progress_handler),
+ destroy_progress_dialog(),
+ receive {'DOWN',Ref,process,_,_} -> ok end.
+
report_progress(Progress) ->
case whereis(?progress_handler) of
Pid when is_pid(Pid) ->
@@ -787,9 +829,8 @@ progress_dialog_new(Parent,Title,Str) ->
[{style,?wxDEFAULT_DIALOG_STYLE}]),
Panel = wxPanel:new(Dialog),
Sizer = wxBoxSizer:new(?wxVERTICAL),
- Message = wxStaticText:new(Panel, 1, Str),
- Gauge = wxGauge:new(Panel, 2, 100, [{size, {170, -1}},
- {style, ?wxGA_HORIZONTAL}]),
+ Message = wxStaticText:new(Panel, 1, Str,[{size,{220,-1}}]),
+ Gauge = wxGauge:new(Panel, 2, 100, [{style, ?wxGA_HORIZONTAL}]),
SizerFlags = ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT bor ?wxTOP,
wxSizer:add(Sizer, Message, [{flag,SizerFlags},{border,15}]),
wxSizer:add(Sizer, Gauge, [{flag, SizerFlags bor ?wxBOTTOM},{border,15}]),
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index 5908e99e36..f7ae07fb85 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -242,6 +242,10 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
{noreply, State#state{timer=Timer}};
+handle_event(#wx{obj=MoreEntry,event=#wxMouse{type=left_down},userData={more,More}}, State) ->
+ observer_lib:add_scroll_entries(MoreEntry,More),
+ {noreply, State};
+
handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
observer ! {open_link, TargetPid},
{noreply, State};
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 2e5fe0bc1a..1c40afba46 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -27,7 +27,7 @@
handle_event/2, handle_cast/2]).
-include_lib("wx/include/wx.hrl").
--include("../include/etop.hrl").
+-include("etop.hrl").
-include("observer_defs.hrl").
-include("etop_defs.hrl").
@@ -572,7 +572,8 @@ change_accum(true, S0) ->
S0#holder{accum=true};
change_accum(false, S0=#holder{info=Info}) ->
self() ! refresh,
- S0#holder{accum=lists:sort(array:to_list(Info))}.
+ Accum = [{Pid, Reds} || #etop_proc_info{pid=Pid, reds=Reds} <- array:to_list(Info)],
+ S0#holder{accum=lists:sort(Accum)}.
handle_update_old(#etop_info{procinfo=ProcInfo0},
S0=#holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index fb02ae2728..5bc17e2aee 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -120,6 +120,10 @@ handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages, expand_
end,
{noreply, State};
+handle_event(#wx{obj=MoreEntry,event=#wxMouse{type=left_down},userData={more,More}}, State) ->
+ observer_lib:add_scroll_entries(MoreEntry,More),
+ {noreply, State};
+
handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
observer ! {open_link, TargetPid},
{noreply, State};
@@ -253,8 +257,6 @@ init_stack_page(Parent, Pid) ->
[Pid, current_stacktrace])
of
{current_stacktrace,RawBt} ->
- observer_wx:try_rpc(node(Pid), erlang, process_info,
- [Pid, current_stacktrace]),
wxListCtrl:deleteAllItems(LCtrl),
wx:foldl(fun({M, F, A, Info}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index e16f3cab6b..2e387f7e74 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -38,13 +38,13 @@
-define(ID_SYSTEM_TABLES, 406).
-define(ID_TABLE_INFO, 407).
-define(ID_SHOW_TABLE, 408).
-
--record(opt, {type=ets,
- sys_hidden=true,
- unread_hidden=true,
- sort_key=2,
- sort_incr=true
- }).
+
+-record(opts, {type=ets,
+ sys_hidden=true,
+ unread_hidden=true}).
+
+-record(sort, {sort_incr=true,
+ sort_key=2}).
-record(state,
{
@@ -52,9 +52,9 @@
grid,
panel,
node=node(),
- opt=#opt{},
+ opts=#opts{},
+ holder,
selected,
- tabs,
timer
}).
@@ -64,8 +64,18 @@ start_link(Notebook, Parent, Config) ->
init([Notebook, Parent, Config]) ->
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxVERTICAL),
- Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
- Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]),
+
+ Opts=#opts{type=maps:get(type, Config, ets),
+ sys_hidden=maps:get(sys_hidden, Config, true),
+ unread_hidden=maps:get(unread_hidden, Config, true)},
+
+ Style = ?wxLC_REPORT bor ?wxLC_VIRTUAL bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
+ Self = self(),
+ Attrs = observer_lib:create_attrs(),
+ Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end),
+ CBs = [{onGetItemText, fun(_, Item,Col) -> get_row(Holder, Item, Col) end},
+ {onGetItemAttr, fun(_, Item) -> get_attr(Holder, Item) end}],
+ Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style} | CBs]),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
{proportion, 1}, {border, 5}]),
wxWindow:setSizer(Panel, Sizer),
@@ -95,38 +105,26 @@ init([Notebook, Parent, Config]) ->
wxWindow:setFocus(Grid),
{Panel, #state{grid=Grid, parent=Parent, panel=Panel,
- timer=Config,
- opt=#opt{type=maps:get(type, Config, ets),
- sys_hidden=maps:get(sys_hidden, Config, true),
- unread_hidden=maps:get(unread_hidden, Config, true)}
- }}.
+ opts=Opts, timer=Config, holder=Holder}}.
handle_event(#wx{id=?ID_REFRESH},
- State = #state{node=Node, grid=Grid, opt=Opt}) ->
- Tables = get_tables(Node, Opt),
- {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
- Sel =/= undefined andalso wxListCtrl:ensureVisible(Grid, Sel),
- {noreply, State#state{tabs=Tabs, selected=Sel}};
+ State = #state{holder=Holder, node=Node, opts=Opts}) ->
+ Tables = get_tables(Node, Opts),
+ Holder ! {refresh, Tables},
+ {noreply, State};
handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
- State = #state{node=Node, grid=Grid,
- opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) ->
- Opt = case col2key(Col) of
- Key -> Opt0#opt{sort_incr=not Bool};
- NewKey -> Opt0#opt{sort_key=NewKey}
- end,
- Tables = get_tables(Node, Opt),
- {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
- wxWindow:setFocus(Grid),
- {noreply, State#state{opt=Opt, tabs=Tabs, selected=Sel}};
+ State = #state{holder=Holder}) ->
+ Holder ! {sort, Col},
+ {noreply, State};
-handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
+handle_event(#wx{id=Id}, State = #state{node=Node, holder=Holder, grid=Grid, opts=Opt0})
when Id >= ?ID_ETS, Id =< ?ID_SYSTEM_TABLES ->
Opt = case Id of
- ?ID_ETS -> Opt0#opt{type=ets};
- ?ID_MNESIA -> Opt0#opt{type=mnesia};
- ?ID_UNREADABLE -> Opt0#opt{unread_hidden= not Opt0#opt.unread_hidden};
- ?ID_SYSTEM_TABLES -> Opt0#opt{sys_hidden= not Opt0#opt.sys_hidden}
+ ?ID_ETS -> Opt0#opts{type=ets};
+ ?ID_MNESIA -> Opt0#opts{type=mnesia};
+ ?ID_UNREADABLE -> Opt0#opts{unread_hidden= not Opt0#opts.unread_hidden};
+ ?ID_SYSTEM_TABLES -> Opt0#opts{sys_hidden= not Opt0#opts.sys_hidden}
end,
case get_tables2(Node, Opt) of
Error = {error, _} ->
@@ -135,9 +133,9 @@ handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
self() ! Error,
{noreply, State};
Tables ->
- {Tabs, Sel} = update_grid(Grid, sel(State), Opt, Tables),
+ Holder ! {refresh, Tables},
wxWindow:setFocus(Grid),
- {noreply, State#state{opt=Opt, tabs=Tabs, selected=Sel}}
+ {noreply, State#state{opts=Opt}}
end;
handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
@@ -146,19 +144,18 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
handle_event(#wx{event=#wxList{type=command_list_item_activated,
itemIndex=Index}},
- State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs}) ->
- Table = lists:nth(Index+1, Tabs),
- case Table#tab.protection of
- private ->
- self() ! {error, "Table has 'private' protection and can not be read"};
- _ ->
- observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}])
+ State=#state{holder=Holder, node=Node, opts=#opts{type=Type}, grid=Grid}) ->
+ case get_table(Holder, Index) of
+ #tab{protection=private} ->
+ self() ! {error, "Table has 'private' protection and can not be read"};
+ #tab{}=Table ->
+ observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}]);
+ _ -> ignore
end,
{noreply, State};
handle_event(#wx{event=#wxList{type=command_list_item_right_click}},
State=#state{panel=Panel}) ->
-
Menu = wxMenu:new(),
wxMenu:append(Menu, ?ID_TABLE_INFO, "Table info"),
wxMenu:append(Menu, ?ID_SHOW_TABLE, "Show Table Content"),
@@ -167,32 +164,33 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click}},
{noreply, State};
handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}},
- State) ->
+ State=#state{holder=Holder}) ->
+ Holder ! {selected, Index},
{noreply, State#state{selected=Index}};
handle_event(#wx{id=?ID_TABLE_INFO},
- State = #state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) ->
+ State = #state{holder=Holder, grid=Grid, node=Node, opts=#opts{type=Type}, selected=Sel}) ->
case Sel of
undefined ->
{noreply, State};
R when is_integer(R) ->
- Table = lists:nth(Sel+1, Tabs),
+ Table = get_table(Holder, Sel),
display_table_info(Grid, Node, Type, Table),
{noreply, State}
end;
handle_event(#wx{id=?ID_SHOW_TABLE},
- State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) ->
+ State=#state{holder=Holder, grid=Grid, node=Node, opts=#opts{type=Type}, selected=Sel}) ->
case Sel of
undefined ->
{noreply, State};
R when is_integer(R) ->
- Table = lists:nth(Sel+1, Tabs),
- case Table#tab.protection of
- private ->
+ case get_table(Holder, R) of
+ #tab{protection=private} ->
self() ! {error, "Table has 'private' protection and can not be read"};
- _ ->
- observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}])
+ #tab{}=Table ->
+ observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}]);
+ _ -> ignore
end,
{noreply, State}
end;
@@ -202,14 +200,14 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
{noreply, State#state{timer=Timer}};
-handle_event(Event, _State) ->
- error({unhandled_event, Event}).
+handle_event(_Event, State) ->
+ {noreply, State}.
handle_sync_event(_Event, _Obj, _State) ->
ok.
-handle_call(get_config, _, #state{timer=Timer, opt=Opt}=State) ->
- #opt{type=Type, sys_hidden=Sys, unread_hidden=Unread} = Opt,
+handle_call(get_config, _, #state{timer=Timer, opts=Opt}=State) ->
+ #opts{type=Type, sys_hidden=Sys, unread_hidden=Unread} = Opt,
Conf0 = observer_lib:timer_config(Timer),
Conf = Conf0#{type=>Type, sys_hidden=>Sys, unread_hidden=>Unread},
{reply, Conf, State};
@@ -220,50 +218,68 @@ handle_call(Event, From, _State) ->
handle_cast(Event, _State) ->
error({unhandled_cast, Event}).
-handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
- tabs=OldTabs}) ->
- case get_tables(Node, Opt) of
- OldTabs ->
- %% no change
- {noreply, State};
- Tables ->
- {Tabs, Sel} = update_grid(Grid, sel(State), Opt, Tables),
- Sel =/= undefined andalso wxListCtrl:ensureVisible(Grid, Sel),
- {noreply, State#state{tabs=Tabs, selected=Sel}}
- end;
+handle_info(refresh_interval, State = #state{holder=Holder, node=Node, opts=Opt}) ->
+ Tables = get_tables(Node, Opt),
+ Holder ! {refresh, Tables},
+ {noreply, State};
-handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0,
- timer=Timer0}) ->
- {Tables, Opt} = case Opt0#opt.type =:= mnesia andalso get_tables2(Node, Opt0) of
+handle_info({active, Node}, State = #state{parent=Parent, holder=Holder, grid=Grid,
+ opts=Opt0, timer=Timer0}) ->
+ {Tables, Opt} = case Opt0#opts.type =:= mnesia andalso get_tables2(Node, Opt0) of
Ts when is_list(Ts) ->
{Ts, Opt0};
_ -> % false or error getting mnesia tables
- Opt1 = Opt0#opt{type=ets},
+ Opt1 = Opt0#opts{type=ets},
{get_tables(Node, Opt1), Opt1}
end,
- {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
+ Holder ! {refresh, Tables},
wxWindow:setFocus(Grid),
create_menus(Parent, Opt),
Timer = observer_lib:start_timer(Timer0, 10),
- {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt, selected=Sel}};
+ {noreply, State#state{node=Node, timer=Timer, opts=Opt}};
handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
{noreply, State#state{timer=Timer}};
-handle_info({error, Error}, #state{panel=Panel,opt=Opt}=State) ->
+handle_info({error, Error}, #state{panel=Panel,opts=Opt}=State) ->
Str = io_lib:format("ERROR: ~ts~n",[Error]),
observer_lib:display_info_dialog(Panel,Str),
- case Opt#opt.type of
+ case Opt#opts.type of
mnesia -> wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true);
_ -> ok
end,
- {noreply, State#state{opt=Opt#opt{type=ets}}};
+ {noreply, State#state{opts=Opt#opts{type=ets}}};
+
+handle_info({refresh, Min, Min}, State = #state{grid=Grid}) ->
+ wxListCtrl:setItemCount(Grid, Min+1),
+ wxListCtrl:refreshItem(Grid, Min), %% Avoid assert in wx below if Max is 0
+ observer_wx:set_status(io_lib:format("Tables: ~w", [Min+1])),
+ {noreply, State};
+handle_info({refresh, Min, Max}, State = #state{grid=Grid}) ->
+ wxListCtrl:setItemCount(Grid, Max+1),
+ Max > 0 andalso wxListCtrl:refreshItems(Grid, Min, Max),
+ observer_wx:set_status(io_lib:format("Tables: ~w", [Max+1])),
+ {noreply, State};
+
+handle_info({selected, New, Size}, #state{grid=Grid, selected=Old} = State) ->
+ if
+ is_integer(Old), Old < Size ->
+ wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_SELECTED);
+ true -> ignore
+ end,
+ if is_integer(New) ->
+ wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_SELECTED),
+ wxListCtrl:ensureVisible(Grid, New);
+ true -> ignore
+ end,
+ {noreply, State#state{selected=New}};
handle_info(_Event, State) ->
{noreply, State}.
-terminate(_Event, _State) ->
+terminate(_Event, #state{holder=Holder}) ->
+ Holder ! stop,
ok.
code_change(_, _, State) ->
@@ -271,7 +287,7 @@ code_change(_, _, State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-create_menus(Parent, #opt{sys_hidden=Sys, unread_hidden=UnR, type=Type}) ->
+create_menus(Parent, #opts{sys_hidden=Sys, unread_hidden=UnR, type=Type}) ->
MenuEntries = [{"View",
[#create_menu{id = ?ID_TABLE_INFO, text = "Table information\tCtrl-I"},
separator,
@@ -298,7 +314,7 @@ get_tables(Node, Opts) ->
Res ->
Res
end.
-get_tables2(Node, #opt{type=Type, sys_hidden=Sys, unread_hidden=Unread}) ->
+get_tables2(Node, #opts{type=Type, sys_hidden=Sys, unread_hidden=Unread}) ->
Args = [Type, [{sys_hidden,Sys}, {unread_hidden,Unread}]],
case rpc:call(Node, observer_backend, get_table_list, Args) of
{badrpc, Error} ->
@@ -386,49 +402,134 @@ list_to_strings([A]) -> integer_to_list(A);
list_to_strings([A|B]) ->
integer_to_list(A) ++ " ," ++ list_to_strings(B).
-update_grid(Grid, Selected, Opt, Tables) ->
- wx:batch(fun() -> update_grid2(Grid, Selected, Opt, Tables) end).
-
-update_grid2(Grid, {SelName,SelId}, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
- wxListCtrl:deleteAllItems(Grid),
- Update =
- fun(#tab{name = Name, id = Id, owner = Owner, size = Size, memory = Memory,
- protection = Protection, reg_name = RegName},
- {Row, Sel}) ->
- _Item = wxListCtrl:insertItem(Grid, Row, ""),
- if (Row rem 2) =:= 0 ->
- wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN);
- true -> ignore
- end,
- if Protection == private ->
- wxListCtrl:setItemTextColour(Grid, Row, {200,130,50});
- true -> ignore
- end,
-
- lists:foreach(fun({_, ignore}) -> ignore;
- ({Col, Val}) ->
- wxListCtrl:setItem(Grid, Row, Col, observer_lib:to_str(Val))
- end,
- [{0,Name}, {1,Size}, {2, Memory div 1024},
- {3,Owner}, {4,RegName}, {5,Id}]),
- if SelName =:= Name, SelId =:= Id ->
- wxListCtrl:setItemState(Grid, Row, 16#FFFF, ?wxLIST_STATE_SELECTED),
- {Row+1, Row};
- true ->
- wxListCtrl:setItemState(Grid, Row, 0, ?wxLIST_STATE_SELECTED),
- {Row+1, Sel}
- end
- end,
- ProcInfo = case Dir of
- false -> lists:reverse(lists:keysort(Sort, Tables));
- true -> lists:keysort(Sort, Tables)
- end,
- {_, Sel} = lists:foldl(Update, {0, undefined}, ProcInfo),
- {ProcInfo, Sel}.
-
-sel(#state{selected=Sel, tabs=Tabs}) ->
- try lists:nth(Sel+1, Tabs) of
- #tab{name=Name, id=Id} -> {Name, Id}
- catch _:_ ->
- {undefined, undefined}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Table holder needs to be in a separate process otherwise
+%% the callback get_row/3 may deadlock if the process do
+%% wx calls when callback is invoked.
+
+get_table(Table, Item) ->
+ get_row(Table, Item, all).
+
+get_row(Table, Item, Column) ->
+ Ref = erlang:monitor(process, Table),
+ Table ! {get_row, self(), Item, Column},
+ receive
+ {'DOWN', Ref, _, _, _} -> "";
+ {Table, Res} ->
+ erlang:demonitor(Ref),
+ Res
end.
+
+get_attr(Table, Item) ->
+ Ref = erlang:monitor(process, Table),
+ Table ! {get_attr, self(), Item},
+ receive
+ {'DOWN', Ref, _, _, _} -> wx:null();
+ {Table, Res} ->
+ erlang:demonitor(Ref),
+ Res
+ end.
+
+-record(holder, {node, parent, pid,
+ tabs=array:new(),
+ sort=#sort{},
+ attrs,
+ sel
+ }).
+
+init_table_holder(Parent, Attrs) ->
+ Parent ! refresh,
+ table_holder(#holder{node=node(), parent=Parent, attrs=Attrs}).
+
+table_holder(S0 = #holder{parent=Parent, tabs=Tabs0, sel=Sel0}) ->
+ receive
+ {get_attr, From, Row} ->
+ get_attr(From, Row, S0),
+ table_holder(S0);
+ {get_row, From, Row, Col} ->
+ get_row(From, Row, Col, Tabs0),
+ table_holder(S0);
+ {sort, Col} ->
+ STab = get_sel(Sel0, Tabs0),
+ Parent ! {refresh, 0, array:size(Tabs0)-1},
+ S1 = sort(col2key(Col), S0),
+ Sel = sel_idx(STab, S1#holder.tabs),
+ Parent ! {selected, Sel, array:size(Tabs0)},
+ table_holder(S1#holder{sel=Sel});
+ {refresh, Tabs1} ->
+ STab = get_sel(Sel0, Tabs0),
+ Tabs = case S0#holder.sort of
+ #sort{sort_incr=false, sort_key=Col} ->
+ array:from_list(lists:reverse(lists:keysort(Col, Tabs1)));
+ #sort{sort_key=Col} ->
+ array:from_list(lists:keysort(Col, Tabs1))
+ end,
+ Parent ! {refresh, 0, array:size(Tabs)-1},
+ Sel = sel_idx(STab, Tabs),
+ Parent ! {selected, Sel,array:size(Tabs)},
+ table_holder(S0#holder{tabs=Tabs, sel=Sel});
+ {selected, Sel} ->
+ table_holder(S0#holder{sel=Sel});
+ stop ->
+ ok;
+ What ->
+ io:format("Table holder got ~tp~n",[What]),
+ Parent ! {refresh, 0, array:size(Tabs0)-1},
+ table_holder(S0)
+ end.
+
+get_sel(undefined, _Tabs) ->
+ undefined;
+get_sel(Idx, Tabs) ->
+ array:get(Idx, Tabs).
+
+sel_idx(undefined, _Tabs) ->
+ undefined;
+sel_idx(Tab, Tabs) ->
+ Find = fun(Idx, C, Acc) -> C =:= Tab andalso throw({found, Idx}), Acc end,
+ try array:foldl(Find, undefined, Tabs)
+ catch {found, Idx} -> Idx
+ end.
+
+sort(Col, #holder{sort=#sort{sort_key=Col, sort_incr=Incr}=S, tabs=Table0}=H) ->
+ Table = lists:reverse(array:to_list(Table0)),
+ H#holder{sort=S#sort{sort_incr=(not Incr)},
+ tabs=array:from_list(Table)};
+sort(Col, #holder{sort=#sort{sort_incr=Incr}=S, tabs=Table0}=H) ->
+ Table = case Incr of
+ false -> lists:reverse(lists:keysort(Col, array:to_list(Table0)));
+ true -> lists:keysort(Col, array:to_list(Table0))
+ end,
+ H#holder{sort=S#sort{sort_key=Col},
+ tabs=array:from_list(Table)}.
+
+get_row(From, Row, Col, Table) ->
+ Object = array:get(Row, Table),
+ From ! {self(), get_col(Col, Object)}.
+
+get_col(all, Rec) ->
+ Rec;
+get_col(2, #tab{}=Rec) -> %% Memory in kB
+ observer_lib:to_str(element(#tab.memory, Rec) div 1024);
+get_col(Col, #tab{}=Rec) ->
+ case element(col2key(Col), Rec) of
+ ignore -> "";
+ Val -> observer_lib:to_str(Val)
+ end;
+get_col(_, _) ->
+ "".
+
+get_attr(From, Row, #holder{tabs=Tabs, attrs=Attrs}) ->
+ EvenOdd = case (Row rem 2) > 0 of
+ true -> Attrs#attrs.odd;
+ false -> Attrs#attrs.even
+ end,
+ What = try array:get(Row, Tabs) of
+ #tab{protection=private} ->
+ Attrs#attrs.deleted;
+ _ ->
+ EvenOdd
+ catch _ ->
+ EvenOdd
+ end,
+ From ! {self(), What}.
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index bb1755f530..b5e94a893a 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -83,6 +83,7 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
link(OtherPid), % own node
link(Pid2), % external node
erlang:monitor(process,OtherPid),
+ erlang:monitor(process,init), % named process
erlang:monitor(process,Pid2),
code:load_file(?MODULE),
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 9fbd1a62a4..32773a779e 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -459,6 +459,27 @@ special(File,Procs) ->
old_attrib=undefined,
old_comp_info=undefined}=Mod2,
ok;
+ ".trunc_mod" ->
+ ModName = atom_to_list(?helper_mod),
+ {ok,Mod=#loaded_mod{},[TW]} =
+ crashdump_viewer:loaded_mod_details(ModName),
+ "WARNING: The crash dump is truncated here."++_ = TW,
+ #loaded_mod{current_attrib=CA,current_comp_info=CCI,
+ old_attrib=OA,old_comp_info=OCI} = Mod,
+ case lists:all(fun(undefined) ->
+ true;
+ (S) when is_list(S) ->
+ io_lib:printable_unicode_list(lists:flatten(S));
+ (_) -> false
+ end,
+ [CA,CCI,OA,OCI]) of
+ true ->
+ ok;
+ false ->
+ ct:fail({should_be_printable_strings_or_undefined,
+ {CA,CCI,OA,OCI}})
+ end,
+ ok;
".trunc_bin1" ->
%% This is 'full_dist' truncated after the first
%% "=binary:"
@@ -658,13 +679,32 @@ do_create_dumps(DataDir,Rel) ->
CD5 = dump_with_size_limit_reached(DataDir,Rel,"trunc_bytes"),
CD6 = dump_with_unicode_atoms(DataDir,Rel,"unicode"),
CD7 = dump_with_maps(DataDir,Rel,"maps"),
- TruncatedDumps = truncate_dump(CD1),
- {[CD1,CD2,CD3,CD4,CD5,CD6,CD7|TruncatedDumps], DosDump};
+ TruncDumpMod = truncate_dump_mod(CD1),
+ TruncatedDumpsBinary = truncate_dump_binary(CD1),
+ {[CD1,CD2,CD3,CD4,CD5,CD6,CD7,TruncDumpMod|TruncatedDumpsBinary],
+ DosDump};
_ ->
{[CD1,CD2], DosDump}
end.
-truncate_dump(File) ->
+truncate_dump_mod(File) ->
+ {ok,Bin} = file:read_file(File),
+ ModNameBin = atom_to_binary(?helper_mod,latin1),
+ NewLine = case os:type() of
+ {win32,_} -> <<"\r\n">>;
+ _ -> <<"\n">>
+ end,
+ RE = <<NewLine/binary,"=mod:",ModNameBin/binary,
+ NewLine/binary,"Current size: [0-9]*",
+ NewLine/binary,"Current attributes: ...">>,
+ {match,[{Pos,Len}]} = re:run(Bin,RE),
+ Size = Pos + Len,
+ <<Truncated:Size/binary,_/binary>> = Bin,
+ DumpName = filename:rootname(File) ++ ".trunc_mod",
+ file:write_file(DumpName,Truncated),
+ DumpName.
+
+truncate_dump_binary(File) ->
{ok,Bin} = file:read_file(File),
BinTag = <<"\n=binary:">>,
Colon = <<":">>,
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index 0db2c1ea77..fd4f93f662 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -113,7 +113,12 @@ appup_file(Config) when is_list(Config) ->
basic(suite) -> [];
basic(doc) -> [""];
basic(Config) when is_list(Config) ->
- timer:send_after(100, "foobar"), %% Otherwise the timer server gets added to procs
+ %% Start these before
+ wx:new(),
+ wx:destroy(),
+ timer:send_after(100, "foobar"),
+ {foo, node@machine} ! dummy_msg, %% start distribution stuff
+ %% Otherwise ever lasting servers gets added to procs
ProcsBefore = processes(),
ProcInfoBefore = [{P,process_info(P)} || P <- ProcsBefore],
NumProcsBefore = length(ProcsBefore),
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index df25297eb9..6a9f3ff58a 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -75,43 +75,37 @@ load_report(file, File) -> load_report(data, from_file(File));
load_report(data, Report) ->
ok = start_internal(), gen_server:call(?SERVER, {load_report, Report}, infinity).
-report() -> [
- {init_arguments, init:get_arguments()},
- {code_paths, code:get_path()},
- {code, code()},
- {system_info, erlang_system_info()},
- {erts_compile_info, erlang:system_info(compile_info)},
- {beam_dynamic_libraries, get_dynamic_libraries()},
- {environment_erts, os_getenv_erts_specific()},
- {environment, [split_env(Env) || Env <- os:getenv()]},
- {sanity_check, sanity_check()}
- ].
+report() ->
+ %% This is ugly but beats having to maintain two distinct implementations,
+ %% and we don't really care about memory use since it's internal and
+ %% undocumented.
+ {ok, Fd} = file:open([], [ram, read, write]),
+ to_fd(Fd),
+ {ok, _} = file:position(Fd, bof),
+ from_fd(Fd).
-spec to_file(FileName) -> ok | {error, Reason} when
FileName :: file:name_all(),
Reason :: file:posix() | badarg | terminated | system_limit.
to_file(File) ->
- file:write_file(File, iolist_to_binary([
- io_lib:format("{system_information_version, ~p}.~n", [
- ?REPORT_FILE_VSN
- ]),
- io_lib:format("{system_information, ~p}.~n", [
- report()
- ])
- ])).
+ case file:open(File, [raw, write, binary, delayed_write]) of
+ {ok, Fd} ->
+ try
+ to_fd(Fd)
+ after
+ file:close(Fd)
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
from_file(File) ->
- case file:consult(File) of
- {ok, Data} ->
- case get_value([system_information_version], Data) of
- ?REPORT_FILE_VSN ->
- get_value([system_information], Data);
- Vsn ->
- erlang:error({unknown_version, Vsn})
- end;
- _ ->
- erlang:error(bad_report_file)
+ {ok, Fd} = file:open(File, [raw, read]),
+ try
+ from_fd(Fd)
+ after
+ file:close(Fd)
end.
applications() -> applications([]).
@@ -457,61 +451,151 @@ split_env([$=|Vs], Key) -> {lists:reverse(Key), Vs};
split_env([I|Vs], Key) -> split_env(Vs, [I|Key]);
split_env([], KV) -> lists:reverse(KV). % should not happen.
-%% get applications
+from_fd(Fd) ->
+ try
+ [{system_information_version, "1.0"},
+ {system_information, Data}] = consult_fd(Fd),
+ Data
+ catch
+ _:_ -> erlang:error(bad_report_file)
+ end.
-code() ->
- % order is important
- get_code_from_paths(code:get_path()).
+consult_fd(Fd) ->
+ consult_fd_1(Fd, [], {ok, []}).
+consult_fd_1(Fd, Cont0, ReadResult) ->
+ Data =
+ case ReadResult of
+ {ok, Characters} -> Characters;
+ eof -> eof
+ end,
+ case erl_scan:tokens(Cont0, Data, 1) of
+ {done, {ok, Tokens, _}, Next} ->
+ {ok, Term} = erl_parse:parse_term(Tokens),
+ [Term | consult_fd_1(Fd, [], {ok, Next})];
+ {more, Cont} ->
+ consult_fd_1(Fd, Cont, file:read(Fd, 1 bsl 20));
+ {done, {eof, _}, eof} -> []
+ end.
-get_code_from_paths([]) -> [];
-get_code_from_paths([Path|Paths]) ->
- case is_application_path(Path) of
- true ->
- [{application, get_application_from_path(Path)}|get_code_from_paths(Paths)];
- false ->
- [{code, [
- {path, Path},
- {modules, get_modules_from_path(Path)}
- ]}|get_code_from_paths(Paths)]
+%%
+%% Dumps a system_information tuple to the given Fd, writing the term in chunks
+%% to avoid eating too much memory on large systems.
+%%
+
+to_fd(Fd) ->
+ EmitChunk =
+ fun(Format, Args) ->
+ ok = file:write(Fd, io_lib:format(Format, Args))
+ end,
+
+ EmitChunk("{system_information_version, ~p}.~n"
+ "{system_information,["
+ "{init_arguments,~p},"
+ "{code_paths,~p},",
+ [?REPORT_FILE_VSN,
+ init:get_arguments(),
+ code:get_path()]),
+
+ emit_code_info(EmitChunk),
+
+ EmitChunk( "," %% Note the leading comma!
+ "{system_info,~p},"
+ "{erts_compile_info,~p},"
+ "{beam_dynamic_libraries,~p},"
+ "{environment_erts,~p},"
+ "{environment,~p},"
+ "{sanity_check,~p}"
+ "]}.~n",
+ [erlang_system_info(),
+ erlang:system_info(compile_info),
+ get_dynamic_libraries(),
+ os_getenv_erts_specific(),
+ [split_env(Env) || Env <- os:getenv()],
+ sanity_check()]).
+
+%% Emits all modules/applications in the *code path order*
+emit_code_info(EmitChunk) ->
+ EmitChunk("{code, [", []),
+ comma_separated_foreach(EmitChunk,
+ fun(Path) ->
+ case is_application_path(Path) of
+ true -> emit_application_info(EmitChunk, Path);
+ false -> emit_code_path_info(EmitChunk, Path)
+ end
+ end, code:get_path()),
+ EmitChunk("]}", []).
+
+emit_application_info(EmitChunk, Path) ->
+ [Appfile|_] = filelib:wildcard(filename:join(Path, "*.app")),
+ case file:consult(Appfile) of
+ {ok, [{application, App, Info}]} ->
+ RtDeps = proplists:get_value(runtime_dependencies, Info, []),
+ Description = proplists:get_value(description, Info, []),
+ Version = proplists:get_value(vsn, Info, []),
+
+ EmitChunk("{application, {~p,["
+ "{description,~p},"
+ "{vsn,~p},"
+ "{path,~p},"
+ "{runtime_dependencies,~p},",
+ [App, Description, Version, Path, RtDeps]),
+ emit_module_info_from_path(EmitChunk, Path),
+ EmitChunk("]}}", [])
end.
+emit_code_path_info(EmitChunk, Path) ->
+ EmitChunk("{code, ["
+ "{path, ~p},", [Path]),
+ emit_module_info_from_path(EmitChunk, Path),
+ EmitChunk("]}", []).
+
+emit_module_info_from_path(EmitChunk, Path) ->
+ BeamFiles = filelib:wildcard(filename:join(Path, "*.beam")),
+
+ EmitChunk("{modules, [", []),
+ comma_separated_foreach(EmitChunk,
+ fun(Beam) ->
+ emit_module_info(EmitChunk, Beam)
+ end, BeamFiles),
+ EmitChunk("]}", []).
+
+emit_module_info(EmitChunk, Beam) ->
+ %% FIXME: The next three calls load *all* significant chunks onto the heap,
+ %% which may cause us to run out of memory if there's a huge module in the
+ %% code path.
+ {ok,{Mod, Md5}} = beam_lib:md5(Beam),
+
+ CompilerVersion = get_compiler_version(Beam),
+ Native = beam_is_native_compiled(Beam),
+
+ Loaded = case code:is_loaded(Mod) of
+ false -> false;
+ _ -> true
+ end,
+
+ EmitChunk("{~p,["
+ "{loaded,~p},"
+ "{native,~p},"
+ "{compiler,~p},"
+ "{md5,~p}"
+ "]}",
+ [Mod, Loaded, Native, CompilerVersion, hexstring(Md5)]).
+
+comma_separated_foreach(_EmitChunk, _Fun, []) ->
+ ok;
+comma_separated_foreach(_EmitChunk, Fun, [H]) ->
+ Fun(H);
+comma_separated_foreach(EmitChunk, Fun, [H | T]) ->
+ Fun(H),
+ EmitChunk(",", []),
+ comma_separated_foreach(EmitChunk, Fun, T).
+
is_application_path(Path) ->
case filelib:wildcard(filename:join(Path, "*.app")) of
[] -> false;
_ -> true
end.
-get_application_from_path(Path) ->
- [Appfile|_] = filelib:wildcard(filename:join(Path, "*.app")),
- case file:consult(Appfile) of
- {ok, [{application, App, Info}]} ->
- {App, [
- {description, proplists:get_value(description, Info, [])},
- {vsn, proplists:get_value(vsn, Info, [])},
- {path, Path},
- {runtime_dependencies,
- proplists:get_value(runtime_dependencies, Info, [])},
- {modules, get_modules_from_path(Path)}
- ]}
- end.
-
-get_modules_from_path(Path) ->
- [
- begin
- {ok,{Mod, Md5}} = beam_lib:md5(Beam),
- Loaded = case code:is_loaded(Mod) of
- false -> false;
- _ -> true
- end,
- {Mod, [
- {loaded, Loaded},
- {native, beam_is_native_compiled(Beam)},
- {compiler, get_compiler_version(Beam)},
- {md5, hexstring(Md5)}
- ]}
- end || Beam <- filelib:wildcard(filename:join(Path, "*.beam"))
- ].
-
hexstring(Bin) when is_binary(Bin) ->
lists:flatten([io_lib:format("~2.16.0b", [V]) || <<V>> <= Bin]).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 154894cda8..ad9efc4755 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -54,7 +54,7 @@
sha/1, sign/3, verify/5]).
%%% For test suites
--export([pack/3]).
+-export([pack/3, adjust_algs_for_peer_version/2]).
-export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove
-define(Estring(X), ?STRING((if is_binary(X) -> X;
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index a18383d148..21359a0386 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -37,6 +37,7 @@ MODULES= \
ssh_renegotiate_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
+ ssh_compat_SUITE \
ssh_connection_SUITE \
ssh_engine_SUITE \
ssh_protocol_SUITE \
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
new file mode 100644
index 0000000000..74ab5aca3a
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -0,0 +1,814 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_compat_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssh/src/ssh_transport.hrl"). % #ssh_msg_kexinit{}
+-include_lib("kernel/include/inet.hrl"). % #hostent{}
+-include_lib("kernel/include/file.hrl"). % #file_info{}
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(USER,"sshtester").
+-define(PWD, "foobar").
+-define(DOCKER_PFX, "ssh_compat_suite-ssh").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [%%{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+ [{group,G} || G <- vers()].
+
+groups() ->
+ [{G, [], tests()} || G <- vers()].
+
+tests() ->
+ [login_with_password_otp_is_client,
+ login_with_password_otp_is_server,
+ login_with_keyboard_interactive_otp_is_client,
+ login_with_keyboard_interactive_otp_is_server,
+ login_with_all_public_keys_otp_is_client,
+ login_with_all_public_keys_otp_is_server,
+ all_algorithms_otp_is_client,
+ all_algorithms_otp_is_server
+ ].
+
+
+
+vers() ->
+ try
+ %% Find all useful containers in such a way that undefined command, too low
+ %% priviliges, no containers and containers found give meaningful result:
+ L0 = ["REPOSITORY"++_|_] = string:tokens(os:cmd("docker images"), "\r\n"),
+ [["REPOSITORY","TAG"|_]|L1] = [string:tokens(E, " ") || E<-L0],
+ [list_to_atom(V) || [?DOCKER_PFX,V|_] <- L1]
+ of
+ Vs ->
+ lists:sort(Vs)
+ catch
+ error:{badmatch,_} ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ?CHECK_CRYPTO(
+ case os:find_executable("docker") of
+ false ->
+ {skip, "No docker"};
+ _ ->
+ ssh:start(),
+ ct:log("Crypto info: ~p",[crypto:info_lib()]),
+ Config
+ end).
+
+end_per_suite(Config) ->
+ %% Remove all containers that are not running:
+%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
+ %% Remove dangling images:
+%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ Config.
+
+
+
+init_per_group(G, Config) ->
+ case lists:member(G, vers()) of
+ true ->
+ try start_docker(G) of
+ {ok,ID} ->
+ ct:log("==> ~p",[G]),
+ [Vssh|VsslRest] = string:tokens(atom_to_list(G), "-"),
+ Vssl = lists:flatten(lists:join($-,VsslRest)),
+ ct:comment("+++ ~s + ~s +++",[Vssh,Vssl]),
+ %% Find the algorithms that both client and server supports:
+ {IP,Port} = ip_port([{id,ID}]),
+ try common_algs([{id,ID}|Config], IP, Port) of
+ {ok, RemoteServerCommon, RemoteClientCommon} ->
+ [{ssh_version,Vssh},{ssl_version,Vssl},
+ {id,ID},
+ {common_server_algs,RemoteServerCommon},
+ {common_client_algs,RemoteClientCommon}
+ |Config];
+ Other ->
+ ct:log("Error in init_per_group: ~p",[Other]),
+ stop_docker(ID),
+ {fail, "Can't contact docker sshd"}
+ catch
+ Class:Exc ->
+ ST = erlang:get_stacktrace(),
+ ct:log("common_algs: ~p:~p~n~p",[Class,Exc,ST]),
+ stop_docker(ID),
+ {fail, "Failed during setup"}
+ end
+ catch
+ cant_start_docker ->
+ {skip, "Can't start docker"};
+
+ C:E ->
+ ST = erlang:get_stacktrace(),
+ ct:log("No ~p~n~p:~p~n~p",[G,C,E,ST]),
+ {skip, "Can't start docker"}
+ end;
+
+ false ->
+ Config
+ end.
+
+end_per_group(_, Config) ->
+ catch stop_docker(proplists:get_value(id,Config)),
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+login_with_password_otp_is_client(Config) ->
+ {IP,Port} = ip_port(Config),
+ {ok,C} = ssh:connect(IP, Port, [{auth_methods,"password"},
+ {user,?USER},
+ {password,?PWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ ssh:close(C).
+
+%%--------------------------------------------------------------------
+login_with_password_otp_is_server(Config) ->
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods,"password"},
+ {system_dir, setup_local_hostdir('ssh-rsa',Config)},
+ {user_dir, new_dir(Config)},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+2]).\r\n'",
+ [<<"Answer=3">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R.
+
+%%--------------------------------------------------------------------
+login_with_keyboard_interactive_otp_is_client(Config) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{auth_methods,"keyboard-interactive"},
+ {user,?USER},
+ {password,?PWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ ssh:close(C).
+
+%%--------------------------------------------------------------------
+login_with_keyboard_interactive_otp_is_server(Config) ->
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods,"keyboard-interactive"},
+ {system_dir, setup_local_hostdir('ssh-rsa',Config)},
+ {user_dir, new_dir(Config)},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+3]).\r\n'",
+ [<<"Answer=4">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R.
+
+%%--------------------------------------------------------------------
+login_with_all_public_keys_otp_is_client(Config) ->
+ CommonAlgs = [{public_key_from_host,A}
+ || {public_key,A} <- proplists:get_value(common_server_algs, Config)],
+ {DockerIP,DockerPort} = ip_port(Config),
+ chk_all_algos(CommonAlgs, Config,
+ fun(_Tag,Alg) ->
+ ssh:connect(DockerIP, DockerPort,
+ [{auth_methods, "publickey"},
+ {user, ?USER},
+ {user_dir, setup_remote_auth_keys_and_local_priv(Alg, Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ])
+ end).
+
+%%--------------------------------------------------------------------
+login_with_all_public_keys_otp_is_server(Config) ->
+ CommonAlgs = [{public_key_to_host,A}
+ || {public_key,A} <- proplists:get_value(common_client_algs, Config)],
+ UserDir = new_dir(Config),
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods, "publickey"},
+ {system_dir, setup_local_hostdir('ssh-rsa',Config)},
+ {user_dir, UserDir},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+
+ R = chk_all_algos(CommonAlgs, Config,
+ fun(_Tag,Alg) ->
+ setup_remote_priv_and_local_auth_keys(Alg, clear_dir(UserDir), Config),
+ exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+4]).\r\n'",
+ [<<"Answer=5">>],
+ "")
+ end),
+ ssh:stop_daemon(Server),
+ R.
+
+%%--------------------------------------------------------------------
+all_algorithms_otp_is_client(Config) ->
+ CommonAlgs = proplists:get_value(common_server_algs, Config),
+ {IP,Port} = ip_port(Config),
+ chk_all_algos(CommonAlgs, Config,
+ fun(Tag, Alg) ->
+ ssh:connect(IP, Port, [{user,?USER},
+ {password,?PWD},
+ {auth_methods, "password"},
+ {user_dir, new_dir(Config)},
+ {preferred_algorithms, [{Tag,[Alg]}]},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ])
+ end).
+
+%%--------------------------------------------------------------------
+all_algorithms_otp_is_server(Config) ->
+ CommonAlgs = proplists:get_value(common_client_algs, Config),
+ UserDir = setup_remote_priv_and_local_auth_keys('ssh-rsa', Config),
+ chk_all_algos(CommonAlgs, Config,
+ fun(Tag,Alg) ->
+ HostKeyAlg = case Tag of
+ public_key -> Alg;
+ _ -> 'ssh-rsa'
+ end,
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{preferred_algorithms, [{Tag,[Alg]}]},
+ {system_dir, setup_local_hostdir(HostKeyAlg, Config)},
+ {user_dir, UserDir},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "hi_there.\r\n",
+ [<<"hi_there">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R
+ end).
+
+%%--------------------------------------------------------------------
+%% Utilities ---------------------------------------------------------
+%%--------------------------------------------------------------------
+exec_from_docker(WhatEver, {0,0,0,0}, HostPort, Command, Expects, ExtraSshArg) ->
+ exec_from_docker(WhatEver, host_ip(), HostPort, Command, Expects, ExtraSshArg);
+
+exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
+ is_list(Config) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg),
+ ssh:close(C),
+ R;
+
+exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)) ->
+ SSH_from_docker =
+ lists:concat(["sshpass -p ",?PWD," ",
+ "/buildroot/ssh/bin/ssh -p ",HostPort," -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
+ ExtraSshArg," ",
+ inet_parse:ntoa(HostIP)," "
+ ]),
+ ExecCommand = SSH_from_docker ++ Command,
+ R = exec(C, ExecCommand),
+ case R of
+ {ok,{ExitStatus,Result}} when ExitStatus == 0 ->
+ case binary:match(Result, Expects) of
+ nomatch ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Bad answer"};
+ _ ->
+ ok
+ end;
+ {ok,_} ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Exit status =/= 0"};
+ _ ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Couldn't login to host"}
+ end.
+
+
+
+
+exec(C, Cmd) ->
+ ct:log("~s",[Cmd]),
+ {ok,Ch} = ssh_connection:session_channel(C, 10000),
+ success = ssh_connection:exec(C, Ch, Cmd, 10000),
+ exec_result(C, Ch).
+
+
+exec_result(C, Ch) ->
+ exec_result(C, Ch, undefined, <<>>).
+
+exec_result(C, Ch, ExitStatus, Acc) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %%ct:log("CHAN ~p got *closed*",[Ch]),
+ {ok, {ExitStatus, Acc}};
+
+ {ssh_cm,C,{exit_status,Ch,ExStat}} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got *exit status ~p*",[Ch,ExStat]),
+ exec_result(C, Ch, ExStat, Acc);
+
+ {ssh_cm,C,{data,Ch,_,Data}=_X} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got ~p",[Ch,_X]),
+ exec_result(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
+
+ _Other ->
+ %%ct:log("OTHER: ~p",[_Other]),
+ exec_result(C, Ch, ExitStatus, Acc)
+
+ after 5000 ->
+ %%ct:log("NO MORE, received so far:~n~s",[Acc]),
+ {error, timeout}
+ end.
+
+
+chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
+ ct:comment("~p algorithms",[length(CommonAlgs)]),
+ %% Check each algorithm
+ Failed =
+ lists:foldl(
+ fun({Tag,Alg}, FailedAlgos) ->
+ ct:log("Try ~p",[Alg]),
+ case DoTestFun(Tag,Alg) of
+ {ok,C} ->
+ ssh:close(C),
+ FailedAlgos;
+ ok ->
+ FailedAlgos;
+ Other ->
+ ct:log("FAILED! ~p ~p: ~p",[Tag,Alg,Other]),
+ [Alg|FailedAlgos]
+ end
+ end, [], CommonAlgs),
+ ct:pal("~s", [format_result_table_use_all_algos(Config, CommonAlgs, Failed)]),
+ case Failed of
+ [] ->
+ ok;
+ _ ->
+ {fail, Failed}
+ end.
+
+setup_local_hostdir(KeyAlg, Config) ->
+ setup_local_hostdir(KeyAlg, new_dir(Config), Config).
+setup_local_hostdir(KeyAlg, HostDir, Config) ->
+ {ok, {Priv,Publ}} = host_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public key
+ DstFile = filename:join(HostDir, dst_filename(host,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ HostDir.
+
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, Config) ->
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public keys
+ DstFile = filename:join(UserDir, dst_filename(user,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ %% Remote auth_methods with public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ ok = ssh_sftp:write_file(Ch, ".ssh/authorized_keys", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh/authorized_keys", #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, Config) ->
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local auth_methods with public key
+ AuthKeyFile = filename:join(UserDir, "authorized_keys"),
+ ok = file:write_file(AuthKeyFile, Publ),
+ %% Remote private and public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ DstFile = filename:join(".ssh", dst_filename(user,KeyAlg)),
+ ok = ssh_sftp:write_file(Ch, DstFile, Priv),
+ ok = ssh_sftp:write_file_info(Ch, DstFile, #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file(Ch, DstFile++".pub", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+user_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("users_keys", user, Config, KeyAlg).
+host_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("host_keys", host, Config, KeyAlg).
+
+priv_pub_keys(KeySubDir, Type, Config, KeyAlg) ->
+ KeyDir = filename:join(proplists:get_value(data_dir,Config), KeySubDir),
+ {ok,Priv} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg))),
+ {ok,Publ} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg)++".pub")),
+ {ok, {Priv,Publ}}.
+
+
+src_filename(user, 'ssh-rsa' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+src_filename(user, 'ssh-dss' ) -> "id_dsa";
+src_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa256";
+src_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa384";
+src_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa521";
+src_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+src_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+src_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key256";
+src_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key384";
+src_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key521".
+
+dst_filename(user, 'ssh-rsa' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+dst_filename(user, 'ssh-dss' ) -> "id_dsa";
+dst_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa";
+dst_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key".
+
+
+format_result_table_use_all_algos(Config, CommonAlgs, Failed) ->
+ %% Write a nice table with the result
+ AlgHead = 'Algorithm',
+ AlgWidth = lists:max([length(atom_to_list(A)) || {_,A} <- CommonAlgs]),
+ {ResultTable,_} =
+ lists:mapfoldl(
+ fun({T,A}, Tprev) ->
+ Tag = case T of
+ Tprev -> "";
+ _ -> io_lib:format('~s~n',[T])
+ end,
+ {io_lib:format('~s ~*s ~s~n',
+ [Tag, -AlgWidth, A,
+ case lists:member(A,Failed) of
+ true -> "<<<< FAIL <<<<";
+ false-> "(ok)"
+ end]),
+ T}
+ end, undefined, CommonAlgs),
+
+ Vssh = proplists:get_value(ssh_version,Config,""),
+ Vssl = proplists:get_value(ssl_version,Config,""),
+ io_lib:format("~nResults, Peer versions: ~s and ~s~n"
+ "Tag ~*s Result~n"
+ "=====~*..=s=======~n~s"
+ ,[Vssh,Vssl,
+ -AlgWidth,AlgHead,
+ AlgWidth, "", ResultTable]).
+
+
+start_docker(Ver) ->
+ Cmnd = lists:concat(["docker run -itd --rm -p 1234 ",?DOCKER_PFX,":",Ver]),
+ Id0 = os:cmd(Cmnd),
+ ct:log("Ver = ~p, Cmnd ~p~n-> ~p",[Ver,Cmnd,Id0]),
+ case is_docker_sha(Id0) of
+ true ->
+ Id = hd(string:tokens(Id0, "\n")),
+ IP = ip(Id),
+ Port = 1234,
+ {ok, {Ver,{IP,Port},Id}};
+ false ->
+ throw(cant_start_docker)
+ end.
+
+
+stop_docker({_Ver,_,Id}) ->
+ Cmnd = lists:concat(["docker kill ",Id]),
+ os:cmd(Cmnd).
+
+is_docker_sha(L) ->
+ lists:all(fun(C) when $a =< C,C =< $z -> true;
+ (C) when $0 =< C,C =< $9 -> true;
+ ($\n) -> true;
+ (_) -> false
+ end, L).
+
+ip_port(Config) ->
+ {_Ver,{IP,Port},_} = proplists:get_value(id,Config),
+ {IP,Port}.
+
+port_mapped_to(Id) ->
+ Cmnd = lists:concat(["docker ps --format \"{{.Ports}}\" --filter id=",Id]),
+ [_, PortStr | _] = string:tokens(os:cmd(Cmnd), ":->/"),
+ list_to_integer(PortStr).
+
+ip(Id) ->
+ Cmnd = lists:concat(["docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ",
+ Id]),
+ IPstr0 = os:cmd(Cmnd),
+ ct:log("Cmnd ~p~n-> ~p",[Cmnd,IPstr0]),
+ IPstr = hd(string:tokens(IPstr0, "\n")),
+ {ok,IP} = inet:parse_address(IPstr),
+ IP.
+
+new_dir(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SubDirName = integer_to_list(erlang:system_time()),
+ Dir = filename:join(PrivDir, SubDirName),
+ case file:read_file_info(Dir) of
+ {error,enoent} ->
+ ok = file:make_dir(Dir),
+ Dir;
+ _ ->
+ timer:sleep(25),
+ new_dir(Config)
+ end.
+
+clear_dir(Dir) ->
+ delete_all_contents(Dir),
+ {ok,[]} = file:list_dir(Dir),
+ Dir.
+
+delete_all_contents(Dir) ->
+ {ok,Fs} = file:list_dir(Dir),
+ lists:map(fun(F0) ->
+ F = filename:join(Dir, F0),
+ case filelib:is_file(F) of
+ true ->
+ file:delete(F);
+ false ->
+ case filelib:is_dir(F) of
+ true ->
+ delete_all_contents(F),
+ file:del_dir(F);
+ false ->
+ ct:log("Neither file nor dir: ~p",[F])
+ end
+ end
+ end, Fs).
+
+common_algs(Config, IP, Port) ->
+ case remote_server_algs(IP, Port) of
+ {ok, {RemoteHelloBin, RemoteServerKexInit}} ->
+ case remote_client_algs(Config) of
+ {ok,{_Hello,RemoteClientKexInit}} ->
+ RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
+ Server = find_common_algs(RemoteServerAlgs,
+ use_algorithms(RemoteHelloBin)),
+ RemoteClientAlgs = kexint_msg2default_algorithms(RemoteClientKexInit),
+ Client = find_common_algs(RemoteClientAlgs,
+ use_algorithms(RemoteHelloBin)),
+ ct:log("Docker server algorithms:~n ~p~n~nDocker client algorithms:~n ~p",
+ [RemoteServerAlgs,RemoteClientAlgs]),
+ {ok, Server, Client};
+ Other ->
+ Other
+ end;
+ Other ->
+ Other
+ end.
+
+
+find_common_algs(Remote, Local) ->
+ [{T,V} || {T,Vs} <- ssh_test_lib:extract_algos(
+ ssh_test_lib:intersection(Remote,
+ Local)),
+ V <- Vs].
+
+
+use_algorithms(RemoteHelloBin) ->
+ MyAlgos = ssh:chk_algos_opts(
+ [{modify_algorithms,
+ [{append,
+ [{kex,['diffie-hellman-group1-sha1']}
+ ]}
+ ]}
+ ]),
+ ssh_transport:adjust_algs_for_peer_version(binary_to_list(RemoteHelloBin)++"\r\n",
+ MyAlgos).
+
+kexint_msg2default_algorithms(#ssh_msg_kexinit{kex_algorithms = Kex,
+ server_host_key_algorithms = PubKey,
+ encryption_algorithms_client_to_server = CipherC2S,
+ encryption_algorithms_server_to_client = CipherS2C,
+ mac_algorithms_client_to_server = MacC2S,
+ mac_algorithms_server_to_client = MacS2C,
+ compression_algorithms_client_to_server = CompC2S,
+ compression_algorithms_server_to_client = CompS2C
+ }) ->
+ [{kex, ssh_test_lib:to_atoms(Kex)},
+ {public_key, ssh_test_lib:to_atoms(PubKey)},
+ {cipher, [{client2server,ssh_test_lib:to_atoms(CipherC2S)},
+ {server2client,ssh_test_lib:to_atoms(CipherS2C)}]},
+ {mac, [{client2server,ssh_test_lib:to_atoms(MacC2S)},
+ {server2client,ssh_test_lib:to_atoms(MacS2C)}]},
+ {compression, [{client2server,ssh_test_lib:to_atoms(CompC2S)},
+ {server2client,ssh_test_lib:to_atoms(CompS2C)}]}].
+
+
+
+remote_server_algs(IP, Port) ->
+ case try_gen_tcp_connect(IP, Port, 5) of
+ {ok,S} ->
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ receive_hello(S, <<>>);
+ {error,Error} ->
+ {error,Error}
+ end.
+
+try_gen_tcp_connect(IP, Port, N) when N>0 ->
+ case gen_tcp:connect(IP, Port, [binary]) of
+ {ok,S} ->
+ {ok,S};
+ {error,_Error} when N>1 ->
+ receive after 1000 -> ok end,
+ try_gen_tcp_connect(IP, Port, N-1);
+ {error,Error} ->
+ {error,Error}
+ end;
+try_gen_tcp_connect(_, _, _) ->
+ {error, "No contact"}.
+
+
+remote_client_algs(Config) ->
+ Parent = self(),
+ Ref = make_ref(),
+ spawn(
+ fun() ->
+ {ok,Sl} = gen_tcp:listen(0, [binary]),
+ {ok,{IP,Port}} = inet:sockname(Sl),
+ Parent ! {addr,Ref,IP,Port},
+ {ok,S} = gen_tcp:accept(Sl),
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ Parent ! {Ref,receive_hello(S, <<>>)}
+ end),
+ receive
+ {addr,Ref,IP,Port} ->
+ spawn(fun() ->
+ exec_from_docker(Config, IP, Port,
+ "howdy.\r\n",
+ [<<"howdy">>],
+ "")
+ end),
+ receive
+ {Ref, Result} ->
+ Result
+ after 15000 ->
+ {error, timeout2}
+ end
+ after 15000 ->
+ {error, timeout1}
+ end.
+
+
+
+receive_hello(S, Ack) ->
+ %% The Ack is to collect bytes until the full message is received
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ case binary:split(<<Ack/binary, Bin0/binary>>, [<<"\r\n">>,<<"\r">>,<<"\n">>]) of
+ [Hello = <<"SSH-2.0-",_/binary>>, NextPacket] ->
+ ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Hello = <<"SSH-1.99-",_/binary>>, NextPacket] ->
+ ct:comment("Old SSH ~s",["1.99"]),
+ ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Bin] when size(Bin) < 256 ->
+ ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
+ [io_lib:format('~2.16.0b ',[C])
+ || C <- binary_to_list(Bin0)
+ ]
+ ]),
+ receive_hello(S, Bin0);
+
+ _ ->
+ ct:log("Bad hello string (line ~p, ~p chars):~n~s~n~s",[?LINE,size(Bin0),Bin0,
+ [io_lib:format('~2.16.0b ',[C])
+ || C <- binary_to_list(Bin0)
+ ]
+ ]),
+ ct:fail("Bad hello string received")
+ end;
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for hello!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+
+receive_kexinit(_S, <<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>)
+ when PacketLen < 5000, % heuristic max len to stop huge attempts if packet decodeing get out of sync
+ size(PayloadAndPadding) >= (PacketLen-1) % Need more bytes?
+ ->
+ ct:log("Has all ~p packet bytes",[PacketLen]),
+ PayloadLen = PacketLen - PaddingLen - 1,
+ <<Payload:PayloadLen/binary, _Padding:PaddingLen/binary>> = PayloadAndPadding,
+ ssh_message:decode(Payload);
+
+receive_kexinit(S, Ack) ->
+ ct:log("Has ~p bytes, need more",[size(Ack)]),
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ receive_kexinit(S, <<Ack/binary, Bin0/binary>>);
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for kexinit!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+
+
+host_ip() ->
+ {ok,Name} = inet:gethostname(),
+ {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
+ IP.
+
+
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
new file mode 100755
index 0000000000..1cb7bf33e1
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+UBUNTU_VER=${1:-16.04}
+
+USER=sshtester
+PWD=foobar
+
+docker build \
+ -t ubuntubuildbase \
+ --build-arg https_proxy=$HTTPS_PROXY \
+ --build-arg http_proxy=$HTTP_PROXY \
+ - <<EOF
+
+ FROM ubuntu:$UBUNTU_VER
+ WORKDIR /buildroot
+
+ # Prepare for installing OpenSSH
+ RUN apt-get update
+ RUN apt-get upgrade -y
+ RUN apt-get -y install apt-utils
+ RUN apt-get -y install build-essential zlib1g-dev
+ RUN apt-get -y install sudo iputils-ping tcptraceroute net-tools
+ RUN apt-get -y install sshpass expect
+ RUN apt-get -y install libpam0g-dev
+
+ # A user for the tests
+ RUN (echo $PWD; echo $PWD; echo; echo; echo; echo; echo; echo ) | adduser $USER
+ RUN adduser $USER sudo
+
+ # Prepare the privsep preauth environment for openssh
+ RUN mkdir -p /var/empty
+ RUN chown root:sys /var/empty
+ RUN chmod 755 /var/empty
+ RUN groupadd -f sshd
+ RUN ls /bin/false
+ RUN id -u sshd 2> /dev/null || useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd
+
+EOF
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
new file mode 100755
index 0000000000..983c57b18b
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# ./create-image openssh 7.3p1 openssl 1.0.2m
+
+set -x
+
+case $1 in
+ openssh)
+ FAMssh=openssh
+ VERssh=$2
+ PFX=https://ftp.eu.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo "Unsupported: $1"
+ exit
+esac
+
+FAMssl=$3
+VERssl=$4
+
+VER=${FAMssh}${VERssh}-${FAMssl}${VERssl}
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-${FAMssl}:${VERssl}
+
+ LABEL openssh-version=${VER}
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ # Build and install
+
+ WORKDIR ${FAMssh}-${VERssh}
+
+ # Probably VERY OpenSSH dependent...:
+ RUN ./configure --without-pie \
+ --prefix=/buildroot/ssh \
+ --with-ssl-dir=/buildroot/ssl \
+ --with-pam
+ RUN make
+ RUN make install
+ RUN echo UsePAM yes >> /buildroot/ssh/etc/sshd_config
+
+ RUN echo Built ${VER}
+
+ # Start the daemon, but keep it in foreground to avoid killing the container
+ CMD /buildroot/ssh/sbin/sshd -D -p 1234
+
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VERssh$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
+
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
new file mode 100755
index 0000000000..66f8358b8a
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+# ./create-image openssl 1.0.2m
+
+case "$1" in
+ "openssl")
+ FAM=openssl
+ VER=$2
+ PFX=https://www.openssl.org/source/openssl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ "libressl")
+ FAM=libressl
+ VER=$2
+ PFX=https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo No lib type
+ exit
+ ;;
+esac
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ LABEL version=$FAM-$VER
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ WORKDIR ${FAM}-${VER}
+
+ RUN ./config --prefix=/buildroot/ssl
+
+ RUN make
+ RUN make install
+
+ RUN echo Built ${FAM}-${VER}
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VER$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-$FAM:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
new file mode 100755
index 0000000000..16b9c21d9f
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+UBUNTU_VERSION=16.04
+
+SSH_SSL_VERSIONS=(\
+ openssh 4.4p1 openssl 0.9.8zh \
+ openssh 4.5p1 openssl 0.9.8zh \
+ openssh 5.0p1 openssl 0.9.8zh \
+ openssh 6.2p2 openssl 0.9.8zh \
+ openssh 6.3p1 openssl 0.9.8zh \
+ \
+ openssh 7.1p1 openssl 1.0.0t \
+ \
+ openssh 7.1p1 openssl 1.0.1p \
+ \
+ openssh 6.6p1 openssl 1.0.2n \
+ openssh 7.1p1 openssl 1.0.2n \
+ openssh 7.6p1 openssl 1.0.2n \
+ )
+
+if [ "x$1" == "x-b" ]
+then
+ shift
+ SKIP_CREATE_BASE=true
+fi
+
+WHAT_TO_DO=$1
+
+function create_one_image ()
+{
+ SSH_FAM=$1
+ SSH_VER=$2
+ SSL_FAM=$3
+ SSL_VER=$4
+
+ [ "x$SKIP_CREATE_BASE" == "xtrue" ] || ./create-base-image || (echo "Create base failed." >&2; exit 1)
+ ./create-ssl-image $SSL_FAM $SSL_VER \
+ || (echo "Create $SSL_FAM $SSL_VER failed." >&2; exit 2)
+
+ ./create-ssh-image $SSH_FAM $SSH_VER $SSL_FAM $SSL_VER \
+ || (echo "Create $SSH_FAM $SSH_VER on $SSL_FAM $SSL_VER failed." >&2; exit 3)
+}
+
+
+case ${WHAT_TO_DO} in
+ list)
+ ;;
+ listatoms)
+ PRE="["
+ POST="]"
+ C=\'
+ COMMA=,
+ ;;
+ build_one)
+ if [ $# != 5 ]
+ then
+ echo "$0 build_one openssh SSH_ver openssl SSL_ver " && exit
+ else
+ create_one_image $2 $3 $4 $5
+ exit
+ fi
+ ;;
+ build_all)
+ ;;
+ *)
+ echo "$0 [-b] list | listatoms | build_one openssh SSH_ver openssl SSL_ver | build_all" && exit
+ ;;
+esac
+
+
+echo -n $PRE
+i=0
+while [ "x${SSH_SSL_VERSIONS[i]}" != "x" ]
+do
+ case ${WHAT_TO_DO} in
+ list*)
+ [ $i -eq 0 ] || echo $COMMA
+ echo -n $C${SSH_SSL_VERSIONS[$i]}${SSH_SSL_VERSIONS[$(( $i + 1 ))]}-${SSH_SSL_VERSIONS[$(( $i + 2 ))]}${SSH_SSL_VERSIONS[$(( $i + 3 ))]}$C
+ ;;
+ build_all)
+ create_one_image ${SSH_SSL_VERSIONS[$i]} ${SSH_SSL_VERSIONS[$(( $i + 1 ))]} ${SSH_SSL_VERSIONS[$(( $i + 2 ))]} ${SSH_SSL_VERSIONS[$(( $i + 3 ))]}
+ ;;
+ esac
+
+ i=$(( $i + 4 ))
+done
+echo $POST
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
new file mode 100644
index 0000000000..8b2354a7ea
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBugIBAAKBgQDlXDEddxFbTtPsu2bRTbSONFVKMxe430iqBoXoKK2Gyhlqn7J8
+SRGlmvTN7T06+9iFqgJi+x+dlSJGlNEY/v67Z8C7rWfJynYuRier4TujLwP452RT
+YrsnCq47pGJXHb9xAWr7UGMv85uDrECUiIdK4xIrwpW/gMb5zPSThDGNiwIVANts
+B9nBX0NH/B0lXthVCg2jRSkpAoGAIS3vG8VmjQNYrGfdcdvQtGubFXs4jZJO6iDe
+9u9/O95dcnH4ZIL4y3ZPHbw73dCKXFe5NlqI/POmn3MyFdpyqH5FTHWB/aAFrma6
+qo00F1mv83DkQCEfg6fwE/SaaBjDecr5I14hWOtocpYqlY1/x1aspahwK6NLPp/D
+A4aAt78CgYAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7Xmyq
+blfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZ
+iEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpgIURgW8SZGj
+X0mMkMJv/Ltdt0gYx60=
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..9116493472
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAOVcMR13EVtO0+y7ZtFNtI40VUozF7jfSKoGhegorYbKGWqfsnxJEaWa9M3tPTr72IWqAmL7H52VIkaU0Rj+/rtnwLutZ8nKdi5GJ6vhO6MvA/jnZFNiuycKrjukYlcdv3EBavtQYy/zm4OsQJSIh0rjEivClb+AxvnM9JOEMY2LAAAAFQDbbAfZwV9DR/wdJV7YVQoNo0UpKQAAAIAhLe8bxWaNA1isZ91x29C0a5sVeziNkk7qIN7273873l1ycfhkgvjLdk8dvDvd0IpcV7k2Woj886afczIV2nKofkVMdYH9oAWuZrqqjTQXWa/zcORAIR+Dp/AT9JpoGMN5yvkjXiFY62hyliqVjX/HVqylqHAro0s+n8MDhoC3vwAAAIAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7XmyqblfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZiEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpg== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
new file mode 100644
index 0000000000..5ed2b361cc
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEILwQIf+Jul+oygeJn7cBSvn2LGqnW1ZfiHDQMDXZ96mooAoGCCqGSM49
+AwEHoUQDQgAEJUo0gCIhXEPJYvxec23IAjq7BjV1xw8deI8JV9vL5BMCZNhyj5Vt
+NbFPbKPuL/Sikn8p4YP/5y336ug7szvYrg==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
new file mode 100644
index 0000000000..240387d901
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCVKNIAiIVxDyWL8XnNtyAI6uwY1dccPHXiPCVfby+QTAmTYco+VbTWxT2yj7i/0opJ/KeGD/+ct9+roO7M72K4= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
new file mode 100644
index 0000000000..9d31d75cd5
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBw+P1sic2i41wTGQgjyUlBtxQfnY77L8TFcDngoRiVrbCugnDrioNo
+JogqymWhSC+gBwYFK4EEACKhZANiAATwaqEp3vyLzfb08kqgIZLv/mAYJyGD+JMt
+f11OswGs3uFkbHZOErFCgeLuBvarSTAFkOlMR9GZGaDEfcrPBTtvKj+jEaAvh6yr
+JxS97rtwk2uadDMem2x4w9Ga4jw4S8E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
new file mode 100644
index 0000000000..cca85bda72
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPBqoSne/IvN9vTySqAhku/+YBgnIYP4ky1/XU6zAaze4WRsdk4SsUKB4u4G9qtJMAWQ6UxH0ZkZoMR9ys8FO28qP6MRoC+HrKsnFL3uu3CTa5p0Mx6bbHjD0ZriPDhLwQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
new file mode 100644
index 0000000000..b698be1ec9
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBtGVvyn7kGX7BfWAYHK2ZXmhWscTOV0J0mAfab0u0ZMw0id2a3O9s
+sBjJoCqoAXTJ7d/OUw85qqQNDE5GDQpDFq6gBwYFK4EEACOhgYkDgYYABAHPWfUD
+tQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJ
+yO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuh
+RPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
new file mode 100644
index 0000000000..d181d30d69
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHPWfUDtQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJyO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuhRPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
new file mode 100644
index 0000000000..84096298ca
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuC6uxC0P8voYQCrwJzczo9iSiwsovPv4etd2BLnu8cKWdnjR
+34tWvtguw2kO+iDyt4hFGGfDBQf2SXl+ZEsE2N1RlSp5A73me2byw/L4MreX2rbU
+TwyNXF3TBvKb3Gbpx7PoiB9frcb9RCMxtypBvGQD6bx6h5UWKuSkYzARaRLv3kbB
+swcqfrA3PfWybkIoaa2RO1Ca86u6K0v+a4r0OfRxTnghuakZkH6CD7+uU3irliPI
+UFt2wTI/qWmnDrMFh4RffToHK0QZHXdkq4ama5kRZdZ0svSorxqkl8EWGPhReoUj
+Yrz0bCNevSlDxHCxLi8epRxuv+AhZHW0YdMCCwIDAQABAoIBAHUyj1aZbfqolWHP
+cL0jbSKnHqiHU0bd9sED9T8QqTEBJwj/3Fwop+wMV8VURol3CbsrZPwgmoHLDTa3
+rmtXKSBtxAns2tA8uDpxyaxSIQj0thYgHHyoehL6SNu06OSYP84pdp+XhyRm6KXA
+11O7+dRMuAi1PCql/VMR5mCPJ6T5qWAVYHFyEBvMm4q5yYSRSPaAaZHC6WbEsxHN
+jGzcyl3tvmOyN0+M7v0U86lQ+H2tSXH+nQg/Ig6hWgFGg8AYoos/9yUGOY+e9bUE
+serYdsuiyxBfo4CgoSeDsjwNp1lAZ5UOrIDdRqK9C8jGVkHDzwfmmtczWXkVVzGZ
+Bd05izECgYEA31yHzSA/umamyZAQbi/5psk1Fc5m6MzsgmJmB6jm7hUZ0EbpSV4C
+6b1nOrk/IAtA12rvDHgWy0zpkJbC5b03C77RnBgTRgLQyolrcpLDJ47+Kxf/AHGk
+m63KaCpwZEQ4f9ARBXySD/jNoW9gz5S6Xa3RnHOC70DsIIk5VOCjWk0CgYEA0xiM
+Ay27PJcbAG/4tnjH8DZfHb8SULfnfUj8kMe3V2SDPDWbhY8zheo45wTBDRflFU5I
+XyGmfuZ7PTTnFVrJz8ua3mAMOzkFn4MmdaRCX9XtuE4YWq3lFvxlrJvfXSjEL0km
+8UwlhJMixaEPqFQjsKc9BHwWKRiKcF4zFQ1DybcCgYB46yfdhYLaj23lmqc6b6Bw
+iWbCql2N1DqJj2l65hY2d5fk6C6s+EcNcOrsoJKq70yoEgzdrDlyz+11yBg0tU2S
+fzgMkAAHG8kajHBts0QRK1kvzSrQe7VITjpQUAFOVpxbnTFJzhloqiHwLlKzremC
+g3IBh4svqO7r4j32VDI61QKBgQCQL4gS872cWSncVp7vI/iNHtZBHy2HbNX1QVEi
+Iwgb7U+mZIdh5roukhlj0l96bgPPVbUhJX7v1sX+vI/KikSmZk/V7IzuNrich5xR
+ZmzfwOOqq8z+wyBjXuqjx6P9oca+9Zxf3L8Tmtx5WNW1CCOImfKXiZopX9XPgsgp
+bPIMaQKBgQCql4uTSacSQ5s6rEEdvR+y6nTohF3zxhOQ+6xivm3Hf1mgTk40lQ+t
+sr6HsSTv8j/ZbhhtaUUb2efro3pDztjlxXFvITar9ZDB2B4QMlpSsDR9UNk8xKGY
+J9aYLr4fJC6J6VA7Wf0yq6LpjSXRH/2GeNtmMl5rFRsHt+VU7GZK9g==
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..4ac6e7b124
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4Lq7ELQ/y+hhAKvAnNzOj2JKLCyi8+/h613YEue7xwpZ2eNHfi1a+2C7DaQ76IPK3iEUYZ8MFB/ZJeX5kSwTY3VGVKnkDveZ7ZvLD8vgyt5fattRPDI1cXdMG8pvcZunHs+iIH1+txv1EIzG3KkG8ZAPpvHqHlRYq5KRjMBFpEu/eRsGzByp+sDc99bJuQihprZE7UJrzq7orS/5rivQ59HFOeCG5qRmQfoIPv65TeKuWI8hQW3bBMj+paacOswWHhF99OgcrRBkdd2SrhqZrmRFl1nSy9KivGqSXwRYY+FF6hSNivPRsI169KUPEcLEuLx6lHG6/4CFkdbRh0wIL uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
new file mode 100644
index 0000000000..01a88acea2
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQC97XncQDaa9PQYEWK7llBxZQ2suVYTz1eadw2HtY+Y8ZKdUBLd
+9LUQ2lymUC9yq66rb5pBBR13k/9Zcbu8I0nafrZT4wJ4H0YGD6Ob5O4HR4EHjO5q
+hgnMJ17e1XnzI31MW5xAuAHTLLClNvnG05T1jaU+tRAsVSCHin3+sOenowIVAMSe
+ANBvw7fm5+Lw+ziOAHPjeYzRAoGBALkWCGpKmlJ65F3Y/RcownHQvsrDAllzKF/a
+cSfriCVVP5qVZ3Ach28ZZ9BFEnRE2SKqVsyBAiceb/+ISlu8CqKEvvoNIMJAu5rU
+MwZh+PeHN4ES6tWTwBGAwu84ke6N4BgV+6Q4qkcyywHsT5oU0EdVbn2zzAZw8c7v
+BpbsJ1KsAoGABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CI
+TjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJ
+MIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+WsCFQCRJayH
+P4vM1XUOVEeX7u04K1EAFg==
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
new file mode 100644
index 0000000000..30661d5adf
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL3tedxANpr09BgRYruWUHFlDay5VhPPV5p3DYe1j5jxkp1QEt30tRDaXKZQL3KrrqtvmkEFHXeT/1lxu7wjSdp+tlPjAngfRgYPo5vk7gdHgQeM7mqGCcwnXt7VefMjfUxbnEC4AdMssKU2+cbTlPWNpT61ECxVIIeKff6w56ejAAAAFQDEngDQb8O35ufi8Ps4jgBz43mM0QAAAIEAuRYIakqaUnrkXdj9FyjCcdC+ysMCWXMoX9pxJ+uIJVU/mpVncByHbxln0EUSdETZIqpWzIECJx5v/4hKW7wKooS++g0gwkC7mtQzBmH494c3gRLq1ZPAEYDC7ziR7o3gGBX7pDiqRzLLAexPmhTQR1VufbPMBnDxzu8GluwnUqwAAACABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CITjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJMIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+Ws= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
new file mode 100644
index 0000000000..b349d26da3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAtyeX0PPvMULKNJYQXaC3bw9eKErEIjLsHl6vg8tvuDI3cNv228NAr2xNviljXIMMUS8nDRnDAgXX/3DoDchpA= sshtester@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
new file mode 100644
index 0000000000..ece6c8f284
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBdgJs/xThHiy/aY1ymtQ4B0URNnRCm8l2WZMFjua57+nvq4Duf+igN
+pN/5p/+azLKgBwYFK4EEACKhZANiAATUw6pT/UW2HvTW6lL2BGY7NfUGEX285XVi
+9AcTXH1K+TOekbGmcpSirlGzSb15Wycajpmaae5vAzH1nnvcVd3FYODVdDXTHgV/
+FeXQ+vaw7CZnEAKZsr8mjXRX3fEyO1E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
new file mode 100644
index 0000000000..fd81e220f7
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNTDqlP9RbYe9NbqUvYEZjs19QYRfbzldWL0BxNcfUr5M56RsaZylKKuUbNJvXlbJxqOmZpp7m8DMfWee9xV3cVg4NV0NdMeBX8V5dD69rDsJmcQApmyvyaNdFfd8TI7UQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
new file mode 100644
index 0000000000..21c000ea03
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHbAgEBBEEhm0w3xcGILU8eP61mThVBwCJfyzrFktGf7cCa1ciL4YLsukd20Q3Z
+yp0YcEDLcEm36CZGabgkEvblJ1Rx2lPTu6AHBgUrgQQAI6GBiQOBhgAEAYep8cX2
+7wUPw5pNYwFkWQXrJ2GSkmO8iHwkWJ6srRay/sF3WoPF/dyDVymFgirtsSTJ+D0u
+ex4qphOOJxkd1Yf+ANHvDFN9LoBvbgtNLTRJlpuNLCdWQlt+mEnPMDGMV/HWHHiz
+7/mWE+XUVIcQjhm5uv0ObI/wroZEurXMGEhTis3L
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
new file mode 100644
index 0000000000..d9830da5de
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGHqfHF9u8FD8OaTWMBZFkF6ydhkpJjvIh8JFierK0Wsv7Bd1qDxf3cg1cphYIq7bEkyfg9LnseKqYTjicZHdWH/gDR7wxTfS6Ab24LTS00SZabjSwnVkJbfphJzzAxjFfx1hx4s+/5lhPl1FSHEI4Zubr9DmyP8K6GRLq1zBhIU4rNyw== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
new file mode 100644
index 0000000000..2e50ac2304
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA7+C3gLoflKybq4I+clbg2SWf6cXyHpnLNDnZeMvIbOz2X/Ce
+XUm17DFeexTaVBs9Zq9WwDFOFkLQhbuXgpvB0shSY0nr+Em7InRM8AiRLxPe0txM
+mFFhL+v083dYwgaJOo1PthNM/tGRZJu+0sQDqrmN7CusFHdZg2NTzTzbwWqPiuP/
+mf3o7W4CWqDTBzbYTgpWlH7vRZf9FYwT4on5YWzLA8TvO2TwBGTfTMK5nswH++iO
+v4jKecoEwyBFMUSKqZ9UYHGw/kshHbltM65Ye/xjXEX0GxDdxu8ZyVKXd4acNbJJ
+P0tcxN4GzKJiR6zNYwCzDhjqDEbM5qCGhShhgQIDAQABAoIBAQCucdGBP9mvmUcs
+Fu+q3xttTztYGqfVMSrhtCA/BJOhA0K4ypegZ/Zw6gY3pBaSi6y/fEuuQSz0a2qR
+lra8OOFflGa15hBA4/2/NKyu8swCXITy+1qIesYev43HcMePcolhl1qcorSfq2/8
+pnbDd+Diy0Y2thvSVmk2b4mF+/gkUx3CHLhgRMcxCHLG1VeqIfLf+pa0jIw94tZ5
+CoIoI096pDTsneO9xhh1QxWQRRFVqdf3Q9zyiBgJCggpX+1fVsbQejuEK4hKRBKx
+SRPX/pX5aU+7+KSZ/DbtXGg1sCw9NUDFTIEV3UPmko4oWawNGv/CQAK80g3go28v
+UnVf11BBAoGBAP2amIFp+Ps33A5eesT7g/NNkGqBEi5W37K8qzYJxqXJvH0xmpFo
+8a3Je3PQRrzbTUJyISA6/XNnA62+bEvWiEXPiK3stQzNHoVz7ftCb19zgW4sLKRW
+Qhjq7QsGeRrdksJnZ7ekfzOv658vHJPElS1MdPu2WWhiNvAjtmdyFQulAoGBAPIk
+6831QAnCdp/ffH/K+cqV9vQYOFig8n4mQNNC+sLghrtZh9kbmTuuNKAhF56vdCCn
+ABD/+RiLXKVsF0PvQ5g9wRLKaiJubXI7XEBemCCLhjtESxGpWEV8GalslUgE1cKs
+d1pwSVjd0sYt0gOAf2VRhlbpSWhXA2xVll34xgetAoGAHaI089pYN7K9SgiMO/xP
+3NxRZcCTSUrpdM9LClN2HOVH2zEyqI8kvnPuswfBXEwb6QnBCS0bdKKy8Vhw+yOk
+ZNPtWrVwKoDFcj6rrlKDBRpQI3mR9doGezboYANvn04I2iKPIgxcuMNzuvQcWL/9
+1n86pDcYl3Pyi3kA1XGlN+kCgYEAz1boBxpqdDDsjGa8X1y5WUviAw8+KD3ghj5R
+IdTnjbjeBUxbc38bTawUac0MQZexE0iMWQImFGs4sHkGzufwdErkqSdjjAoMc1T6
+4C9fifaOwO7wbLYZ3J2wB4/vn5RsSV6OcIVXeN2wXnvbqZ38+A+/vWnSrqJbTwdW
+Uy7yup0CgYEA8M9vjpAoCr3XzNDwJyWRQcT7e+nRYUNDlXBl3jpQhHuJtnSnkoUv
+HXYXEwvp8peycNzeVz5OwFVMzCH8OG4WiGN4Pmo0rDWHED/W7eIRHIitHGTzZ+Qw
+gRxscoewblSLSkYMXidBLmQjr4U5bDBesRuGhm5NuLyMTa1f3Pc/90k=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
new file mode 100644
index 0000000000..26e560d4f8
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDv4LeAuh+UrJurgj5yVuDZJZ/pxfIemcs0Odl4y8hs7PZf8J5dSbXsMV57FNpUGz1mr1bAMU4WQtCFu5eCm8HSyFJjSev4SbsidEzwCJEvE97S3EyYUWEv6/Tzd1jCBok6jU+2E0z+0ZFkm77SxAOquY3sK6wUd1mDY1PNPNvBao+K4/+Z/ejtbgJaoNMHNthOClaUfu9Fl/0VjBPiiflhbMsDxO87ZPAEZN9MwrmezAf76I6/iMp5ygTDIEUxRIqpn1RgcbD+SyEduW0zrlh7/GNcRfQbEN3G7xnJUpd3hpw1skk/S1zE3gbMomJHrM1jALMOGOoMRszmoIaFKGGB uabhnil@elxadlj3q32
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 18271f325a..2c46de29e2 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -422,7 +422,7 @@ srp_suites() ->
?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA].
%%--------------------------------------------------------------------
--spec rc4_suites(Version::ssl_record:ssl_version()) -> [cipher_suite()].
+-spec rc4_suites(Version::ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
%% Description: Returns a list of the RSA|(ECDH/RSA)| (ECDH/ECDSA)
%% with RC4 cipher suites, only supported if explicitly set by user.
@@ -430,13 +430,15 @@ srp_suites() ->
%% belonged to the user configured only category.
%%--------------------------------------------------------------------
rc4_suites({3, 0}) ->
+ rc4_suites(0);
+rc4_suites({3, Minor}) ->
+ rc4_suites(Minor) ++ rc4_suites(0);
+rc4_suites(0) ->
[?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5];
-rc4_suites({3, N}) when N =< 3 ->
+rc4_suites(N) when N =< 3 ->
[?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDHE_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_MD5,
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDH_RSA_WITH_RC4_128_SHA].
%%--------------------------------------------------------------------
@@ -2148,7 +2150,7 @@ calc_mac_hash(Type, Version,
MacSecret, SeqNo, Type,
Length, PlainFragment).
-is_stream_ciphersuite({_, rc4_128, _, _}) ->
+is_stream_ciphersuite(#{cipher := rc4_128}) ->
true;
is_stream_ciphersuite(_) ->
false.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 3b4ca40058..179d1562cd 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -280,8 +280,11 @@ end_per_suite(_Config) ->
init_per_group(GroupName, Config) when GroupName == basic_tls;
GroupName == options_tls;
+ GroupName == options;
GroupName == basic;
- GroupName == options ->
+ GroupName == session;
+ GroupName == error_handling_tests_tls
+ ->
ssl_test_lib:clean_tls_version(Config);
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
@@ -381,12 +384,12 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
TestCase == anonymous_cipher_suites;
TestCase == psk_anon_cipher_suites;
TestCase == psk_anon_with_hint_cipher_suites;
- TestCase == srp_cipher_suites,
- TestCase == srp_anon_cipher_suites,
- TestCase == srp_dsa_cipher_suites,
- TestCase == des_rsa_cipher_suites,
- TestCase == des_ecdh_rsa_cipher_suites,
- TestCase == versions_option,
+ TestCase == srp_cipher_suites;
+ TestCase == srp_anon_cipher_suites;
+ TestCase == srp_dsa_cipher_suites;
+ TestCase == des_rsa_cipher_suites;
+ TestCase == des_ecdh_rsa_cipher_suites;
+ TestCase == versions_option;
TestCase == tls_tcp_connect_big ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 60}),
@@ -427,6 +430,12 @@ init_per_testcase(rizzo_disabled, Config) ->
ct:timetrap({seconds, 60}),
rizzo_add_mitigation_option(disabled, Config);
+init_per_testcase(TestCase, Config) when TestCase == no_reuses_session_server_restart_new_cert_file;
+ TestCase == no_reuses_session_server_restart_new_cert ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 15}),
+ Config;
+
init_per_testcase(prf, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 40}),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 7248411d15..f9cc6ab8b7 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1024,15 +1024,26 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version))].
-
-psk_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version))].
-
-psk_anon_suites(Version) ->
- [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)].
-
+anonymous_suites({3,_ } = Version) ->
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version))];
+anonymous_suites(DTLSVersion) ->
+ Version = dtls_v1:corresponding_tls_version(DTLSVersion),
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version)),
+ not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
+
+psk_suites({3,_ } = Version) ->
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version))];
+psk_suites(DTLSVersion) ->
+ Version = dtls_v1:corresponding_tls_version(DTLSVersion),
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version)),
+ not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
+
+psk_anon_suites({3,_ } = Version) ->
+ [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)];
+psk_anon_suites(DTLSVersion) ->
+ Version = dtls_v1:corresponding_tls_version(DTLSVersion),
+ [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite),
+ not ssl_cipher:is_stream_ciphersuite(tuple_to_map(Suite))].
srp_suites() ->
[ssl_cipher:erl_suite_definition(Suite) ||
Suite <-
@@ -1335,8 +1346,9 @@ enough_openssl_crl_support(_) -> true.
wait_for_openssl_server(Port, tls) ->
do_wait_for_openssl_tls_server(Port, 10);
-wait_for_openssl_server(Port, dtls) ->
- do_wait_for_openssl_dtls_server(Port, 10).
+wait_for_openssl_server(_Port, dtls) ->
+ ok. %% No need to wait for DTLS over UDP server
+ %% client will retransmitt until it is up.
do_wait_for_openssl_tls_server(_, 0) ->
exit(failed_to_connect_to_openssl);
@@ -1349,21 +1361,6 @@ do_wait_for_openssl_tls_server(Port, N) ->
do_wait_for_openssl_tls_server(Port, N-1)
end.
-do_wait_for_openssl_dtls_server(_, 0) ->
- %%exit(failed_to_connect_to_openssl);
- ok;
-do_wait_for_openssl_dtls_server(Port, N) ->
- %% case gen_udp:open(0) of
- %% {ok, S} ->
- %% gen_udp:connect(S, "localhost", Port),
- %% gen_udp:close(S);
- %% _ ->
- %% ct:sleep(?SLEEP),
- %% do_wait_for_openssl_dtls_server(Port, N-1)
- %% end.
- ct:sleep(500),
- do_wait_for_openssl_dtls_server(Port, N-1).
-
version_flag(tlsv1) ->
"-tls1";
version_flag('tlsv1.1') ->
@@ -1660,78 +1657,3 @@ hardcode_dsa_key(3) ->
y = 48598545580251057979126570873881530215432219542526130654707948736559463436274835406081281466091739849794036308281564299754438126857606949027748889019480936572605967021944405048011118039171039273602705998112739400664375208228641666852589396502386172780433510070337359132965412405544709871654840859752776060358,
x = 1457508827177594730669011716588605181448418352823}.
-dtls_hello() ->
- [1,
- <<0,1,4>>,
- <<0,0>>,
- <<0,0,0>>,
- <<0,1,4>>,
- <<254,253,88,
- 156,129,61,
- 131,216,15,
- 131,194,242,
- 46,154,190,
- 20,228,234,
- 234,150,44,
- 62,96,96,103,
- 127,95,103,
- 23,24,42,138,
- 13,142,32,57,
- 230,177,32,
- 210,154,152,
- 188,121,134,
- 136,53,105,
- 118,96,106,
- 103,231,223,
- 133,10,165,
- 50,32,211,
- 227,193,14,
- 181,143,48,
- 66,0,0,100,0,
- 255,192,44,
- 192,48,192,
- 36,192,40,
- 192,46,192,
- 50,192,38,
- 192,42,0,159,
- 0,163,0,107,
- 0,106,0,157,
- 0,61,192,43,
- 192,47,192,
- 35,192,39,
- 192,45,192,
- 49,192,37,
- 192,41,0,158,
- 0,162,0,103,
- 0,64,0,156,0,
- 60,192,10,
- 192,20,0,57,
- 0,56,192,5,
- 192,15,0,53,
- 192,8,192,18,
- 0,22,0,19,
- 192,3,192,13,
- 0,10,192,9,
- 192,19,0,51,
- 0,50,192,4,
- 192,14,0,47,
- 1,0,0,86,0,0,
- 0,14,0,12,0,
- 0,9,108,111,
- 99,97,108,
- 104,111,115,
- 116,0,10,0,
- 58,0,56,0,14,
- 0,13,0,25,0,
- 28,0,11,0,12,
- 0,27,0,24,0,
- 9,0,10,0,26,
- 0,22,0,23,0,
- 8,0,6,0,7,0,
- 20,0,21,0,4,
- 0,5,0,18,0,
- 19,0,1,0,2,0,
- 3,0,15,0,16,
- 0,17,0,11,0,
- 2,1,0>>].
-
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 9118e4b7e3..bbb925e742 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -201,11 +201,11 @@ init_per_testcase(expired_session, Config) ->
init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs;
TestCase == ciphers_dsa_signed_certs ->
- ct:timetrap({seconds, 60}),
+ ct:timetrap({seconds, 90}),
special_init(TestCase, Config);
init_per_testcase(TestCase, Config) ->
- ct:timetrap({seconds, 20}),
+ ct:timetrap({seconds, 35}),
special_init(TestCase, Config).
special_init(TestCase, Config)
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index 80c4acffdb..11762a3c5a 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -267,7 +267,7 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code>
for a file with the extension <c>.beam</c>, the default rule is to
look for a file with a corresponding extension <c>.erl</c> by
replacing the suffix <c>"ebin"</c> of the object directory path with
- <c>"src"</c>.
+ <c>"src"</c> or <c>"src/*"</c>.
The file search is done through <seealso
marker="#find_file/3"><c>find_file/3</c></seealso>. The directory of
the object file is always tried before any other directory specified
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 14fd5ef787..1135a6dd80 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2017</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -372,15 +372,18 @@ true
tuples <c>{<anno>BinSuffix</anno>, <anno>SourceSuffix</anno>}</c> and
is interpreted as follows: if the end of the directory name where the
object is located matches <c><anno>BinSuffix</anno></c>, then the
- source code directory has the same name, but with
- <c><anno>BinSuffix</anno></c> replaced by
- <c><anno>SourceSuffix</anno></c>. <c><anno>Rules</anno></c> defaults
+ name created by replacing <c><anno>BinSuffix</anno></c> with
+ <c><anno>SourceSuffix</anno></c> is expanded by calling
+ <seealso marker="filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seealso>.
+ If a regular file is found among the matches, the function
+ returns that location together with <c><anno>Options</anno></c>.
+ Otherwise the next rule is tried, and so on.</p>
+ <p><c><anno>Rules</anno></c> defaults
to:</p>
<code type="none">
-[{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}]</code>
- <p>If the source file is found in the resulting directory, the function
- returns that location together with <c><anno>Options</anno></c>.
- Otherwise the next rule is tried, and so on.</p>
+[{"", ""}, {"ebin", "src"}, {"ebin", "esrc"},
+ {"ebin", "src/*"}, {"ebin", "esrc/*"}]</code>
<p>The function returns <c>{<anno>SourceFile</anno>,
<anno>Options</anno>}</c> if it succeeds.
<c><anno>SourceFile</anno></c> is the absolute path to the source
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 76f0b38108..5ee584d612 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -189,7 +189,7 @@ table(Name) ->
%% Returns a list of names of the files in the tar file Name.
%% Options accepted: compressed, verbose, cooked.
-spec table(open_handle(), [compressed | verbose | cooked]) ->
- {ok, [tar_entry()]} | {error, term()}.
+ {ok, [string() | tar_entry()]} | {error, term()}.
table(Name, Opts) when is_list(Opts) ->
foldl_read(Name, fun table1/4, [], table_opts(Opts)).
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index d7c313f214..a9c055f72d 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -544,17 +544,16 @@ default_search_rules() ->
{"", ".c", c_source_search_rules()},
{"", ".in", basic_source_search_rules()},
%% plain old directory rules, backwards compatible
- {"", ""},
- {"ebin","src"},
- {"ebin","esrc"}
- ].
+ {"", ""}] ++ erl_source_search_rules().
basic_source_search_rules() ->
(erl_source_search_rules()
++ c_source_search_rules()).
erl_source_search_rules() ->
- [{"ebin","src"}, {"ebin","esrc"}].
+ [{"ebin","src"}, {"ebin","esrc"},
+ {"ebin",filename:join("src", "*")},
+ {"ebin",filename:join("esrc", "*")}].
c_source_search_rules() ->
[{"priv","c_src"}, {"priv","src"}, {"bin","c_src"}, {"bin","src"}, {"", "src"}].
@@ -634,8 +633,16 @@ try_dir_rule(Dir, Filename, From, To) ->
Src = filename:join(NewDir, Filename),
case is_regular(Src) of
true -> {ok, Src};
- false -> error
+ false -> find_regular_file(wildcard(Src))
end;
false ->
error
end.
+
+find_regular_file([]) ->
+ error;
+find_regular_file([File|Files]) ->
+ case is_regular(File) of
+ true -> {ok, File};
+ false -> find_regular_file(Files)
+ end.
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index a9b98911e2..73e4457bd0 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -125,7 +125,8 @@
| {'logfile', string()}.
-type option() :: {'timeout', timeout()}
| {'debug', [debug_flag()]}
- | {'spawn_opt', [proc_lib:spawn_option()]}.
+ | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'hibernate_after', timeout()}.
-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()}
| {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 96a53426e2..8c7db65563 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -198,7 +198,7 @@
%%% start(Name, Mod, Args, Options)
%%% start_link(Mod, Args, Options)
%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()}
+%%% Name ::= {local, atom()} | {global, term()} | {via, atom(), term()}
%%% Mod ::= atom(), callback module implementing the 'real' fsm
%%% Args ::= term(), init arguments (to Mod:init/1)
%%% Options ::= [{debug, [Flag]}]
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 9d447418f8..50bf959db5 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -149,7 +149,7 @@ fread(Chars, Format) ->
-spec fread(Continuation, CharSpec, Format) -> Return when
Continuation :: continuation() | [],
- CharSpec :: string() | eof,
+ CharSpec :: string() | 'eof',
Format :: string(),
Return :: {'more', Continuation1 :: continuation()}
| {'done', Result, LeftOverChars :: string()},
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index 983e8d4566..319bff484e 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@
-spec fread(Continuation, String, Format) -> Return when
Continuation :: io_lib:continuation() | [],
- String :: string(),
+ String :: string() | 'eof',
Format :: string(),
Return :: {'more', Continuation1 :: io_lib:continuation()}
| {'done', Result, LeftOverChars :: string()},
diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl
index c6eb0d7915..be11e86100 100644
--- a/lib/stdlib/src/lib.erl
+++ b/lib/stdlib/src/lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -551,7 +551,7 @@ format_stacktrace1(S0, Stack0, PF, SF, Enc) ->
format_stacktrace2(S, Stack, 1, PF, Enc).
format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) ->
- [io_lib:fwrite(<<"~s~s ~ts ~s">>,
+ [io_lib:fwrite(<<"~s~s ~ts ~ts">>,
[sep(N, S), origin(N, M, F, A),
mfa_to_string(M, F, A, Enc),
location(L)])
@@ -573,7 +573,7 @@ location(L) ->
Line = proplists:get_value(line, L),
if
File =/= undefined, Line =/= undefined ->
- io_lib:format("(~s, line ~w)", [File, Line]);
+ io_lib:format("(~ts, line ~w)", [File, Line]);
true ->
""
end.
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 212b143b1d..ad4984b64c 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -701,7 +701,9 @@ exprs([E0|Es], Bs1, RT, Lf, Ef, Bs0, W) ->
{W,V0};
true -> case result_will_be_saved() of
true -> V0;
- false -> ignored
+ false ->
+ erlang:garbage_collect(),
+ ignored
end
end,
{{value,V,Bs,get()},Bs};
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 1f966411c5..0c578acf21 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,9 @@
-export_type([dbg_opt/0]).
--type name() :: pid() | atom() | {'global', atom()}.
+-type name() :: pid() | atom()
+ | {'global', term()}
+ | {'via', module(), term()}.
-type system_event() :: {'in', Msg :: _}
| {'in', Msg :: _, From :: _}
| {'out', Msg :: _, To :: _}
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 915f478dfa..9123bf2f28 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -551,8 +551,8 @@ otp_8130(Config) when is_list(Config) ->
"t() -> "
" L = \"{ 34 , \\\"1\\\\x{AAA}\\\" , \\\"34\\\" , X . a , $\\\\x{AAA} }\", "
" R = ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}),"
- " Lt = erl_scan:string(L, 1, [unicode]),"
- " Rt = erl_scan:string(R, 1, [unicode]),"
+ " Lt = erl_scan:string(L, 1),"
+ " Rt = erl_scan:string(R, 1),"
" Lt = Rt, ok. ">>,
ok},
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index b76bece07f..272a71432a 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -3981,8 +3981,9 @@ non_latin1_module(Config) ->
do_non_latin1_module(Mod) ->
File = atom_to_list(Mod) ++ ".erl",
- Forms = [{attribute,1,file,{File,1}},
- {attribute,1,module,Mod},
+ L1 = erl_anno:new(1),
+ Forms = [{attribute,L1,file,{File,1}},
+ {attribute,L1,module,Mod},
{eof,2}],
error = compile:forms(Forms),
{error,_,[]} = compile:forms(Forms, [return]),
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 808ba9b4c1..dda8d0a12e 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1262,7 +1262,7 @@ parse_forms(Chars) ->
parse_forms2([], _Cont, _Line, Forms) ->
lists:reverse(Forms);
parse_forms2(String, Cont0, Line, Forms) ->
- case erl_scan:tokens(Cont0, String, Line, [unicode]) of
+ case erl_scan:tokens(Cont0, String, Line) of
{done, {ok, Tokens, EndLine}, Chars} ->
{ok, Form} = erl_parse:parse_form(Tokens),
parse_forms2(Chars, [], EndLine, [Form | Forms]);
@@ -1303,7 +1303,7 @@ parse_and_pp_expr(String, Indent, Options) ->
erl_pp:expr(parse_expr(StringDot), Indent, Options).
parse_expr(Chars) ->
- {ok, Tokens, _} = erl_scan:string(Chars, 1, [unicode]),
+ {ok, Tokens, _} = erl_scan:string(Chars, 1),
{ok, [Expr]} = erl_parse:parse_exprs(Tokens),
Expr.
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode1 b/lib/stdlib/test/escript_SUITE_data/unicode1
index 351bb785e5..8dc9d450b8 100755
--- a/lib/stdlib/test/escript_SUITE_data/unicode1
+++ b/lib/stdlib/test/escript_SUITE_data/unicode1
@@ -8,7 +8,7 @@ main(_) ->
_D = erlang:system_flag(backtrace_depth, 0),
A = <<"\x{aaa}"/utf8>>,
S = lists:flatten(io_lib:format("~p/~p.", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).
diff --git a/lib/stdlib/test/escript_SUITE_data/unicode2 b/lib/stdlib/test/escript_SUITE_data/unicode2
index 495188f6f0..d0195b036c 100755
--- a/lib/stdlib/test/escript_SUITE_data/unicode2
+++ b/lib/stdlib/test/escript_SUITE_data/unicode2
@@ -8,7 +8,7 @@ main(_) ->
_D = erlang:system_flag(backtrace_depth, 0),
A = <<"\x{aa}">>,
S = lists:flatten(io_lib:format("~p/~p.", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index c94821bc75..afaf2404fa 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
fold_files/1,otp_5960/1,ensure_dir_eexist/1,ensure_dir_symlink/1,
wildcard_symlink/1, is_file_symlink/1, file_props_symlink/1,
- find_source/1]).
+ find_source/1, find_source_subdir/1]).
-import(lists, [foreach/2]).
@@ -47,7 +47,7 @@ all() ->
[wildcard_one, wildcard_two, wildcard_errors,
fold_files, otp_5960, ensure_dir_eexist, ensure_dir_symlink,
wildcard_symlink, is_file_symlink, file_props_symlink,
- find_source].
+ find_source, find_source_subdir].
groups() ->
[].
@@ -536,16 +536,18 @@ find_source(Config) when is_list(Config) ->
[{".erl",".yrl",[{"",""}]}]),
{ok, ParserErl} = filelib:find_source(code:which(core_parse)),
+ ParserErlName = filename:basename(ParserErl),
+ ParserErlDir = filename:dirname(ParserErl),
{ok, ParserYrl} = filelib:find_source(ParserErl),
"lry." ++ _ = lists:reverse(ParserYrl),
- {ok, ParserYrl} = filelib:find_source(ParserErl,
+ {ok, ParserYrl} = filelib:find_source(ParserErlName, ParserErlDir,
[{".beam",".erl",[{"ebin","src"}]},
{".erl",".yrl",[{"",""}]}]),
%% find_source automatically checks the local directory regardless of rules
{ok, ParserYrl} = filelib:find_source(ParserErl),
- {ok, ParserYrl} = filelib:find_source(ParserErl,
- [{".beam",".erl",[{"ebin","src"}]}]),
+ {ok, ParserYrl} = filelib:find_source(ParserErlName, ParserErlDir,
+ [{".erl",".yrl",[{"ebin","src"}]}]),
%% find_file does not check the local directory unless in the rules
ParserYrlName = filename:basename(ParserYrl),
@@ -559,3 +561,24 @@ find_source(Config) when is_list(Config) ->
{ok, ParserYrl} = filelib:find_file(ParserYrlName, ParserYrlDir),
{ok, ParserYrl} = filelib:find_file(ParserYrlName, ParserYrlDir, []),
ok.
+
+find_source_subdir(Config) when is_list(Config) ->
+ BeamFile = code:which(inets), % Located in lib/inets/src/inets_app/
+ BeamName = filename:basename(BeamFile),
+ BeamDir = filename:dirname(BeamFile),
+ SrcName = filename:basename(BeamFile, ".beam") ++ ".erl",
+
+ {ok, SrcFile} = filelib:find_source(BeamName, BeamDir),
+ SrcName = filename:basename(SrcFile),
+
+ {error, not_found} =
+ filelib:find_source(BeamName, BeamDir,
+ [{".beam",".erl",[{"ebin","src"}]}]),
+ {ok, SrcFile} =
+ filelib:find_source(BeamName, BeamDir,
+ [{".beam",".erl",
+ [{"ebin",filename:join("src", "*")}]}]),
+
+ {ok, SrcFile} = filelib:find_file(SrcName, BeamDir),
+
+ ok.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 86cf58566b..41ee3246f5 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -389,7 +389,7 @@ stop10(_Config) ->
Dir = filename:dirname(code:which(?MODULE)),
rpc:call(Node,code,add_path,[Dir]),
{ok, Pid} = rpc:call(Node,gen_fsm,start,[{global,to_stop},?MODULE,[],[]]),
- global:sync(),
+ ok = global:sync(),
ok = gen_fsm:stop({global,to_stop}),
false = rpc:call(Node,erlang,is_process_alive,[Pid]),
{'EXIT',noproc} = (catch gen_fsm:stop({global,to_stop})),
@@ -1005,7 +1005,7 @@ undef_in_terminate(Config) when is_list(Config) ->
State = {undef_in_terminate, {?MODULE, terminate}},
{ok, FSM} = gen_fsm:start(?MODULE, {state_data, State}, []),
try
- gen_fsm:stop(FSM),
+ ok = gen_fsm:stop(FSM),
ct:fail(failed)
catch
exit:{undef, [{?MODULE, terminate, _, _}|_]} ->
@@ -1201,7 +1201,7 @@ timeout({timeout,Ref,{timeout,Time}}, {From,Ref}) ->
Cref = gen_fsm:start_timer(Time, cancel),
Time4 = Time*4,
receive after Time4 -> ok end,
- gen_fsm:cancel_timer(Cref),
+ _= gen_fsm:cancel_timer(Cref),
{next_state, timeout, {From,Ref2}};
timeout({timeout,Ref2,ok},{From,Ref2}) ->
gen_fsm:reply(From, ok),
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 2e9dc4d4fb..7d9561db24 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -346,7 +346,7 @@ stop10(_Config) ->
Dir = filename:dirname(code:which(?MODULE)),
rpc:call(Node,code,add_path,[Dir]),
{ok, Pid} = rpc:call(Node,gen_server,start,[{global,to_stop},?MODULE,[],[]]),
- global:sync(),
+ ok = global:sync(),
ok = gen_server:stop({global,to_stop}),
false = rpc:call(Node,erlang,is_process_alive,[Pid]),
{'EXIT',noproc} = (catch gen_server:stop({global,to_stop})),
@@ -467,7 +467,7 @@ start_node(Name) ->
%% After starting a slave, it takes a little while until global knows
%% about it, even if nodes() includes it, so we make sure that global
%% knows about it before registering something on all nodes.
- global:sync(),
+ ok = global:sync(),
N.
call_remote1(Config) when is_list(Config) ->
@@ -605,7 +605,7 @@ cast_fast(Config) when is_list(Config) ->
cast_fast_messup() ->
%% Register a false node: hopp@hostname
unregister(erl_epmd),
- erl_epmd:start_link(),
+ {ok, _} = erl_epmd:start_link(),
{ok,S} = gen_tcp:listen(0, []),
{ok,P} = inet:port(S),
{ok,_Creation} = erl_epmd:register_node(hopp, P),
@@ -1309,7 +1309,7 @@ do_call_with_huge_message_queue() ->
{Time,ok} = tc(fun() -> calls(10000, Pid) end),
- [self() ! {msg,N} || N <- lists:seq(1, 500000)],
+ _ = [self() ! {msg,N} || N <- lists:seq(1, 500000)],
erlang:garbage_collect(),
{NewTime,ok} = tc(fun() -> calls(10000, Pid) end),
io:format("Time for empty message queue: ~p", [Time]),
@@ -1426,7 +1426,7 @@ undef_in_terminate(Config) when is_list(Config) ->
State = {undef_in_terminate, {oc_server, terminate}},
{ok, Server} = gen_server:start(?MODULE, {state, State}, []),
try
- gen_server:stop(Server),
+ ok = gen_server:stop(Server),
ct:fail(failed)
catch
exit:{undef, [{oc_server, terminate, [], _}|_]} ->
@@ -1615,7 +1615,7 @@ handle_cast({From,delayed_cast,T}, _State) ->
handle_cast(hibernate_now, _State) ->
{noreply, [], hibernate};
handle_cast(hibernate_later, _State) ->
- timer:send_after(1000,self(),hibernate_now),
+ {ok, _} = timer:send_after(1000,self(),hibernate_now),
{noreply, []};
handle_cast({call_undef_fun, Mod, Fun}, State) ->
Mod:Fun(),
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index e2c73371cd..16e3dba969 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -714,7 +714,7 @@ p(Term, D) ->
rp(Term, 1, 80, D).
p(Term, Col, Ll, D) ->
- rp(Term, Col, Ll, D, no_fun).
+ rp(Term, Col, Ll, D, none).
rp(Term, Col, Ll, D) ->
rp(Term, Col, Ll, D, fun rfd/2).
@@ -724,6 +724,8 @@ rp(Term, Col, Ll, D) ->
rp(Term, Col, Ll, D, RF) ->
rp(Term, Col, Ll, D, ?MAXCS, RF).
+rp(Term, Col, Ll, D, M, none) ->
+ rp(Term, Col, Ll, D, M, fun(_, _) -> no end);
rp(Term, Col, Ll, D, M, RF) ->
%% io:format("~n~n*** Col = ~p Ll = ~p D = ~p~n~p~n-->~n",
%% [Col, Ll, D, Term]),
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 217e8cc252..ca85314775 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -561,9 +561,10 @@ otp_5226(Config) when is_list(Config) ->
otp_5327(Config) when is_list(Config) ->
"exception error: bad argument" =
comm_err(<<"<<\"hej\":default>>.">>),
+ L1 = erl_anno:new(1),
<<"abc">> =
- erl_parse:normalise({bin,1,[{bin_element,1,{string,1,"abc"},
- default,default}]}),
+ erl_parse:normalise({bin,L1,[{bin_element,L1,{string,L1,"abc"},
+ default,default}]}),
[<<"abc">>] = scan(<<"<<(<<\"abc\">>):3/binary>>.">>),
[<<"abc">>] = scan(<<"<<(<<\"abc\">>)/binary>>.">>),
"exception error: bad argument" =
@@ -576,9 +577,9 @@ otp_5327(Config) when is_list(Config) ->
comm_err(<<"<<10:default>>.">>),
[<<98,1:1>>] = scan(<<"<<3:3,5:6>>.">>),
{'EXIT',{badarg,_}} =
- (catch erl_parse:normalise({bin,1,[{bin_element,1,{integer,1,17},
- {atom,1,all},
- default}]})),
+ (catch erl_parse:normalise({bin,L1,[{bin_element,L1,{integer,L1,17},
+ {atom,L1,all},
+ default}]})),
[<<-20/signed>>] = scan(<<"<<-20/signed>> = <<-20>>.">>),
[<<-300:16/signed>>] =
scan(<<"<<-300:16/signed>> = <<-300:16>>.">>),
@@ -2784,7 +2785,7 @@ otp_10302(Config) when is_list(Config) ->
<<"begin
A = <<\"\\xaa\">>,
S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B)
@@ -2797,7 +2798,7 @@ otp_10302(Config) when is_list(Config) ->
<<"io:setopts([{encoding,utf8}]).
A = <<\"\\xaa\">>,
S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).">>,
@@ -2809,7 +2810,7 @@ otp_10302(Config) when is_list(Config) ->
<<"begin
A = [1089],
S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B)
@@ -2821,7 +2822,7 @@ otp_10302(Config) when is_list(Config) ->
<<"io:setopts([{encoding,utf8}]).
A = [1089],
S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])),
- {ok, Ts, _} = erl_scan:string(S, 1, [unicode]),
+ {ok, Ts, _} = erl_scan:string(S, 1),
{ok, Es} = erl_parse:parse_exprs(Ts),
B = erl_eval:new_bindings(),
erl_eval:exprs(Es, B).">>,
@@ -2940,7 +2941,7 @@ otp_14296(Config) when is_list(Config) ->
end(),
fun() ->
- Port = open_port({spawn, "ls"}, [line]),
+ Port = open_port({spawn, "ls"}, [{line,1}]),
KnownPort = erlang:port_to_list(Port),
S = KnownPort ++ ".",
R = KnownPort ++ ".\n",
@@ -3012,7 +3013,7 @@ scan(B) ->
scan(t(B), F).
scan(S0, F) ->
- case erl_scan:tokens([], S0, 1, [unicode]) of
+ case erl_scan:tokens([], S0, 1) of
{done,{ok,Ts,_},S} ->
[F(Ts) | scan(S, F)];
_Else ->