aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin2528 -> 2700 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11668 -> 11652 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin14924 -> 14776 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin12580 -> 12540 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin9760 -> 9416 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dead.beambin11888 -> 12264 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin9428 -> 9384 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin14664 -> 14688 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin13380 -> 13384 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin2380 -> 2652 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin39704 -> 39124 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin55468 -> 56788 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin47172 -> 47316 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin19672 -> 19172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin22716 -> 22616 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin10020 -> 10008 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/orddict.beambin2812 -> 2792 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin10532 -> 10532 bytes
-rw-r--r--erts/configure.in2
-rw-r--r--erts/doc/src/erl_nif.xml81
-rw-r--r--erts/emulator/beam/beam_emu.c2
-rw-r--r--erts/emulator/beam/erl_init.c2
-rw-r--r--erts/emulator/beam/erl_nif.c22
-rw-r--r--erts/emulator/beam/erl_nif.h3
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h2
-rw-r--r--erts/emulator/beam/global.h1
-rw-r--r--erts/emulator/beam/io.c4
-rw-r--r--erts/emulator/sys/ose/erl_ose_sys.h2
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h2
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h2
-rw-r--r--erts/emulator/test/nif_SUITE.erl82
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c104
-rw-r--r--erts/etc/unix/etp-commands.in65
-rw-r--r--lib/compiler/src/beam_a.erl4
-rw-r--r--lib/compiler/src/beam_asm.erl8
-rw-r--r--lib/compiler/src/beam_block.erl9
-rw-r--r--lib/compiler/src/beam_bsm.erl12
-rw-r--r--lib/compiler/src/beam_clean.erl8
-rw-r--r--lib/compiler/src/beam_dead.erl14
-rw-r--r--lib/compiler/src/beam_jump.erl11
-rw-r--r--lib/compiler/src/beam_type.erl14
-rw-r--r--lib/compiler/src/beam_utils.erl6
-rw-r--r--lib/compiler/src/beam_z.erl7
-rw-r--r--lib/compiler/src/compile.erl53
-rw-r--r--lib/compiler/src/v3_codegen.erl95
-rw-r--r--lib/compiler/src/v3_kernel.erl23
-rw-r--r--lib/compiler/src/v3_life.erl117
-rw-r--r--lib/compiler/test/Makefile11
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl28
-rw-r--r--lib/compiler/test/bs_shadowed_size_var.core25
-rw-r--r--lib/compiler/test/compilation_SUITE.erl4
-rw-r--r--lib/compiler/test/compile_SUITE.erl2
-rw-r--r--lib/compiler/test/core_SUITE.erl14
-rw-r--r--lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core66
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl22
-rw-r--r--lib/compiler/test/core_fold_SUITE_data/nested_call_in_case.core (renamed from lib/compiler/test/nested_call_in_case.core)3
-rw-r--r--lib/compiler/test/core_fold_SUITE_data/unused_multiple_values_error.core (renamed from lib/compiler/test/unused_multiple_values_error.core)0
-rw-r--r--lib/compiler/test/guard_SUITE.erl2
-rw-r--r--lib/compiler/test/z_SUITE.erl62
-rw-r--r--lib/hipe/cerl/erl_types.erl12
-rw-r--r--lib/inets/doc/src/httpd.xml3
-rw-r--r--lib/inets/doc/src/notes.xml23
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl8
-rw-r--r--lib/inets/src/http_server/httpd_response.erl7
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/ssh/doc/src/introduction.xml182
-rw-r--r--lib/ssh/doc/src/notes.xml21
-rw-r--r--lib/ssh/doc/src/ref_man.xml4
-rw-r--r--lib/ssh/doc/src/ssh.xml344
-rw-r--r--lib/ssh/doc/src/ssh_app.xml122
-rw-r--r--lib/ssh/doc/src/ssh_channel.xml288
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml96
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml454
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml75
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml706
-rw-r--r--lib/ssh/doc/src/ssh_sftpd.xml56
-rw-r--r--lib/ssh/doc/src/usersguide.xml7
-rw-r--r--lib/ssh/doc/src/using_ssh.xml206
-rw-r--r--lib/ssh/src/ssh.erl6
-rw-r--r--lib/ssh/src/ssh_transport.erl34
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl109
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/src/ssl_handshake.erl15
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl59
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl10
-rw-r--r--lib/stdlib/src/erl_expand_records.erl25
-rw-r--r--lib/stdlib/src/orddict.erl20
-rw-r--r--lib/stdlib/src/otp_internal.erl2
-rw-r--r--lib/tools/emacs/erlang.el31
-rw-r--r--lib/tools/emacs/test.erl.indented8
-rw-r--r--lib/tools/emacs/test.erl.orig8
-rw-r--r--otp_versions.table1
92 files changed, 2514 insertions, 1428 deletions
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
index 696220191b..3ff689bd81 100644
--- a/bootstrap/lib/compiler/ebin/beam_a.beam
+++ b/bootstrap/lib/compiler/ebin/beam_a.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index 4c3608b953..7ddcb3a37e 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index 74969880e0..59084464a0 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam
index 1b51483bb1..7d7ade33f6 100644
--- a/bootstrap/lib/compiler/ebin/beam_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 086720f13c..2fd2d2a82f 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam
index dfeec25a9c..0bd9767208 100644
--- a/bootstrap/lib/compiler/ebin/beam_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 3eef625909..136036ae39 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam
index 2b2cd6030d..4094cec9e6 100644
--- a/bootstrap/lib/compiler/ebin/beam_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 59ab21959a..d3d9440248 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
index 4738099dc3..ac156a37e9 100644
--- a/bootstrap/lib/compiler/ebin/beam_z.beam
+++ b/bootstrap/lib/compiler/ebin/beam_z.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 48a9d55bf0..8dd26f307b 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index da1d92c57f..93ecf832bb 100644
--- a/bootstrap/lib/compiler/ebin/v3_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 5f42d5acb6..5817c6ae58 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index 3c977ceb0e..741a702e88 100644
--- a/bootstrap/lib/compiler/ebin/v3_life.beam
+++ b/bootstrap/lib/compiler/ebin/v3_life.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index d4f56f298c..cab9c9bdbf 100644
--- a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index 67d2f9570a..c95a88c0f0 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/orddict.beam b/bootstrap/lib/stdlib/ebin/orddict.beam
index 5fea6d0622..665941a17c 100644
--- a/bootstrap/lib/stdlib/ebin/orddict.beam
+++ b/bootstrap/lib/stdlib/ebin/orddict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index d144d72394..d978382590 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index 481dfe405e..873e1e30fe 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -631,7 +631,7 @@ fi
case $chk_opsys_ in
win32) OPSYS=win32;;
solaris2.*|SunOS5.*) OPSYS=sol2;;
- linux|Linux) OPSYS=linux;;
+ linux*|Linux) OPSYS=linux;;
darwin|Darwin) OPSYS=darwin;;
freebsd|FreeBSD) OPSYS=freebsd;;
*) OPSYS=noopsys
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 3de94be9ff..4bad8b253c 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -66,34 +66,6 @@
</list>
</warning>
- <p>The NIF concept is officially supported from R14B. NIF source code
- written for earlier experimental versions might need adaption to run on R14B
- or later versions:</p>
- <list>
- <item>No incompatible changes between <em>R14B</em> and R14A.</item>
- <item>Incompatible changes between <em>R14A</em> and R13B04:
- <list>
- <item>Environment argument removed for <c>enif_alloc</c>,
- <c>enif_realloc</c>, <c>enif_free</c>, <c>enif_alloc_binary</c>,
- <c>enif_realloc_binary</c>, <c>enif_release_binary</c>,
- <c>enif_alloc_resource</c>, <c>enif_release_resource</c>,
- <c>enif_is_identical</c> and <c>enif_compare</c>.</item>
- <item>Character encoding argument added to <c>enif_get_atom</c>
- and <c>enif_make_existing_atom</c>.</item>
- <item>Module argument added to <c>enif_open_resource_type</c>
- while changing name spaces of resource types from global to module local.</item>
- </list>
- </item>
- <item>Incompatible changes between <em>R13B04</em> and R13B03:
- <list>
- <item>The function prototypes of the NIFs have changed to expect <c>argc</c> and <c>argv</c>
- arguments. The arity of a NIF is by that no longer limited to 3.</item>
- <item><c>enif_get_data</c> renamed as <c>enif_priv_data</c>.</item>
- <item><c>enif_make_string</c> got a third argument for character encoding.</item>
- </list>
- </item>
- </list>
-
<p>A minimal example of a NIF library can look like this:</p>
<p/>
<code type="none">
@@ -806,6 +778,12 @@ typedef enum {
and return true, or return false if <c>term</c> is not an unsigned integer or is
outside the bounds of type <c>unsigned long</c>.</p></desc>
</func>
+ <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env)</nametext></name>
+ <fsummary>Check if an exception has been raised.</fsummary>
+ <desc><p>Return true if a pending exception is associated
+ with the environment <c>env</c>. The only possible exception is currently
+ <c>badarg</c> (see <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>).</p></desc>
+ </func>
<func><name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
<fsummary>Inspect the content of a binary</fsummary>
<desc><p>Initialize the structure pointed to by <c>bin</c> with
@@ -898,23 +876,37 @@ typedef enum {
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext></name>
<fsummary>Create an atom term</fsummary>
<desc><p>Create an atom term from the null-terminated C-string <c>name</c>
- with iso-latin-1 encoding.</p></desc>
+ with iso-latin-1 encoding. If the length of <c>name</c> exceeds the maximum length
+ allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
+ <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
+ </p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)</nametext></name>
<fsummary>Create an atom term</fsummary>
<desc><p>Create an atom term from the string <c>name</c> with length <c>len</c>.
- Null-characters are treated as any other characters.</p></desc>
+ Null-characters are treated as any other characters. If <c>len</c> is greater than the maximum length
+ allowed for an atom (255 characters), <c>enif_make_atom</c> invokes
+ <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
+ </p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name>
<fsummary>Make a badarg exception.</fsummary>
- <desc><p>Make a badarg exception to be returned from a NIF, and set
- an associated exception reason in <c>env</c>. If
- <c>enif_make_badarg</c> is called, the term it returns <em>must</em>
- be returned from the function that called it. No other return value
- is allowed. Also, the term returned from <c>enif_make_badarg</c> may
- be passed only to
- <seealso marker="#enif_is_exception">enif_is_exception</seealso> and
- not to any other NIF API function.</p></desc>
+ <desc><p>Make a badarg exception to be returned from a NIF, and associate
+ it with the environment <c>env</c>. Once a NIF or any function
+ it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a
+ <c>badarg</c> exception is raised when the NIF returns, even if the NIF
+ attempts to return a non-exception term instead.
+ The return value from <c>enif_make_badarg</c> may only be used as
+ return value from the NIF that invoked it (direct or indirectly)
+ or be passed to
+ <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but
+ not to any other NIF API function.</p>
+ <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>.
+ </p>
+ <note><p>In earlier versions (older than erts-7.0, OTP 18) the return value
+ from <c>enif_make_badarg</c> had to be returned from the NIF. This
+ requirement is now lifted as the return value from the NIF is ignored
+ if <c>enif_make_badarg</c> has been invoked.</p></note></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext></name>
<fsummary>Make a binary term.</fsummary>
@@ -931,7 +923,10 @@ typedef enum {
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name>
<fsummary>Create a floating-point term</fsummary>
- <desc><p>Create a floating-point term from a <c>double</c>.</p></desc>
+ <desc><p>Create a floating-point term from a <c>double</c>. If the <c>double</c> argument is
+ not finite or is NaN, <c>enif_make_double</c> invokes
+ <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.
+ </p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)</nametext></name>
<fsummary>Create an existing atom term</fsummary>
@@ -939,7 +934,9 @@ typedef enum {
the null-terminated C-string <c>name</c> with encoding
<seealso marker="#ErlNifCharEncoding">encode</seealso>. If the atom
already exists store the term in <c>*atom</c> and return true, otherwise
- return false.</p></desc>
+ return false. If the length of <c>name</c> exceeds the maximum length
+ allowed for an atom (255 characters), <c>enif_make_existing_atom</c>
+ returns false.</p></desc>
</func>
<func><name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)</nametext></name>
<fsummary>Create an existing atom term</fsummary>
@@ -947,7 +944,9 @@ typedef enum {
string <c>name</c> with length <c>len</c> and encoding
<seealso marker="#ErlNifCharEncoding">encode</seealso>. Null-characters
are treated as any other characters. If the atom already exists store the term
- in <c>*atom</c> and return true, otherwise return false.</p></desc>
+ in <c>*atom</c> and return true, otherwise return false. If <c>len</c> is greater
+ than the maximum length allowed for an atom (255 characters),
+ <c>enif_make_existing_atom_len</c> returns false.</p></desc>
</func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name>
<fsummary>Create an integer term</fsummary>
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index bb7b799950..fce4fd498a 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -3516,6 +3516,8 @@ do { \
erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]);
reg[0] = r(0);
nif_bif_result = (*fp)(&env, bif_nif_arity, reg);
+ if (env.exception_thrown)
+ nif_bif_result = THE_NON_VALUE;
erts_post_nif(&env);
}
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result));
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index a4f1e0d7ee..4f12727044 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -118,6 +118,8 @@ const int etp_big_endian = 1;
#else
const int etp_big_endian = 0;
#endif
+const Eterm etp_the_non_value = THE_NON_VALUE;
+
/*
* Note about VxWorks: All variables must be initialized by executable code,
* not by an initializer. Otherwise a new instance of the emulator will
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 776bbf6719..25caaa4e44 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -128,6 +128,7 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif)
env->heap_frag = NULL;
env->fpe_was_unmasked = erts_block_fpe();
env->tmp_obj_list = NULL;
+ env->exception_thrown = 0;
}
static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif)
@@ -736,9 +737,15 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
Eterm enif_make_badarg(ErlNifEnv* env)
{
+ env->exception_thrown = 1;
BIF_ERROR(env->proc, BADARG);
}
+int enif_has_pending_exception(ErlNifEnv* env)
+{
+ return env->exception_thrown;
+}
+
int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len,
ErlNifCharEncoding encoding)
{
@@ -958,8 +965,12 @@ ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)
ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d)
{
- Eterm* hp = alloc_heap(env,FLOAT_SIZE_OBJECT);
+ Eterm* hp;
FloatDef f;
+
+ if (!erts_isfinite(d))
+ return enif_make_badarg(env);
+ hp = alloc_heap(env,FLOAT_SIZE_OBJECT);
f.fd = d;
PUT_DOUBLE(f, hp);
return make_float(hp);
@@ -972,6 +983,8 @@ ERL_NIF_TERM enif_make_atom(ErlNifEnv* env, const char* name)
ERL_NIF_TERM enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)
{
+ if (len > MAX_ATOM_CHARACTERS)
+ return enif_make_badarg(env);
return erts_atom_put((byte*)name, len, ERTS_ATOM_ENC_LATIN1, 1);
}
@@ -985,6 +998,8 @@ int enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len,
ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)
{
ASSERT(encoding == ERL_NIF_LATIN1);
+ if (len > MAX_ATOM_CHARACTERS)
+ return 0;
return erts_atom_get(name, len, atom, ERTS_ATOM_ENC_LATIN1);
}
@@ -1748,14 +1763,13 @@ execute_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ASSERT(ep);
if (ep->fp)
fp = NULL;
- if (is_non_value(result)) {
+ if (is_non_value(result) || env->exception_thrown) {
if (proc->freason != TRAP) {
- ASSERT(proc->freason == BADARG);
return init_nif_sched_data(env, dirty_nif_exception, fp, 0, argc, argv);
} else {
if (ep->fp == NULL)
restore_nif_mfa(proc, ep, 1);
- return result;
+ return THE_NON_VALUE;
}
}
else
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 9b2b90c82d..c4fdfd4187 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -46,9 +46,10 @@
** remove enif_schedule_dirty_nif, enif_schedule_dirty_nif_finalizer, enif_dirty_nif_finalizer
** add ErlNifEntry options
** add ErlNifFunc flags
+** 2.8: 18.0 add enif_has_pending_exception
*/
#define ERL_NIF_MAJOR_VERSION 2
-#define ERL_NIF_MINOR_VERSION 7
+#define ERL_NIF_MINOR_VERSION 8
/*
* The emulator will refuse to load a nif-lib with a major version
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 630cefae93..bdcbb32c46 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -156,6 +156,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIte
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[]));
+ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
@@ -305,6 +306,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
# define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev)
# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair)
# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif)
+# define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception)
/*
** ADD NEW ENTRIES HERE (before this comment)
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 634fe533d0..40b043d1cc 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -52,6 +52,7 @@ struct enif_environment_t /* ErlNifEnv */
ErlHeapFragment* heap_frag;
int fpe_was_unmasked;
struct enif_tmp_obj_t* tmp_obj_list;
+ int exception_thrown; /* boolean */
};
extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 8cb185cb2b..dec92be40a 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -5559,7 +5559,9 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len)
mess = make_float(hp);
f.fd = *((double *) ptr[0]);
- PUT_DOUBLE(f, hp);
+ if (!erts_isfinite(f.fd))
+ ERTS_DDT_FAIL;
+ PUT_DOUBLE(f, hp);
hp += FLOAT_SIZE_OBJECT;
ptr++;
break;
diff --git a/erts/emulator/sys/ose/erl_ose_sys.h b/erts/emulator/sys/ose/erl_ose_sys.h
index cd66d95c26..f6526a4714 100644
--- a/erts/emulator/sys/ose/erl_ose_sys.h
+++ b/erts/emulator/sys/ose/erl_ose_sys.h
@@ -112,6 +112,8 @@ extern clock_t sys_times(SysTimes *buffer);
/* No use in having other resolutions than 1 Ms. */
#define SYS_CLOCK_RESOLUTION 1
+#define erts_isfinite finite
+
#ifdef NO_FPE_SIGNALS
#define erts_get_current_fp_exception() NULL
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 8fc5a3ca49..94adcc00c8 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -338,6 +338,8 @@ extern void sys_stop_cat(void);
# define HAVE_ISFINITE
#endif
+#define erts_isfinite isfinite
+
#ifdef NO_FPE_SIGNALS
#define erts_get_current_fp_exception() NULL
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 5181d6b584..714e7357d4 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -267,6 +267,8 @@ extern volatile int erl_fp_exception;
int _finite(double x);
#endif
+#define erts_isfinite _finite
+
/*#define NO_FPE_SIGNALS*/
#define erts_get_current_fp_exception() NULL
#define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index b0624fb8c1..502ada95a1 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -39,7 +39,8 @@
get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
otp_9828/1,
otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1,
- dirty_nif_exception/1, nif_schedule/1
+ dirty_nif_exception/1, nif_schedule/1,
+ nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1
]).
-export([many_args_100/100]).
@@ -68,7 +69,8 @@ all() ->
make_string,reverse_list_test,
otp_9828,
otp_9668, consume_timeslice,
- nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception
+ nif_schedule, dirty_nif, dirty_nif_send, dirty_nif_exception,
+ nif_exception, nif_nan_and_inf, nif_atom_too_long
].
groups() ->
@@ -1595,11 +1597,27 @@ dirty_nif_exception(Config) when is_list(Config) ->
N when is_integer(N) ->
ensure_lib_loaded(Config),
try
- call_dirty_nif_exception(),
+ %% this checks that the expected exception
+ %% occurs when the NIF returns the result
+ %% of enif_make_badarg directly
+ call_dirty_nif_exception(1),
?t:fail(expected_badarg)
catch
error:badarg ->
- [{?MODULE,call_dirty_nif_exception,[],_}|_] =
+ [{?MODULE,call_dirty_nif_exception,[1],_}|_] =
+ erlang:get_stacktrace(),
+ ok
+ end,
+ try
+ %% this checks that the expected exception
+ %% occurs when the NIF calls enif_make_badarg
+ %% at some point but then returns a value that
+ %% isn't an exception
+ call_dirty_nif_exception(0),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ [{?MODULE,call_dirty_nif_exception,[0],_}|_] =
erlang:get_stacktrace(),
ok
end
@@ -1608,6 +1626,57 @@ dirty_nif_exception(Config) when is_list(Config) ->
{skipped,"No dirty scheduler support"}
end.
+nif_exception(Config) when is_list(Config) ->
+ ensure_lib_loaded(Config),
+ try
+ call_nif_exception(),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ ok
+ end.
+
+nif_nan_and_inf(Config) when is_list(Config) ->
+ ensure_lib_loaded(Config),
+ try
+ call_nif_nan_or_inf(nan),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ call_nif_nan_or_inf(inf),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ call_nif_nan_or_inf(tuple),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ ok
+ end.
+
+nif_atom_too_long(Config) when is_list(Config) ->
+ ensure_lib_loaded(Config),
+ try
+ call_nif_atom_too_long(all),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ call_nif_atom_too_long(len),
+ ?t:fail(expected_badarg)
+ catch
+ error:badarg ->
+ ok
+ end.
+
next_msg(_Pid) ->
receive
M -> M
@@ -1741,8 +1810,11 @@ consume_timeslice_nif(_,_) -> ?nif_stub.
call_nif_schedule(_,_) -> ?nif_stub.
call_dirty_nif(_,_,_) -> ?nif_stub.
send_from_dirty_nif(_) -> ?nif_stub.
-call_dirty_nif_exception() -> ?nif_stub.
+call_dirty_nif_exception(_) -> ?nif_stub.
call_dirty_nif_zero_args() -> ?nif_stub.
+call_nif_exception() -> ?nif_stub.
+call_nif_nan_or_inf(_) -> ?nif_stub.
+call_nif_atom_too_long(_) -> ?nif_stub.
%% maps
is_map_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 5a3be84825..3cc9f51ef8 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -380,7 +380,8 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifSInt64 sint64;
ErlNifUInt64 uint64;
double d;
- ERL_NIF_TERM atom, ref1, ref2;
+ ERL_NIF_TERM atom, ref1, ref2, term;
+ size_t len;
sint = INT_MIN;
do {
@@ -502,6 +503,7 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
goto error;
}
}
+
ref1 = enif_make_ref(env);
ref2 = enif_make_ref(env);
if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2)
@@ -890,6 +892,7 @@ static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_T
ERL_NIF_TERM badarg = enif_make_badarg(env);
if (enif_is_exception(env, error_atom)) return error_atom;
if (!enif_is_exception(env, badarg)) return error_atom;
+ if (!enif_has_pending_exception(env)) return error_atom;
return badarg;
}
@@ -1608,16 +1611,26 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
switch (argc) {
- case 0: {
+ case 1: {
ERL_NIF_TERM args[255];
int i;
- for (i = 0; i < 255; i++)
+ args[0] = argv[0];
+ for (i = 1; i < 255; i++)
args[i] = enif_make_int(env, i);
return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
call_dirty_nif_exception, 255, argv);
}
- case 1:
- return enif_make_badarg(env);
+ case 2: {
+ int return_badarg_directly;
+ enif_get_int(env, argv[0], &return_badarg_directly);
+ assert(return_badarg_directly == 1 || return_badarg_directly == 0);
+ if (return_badarg_directly)
+ return enif_make_badarg(env);
+ else {
+ /* ignore return value */ enif_make_badarg(env);
+ return enif_make_atom(env, "ok");
+ }
+ }
default:
return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
call_dirty_nif_exception, argc-1, argv);
@@ -1637,6 +1650,82 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL
}
#endif
+/*
+ * Call enif_make_badarg, but don't return its return value. Instead,
+ * return ok. Result should still be a badarg exception for the erlang
+ * caller.
+ */
+static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ /* ignore return value */ enif_make_badarg(env);
+ return enif_make_atom(env, "ok");
+}
+
+#if !defined(NAN) || !defined(INFINITY)
+double zero(void)
+{
+ return 0.0;
+}
+#endif
+
+static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ double val;
+ char arg[6];
+ ERL_NIF_TERM res;
+
+ assert(argc == 1);
+ enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
+ if (strcmp(arg, "nan") == 0) {
+ /* Verify that enif_make_double raises a badarg for NaN */
+#ifdef NAN
+ val = NAN;
+#else
+ val = 0.0/zero();
+#endif
+ } else {
+ /* Verify that enif_make_double raises a badarg for NaN and infinity */
+#ifdef INFINITY
+ val = INFINITY;
+#else
+ val = 1.0/zero();
+#endif
+ }
+ res = enif_make_double(env, val);
+ assert(enif_is_exception(env, res));
+ assert(enif_has_pending_exception(env));
+ if (strcmp(arg, "tuple") == 0) {
+ return enif_make_tuple2(env, argv[0], res);
+ } else {
+ return res;
+ }
+}
+
+static ERL_NIF_TERM call_nif_atom_too_long(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ char str[257];
+ char arg[4];
+ size_t len;
+ int i;
+ ERL_NIF_TERM res;
+
+ assert(argc == 1);
+ enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
+ /* Verify that creating an atom from a string that's too long results in a badarg */
+ for (i = 0; i < sizeof str; ++i) {
+ str[i] = 'a';
+ }
+ str[256] = '\0';
+ if (strcmp(arg, "len") == 0) {
+ len = strlen(str);
+ res = enif_make_atom_len(env, str, len);
+ } else {
+ res = enif_make_atom(env, str);
+ }
+ assert(enif_is_exception(env, res));
+ return res;
+}
+
static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_make_int(env, enif_is_map(env,argv[0]));
@@ -1821,9 +1910,12 @@ static ErlNifFunc nif_funcs[] =
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
{"call_dirty_nif", 3, call_dirty_nif},
{"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
- {"call_dirty_nif_exception", 0, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
#endif
+ {"call_nif_exception", 0, call_nif_exception},
+ {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
+ {"call_nif_atom_too_long", 1, call_nif_atom_too_long},
{"is_map_nif", 1, is_map_nif},
{"get_map_size_nif", 1, get_map_size_nif},
{"make_new_map_nif", 0, make_new_map_nif},
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index c117a62a21..3ee092418e 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -146,14 +146,10 @@ define etp-1
etp-immediate-1 ($arg0)
else
# (($arg0) & 0x3) == 0
- if (($arg0) == 0x0)
+ if (($arg0) == etp_the_non_value)
printf "<the non-value>"
else
- if (($arg0) == 0x4)
- printf "<the non-value debug>"
- else
- etp-cp-1 ($arg0)
- end
+ etp-cp-1 ($arg0)
end
end
end
@@ -355,7 +351,32 @@ define etp-boxed-1
etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \
1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}'
else
- etp-boxed-immediate-1 ($arg0)
+ if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3c) == 0x3c
+ # A map
+ if (((Eterm*)(($arg0) & ~0x3))[0] & 0xc0) == 0x0
+ # Flat map
+ printf "#{Keys:"
+ etp-1 ((flatmap_t*)(($arg0)&~0x3))->keys (($arg1)+1)
+ printf " Values:{"
+ etp-array-1 ((Eterm*)(($arg0)&~0x3)+3) ($arg1) ($arg1) \
+ 0 ((flatmap_t*)(($arg0)&~0x3))->size '}'
+ printf "}"
+ else
+ # Hashmap
+ printf "#<%x>{", (((((Eterm*)(($arg0)&~0x3))[0])>>(6+2+8))&0xffff)
+ if (((Eterm*)(($arg0) & ~0x3))[0] & 0xc0) >= 0x80
+ # head bitmap/array
+ etp-bitmap-array-1 ((Eterm*)(($arg0)&~0x3)+2) ($arg1) ($arg1) \
+ 0 (((((Eterm*)(($arg0)&~0x3))[0])>>(6+2+8))&0xffff) '}'
+ else
+ # node bitmap
+ etp-bitmap-array-1 ((Eterm*)(($arg0)&~0x3)+1) ($arg1) ($arg1) \
+ 0 (((((Eterm*)(($arg0)&~0x3))[0])>>(6+2+8))&0xffff) '}'
+ end
+ end
+ else
+ etp-boxed-immediate-1 ($arg0)
+ end
end
end
end
@@ -478,6 +499,36 @@ define etp-array-1
end
end
+define etp-bitmap-array-1
+# Args: Eterm* p, int depth, int width, int pos, int bitmap, int end_char
+#
+# Reentrant
+#
+# Same as etp-array-1 with size = bitcount(bitmap)
+#
+ if ($arg4) & 1 != 0
+ if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth)
+ etp-1 (($arg0)[($arg3)]) (($arg1)+1)
+ if (($arg4) & (($arg4)-1)) != 0
+ printf ","
+ end
+ etp-bitmap-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) (($arg4)>>1) ($arg5)
+ else
+ printf "...%c", ($arg5)
+ end
+ else
+ if ($arg4) == 0
+ printf "%c", ($arg5)
+ else
+ etp-bitmap-array-1 $arg0 $arg1 $arg2 $arg3 (($arg4)>>1) $arg5
+
+ # WARNING: One might be tempted to optimize the bitcounting here
+ # by passing the bitmap argument as ($arg4 & ($arg4 - 1)). This is a very
+ # bad idea as arguments are passed as string substitution.
+ # The size of $arg4 would thus grow exponentially for each recursion.
+ end
+ end
+end
#define etpa-1
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index dd7e03dd28..410f598665 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -91,6 +91,10 @@ rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) ->
{bs_init,F,{I,U,Flags},none,[Sz,Src],Dst};
rename_instr(bs_init_writable=I) ->
{bs_init,{f,0},I,1,[{x,0}],{x,0}};
+rename_instr({test,Op,F,[Ctx,Bits,{string,Str}]}) ->
+ %% When compiling from a .S file.
+ <<Bs:Bits/bits,_/bits>> = list_to_binary(Str),
+ {test,Op,F,[Ctx,Bs]};
rename_instr({put_map_assoc,Fail,S,D,R,L}) ->
{put_map,Fail,assoc,S,D,R,L};
rename_instr({put_map_exact,Fail,S,D,R,L}) ->
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index f8cf178d2e..084686def7 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -132,10 +132,10 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->
LiteralChunk = case beam_dict:literal_table(Dict) of
{0,[]} -> [];
{NumLiterals,LitTab0} ->
- LitTab1 = iolist_to_binary(LitTab0),
- LitTab2 = <<NumLiterals:32,LitTab1/binary>>,
- LitTab = iolist_to_binary(zlib:compress(LitTab2)),
- chunk(<<"LitT">>, <<(byte_size(LitTab2)):32>>, LitTab)
+ LitTab1 = [<<NumLiterals:32>>,LitTab0],
+ LitTab = zlib:compress(LitTab1),
+ chunk(<<"LitT">>, <<(iolist_size(LitTab1)):32>>,
+ LitTab)
end,
%% Create the line chunk.
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 5216f39296..e2639e9cac 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -61,15 +61,6 @@ blockify(Is) ->
blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
%% Useless instruction sequence.
blockify(Is, Acc);
-
-%% New bit syntax matching.
-blockify([{bs_save2,R,Point}=I,{bs_restore2,R,Point}|Is], Acc) ->
- blockify([I|Is], Acc);
-blockify([{bs_save2,R,Point}=I,{test,is_eq_exact,_,_}=Test,
- {bs_restore2,R,Point}|Is], Acc) ->
- blockify([I,Test|Is], Acc);
-
-%% Do other peep-hole optimizations.
blockify([{test,is_atom,{f,Fail},[Reg]}=I|
[{select,select_val,Reg,{f,Fail},
[{atom,false},{f,_}=BrFalse,
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index d54c2a9fde..427b7071ac 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -20,7 +20,7 @@
-module(beam_bsm).
-export([module/2,format_error/1]).
--import(lists, [member/2,foldl/3,reverse/1,sort/1,all/2,dropwhile/2]).
+-import(lists, [member/2,foldl/3,reverse/1,sort/1,all/2]).
%%%
%%% We optimize bit syntax matching where the tail end of a binary is
@@ -542,16 +542,13 @@ btb_context_regs_1(Regs, N, Tag, Acc) ->
%% a binary. MustSave is true if the function may pass the match
%% context to the bs_context_to_binary instruction (in which case
%% the current position in the binary must have saved into the
-%% start position using "bs_save_2 Ctx start".
+%% start position using "bs_save_2 Ctx start").
btb_index(Fs) ->
btb_index_1(Fs, []).
btb_index_1([{function,_,_,Entry,Is0}|Fs], Acc0) ->
- [{label,Entry}|Is] =
- dropwhile(fun({label,L}) when L =:= Entry -> false;
- (_) -> true
- end, Is0),
+ Is = drop_to_label(Is0, Entry),
Acc = btb_index_2(Is, Entry, false, Acc0),
btb_index_1(Fs, Acc);
btb_index_1([], Acc) -> gb_trees:from_orddict(sort(Acc)).
@@ -566,6 +563,9 @@ btb_index_2(Is0, Entry, _, Acc) ->
throw:none -> Acc
end.
+drop_to_label([{label,L}|Is], L) -> Is;
+drop_to_label([_|Is], L) -> drop_to_label(Is, L).
+
btb_index_find_start_match([{test,_,{f,F},_},{bs_context_to_binary,_}|Is]) ->
btb_index_find_label(Is, F);
btb_index_find_start_match(_) ->
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index b68b8702e0..1d26993103 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -184,14 +184,6 @@ function_replace([{function,Name,Arity,Entry,Asm0}|Fs], Dict, Acc) ->
function_replace(Fs, Dict, [{function,Name,Arity,Entry,Asm}|Acc]);
function_replace([], _, Acc) -> Acc.
-replace([{test,bs_match_string=Op,{f,Lbl},[Ctx,Bin0]}|Is], Acc, D) ->
- Bits = bit_size(Bin0),
- Bin = case Bits rem 8 of
- 0 -> Bin0;
- Rem -> <<Bin0/bitstring,0:(8-Rem)>>
- end,
- I = {test,Op,{f,label(Lbl, D)},[Ctx,Bits,{string,binary_to_list(Bin)}]},
- replace(Is, [I|Acc], D);
replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) ->
replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D);
replace([{test,Test,{f,Lbl},Live,Ops,Dst}|Is], Acc, D) ->
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index f4515ba2a7..5932d8ce1d 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -255,6 +255,16 @@ backward([{jump,{f,To}}=J|[{bif,Op,_,Ops,Reg}|Is]=Is0], D, Acc) ->
catch
throw:not_possible -> backward(Is0, D, [J|Acc])
end;
+backward([{test,bs_start_match2,F,_,[R,_],Ctxt}=I|Is], D,
+ [{test,bs_match_string,F,[Ctxt,Bs]},
+ {test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) ->
+ case beam_utils:is_killed(Ctxt, Acc0, D) of
+ true ->
+ Eq = {test,is_eq_exact,F,[R,{literal,Bs}]},
+ backward(Is, D, [Eq|Acc0]);
+ false ->
+ backward(Is, D, [I|Acc])
+ end;
backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) ->
To = shortcut_bs_start_match(To0, Src, D),
I = {test,bs_start_match2,{f,To},Live,Info,Dst},
@@ -459,8 +469,8 @@ count_bits_matched([{test,_,_,_,[_,Sz,U,{field_flags,_}],_}|Is], SavePoint, Bits
{integer,N} -> count_bits_matched(Is, SavePoint, Bits+N*U);
_ -> count_bits_matched(Is, SavePoint, Bits)
end;
-count_bits_matched([{test,bs_match_string,_,[_,Bits,_]}|Is], SavePoint, Bits0) ->
- count_bits_matched(Is, SavePoint, Bits0+Bits);
+count_bits_matched([{test,bs_match_string,_,[_,Bs]}|Is], SavePoint, Bits) ->
+ count_bits_matched(Is, SavePoint, Bits+bit_size(Bs));
count_bits_matched([{test,_,_,_}|Is], SavePoint, Bits) ->
count_bits_matched(Is, SavePoint, Bits);
count_bits_matched([{bs_save2,Reg,SavePoint}|_], {Reg,SavePoint}, Bits) ->
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index ba71d4efae..52b6464c7f 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -127,7 +127,7 @@
%%% on the program state.
%%%
--import(lists, [reverse/1,reverse/2,foldl/3,dropwhile/2]).
+-import(lists, [reverse/1,reverse/2,foldl/3]).
module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
Fs = [function(F) || F <- Fs0],
@@ -509,10 +509,7 @@ rem_unused([{label,Lbl}=I|Is0], Used, [Prev|_]=Acc) ->
case gb_sets:is_member(Lbl, Used) of
false ->
Is = case is_unreachable_after(Prev) of
- true ->
- dropwhile(fun({label,_}) -> false;
- (_) -> true
- end, Is0);
+ true -> drop_upto_label(Is0);
false -> Is0
end,
rem_unused(Is, Used, Acc);
@@ -533,6 +530,10 @@ initial_labels([{label,Lbl}|Is], Acc) ->
initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) ->
gb_sets:from_list([Lbl|Acc]).
+drop_upto_label([{label,_}|_]=Is) -> Is;
+drop_upto_label([_|Is]) -> drop_upto_label(Is);
+drop_upto_label([]) -> [].
+
%% ulbl(Instruction, UsedGbSet) -> UsedGbSet'
%% Update the gb_set UsedGbSet with any function-local labels
%% (i.e. not with labels in call instructions) referenced by
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 26c933481a..4731b5e78e 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -149,9 +149,10 @@ simplify_basic_1([], Ts, Acc) ->
%%
simplify_float(Is0, Ts0) ->
{Is1,Ts} = simplify_float_1(Is0, Ts0, [], []),
- Is2 = flt_need_heap(Is1),
+ Is2 = opt_fmoves(Is1, []),
+ Is3 = flt_need_heap(Is2),
try
- {flt_liveness(Is2),Ts}
+ {flt_liveness(Is3),Ts}
catch
throw:not_possible -> not_possible
end.
@@ -202,14 +203,15 @@ simplify_float_1([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
simplify_float_1(Is, tdb_new(), Rs0, [I|Acc]);
simplify_float_1([{set,_,_,{line,_}}=I|Is], Ts, Rs, Acc) ->
simplify_float_1(Is, Ts, Rs, [I|Acc]);
+simplify_float_1([I|Is], Ts0, [], Acc) ->
+ Ts = update(I, Ts0),
+ simplify_float_1(Is, Ts, [], [I|Acc]);
simplify_float_1([I|Is]=Is0, Ts0, Rs0, Acc0) ->
Ts = update(I, Ts0),
{Rs,Acc} = flush(Rs0, Is0, Acc0),
simplify_float_1(Is, Ts, Rs, [I|checkerror(Acc)]);
-simplify_float_1([], Ts, Rs, Acc0) ->
- Acc = checkerror(Acc0),
- Is0 = reverse(flush_all(Rs, [], Acc)),
- Is = opt_fmoves(Is0, []),
+simplify_float_1([], Ts, [], Acc) ->
+ Is = reverse(Acc),
{Is,Ts}.
coerce_to_float({integer,I}=Int) ->
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 7704690f86..fd666be41e 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -612,13 +612,15 @@ is_reg_used_at_1(R, Lbl, St0) ->
end.
index_labels_1([{label,Lbl}|Is0], Acc) ->
- Is = lists:dropwhile(fun({label,_}) -> true;
- (_) -> false end, Is0),
+ Is = drop_labels(Is0),
index_labels_1(Is0, [{Lbl,Is}|Acc]);
index_labels_1([_|Is], Acc) ->
index_labels_1(Is, Acc);
index_labels_1([], Acc) -> gb_trees:from_orddict(sort(Acc)).
+drop_labels([{label,_}|Is]) -> drop_labels(Is);
+drop_labels(Is) -> Is.
+
%% Help functions for combine_heap_needs.
combine_alloc_lists(Al1, Al2) ->
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
index 0c7bef9183..47e786034d 100644
--- a/lib/compiler/src/beam_z.erl
+++ b/lib/compiler/src/beam_z.erl
@@ -74,6 +74,13 @@ undo_rename({bs_init,F,{I,Extra,U,Flags},Live,[Sz,Src],Dst}) ->
{I,F,Sz,Extra,Live,U,Src,Flags,Dst};
undo_rename({bs_init,_,bs_init_writable=I,_,_,_}) ->
I;
+undo_rename({test,bs_match_string=Op,F,[Ctx,Bin0]}) ->
+ Bits = bit_size(Bin0),
+ Bin = case Bits rem 8 of
+ 0 -> Bin0;
+ Rem -> <<Bin0/bitstring,0:(8-Rem)>>
+ end,
+ {test,Op,F,[Ctx,Bits,{string,binary_to_list(Bin)}]};
undo_rename({put_map,Fail,assoc,S,D,R,L}) ->
{put_map_assoc,Fail,S,D,R,L};
undo_rename({put_map,Fail,exact,S,D,R,L}) ->
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index c45c9a1a29..0b021073db 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -285,11 +285,20 @@ internal_comp(Passes, File, Suffix, St0) ->
St1 = St0#compile{filename=File, dir=Dir, base=Base,
ifile=erlfile(Dir, Base, Suffix),
ofile=objfile(Base, St0)},
- Run = case member(time, St1#compile.options) of
- true ->
- io:format("Compiling ~tp\n", [File]),
- fun run_tc/2;
- false -> fun({_Name,Fun}, St) -> catch Fun(St) end
+ Opts = St1#compile.options,
+ Run0 = case member(time, Opts) of
+ true ->
+ io:format("Compiling ~tp\n", [File]),
+ fun run_tc/2;
+ false -> fun({_Name,Fun}, St) -> catch Fun(St) end
+ end,
+ Run = case keyfind(eprof, 1, Opts) of
+ {eprof,EprofPass} ->
+ fun(P, St) ->
+ run_eprof(P, EprofPass, St)
+ end;
+ false ->
+ Run0
end,
case fold_comp(Passes, Run, St1) of
{ok,St2} -> comp_ret_ok(St2);
@@ -331,6 +340,16 @@ run_tc({Name,Fun}, St) ->
[Name,(After_c-Before_c) / 1000,Mem]),
Val.
+run_eprof({Name,Fun}, Name, St) ->
+ io:format("~p: Running eprof\n", [Name]),
+ eprof:start_profiling([self()]),
+ Val = (catch Fun(St)),
+ eprof:stop_profiling(),
+ eprof:analyze(),
+ Val;
+run_eprof({_,Fun}, _, St) ->
+ catch Fun(St).
+
comp_ret_ok(#compile{code=Code,warnings=Warn0,module=Mod,options=Opts}=St) ->
case werror(St) of
true ->
@@ -606,7 +625,7 @@ standard_passes() ->
{iff,'to_exp',{done,"E"}},
%% Conversion to Core Erlang.
- ?pass(core_module),
+ {pass,v3_core},
{iff,'dcore',{listing,"core"}},
{iff,'to_core0',{done,"core"}}
| core_passes()].
@@ -618,7 +637,7 @@ core_passes() ->
[{unless,no_copt,
[{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},
{iff,doldinline,{listing,"oldinline"}},
- ?pass(core_fold_module),
+ {pass,sys_core_fold},
{iff,dcorefold,{listing,"corefold"}},
{core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},
{iff,dinline,{listing,"inline"}},
@@ -631,14 +650,14 @@ core_passes() ->
kernel_passes() ->
%% Destructive setelement/3 optimization and core lint.
- [?pass(core_dsetel_module),
+ [{pass,sys_core_dsetel},
{iff,dsetel,{listing,"dsetel"}},
{iff,clint,?pass(core_lint_module)},
{iff,core,?pass(save_core_code)},
%% Kernel Erlang and code generation.
- ?pass(kernel_module),
+ {pass,v3_kernel},
{iff,dkern,{listing,"kernel"}},
{iff,'to_kernel',{done,"kernel"}},
{pass,v3_life},
@@ -1176,14 +1195,6 @@ expand_module(#compile{code=Code,options=Opts0}=St0) ->
Opts = expand_opts(Opts1),
{ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}.
-core_module(#compile{code=Code0,options=Opts}=St) ->
- {ok,Code,Ws} = v3_core:module(Code0, Opts),
- {ok,St#compile{code=Code,warnings=St#compile.warnings ++ Ws}}.
-
-core_fold_module(#compile{code=Code0,options=Opts,warnings=Warns}=St) ->
- {ok,Code,Ws} = sys_core_fold:module(Code0, Opts),
- {ok,St#compile{code=Code,warnings=Warns ++ Ws}}.
-
core_fold_module_after_inlining(#compile{code=Code0,options=Opts}=St) ->
%% Inlining may produce code that generates spurious warnings.
%% Ignore all warnings.
@@ -1219,14 +1230,6 @@ core_inline_module(#compile{code=Code0,options=Opts}=St) ->
Code = cerl_inline:core_transform(Code0, Opts),
{ok,St#compile{code=Code}}.
-core_dsetel_module(#compile{code=Code0,options=Opts}=St) ->
- {ok,Code} = sys_core_dsetel:module(Code0, Opts),
- {ok,St#compile{code=Code}}.
-
-kernel_module(#compile{code=Code0,options=Opts}=St) ->
- {ok,Code,Ws} = v3_kernel:module(Code0, Opts),
- {ok,St#compile{code=Code,warnings=St#compile.warnings ++ Ws}}.
-
save_abstract_code(#compile{ifile=File}=St) ->
case abstract_code(St) of
{ok,Code} ->
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index 40235d6767..15a54a5886 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -121,24 +121,15 @@ cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, Anno, St0) ->
put_reg(V, Reg)
end, [], Hvs),
stk=[]}, 0, Vdb),
- {B0,_Aft,St} = cg_list(Les, 0, Vdb, Bef,
+ {B,_Aft,St} = cg_list(Les, 0, Vdb, Bef,
St3#cg{bfail=0,
ultimate_failure=UltimateMatchFail,
is_top_block=true}),
- B = fix_bs_match_strings(B0),
{Name,Arity} = NameArity,
Asm = [{label,Fi},line(Anno),{func_info,AtomMod,{atom,Name},Arity},
{label,Fl}|B++[{label,UltimateMatchFail},if_end]],
{Asm,Fl,St}.
-fix_bs_match_strings([{test,bs_match_string,F,[Ctx,BinList]}|Is])
- when is_list(BinList) ->
- I = {test,bs_match_string,F,[Ctx,list_to_bitstring(BinList)]},
- [I|fix_bs_match_strings(Is)];
-fix_bs_match_strings([I|Is]) ->
- [I|fix_bs_match_strings(Is)];
-fix_bs_match_strings([]) -> [].
-
%% cg(Lkexpr, Vdb, StackReg, State) -> {[Ainstr],StackReg,State}.
%% Generate code for a kexpr.
%% Split function into two steps for clarity, not efficiency.
@@ -584,7 +575,7 @@ top_level_block(Keis, Bef, MaxRegs, _St) ->
(return) ->
[{deallocate,FrameSz},return];
(Tuple) when is_tuple(Tuple) ->
- [turn_yregs(tuple_size(Tuple), Tuple, MaxY)];
+ [turn_yregs(Tuple, MaxY)];
(Other) ->
[Other]
end, Keis),
@@ -596,14 +587,49 @@ top_level_block(Keis, Bef, MaxRegs, _St) ->
%% catches work. The code generation algorithm gives a lower register
%% number to the outer catch, which is wrong.
-turn_yregs(0, Tp, _) -> Tp;
-turn_yregs(El, Tp, MaxY) ->
- turn_yregs(El-1,setelement(El,Tp,turn_yreg(element(El,Tp),MaxY)),MaxY).
-
-turn_yreg({yy,YY},MaxY) -> {y,MaxY-YY};
-turn_yreg({list,Ls},MaxY) -> {list, turn_yreg(Ls,MaxY)};
-turn_yreg(Ts,MaxY) when is_list(Ts) -> [turn_yreg(T,MaxY)||T<-Ts];
-turn_yreg(Other,_MaxY) -> Other.
+turn_yregs({call,_,_}=I, _MaxY) -> I;
+turn_yregs({call_ext,_,_}=I, _MaxY) -> I;
+turn_yregs({jump,_}=I, _MaxY) -> I;
+turn_yregs({label,_}=I, _MaxY) -> I;
+turn_yregs({line,_}=I, _MaxY) -> I;
+turn_yregs({test_heap,_,_}=I, _MaxY) -> I;
+turn_yregs({bif,Op,F,A,B}, MaxY) ->
+ {bif,Op,F,turn_yreg(A, MaxY),turn_yreg(B, MaxY)};
+turn_yregs({gc_bif,Op,F,Live,A,B}, MaxY) when is_integer(Live) ->
+ {gc_bif,Op,F,Live,turn_yreg(A, MaxY),turn_yreg(B, MaxY)};
+turn_yregs({get_tuple_element,S,N,D}, MaxY) ->
+ {get_tuple_element,turn_yreg(S, MaxY),N,turn_yreg(D, MaxY)};
+turn_yregs({put_tuple,Arity,D}, MaxY) ->
+ {put_tuple,Arity,turn_yreg(D, MaxY)};
+turn_yregs({select_val,R,F,L}, MaxY) ->
+ {select_val,turn_yreg(R, MaxY),F,L};
+turn_yregs({test,Op,F,L}, MaxY) ->
+ {test,Op,F,turn_yreg(L, MaxY)};
+turn_yregs({test,Op,F,Live,A,B}, MaxY) when is_integer(Live) ->
+ {test,Op,F,Live,turn_yreg(A, MaxY),turn_yreg(B, MaxY)};
+turn_yregs({Op,A}, MaxY) ->
+ {Op,turn_yreg(A, MaxY)};
+turn_yregs({Op,A,B}, MaxY) ->
+ {Op,turn_yreg(A, MaxY),turn_yreg(B, MaxY)};
+turn_yregs({Op,A,B,C}, MaxY) ->
+ {Op,turn_yreg(A, MaxY),turn_yreg(B, MaxY),turn_yreg(C, MaxY)};
+turn_yregs(Tuple, MaxY) ->
+ turn_yregs(tuple_size(Tuple), Tuple, MaxY).
+
+turn_yregs(1, Tp, _) ->
+ Tp;
+turn_yregs(N, Tp, MaxY) ->
+ E = turn_yreg(element(N, Tp), MaxY),
+ turn_yregs(N-1, setelement(N, Tp, E), MaxY).
+
+turn_yreg({yy,YY}, MaxY) ->
+ {y,MaxY-YY};
+turn_yreg({list,Ls},MaxY) ->
+ {list,turn_yreg(Ls, MaxY)};
+turn_yreg([_|_]=Ts, MaxY) ->
+ [turn_yreg(T, MaxY) || T <- Ts];
+turn_yreg(Other, _MaxY) ->
+ Other.
%% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) ->
%% {Is,StackReg,State}.
@@ -682,22 +708,37 @@ select_nil(#l{ke={val_clause,nil,B}}, V, Tf, Vf, Bef, St0) ->
select_binary(#l{ke={val_clause,{binary,{var,V}},B},i=I,vdb=Vdb},
V, Tf, Vf, Bef, St0) ->
Int0 = clear_dead(Bef#sr{reg=Bef#sr.reg}, I, Vdb),
- {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0),
+ {Bis0,Aft,St1} = match_cg(B, Vf, Int0, St0),
CtxReg = fetch_var(V, Int0),
Live = max_reg(Bef#sr.reg),
- {[{test,bs_start_match2,{f,Tf},Live,[CtxReg,V],CtxReg},
- {bs_save2,CtxReg,{V,V}}|Bis],
- Aft,St1};
+ Bis1 = [{test,bs_start_match2,{f,Tf},Live,[CtxReg,V],CtxReg},
+ {bs_save2,CtxReg,{V,V}}|Bis0],
+ Bis = finish_select_binary(Bis1),
+ {Bis,Aft,St1};
select_binary(#l{ke={val_clause,{binary,{var,Ivar}},B},i=I,vdb=Vdb},
V, Tf, Vf, Bef, St0) ->
Regs = put_reg(Ivar, Bef#sr.reg),
Int0 = clear_dead(Bef#sr{reg=Regs}, I, Vdb),
- {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0),
+ {Bis0,Aft,St1} = match_cg(B, Vf, Int0, St0),
CtxReg = fetch_var(Ivar, Int0),
Live = max_reg(Bef#sr.reg),
- {[{test,bs_start_match2,{f,Tf},Live,[fetch_var(V, Bef),Ivar],CtxReg},
- {bs_save2,CtxReg,{Ivar,Ivar}}|Bis],
- Aft,St1}.
+ Bis1 = [{test,bs_start_match2,{f,Tf},Live,[fetch_var(V, Bef),Ivar],CtxReg},
+ {bs_save2,CtxReg,{Ivar,Ivar}}|Bis0],
+ Bis = finish_select_binary(Bis1),
+ {Bis,Aft,St1}.
+
+finish_select_binary([{bs_save2,R,Point}=I,{bs_restore2,R,Point}|Is]) ->
+ [I|finish_select_binary(Is)];
+finish_select_binary([{bs_save2,R,Point}=I,{test,is_eq_exact,_,_}=Test,
+ {bs_restore2,R,Point}|Is]) ->
+ [I,Test|finish_select_binary(Is)];
+finish_select_binary([{test,bs_match_string,F,[Ctx,BinList]}|Is])
+ when is_list(BinList) ->
+ I = {test,bs_match_string,F,[Ctx,list_to_bitstring(BinList)]},
+ [I|finish_select_binary(Is)];
+finish_select_binary([I|Is]) ->
+ [I|finish_select_binary(Is)];
+finish_select_binary([]) -> [].
%% New instructions for selection of binary segments.
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 0ac1aaf158..7dff58582e 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -836,12 +836,23 @@ get_vsub(V, Vsub) ->
set_vsub(V, S, Vsub) ->
orddict:store(V, S, Vsub).
-subst_vsub(V, S, Vsub0) ->
- %% Fold chained substitutions.
- Vsub1 = orddict:map(fun (_, V1) when V1 =:= V -> S;
- (_, V1) -> V1
- end, Vsub0),
- orddict:store(V, S, Vsub1).
+subst_vsub(Key, New, [{K,Key}|Dict]) ->
+ %% Fold chained substitution.
+ [{K,New}|subst_vsub(Key, New, Dict)];
+subst_vsub(Key, New, [{K,_}|_]=Dict) when Key < K ->
+ %% Insert the new substitution here, and continue
+ %% look for chained substitutions.
+ [{Key,New}|subst_vsub_1(Key, New, Dict)];
+subst_vsub(Key, New, [{K,_}=E|Dict]) when Key > K ->
+ [E|subst_vsub(Key, New, Dict)];
+subst_vsub(Key, New, []) -> [{Key,New}].
+
+subst_vsub_1(V, S, [{K,V}|Dict]) ->
+ %% Fold chained substitution.
+ [{K,S}|subst_vsub_1(V, S, Dict)];
+subst_vsub_1(V, S, [E|Dict]) ->
+ [E|subst_vsub_1(V, S, Dict)];
+subst_vsub_1(_, _, []) -> [].
get_fsub(F, A, Fsub) ->
case orddict:find({F,A}, Fsub) of
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index 75bd188479..4b1f1c3f71 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -45,7 +45,7 @@
-export([vdb_find/2]).
--import(lists, [member/2,map/2,foldl/3,reverse/1,sort/1]).
+-import(lists, [member/2,map/2,reverse/1,sort/1]).
-import(ordsets, [add_element/2,intersection/2,union/2]).
-include("v3_kernel.hrl").
@@ -68,7 +68,7 @@ functions([], Acc) -> reverse(Acc).
function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) ->
try
As = var_list(Vs),
- Vdb0 = foldl(fun ({var,N}, Vdb) -> new_var(N, 0, Vdb) end, [], As),
+ Vdb0 = init_vars(As),
%% Force a top-level match!
B0 = case Kb of
#k_match{} -> Kb;
@@ -94,14 +94,14 @@ function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) ->
body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
%%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ Vdb1 = use_vars(union(A#k.us, A#k.ns), I, Vdb0),
{Es,MaxI,Vdb2} = body(Kb, I+1, Vdb1),
E = expr(Ke, I, Vdb2),
{[E|Es],MaxI,Vdb2};
body(Ke, I, Vdb0) ->
%%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ Vdb1 = use_vars(union(A#k.us, A#k.ns), I, Vdb0),
E = expr(Ke, I, Vdb1),
{[E],I,Vdb1}.
@@ -150,12 +150,12 @@ expr(#k_try_enter{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh}, I, Vdb) -
%% the body and handler. Add try tag 'variable'.
Ab = get_kanno(Kb),
Ah = get_kanno(Kh),
- Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
+ Tdb1 = use_vars(union(Ab#k.us, Ah#k.us), I+3, Tdb0),
Tdb2 = vdb_sub(I, I+2, Tdb1),
Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
{Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, 1000000, Tdb2)),
- {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
- {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
+ {Bes,_,Bdb} = body(Kb, I+4, new_vars(sort(map(Vnames, Vs)), I+3, Tdb2)),
+ {Hes,_,Hdb} = body(Kh, I+4, new_vars(sort(map(Vnames, Evs)), I+3, Tdb2)),
#l{ke={try_enter,#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]}},
@@ -171,7 +171,7 @@ expr(#k_receive{anno=A,var=V,body=Kb,timeout=T,action=Ka,ret=Rs}, I, Vdb) ->
%% Work out imported variables which need to be locked.
Rdb = vdb_sub(I, I+1, Vdb),
M = match(Kb, add_element(V#k_var.name, A#k.us), I+1, [],
- new_var(V#k_var.name, I, Rdb)),
+ new_vars([V#k_var.name], I, Rdb)),
{Tes,_,Adb} = body(Ka, I+1, Rdb),
#l{ke={receive_loop,atomic(T),variable(V),M,
#l{ke=Tes,i=I+1,vdb=Adb,a=[]},var_list(Rs)},
@@ -199,12 +199,12 @@ body_try(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs},
%% the body and handler. Add try tag 'variable'.
Ab = get_kanno(Kb),
Ah = get_kanno(Kh),
- Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
+ Tdb1 = use_vars(union(Ab#k.us, Ah#k.us), I+3, Tdb0),
Tdb2 = vdb_sub(I, I+2, Tdb1),
Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
{Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, locked, Tdb2)),
- {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
- {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
+ {Bes,_,Bdb} = body(Kb, I+4, new_vars(sort(map(Vnames, Vs)), I+3, Tdb2)),
+ {Hes,_,Hdb} = body(Kh, I+4, new_vars(sort(map(Vnames, Evs)), I+3, Tdb2)),
#l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]},
@@ -400,79 +400,68 @@ is_gc_bif(Bif, Arity) ->
erl_internal:new_type_test(Bif, Arity) orelse
erl_internal:comp_op(Bif, Arity)).
-%% new_var(VarName, I, Vdb) -> Vdb.
+%% Keep track of life time for variables.
+%%
+%% init_vars([{var,VarName}]) -> Vdb.
%% new_vars([VarName], I, Vdb) -> Vdb.
-%% use_var(VarName, I, Vdb) -> Vdb.
%% use_vars([VarName], I, Vdb) -> Vdb.
%% add_var(VarName, F, L, Vdb) -> Vdb.
+%%
+%% The list of variable names for new_vars/3 and use_vars/3
+%% must be sorted.
-new_var(V, I, Vdb) ->
- vdb_store_new(V, I, I, Vdb).
+init_vars(Vs) ->
+ sort([{V,0,0} || {var,V} <- Vs]).
-new_vars(Vs, I, Vdb0) ->
- foldl(fun (V, Vdb) -> new_var(V, I, Vdb) end, Vdb0, Vs).
+new_vars([], _, Vdb) -> Vdb;
+new_vars([V], I, Vdb) -> vdb_store_new(V, {V,I,I}, Vdb);
+new_vars(Vs, I, Vdb) -> vdb_update_vars(Vs, Vdb, I).
-use_var(V, I, Vdb) ->
+use_vars([], _, Vdb) ->
+ Vdb;
+use_vars([V], I, Vdb) ->
case vdb_find(V, Vdb) of
- {V,F,L} when I > L -> vdb_update(V, F, I, Vdb);
+ {V,F,L} when I > L -> vdb_update(V, {V,F,I}, Vdb);
{V,_,_} -> Vdb;
- error -> vdb_store_new(V, I, I, Vdb)
- end.
-
-use_vars([], _, Vdb) -> Vdb;
-use_vars([V], I, Vdb) -> use_var(V, I, Vdb);
-use_vars(Vs, I, Vdb) ->
- Res = use_vars_1(sort(Vs), Vdb, I),
- %% The following line can be used as an assertion.
- %% Res = foldl(fun (V, Vdb) -> use_var(V, I, Vdb) end, Vdb, Vs),
- Res.
-
-%% Measurements show that it is worthwhile having this special
-%% function that updates/inserts several variables at once.
-
-use_vars_1([V|_]=Vs, [{V1,_,_}=Vd|Vdb], I) when V > V1 ->
- [Vd|use_vars_1(Vs, Vdb, I)];
-use_vars_1([V|Vs], [{V1,_,_}|_]=Vdb, I) when V < V1 ->
- %% New variable.
- [{V,I,I}|use_vars_1(Vs, Vdb, I)];
-use_vars_1([V|Vs], [{_,F,L}=Vd|Vdb], I) ->
- %% Existing variable.
- if
- I > L ->[{V,F,I}|use_vars_1(Vs, Vdb, I)];
- true -> [Vd|use_vars_1(Vs, Vdb, I)]
+ error -> vdb_store_new(V, {V,I,I}, Vdb)
end;
-use_vars_1([V|Vs], [], I) ->
- %% New variable.
- [{V,I,I}|use_vars_1(Vs, [], I)];
-use_vars_1([], Vdb, _) -> Vdb.
+use_vars(Vs, I, Vdb) -> vdb_update_vars(Vs, Vdb, I).
add_var(V, F, L, Vdb) ->
- vdb_store_new(V, F, L, Vdb).
+ vdb_store_new(V, {V,F,L}, Vdb).
vdb_find(V, Vdb) ->
- %% Performance note: Profiling shows that this function accounts for
- %% a lot of the execution time when huge constant terms are built.
- %% Using the BIF lists:keyfind/3 is a lot faster than the
- %% original Erlang version.
case lists:keyfind(V, 1, Vdb) of
false -> error;
Vd -> Vd
end.
-%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V < V1 -> error;
-%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V == V1 -> Vd;
-%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V > V1 -> vdb_find(V, Vdb);
-%vdb_find(V, []) -> error.
+vdb_update(V, Update, [{V,_,_}|Vdb]) ->
+ [Update|Vdb];
+vdb_update(V, Update, [Vd|Vdb]) ->
+ [Vd|vdb_update(V, Update, Vdb)].
-vdb_update(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 ->
- [Vd|vdb_update(V, F, L, Vdb)];
-vdb_update(V, F, L, [{V1,_,_}|Vdb]) when V == V1 ->
- [{V,F,L}|Vdb].
+vdb_store_new(V, New, [{V1,_,_}=Vd|Vdb]) when V > V1 ->
+ [Vd|vdb_store_new(V, New, Vdb)];
+vdb_store_new(V, New, [{V1,_,_}|_]=Vdb) when V < V1 ->
+ [New|Vdb];
+vdb_store_new(_, New, []) -> [New].
-vdb_store_new(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 ->
- [Vd|vdb_store_new(V, F, L, Vdb)];
-vdb_store_new(V, F, L, [{V1,_,_}|_]=Vdb) when V < V1 -> [{V,F,L}|Vdb];
-vdb_store_new(V, F, L, []) -> [{V,F,L}].
+vdb_update_vars([V|_]=Vs, [{V1,_,_}=Vd|Vdb], I) when V > V1 ->
+ [Vd|vdb_update_vars(Vs, Vdb, I)];
+vdb_update_vars([V|Vs], [{V1,_,_}|_]=Vdb, I) when V < V1 ->
+ %% New variable.
+ [{V,I,I}|vdb_update_vars(Vs, Vdb, I)];
+vdb_update_vars([V|Vs], [{_,F,L}=Vd|Vdb], I) ->
+ %% Existing variable.
+ if
+ I > L -> [{V,F,I}|vdb_update_vars(Vs, Vdb, I)];
+ true -> [Vd|vdb_update_vars(Vs, Vdb, I)]
+ end;
+vdb_update_vars([V|Vs], [], I) ->
+ %% New variable.
+ [{V,I,I}|vdb_update_vars(Vs, [], I)];
+vdb_update_vars([], Vdb, _) -> Vdb.
%% vdb_sub(Min, Max, Vdb) -> Vdb.
%% Extract variables which are used before and after Min. Lock
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 73d52a48bc..98125fc84e 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -35,6 +35,7 @@ MODULES= \
record_SUITE \
trycatch_SUITE \
warnings_SUITE \
+ z_SUITE \
test_lib
NO_OPT= \
@@ -79,12 +80,6 @@ INLINE= \
receive \
record
-CORE_MODULES = \
- bs_shadowed_size_var \
- unused_multiple_values_error \
- nested_call_in_case
-
-
NO_OPT_MODULES= $(NO_OPT:%=%_no_opt_SUITE)
NO_OPT_ERL_FILES= $(NO_OPT_MODULES:%=%.erl)
POST_OPT_MODULES= $(NO_OPT:%=%_post_opt_SUITE)
@@ -94,8 +89,6 @@ INLINE_ERL_FILES= $(INLINE_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
-CORE_FILES= $(CORE_MODULES:%=%.core)
-
##TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
##INSTALL_PROGS= $(TARGET_FILES)
@@ -162,7 +155,7 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) compiler.spec compiler.cover \
- $(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) "$(RELSYSDIR)"
+ $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \
$(INLINE_ERL_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index f7af56afcc..b54db06339 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -24,7 +24,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
fun_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
- bin_tail/1,save_restore/1,shadowed_size_var/1,
+ bin_tail/1,save_restore/1,
partitioned_bs_match/1,function_clause/1,
unit/1,shared_sub_bins/1,bin_and_float/1,
dec_subidentifiers/1,skip_optional_tag/1,
@@ -34,7 +34,8 @@
otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1,
match_string/1,zero_width/1,bad_size/1,haystack/1,
cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
- no_partition/1,calling_a_binary/1,binary_in_map/1]).
+ no_partition/1,calling_a_binary/1,binary_in_map/1,
+ match_string_opt/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -50,7 +51,7 @@ all() ->
groups() ->
[{p,[parallel],
[fun_shadow,int_float,otp_5269,null_fields,wiger,
- bin_tail,save_restore,shadowed_size_var,
+ bin_tail,save_restore,
partitioned_bs_match,function_clause,unit,
shared_sub_bins,bin_and_float,dec_subidentifiers,
skip_optional_tag,wfbm,degenerated_match,bs_sum,
@@ -59,7 +60,8 @@ groups() ->
matching_and_andalso,otp_7188,otp_7233,otp_7240,
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
- no_partition,calling_a_binary,binary_in_map]}].
+ no_partition,calling_a_binary,binary_in_map,
+ match_string_opt]}].
init_per_suite(Config) ->
@@ -322,16 +324,6 @@ bad_float_unpack_match(<<F:64/float>>) -> F;
bad_float_unpack_match(<<I:64/integer-signed>>) -> I.
-shadowed_size_var(Config) when is_list(Config) ->
- ?line PrivDir = ?config(priv_dir, Config),
- ?line Dir = filename:dirname(code:which(?MODULE)),
- ?line Core = filename:join(Dir, "bs_shadowed_size_var"),
- ?line Opts = [from_core,{outdir,PrivDir}|test_lib:opt_opts(?MODULE)],
- ?line io:format("~p", [Opts]),
- ?line {ok,Mod} = c:c(Core, Opts),
- ?line [42|<<"abcde">>] = Mod:filter_essentials([<<42:32>>|<<5:32,"abcde">>]),
- ok.
-
partitioned_bs_match(Config) when is_list(Config) ->
?line <<1,2,3>> = partitioned_bs_match(blurf, <<42,1,2,3>>),
?line error = partitioned_bs_match(10, <<7,8,15,13>>),
@@ -1224,6 +1216,14 @@ match_binary_in_map(Map) ->
ok
end.
+match_string_opt(Config) when is_list(Config) ->
+ {x,<<1,2,3>>,{<<1>>,{v,<<1,2,3>>}}} =
+ do_match_string_opt({<<1>>,{v,<<1,2,3>>}}),
+ ok.
+
+do_match_string_opt({<<1>>,{v,V}}=T) ->
+ {x,V,T}.
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/bs_shadowed_size_var.core b/lib/compiler/test/bs_shadowed_size_var.core
deleted file mode 100644
index d1d5ebba6d..0000000000
--- a/lib/compiler/test/bs_shadowed_size_var.core
+++ /dev/null
@@ -1,25 +0,0 @@
-module 'bs_shadowed_size_var' ['filter_essentials'/1]
- attributes []
-
-%% Reduced code from beam_asm inlined using the old inliner.
-
-'filter_essentials'/1 =
- fun (_cor0) ->
- case _cor0 of
- <[#{#<Sz>(32,1,'integer',['unsigned','big']) }#|T]> when 'true' ->
- let <_cor4> =
- case T of
- %% Variable 'Sz' repeated here. Should work.
- <#{#<Sz>(32,1,'integer',['unsigned','big']),
- #<Data>(Sz,8,'binary',['unsigned','big'])}#> when 'true' ->
- Data
- <_cor5> when 'true' ->
- primop 'match_fail'
- ({'case_clause',{_cor5}})
- end
- in [Sz|_cor4]
- <_cor5> when 'true' ->
- primop 'match_fail'
- ({'function_clause',_cor5})
- end
-end
diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl
index 296774e083..51e1da2cb6 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -309,8 +309,8 @@ load_and_call(Out, Module) ->
%% Smoke-test of beam disassembler.
?line test_lib:smoke_disasm(Module),
- ?line true = erlang:delete_module(Module),
- ?line true = erlang:purge_module(Module),
+ _ = code:delete(Module),
+ _ = code:purge(Module),
%% Restore state of trap_exit just in case. (Since the compiler
%% uses a temporary process, we will get {'EXIT',Pid,normal} messages
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 1c96abe017..6d4fde662b 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -102,6 +102,8 @@ file_1(Config) when is_list(Config) ->
?line compile_and_verify(Simple, Target, [debug_info]),
?line {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage
+ {ok,simple} = compile:file(Simple, [{eprof,beam_z}]), %Coverage
+
?line ok = file:set_cwd(Cwd),
?line true = exists(Target),
?line passed = run(Target, test, []),
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 428ad65364..c4a7efbfc4 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -24,7 +24,9 @@
dehydrated_itracer/1,nested_tries/1,
seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
unsafe_case/1,nomatch_shadow/1,reversed_annos/1,
- map_core_test/1,eval_case/1,bad_boolean_guard/1]).
+ map_core_test/1,eval_case/1,bad_boolean_guard/1,
+ bs_shadowed_size_var/1
+ ]).
-include_lib("test_server/include/test_server.hrl").
@@ -50,7 +52,8 @@ groups() ->
[{p,test_lib:parallel(),
[dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos,
- map_core_test,eval_case,bad_boolean_guard
+ map_core_test,eval_case,bad_boolean_guard,
+ bs_shadowed_size_var
]}].
@@ -78,6 +81,8 @@ end_per_group(_GroupName, Config) ->
?comp(map_core_test).
?comp(eval_case).
?comp(bad_boolean_guard).
+?comp(bs_shadowed_size_var).
+
try_it(Mod, Conf) ->
Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)),
@@ -87,4 +92,7 @@ try_it(Mod, Conf) ->
compile_and_load(Src, Opts) ->
{ok,Mod,Bin} = compile:file(Src, [from_core,report,time,binary|Opts]),
{module,Mod} = code:load_binary(Mod, Mod, Bin),
- ok = Mod:Mod().
+ ok = Mod:Mod(),
+ _ = code:delete(Mod),
+ _ = code:purge(Mod),
+ ok.
diff --git a/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core b/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core
new file mode 100644
index 0000000000..0ade037e05
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core
@@ -0,0 +1,66 @@
+module 'bs_shadowed_size_var'
+ ['filter_essentials'/1,
+ 'bs_shadowed_size_var'/0]
+ attributes []
+
+%% bs_shadowed_size_var() ->
+%% [42|<<"abcde">>] = Mod:filter_essentials([<<42:32>>|<<5:32,"abcde">>]),
+%% ok.
+
+'bs_shadowed_size_var'/0 =
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ case apply 'filter_essentials'/1
+ ([#{#<0>(8,1,'integer',['unsigned'|['big']]),
+ #<0>(8,1,'integer',['unsigned'|['big']]),
+ #<0>(8,1,'integer',['unsigned'|['big']]),
+ #<42>(8,1,'integer',['unsigned'|['big']])}#|#{#<0>(8,1,'integer',['unsigned'|['big']]),
+ #<0>(8,1,'integer',['unsigned'|['big']]),
+ #<0>(8,1,'integer',['unsigned'|['big']]),
+ #<5>(8,1,'integer',['unsigned'|['big']]),
+ #<97>(8,1,'integer',['unsigned'|['big']]),
+ #<98>(8,1,'integer',['unsigned'|['big']]),
+ #<99>(8,1,'integer',['unsigned'|['big']]),
+ #<100>(8,1,'integer',['unsigned'|['big']]),
+ #<101>(8,1,'integer',['unsigned'|['big']])}#]) of
+ <[42|#{#<97>(8,1,'integer',['unsigned'|['big']]),
+ #<98>(8,1,'integer',['unsigned'|['big']]),
+ #<99>(8,1,'integer',['unsigned'|['big']]),
+ #<100>(8,1,'integer',['unsigned'|['big']]),
+ #<101>(8,1,'integer',['unsigned'|['big']])}#]> when 'true' ->
+ 'ok'
+ ( <_cor0> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_cor0})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ ( primop 'match_fail'
+ ({'function_clause'})
+ -| [{'function_name',{'bs_shadowed_size_var',0}}] )
+ -| ['compiler_generated'] )
+ end
+
+%% Reduced code from beam_asm inlined using the old inliner.
+
+'filter_essentials'/1 =
+ fun (_cor0) ->
+ case _cor0 of
+ <[#{#<Sz>(32,1,'integer',['unsigned','big']) }#|T]> when 'true' ->
+ let <_cor4> =
+ case T of
+ %% Variable 'Sz' repeated here. Should work.
+ <#{#<Sz>(32,1,'integer',['unsigned','big']),
+ #<Data>(Sz,8,'binary',['unsigned','big'])}#> when 'true' ->
+ Data
+ <_cor5> when 'true' ->
+ primop 'match_fail'
+ ({'case_clause',{_cor5}})
+ end
+ in [Sz|_cor4]
+ <_cor5> when 'true' ->
+ primop 'match_fail'
+ ({'function_clause',_cor5})
+ end
+end
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index bc82eaf5aa..bff9806bdd 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -231,15 +231,17 @@ eq(Config) when is_list(Config) ->
%% OTP-7117.
nested_call_in_case(Config) when is_list(Config) ->
- ?line PrivDir = ?config(priv_dir, Config),
- ?line Dir = filename:dirname(code:which(?MODULE)),
- ?line Core = filename:join(Dir, "nested_call_in_case"),
- ?line Opts = [from_core,{outdir,PrivDir}|test_lib:opt_opts(?MODULE)],
- ?line io:format("~p", [Opts]),
- ?line {ok,Mod} = c:c(Core, Opts),
- ?line yes = Mod:a([1,2,3], 2),
- ?line no = Mod:a([1,2,3], 4),
- ?line {'EXIT',_} = (catch Mod:a(not_a_list, 42)),
+ PrivDir = ?config(priv_dir, Config),
+ Dir = test_lib:get_data_dir(Config),
+ Core = filename:join(Dir, "nested_call_in_case"),
+ Opts = [from_core,{outdir,PrivDir}|test_lib:opt_opts(?MODULE)],
+ io:format("~p", [Opts]),
+ {ok,Mod} = c:c(Core, Opts),
+ yes = Mod:a([1,2,3], 2),
+ no = Mod:a([1,2,3], 4),
+ {'EXIT',_} = (catch Mod:a(not_a_list, 42)),
+ _ = code:delete(Mod),
+ _ = code:purge(Mod),
ok.
guard_try_catch(_Config) ->
@@ -345,7 +347,7 @@ bsm_an_inlined(_, _) -> error.
unused_multiple_values_error(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
- Dir = filename:dirname(code:which(?MODULE)),
+ Dir = test_lib:get_data_dir(Config),
Core = filename:join(Dir, "unused_multiple_values_error"),
Opts = [no_copt,clint,return,from_core,{outdir,PrivDir}
|test_lib:opt_opts(?MODULE)],
diff --git a/lib/compiler/test/nested_call_in_case.core b/lib/compiler/test/core_fold_SUITE_data/nested_call_in_case.core
index 5c6b6909bd..c46906b2ed 100644
--- a/lib/compiler/test/nested_call_in_case.core
+++ b/lib/compiler/test/core_fold_SUITE_data/nested_call_in_case.core
@@ -16,6 +16,3 @@ module 'nested_call_in_case' ['a'/2]
-| ['compiler_generated'] )
end
end
-
-
-
diff --git a/lib/compiler/test/unused_multiple_values_error.core b/lib/compiler/test/core_fold_SUITE_data/unused_multiple_values_error.core
index e06587c936..e06587c936 100644
--- a/lib/compiler/test/unused_multiple_values_error.core
+++ b/lib/compiler/test/core_fold_SUITE_data/unused_multiple_values_error.core
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index 08279d9408..d91ee7ea08 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -1614,6 +1614,8 @@ t_tuple_size(Config) when is_list(Config) ->
?line {ok,Mod,Code} = compile:file(File, [from_asm,binary]),
?line code:load_binary(Mod, File, Code),
?line 14 = Mod:t({1,2,3,4}),
+ _ = code:delete(Mod),
+ _ = code:purge(Mod),
ok.
diff --git a/lib/compiler/test/z_SUITE.erl b/lib/compiler/test/z_SUITE.erl
new file mode 100644
index 0000000000..eff8a1877f
--- /dev/null
+++ b/lib/compiler/test/z_SUITE.erl
@@ -0,0 +1,62 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(z_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ loaded/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(?MODULE),
+ [loaded].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+loaded(_Config) ->
+ 0 = do_loaded(code:all_loaded(), 0),
+ ok.
+
+do_loaded([{M,_}|Ms], E0) ->
+ E = try
+ _ = M:module_info(),
+ _ = M:module_info(functions),
+ E0
+ catch
+ C:Error ->
+ Stk = erlang:get_stacktrace(),
+ io:format("~p:~p\n~p\n", [C,Error,Stk]),
+ E0 + 1
+ end,
+ do_loaded(Ms, E);
+do_loaded([], E) -> E.
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 09dffe1280..798212d5f9 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -2636,15 +2636,19 @@ inf_collect(_T1, [], _Opaques, OpL) ->
combine(S, T1, T2) ->
#opaque{mod = Mod1, name = Name1, args = Args1} = T1,
#opaque{mod = Mod2, name = Name2, args = Args2} = T2,
+ Comb1 = comb(Mod1, Name1, Args1, S, T1),
case is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of
- true -> [comb(Mod1, Name1, Args1, S, T1)];
- false -> [comb(Mod1, Name1, Args1, S, T1), comb(Mod2, Name2, Args2, S, T2)]
+ true -> Comb1;
+ false -> Comb1 ++ comb(Mod2, Name2, Args2, S, T2)
end.
comb(Mod, Name, Args, S, T) ->
case is_same_name(Mod, Name, Args, S) of
- true -> S;
- false -> T#opaque{struct = S}
+ true ->
+ ?opaque(Set) = S,
+ Set;
+ false ->
+ [T#opaque{struct = S}]
end.
is_same_name(Mod1, Name1, Args1,
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 20c8a6b1b1..e40660ab39 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -315,7 +315,7 @@ text/plain asc txt
</item>
<marker id="prop_server_tokens"></marker>
- <tag>{server_tokens, prod|major|minor|minimal|os|full|{private, string()}}</tag>
+ <tag>{server_tokens, none|prod|major|minor|minimal|os|full|{private, string()}}</tag>
<item>
<p>ServerTokens defines how the value of the server header
should look. </p>
@@ -323,6 +323,7 @@ text/plain asc txt
here is what the server header string could look like for
the different values of server-tokens: </p>
<pre>
+none "" % A Server: header will not be generated
prod "inets"
major "inets/5"
minor "inets/5.8"
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 2c3ee79f31..12bbc2b736 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,7 +32,28 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.10.6</title>
+ <section><title>Inets 5.10.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New value in <c>server_tokens</c> config for limiting
+ banner grabbing attempts. </p>
+ <p>
+ By setting <c>{server_tokens, none}</c> in
+ <c>ServiceConfig</c> for <c>inets:start(httpd,
+ ServiceConfig)</c>, the "Server:" header will not be set
+ in messages from the server.</p>
+ <p>
+ Own Id: OTP-12661 Aux Id: seq12840 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 78dda794db..dbdc1be272 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -219,14 +219,14 @@ load("ServerName " ++ ServerName, []) ->
load("ServerTokens " ++ ServerTokens, []) ->
%% These are the valid *plain* server tokens:
- %% sprod, major, minor, minimum, os, full
+ %% none, prod, major, minor, minimum, os, full
%% It can also be a "private" server token: private:<any string>
case string:tokens(ServerTokens, [$:]) of
["private", Private] ->
{ok,[], {server_tokens, clean(Private)}};
[TokStr] ->
Tok = list_to_atom(clean(TokStr)),
- case lists:member(Tok, [prod, major, minor, minimum, os, full]) of
+ case lists:member(Tok, [none, prod, major, minor, minimum, os, full]) of
true ->
{ok,[], {server_tokens, Tok}};
false ->
@@ -850,6 +850,8 @@ server(full = _ServerTokens) ->
OS = os_info(full),
lists:flatten(
io_lib:format("~s ~s OTP/~s", [?SERVER_SOFTWARE, OS, OTPRelease]));
+server(none = _ServerTokens) ->
+ "";
server({private, Server} = _ServerTokens) when is_list(Server) ->
%% The user provide its own
Server;
@@ -1299,7 +1301,7 @@ ssl_ca_certificate_file(ConfigDB) ->
end.
plain_server_tokens() ->
- [prod, major, minor, minimum, os, full].
+ [none, prod, major, minor, minimum, os, full].
error_report(Where,M,F,Error) ->
error_logger:error_report([{?MODULE, Where},
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 0895729d05..2fa91d47a0 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -287,8 +287,11 @@ create_header(ConfigDb, KeyValueTupleHeaders) ->
ContentType = "text/html",
Server = server(ConfigDb),
NewHeaders = add_default_headers([{"date", Date},
- {"content-type", ContentType},
- {"server", Server}],
+ {"content-type", ContentType}
+ | if Server=="" -> [];
+ true -> [{"server", Server}]
+ end
+ ],
KeyValueTupleHeaders),
lists:map(fun fix_header/1, NewHeaders).
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index e5b63a6446..e9ecb2632a 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.10.6
+INETS_VSN = 5.10.7
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/introduction.xml b/lib/ssh/doc/src/introduction.xml
index b42910cb34..1efbc16016 100644
--- a/lib/ssh/doc/src/introduction.xml
+++ b/lib/ssh/doc/src/introduction.xml
@@ -25,31 +25,181 @@
<title>Introduction</title>
<prepared>OTP team</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
<file>introduction.xml</file>
</header>
-
+ <p>SSH is a protocol for secure remote logon and
+ other secure network services over an insecure network.</p>
<section>
- <title>Purpose</title>
+ <title>Scope and Purpose</title>
- <p>Secure Shell (SSH) is a protocol for secure remote login and
- other secure network services over an insecure network. SSH
- provides a single, full-duplex, byte-oriented connection between
+ <p>SSH provides a single, full-duplex, and byte-oriented connection between
client and server. The protocol also provides privacy, integrity,
- server authentication and man-in-the-middle protection.</p>
-
- <p>The Erlang SSH application is an implementation of the SSH
- protocol in Erlang which offers API functions to write customized
- SSH clients and servers as well as making the Erlang shell
- available via SSH. Also included in the SSH application are an
- SFTP (SSH File Transfer Protocol) client <seealso
- marker="ssh_sftp">ssh_sftp</seealso> and server <seealso
- marker="ssh_sftp">ssh_sftpd</seealso>.</p>
+ server authentication, and man-in-the-middle protection.</p>
+
+ <p>The <c>ssh</c> application is an implementation of the SSH Transport, Connection and Authentication
+ Layer Protocols in Erlang. It provides the following:</p>
+ <list type="bulleted">
+ <item>API functions to write customized SSH clients and servers applications</item>
+ <item>The Erlang shell available over SSH</item>
+ <item>An SFTP client (<seealso marker="ssh_sftp">ssh_sftp</seealso>)
+ and server (<seealso marker="ssh_sftp">ssh_sftpd</seealso>)</item>
+ </list>
</section>
<section>
<title>Prerequisites</title>
- <p>It is assumed that the reader is familiar with the concepts of <seealso marker="doc/design_principles:des_princ">OTP</seealso>
- and has a basic understanding of <url href="http://en.wikipedia.org/wiki/Public-key_cryptography">public keys</url>.</p>
+ <p>It is assumed that the reader is familiar with the Erlang programming language,
+ concepts of <em>OTP</em>, and has a basic understanding of <em>public keys</em>.</p>
+ </section>
+
+<section>
+ <title>SSH Protocol Overview</title>
+
+ <p>Conceptually, the SSH protocol can be partitioned into four
+ layers:</p>
+
+ <image file="SSH_protocols.png">
+ <icaption>SSH Protocol Architecture</icaption>
+ </image>
+
+ <section>
+ <title>Transport Protocol</title>
+
+ <p>The SSH Transport Protocol is a secure, low-level transport.
+ It provides strong encryption, cryptographic host
+ authentication, and integrity protection. A minimum of
+ Message Authentication Code (MAC) and encryption
+ algorithms are supported. For details, see the
+ <seealso marker="ssh">ssh(3)</seealso> manual page in <c>ssh</c>.</p>
+ </section>
+
+ <section>
+ <title>Authentication Protocol</title>
+
+ <p>The SSH Authentication Protocol is a general-purpose user
+ authentication protocol run over the SSH Transport Layer
+ Protocol. The <c>ssh</c> application supports user authentication as follows:
+ </p>
+ <list type="bulleted">
+ <item>
+ Using public key technology. RSA and DSA, X509-certificates
+ are not supported.
+ </item>
+ <item>
+ Using keyboard-interactive authentication.
+ This is suitable for interactive authentication methods
+ that do not need any special software support on the client side.
+ Instead, all authentication data is entered from the keyboard.
+ </item>
+ <item>
+ Using a pure password-based authentication scheme.
+ Here, the plain text password is encrypted before sent
+ over the network.
+ </item>
+ </list>
+ <p>Several configuration options for
+ authentication handling are available in
+ <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>
+ and <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</p>
+ <p>
+ The public key handling can be customized by implementing
+ the following behaviours from <c>ssh</c>:</p>
+ <list type="bulleted">
+ <item>Module
+ <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>.
+ </item>
+ <item>Module
+ <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Connection Protocol</title>
+
+ <p>The SSH Connection Protocol provides application-support
+ services over the transport pipe, for example, channel multiplexing,
+ flow control, remote program execution, signal propagation, and
+ connection forwarding. Functions for handling the SSH
+ Connection Protocol can be found in the module <seealso
+ marker="ssh_connection">ssh_connection</seealso> in <c>ssh</c>.
+ </p>
+ </section>
+
+ <section>
+ <title>Channels</title>
+
+ <p>All terminal sessions, forwarded connections, and so on, are
+ channels. Multiple channels are multiplexed into a single
+ connection. All channels are flow-controlled. This means that no
+ data is sent to a channel peer until a message is received to
+ indicate that window space is available.
+ The <em>initial window size</em> specifies how many bytes of channel
+ data that can be sent to the channel peer without adjusting the
+ window. Typically, an SSH client opens a channel, sends data (commands),
+ receives data (control information), and then closes the channel.
+ The <seealso marker="ssh_channel">ssh_channel</seealso> behaviour
+ handles generic parts of SSH channel management. This makes it easy
+ to write your own SSH client/server processes that use flow-control
+ and thus opens for more focus on the application logic.
+ </p>
+
+ <p>Channels come in the following three flavors:</p>
+
+ <list type="bulleted">
+ <item><em>Subsystem</em> - Named services that can be run as
+ part of an SSH server, such as SFTP <seealso
+ marker="ssh_sftpd">(ssh_sftpd)</seealso>, that is built into the
+ SSH daemon (server) by default, but it can be disabled. The Erlang <c>ssh</c>
+ daemon can be configured to run any Erlang-
+ implemented SSH subsystem.
+ </item>
+ <item><em>Shell</em> - Interactive shell. By default the
+ Erlang daemon runs the Erlang shell. The shell can be customized by
+ providing your own read-eval-print loop. You can also provide your
+ own Command-Line Interface (CLI) implementation,
+ but that is much more work.
+ </item>
+ <item><em>Exec</em> - One-time remote execution of commands. See function
+ <seealso marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso>
+ for more information.</item>
+ </list>
+ </section>
+
+
+
</section>
+ <section>
+ <title>Where to Find More Information</title>
+ <p>
+ For detailed information about the SSH protocol, refer to the
+ following Request for Comments(RFCs):
+ </p>
+
+ <list type="bulleted">
+ <item><url href="http://www.ietf.org/rfc/rfc4250.txt">RFC 4250</url> -
+ Protocol Assigned Numbers</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4251.txt">RFC 4251</url> -
+ Protocol Architecture</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4252.txt">RFC 4252</url> -
+ Authentication Protocol</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</url> -
+ Transport Layer Protocol</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> -
+ Connection Protocol</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4255.txt">RFC 4255</url> -
+ Key Fingerprints</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4344.txt">RFC 4344</url> -
+ Transport Layer Encryption Modes</item>
+ <item><url href="http://www.ietf.org/rfc/rfc4716.txt">RFC 4716</url> -
+ Public Key File Format</item>
+ </list>
+ </section>
</chapter>
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index acbf3124ef..41885c684c 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,27 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New option <c>id_string</c> for <c>ssh:daemon</c> and
+ <c>ssh:connect</c> for limiting banner grabbing attempts.</p>
+ <p>
+ The possible values are: <c>{id_string,string()}</c> and
+ <c>{id_string,random}</c>. The latter will make ssh
+ generate a random nonsence id-string for each new
+ connection.</p>
+ <p>
+ Own Id: OTP-12659</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ref_man.xml b/lib/ssh/doc/src/ref_man.xml
index 55339298e8..afe3f2ddf9 100644
--- a/lib/ssh/doc/src/ref_man.xml
+++ b/lib/ssh/doc/src/ref_man.xml
@@ -28,8 +28,8 @@
<file>ref_man.xml</file>
</header>
<description>
- <p>The SSH application is an erlang implementation of the
- secure shell protocol (SSH) as defined by RFC 4250 - 4254</p>
+ <p>The <c>ssh</c> application is an Erlang implementation of the
+ Secure Shell Protocol (SSH) as defined by RFC 4250 - 4254.</p>
</description>
<xi:include href="ssh_app.xml"/>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 0e7e3848ad..d49d3ac2a7 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -22,54 +22,72 @@
</legalnotice>
<title>ssh</title>
+ <prepared></prepared>
+ <docno></docno>
<date>2007-10-06</date>
+ <rev></rev>
</header>
<module>ssh</module>
- <modulesummary>Main API of the SSH application</modulesummary>
+ <modulesummary>Main API of the ssh application</modulesummary>
<description>
- <p>Interface module for the SSH application. </p>
+ <p>Interface module for the <c>ssh</c> application.</p>
</description>
<section>
<title>SSH</title>
<list type="bulleted">
- <item>SSH requires the crypto and public_key applications.</item>
- <item>Supported SSH version is 2.0 </item>
- <item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1</item>
- <item>Supported encryption algorithms: aes128-ctr, aes128-cb and 3des-cbc</item>
- <item>Supports unicode filenames if the emulator and the underlaying OS supports it. See the DESCRIPTION section in <seealso marker="kernel:file">file</seealso> for information about this subject</item>
- <item>Supports unicode in shell and cli</item>
+ <item>For application dependencies see <seealso marker="SSH_app"> ssh(6)</seealso> </item>
+ <item>Supported SSH version is 2.0.</item>
+ <item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1.</item>
+ <item>Supported encryption algorithms: aes128-ctr, aes128-cb and 3des-cbc.</item>
+ <item>Supported key exchange algorithms: diffie-hellman-group1-sha1.</item>
+ <item>Supports unicode filenames if the emulator and the underlaying OS support it.
+ See section DESCRIPTION in the
+ <seealso marker="kernel:file">file</seealso> manual page in <c>kernel</c>
+ for information about this subject.</item>
+ <item>Supports unicode in shell and CLI.</item>
</list>
</section>
<section>
- <title>DATA TYPES </title>
+ <title>DATA TYPES</title>
<p>Type definitions that are used more than once in
- this module and/or abstractions to indicate the intended use of the data
- type:</p>
- <p><c>boolean() = true | false </c></p>
- <p><c>string() = [byte()]</c></p>
- <p><c>ssh_daemon_ref() - opaque to the user
- returned by ssh:daemon/[1,2,3]</c></p>
- <p><c>ssh_connection_ref() - opaque to the user
- returned by ssh:connect/3</c></p>
- <p><c>ip_address() - inet::ip_address()</c></p>
- <p><c>subsystem_spec() = {subsystem_name(),
- {channel_callback(), channel_init_args()}} </c></p>
- <p><c>subsystem_name() = string() </c></p>
- <p><c>channel_callback() = atom() - Name of the erlang module
- implementing the subsystem using the ssh_channel behavior see</c>
- <seealso marker="ssh_channel">ssh_channel(3)</seealso></p>
- <p><c>channel_init_args() = list()</c></p>
- </section>
+ this module, or abstractions to indicate the intended use of the data
+ type, or both:</p>
+ <taglist>
+ <tag><c>boolean()</c></tag>
+ <item><p>= <c>true | false</c></p></item>
+ <tag><c>string()</c></tag>
+ <item><p>= <c>[byte()]</c></p></item>
+ <tag><c>ssh_daemon_ref()</c></tag>
+ <item><p>Opaque to the user,
+ returned by <c>ssh:daemon/[1,2,3]</c></p></item>
+ <tag><c>ssh_connection_ref()</c></tag>
+ <item><p>Opaque to the user,
+ returned by <c>ssh:connect/3</c></p></item>
+ <tag><c>ip_address()</c></tag>
+ <item><p><c>inet::ip_address</c></p></item>
+ <tag><c>subsystem_spec()</c></tag>
+ <item><p>= <c>{subsystem_name(),
+ {channel_callback(), channel_init_args()}}</c></p></item>
+ <tag><c>subsystem_name()</c></tag>
+ <item><p>= <c>string()</c></p></item>
+ <tag><c>channel_callback()</c></tag>
+ <item><p>= <c>atom()</c> - Name of the Erlang module
+ implementing the subsystem using the <c>ssh_channel</c> behavior, see
+ <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item>
+ <tag><c>channel_init_args()</c></tag>
+ <item><p>= <c>list()</c></p></item>
+ </taglist>
+</section>
<funcs>
<func>
<name>close(ConnectionRef) -> ok </name>
- <fsummary>Closes an SSH connection</fsummary>
+ <fsummary>Closes an SSH connection.</fsummary>
<type>
<v>ConnectionRef = ssh_connection_ref()</v>
</type>
@@ -81,135 +99,151 @@
<name>connect(Host, Port, Options) -> </name>
<name>connect(Host, Port, Options, Timeout) -> {ok,
ssh_connection_ref()} | {error, Reason}</name>
- <fsummary>Connect to an ssh server.</fsummary>
+ <fsummary>Connects to an SSH server.</fsummary>
<type>
<v>Host = string()</v>
<v>Port = integer()</v>
- <d>The default is <c><![CDATA[22]]></c>, the assigned well known port
+ <d><c><![CDATA[22]]></c> is default, the assigned well-known port
number for SSH.</d>
<v>Options = [{Option, Value}]</v>
- <v>Timeout = infinity | integer(milliseconds)</v>
- <d>Negotiation timeout, for connection timeout use the option <c>{connect_timeout, timeout()}</c>.</d>
+ <v>Timeout = infinity | integer()</v>
+ <d>Negotiation time-out in milli-seconds. The default value is <c>infinity</c>.
+ For connection time-out, use option <c>{connect_timeout, timeout()}</c>.</d>
</type>
<desc>
<p>Connects to an SSH server. No channel is started. This is done
by calling
- <seealso marker="ssh_connection#session_channel/2">ssh_connection:session_channel/[2, 4]</seealso>.</p>
- <p>Options are:</p>
+ <seealso marker="ssh_connection#session_channel/2">
+ ssh_connection:session_channel/[2, 4]</seealso>.</p>
+ <p>Options:</p>
<taglist>
<tag><c><![CDATA[{inet, inet | inet6}]]></c></tag>
- <item> IP version to use.</item>
+ <item>
+ <p>IP version to use.</p>
+ </item>
<tag><c><![CDATA[{user_dir, string()}]]></c></tag>
<item>
- <p>Sets the user directory i.e. the directory containing
- ssh configuration files for the user such as
+ <p>Sets the user directory, that is, the directory containing
+ <c>ssh</c> configuration files for the user, such as
<c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa,
- id_dsa]]></c> and
+ id_dsa]]></c>, and
<c><![CDATA[authorized_key]]></c>. Defaults to the
directory normally referred to as
- <c><![CDATA[~/.ssh]]></c> </p>
+ <c><![CDATA[~/.ssh]]></c>.</p>
</item>
<tag><c><![CDATA[{dsa_pass_phrase, string()}]]></c></tag>
<item>
- <p>If the user dsa key is protected by a passphrase it can be
+ <p>If the user DSA key is protected by a passphrase, it can be
supplied with this option.
</p>
</item>
<tag><c><![CDATA[{rsa_pass_phrase, string()}]]></c></tag>
<item>
- <p>If the user rsa key is protected by a passphrase it can be
+ <p>If the user RSA key is protected by a passphrase, it can be
supplied with this option.
</p>
</item>
<tag><c><![CDATA[{silently_accept_hosts, boolean()}]]></c></tag>
<item>
- <p>When true hosts are added to the
+ <p>When <c>true</c>, hosts are added to the
file <c><![CDATA[known_hosts]]></c> without asking the user.
- Defaults to false.
+ Defaults to <c>false</c>.
</p>
</item>
<tag><c><![CDATA[{user_interaction, boolean()}]]></c></tag>
<item>
- <p>If false disables the client to connect to the server
- if any user interaction is needed such as accepting that
- the server will be added to the <c>known_hosts</c> file or
- supplying a password. Defaults to true.
+ <p>If <c>false</c>, disables the client to connect to the server
+ if any user interaction is needed, such as accepting
+ the server to be added to the <c>known_hosts</c> file, or
+ supplying a password. Defaults to <c>true</c>.
Even if user interaction is allowed it can be
- suppressed by other options such as silently_accept_hosts and
- password. Do note that it may not always be desirable to use
- those options from a security point of view.</p>
+ suppressed by other options, such as <c>silently_accept_hosts</c>
+ and <c>password</c>. However, those optins are not always desirable
+ to use from a security point of view.</p>
</item>
<tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag>
<item>
<p>Sets the preferred public key algorithm to use for user
- authentication. If the the preferred algorithm fails for
- some reason, the other algorithm is tried. The default is
+ authentication. If the preferred algorithm fails,
+ the other algorithm is tried. The default is
to try <c><![CDATA['ssh-rsa']]></c> first.</p>
</item>
<tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag>
<item>
- <p>List of public key algorithms to try to use, 'ssh-rsa' and 'ssh-dss' available.
- Will override <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p>
+ <p>List of public key algorithms to try to use.
+ <c>'ssh-rsa'</c> and <c>'ssh-dss'</c> are available.
+ Overrides <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p>
</item>
<tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag>
<item>
- <p>Sets a timeout on the transport layer
- connection. Defaults to <c>infinity</c>.</p>
+ <p>Sets a time-out on the transport layer
+ connection. For <c>gen_tcp</c> the time is in milli-seconds and the default value is
+ <c>infinity</c>.</p>
</item>
<tag><c><![CDATA[{user, string()}]]></c></tag>
<item>
- <p>Provides a user name. If this option is not given, ssh
+ <p>Provides a username. If this option is not given, <c>ssh</c>
reads from the environment (<c><![CDATA[LOGNAME]]></c> or
- <c><![CDATA[USER]]></c> on unix,
+ <c><![CDATA[USER]]></c> on UNIX,
<c><![CDATA[USERNAME]]></c> on Windows).</p>
</item>
<tag><c><![CDATA[{password, string()}]]></c></tag>
<item>
- <p>Provide a password for password authentication. If
- this option is not given, the user will be asked for a
- password if the password authentication method is
+ <p>Provides a password for password authentication.
+ If this option is not given, the user is asked for a
+ password, if the password authentication method is
attempted.</p>
</item>
<tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
<item>
- <p>Module implementing the behaviour <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>.
+ <p>Module implementing the behaviour
+ <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>.
Can be used to customize the handling of public keys.
</p>
</item>
<tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag>
<item>
- <p>If true, the client will not print out anything on authorization.</p>
+ <p>If <c>true</c>, the client does not print anything on authorization.</p>
</item>
+
+ <tag><c><![CDATA[{id_string, random | string()}]]></c></tag>
+ <item>
+ <p>The string that the client presents to a connected server initially. The default value is "Erlang/VSN" where VSN is the ssh application version number.
+ </p>
+ <p>The value <c>random</c> will cause a random string to be created at each connection attempt. This is to make it a bit more difficult for a malicious peer to find the ssh software brand and version.
+ </p>
+ </item>
+
<tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag>
<item>
- <p>Allow an existing file descriptor to be used
- (simply passed on to the transport protocol).</p></item>
+ <p>Allows an existing file descriptor to be used
+ (by passing it on to the transport protocol).</p></item>
<tag><c><![CDATA[{rekey_limit, integer()}]]></c></tag>
<item>
- <p>Provide, in bytes, when rekeying should be initiated,
- defaults to one time each GB and one time per hour.</p>
+ <p>Provides, in bytes, when rekeying is to be initiated.
+ Defaults to once per each GB and once per hour.</p>
</item>
<tag><c><![CDATA[{idle_time, integer()}]]></c></tag>
<item>
- <p>Sets a timeout on connection when no channels are active, default is infinity</p></item>
+ <p>Sets a time-out on a connection when no channels are active.
+ Defaults to <c>infinity</c>.</p></item>
</taglist>
</desc>
</func>
<func>
<name>connection_info(ConnectionRef, [Option]) ->[{Option,
- Value}] </name>
- <fsummary> Retrieves information about a connection. </fsummary>
+ Value}]</name>
+ <fsummary>Retrieves information about a connection.</fsummary>
<type>
<v>Option = client_version | server_version | user | peer | sockname </v>
<v>Value = [option_value()] </v>
- <v>option_value() = {{Major::integer(), Minor::integer()}, VersionString::string()} | User::string() |
- Peer::{inet:hostname(), {inet::ip_adress(), inet::port_number()}} |
- Sockname::{inet::ip_adress(), inet::port_number()} () </v>
+ <v>option_value() = {{Major::integer(), Minor::integer()}, VersionString::string()} |
+ User::string() | Peer::{inet:hostname(), {inet::ip_adress(), inet::port_number()}} |
+ Sockname::{inet::ip_adress(), inet::port_number()}</v>
</type>
<desc>
- <p> Retrieves information about a connection.
- </p>
+ <p>Retrieves information about a connection.</p>
</desc>
</func>
@@ -230,111 +264,127 @@
<desc>
<p>Starts a server listening for SSH connections on the given
port.</p>
- <p>Options are:</p>
+ <p>Options:</p>
<taglist>
<tag><c><![CDATA[{inet, inet | inet6}]]></c></tag>
- <item> IP version to use when the host address is specified as <c>any</c>. </item>
+ <item><p>IP version to use when the host address is specified as <c>any</c>.</p></item>
<tag><c><![CDATA[{subsystems, [subsystem_spec()]}]]></c></tag>
<item>
- Provides specifications for handling of subsystems. The
- "sftp" subsystem spec can be retrieved by calling
- ssh_sftpd:subsystem_spec/1. If the subsystems option is
- not present the value of
- <c>[ssh_sftpd:subsystem_spec([])]</c> will be used. It is
- of course possible to set the option to the empty list if
- you do not want the daemon to run any subsystems at all.
+ <p>Provides specifications for handling of subsystems. The
+ "sftp" subsystem specification is retrieved by calling
+ <c>ssh_sftpd:subsystem_spec/1</c>. If the subsystems option is
+ not present, the value of
+ <c>[ssh_sftpd:subsystem_spec([])]</c> is used.
+ The option can be set to the empty list if
+ you do not want the daemon to run any subsystems.</p>
</item>
<tag><c><![CDATA[{shell, {Module, Function, Args} |
fun(string() = User) - > pid() | fun(string() = User,
ip_address() = PeerAddr) -> pid()}]]></c></tag>
<item>
- Defines the read-eval-print loop used when a shell is
- requested by the client. Default is to use the erlang shell:
- <c><![CDATA[{shell, start, []}]]></c>
+ <p>Defines the read-eval-print loop used when a shell is
+ requested by the client. The default is to use the Erlang shell:
+ <c><![CDATA[{shell, start, []}]]></c></p>
</item>
<tag><c><![CDATA[{ssh_cli, {channel_callback(),
channel_init_args()} | no_cli}]]></c></tag>
<item>
- Provides your own CLI implementation, i.e. a channel callback
- module that implements a shell and command execution. Note
- that you may customize the shell read-eval-print loop using the
- option <c>shell</c> which is much less work than implementing
- your own CLI channel. If set to <c>no_cli</c> you will disable
- CLI channels and only subsystem channels will be allowed.
+ <p>Provides your own CLI implementation, that is, a channel callback
+ module that implements a shell and command execution. The shell
+ read-eval-print loop can be customized, using the
+ option <c>shell</c>. This means less work than implementing
+ an own CLI channel. If set to <c>no_cli</c>, the CLI channels
+ are disabled and only subsystem channels are allowed.</p>
</item>
<tag><c><![CDATA[{user_dir, String}]]></c></tag>
<item>
- <p>Sets the user directory i.e. the directory containing
- ssh configuration files for the user such as
+ <p>Sets the user directory. That is, the directory containing
+ <c>ssh</c> configuration files for the user, such as
<c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa,
- id_dsa]]></c> and
+ id_dsa]]></c>, and
<c><![CDATA[authorized_key]]></c>. Defaults to the
directory normally referred to as
- <c><![CDATA[~/.ssh]]></c> </p>
+ <c><![CDATA[~/.ssh]]></c>.</p>
</item>
<tag><c><![CDATA[{system_dir, string()}]]></c></tag>
<item>
<p>Sets the system directory, containing the host key files
- that identifies the host keys for ssh. The default is
- <c><![CDATA[/etc/ssh]]></c>, note that for security reasons
- this directory is normally only accessible by the root user.</p>
+ that identify the host keys for <c>ssh</c>. Defaults to
+ <c><![CDATA[/etc/ssh]]></c>. For security reasons,
+ this directory is normally accessible only to the root user.</p>
</item>
<tag><c><![CDATA[{auth_methods, string()}]]></c></tag>
<item>
- <p>Comma separated string that determines which
- authentication methodes that the server should support and
- in what order they will be tried. Defaults to
+ <p>Comma-separated string that determines which
+ authentication methods that the server is to support and
+ in what order they are tried. Defaults to
<c><![CDATA["publickey,keyboard-interactive,password"]]></c></p>
</item>
<tag><c><![CDATA[{user_passwords, [{string() = User,
string() = Password}]}]]></c></tag>
<item>
- <p>Provide passwords for password authentication.They will
- be used when someone tries to connect to the server and
- public key user authentication fails. The option provides
- a list of valid user names and the corresponding password.
+ <p>Provides passwords for password authentication. The passwords
+ are used when someone tries to connect to the server and
+ public key user-authentication fails. The option provides
+ a list of valid usernames and the corresponding passwords.
</p>
</item>
<tag><c><![CDATA[{password, string()}]]></c></tag>
<item>
- <p>Provide a global password that will authenticate any
+ <p>Provides a global password that authenticates any
user. From a security perspective this option makes
the server very vulnerable.</p>
</item>
<tag><c><![CDATA[{pwdfun, fun(User::string(), password::string()) -> boolean()}]]></c></tag>
<item>
- <p>Provide a function for password validation. This is called
- with user and password as strings, and should return
+ <p>Provides a function for password validation. This function is called
+ with user and password as strings, and returns
<c><![CDATA[true]]></c> if the password is valid and
<c><![CDATA[false]]></c> otherwise.</p>
</item>
<tag><c><![CDATA[{negotiation_timeout, integer()}]]></c></tag>
<item>
- <p>Max time in milliseconds for the authentication negotiation. The default value is 2 minutes. If the client fails to login within this time, the connection is closed.
+ <p>Maximum time in milliseconds for the authentication negotiation.
+ Defaults to 120000 (2 minutes). If the client fails to log in within this time,
+ the connection is closed.
</p>
</item>
<tag><c><![CDATA[{max_sessions, pos_integer()}]]></c></tag>
<item>
- <p>The maximum number of simultaneous sessions that are accepted at any time for this daemon. This includes sessions that are being authorized. So if set to <c>N</c>, and <c>N</c> clients have connected but not started the login process, the <c>N+1</c> connection attempt will be aborted. If <c>N</c> connections are authenticated and still logged in, no more loggins will be accepted until one of the existing ones log out.
+ <p>The maximum number of simultaneous sessions that are accepted at any time
+ for this daemon. This includes sessions that are being authorized.
+ Thus, if set to <c>N</c>, and <c>N</c> clients have connected but not started
+ the login process, connection attempt <c>N+1</c> is aborted.
+ If <c>N</c> connections are authenticated and still logged in, no more logins
+ are accepted until one of the existing ones log out.
</p>
- <p>The counter is per listening port, so if two daemons are started, one with <c>{max_sessions,N}</c> and the other with <c>{max_sessions,M}</c> there will be in total <c>N+M</c> connections accepted for the whole ssh application.
+ <p>The counter is per listening port. Thus, if two daemons are started, one with
+ <c>{max_sessions,N}</c> and the other with <c>{max_sessions,M}</c>, in total
+ <c>N+M</c> connections are accepted for the whole <c>ssh</c> application.
</p>
- <p>Note that if <c>parallel_login</c> is <c>false</c>, only one client at a time may be in the authentication phase.
+ <p>Notice that if <c>parallel_login</c> is <c>false</c>, only one client
+ at a time can be in the authentication phase.
</p>
- <p>As default, the option is not set. This means that the number is not limited.
+ <p>By default, this option is not set. This means that the number is not limited.
</p>
</item>
<tag><c><![CDATA[{parallel_login, boolean()}]]></c></tag>
<item>
- <p>If set to false (the default value), only one login is handled a time. If set to true, an unlimited number of login attempts will be allowed simultanously.
+ <p>If set to false (the default value), only one login is handled at a time.
+ If set to true, an unlimited number of login attempts are allowed simultaneously.
</p>
- <p>If the <c>max_sessions</c> option is set to <c>N</c> and <c>parallel_login</c> is set to <c>true</c>, the max number of simultaneous login attempts at any time is limited to <c>N-K</c> where <c>K</c> is the number of authenticated connections present at this daemon.
+ <p>If the <c>max_sessions</c> option is set to <c>N</c> and <c>parallel_login</c>
+ is set to <c>true</c>, the maximum number of simultaneous login attempts at any time is
+ limited to <c>N-K</c>, where <c>K</c> is the number of authenticated connections present
+ at this daemon.
</p>
<warning>
- <p>Do not enable <c>parallel_logins</c> without protecting the server by other means, for example the <c>max_sessions</c> option or a firewall configuration. If set to <c>true</c>, there is no protection against DOS attacks.</p>
+ <p>Do not enable <c>parallel_logins</c> without protecting the server by other means,
+ for example, by the <c>max_sessions</c> option or a firewall configuration. If set to
+ <c>true</c>, there is no protection against DOS attacks.</p>
</warning>
</item>
@@ -344,27 +394,38 @@
</p>
</item>
+ <tag><c><![CDATA[{id_string, random | string()}]]></c></tag>
+ <item>
+ <p>The string the daemon will present to a connecting peer initially. The default value is "Erlang/VSN" where VSN is the ssh application version number.
+ </p>
+ <p>The value <c>random</c> will cause a random string to be created at each connection attempt. This is to make it a bit more difficult for a malicious peer to find the ssh software brand and version.
+ </p>
+ </item>
+
<tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
<item>
- <p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
+ <p>Module implementing the behaviour
+ <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
Can be used to customize the handling of public keys.
</p>
</item>
<tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag>
<item>
- <p>Allow an existing file-descriptor to be used
- (simply passed on to the transport protocol).</p></item>
- <tag><c><![CDATA[{failfun, fun(User::string(), PeerAddress::ip_address(), Reason::term()) -> _}]]></c></tag>
+ <p>Allows an existing file-descriptor to be used
+ (passed on to the transport protocol).</p></item>
+ <tag><c><![CDATA[{failfun, fun(User::string(),
+ PeerAddress::ip_address(), Reason::term()) -> _}]]></c></tag>
<item>
- <p>Provide a fun to implement your own logging when a user fails to authenticate.</p>
+ <p>Provides a fun to implement your own logging when a user fails to authenticate.</p>
</item>
- <tag><c><![CDATA[{connectfun, fun(User::string(), PeerAddress::ip_address(), Method::string()) ->_}]]></c></tag>
+ <tag><c><![CDATA[{connectfun, fun(User::string(), PeerAddress::ip_address(),
+ Method::string()) ->_}]]></c></tag>
<item>
- <p>Provide a fun to implement your own logging when a user authenticates to the server.</p>
+ <p>Provides a fun to implement your own logging when a user authenticates to the server.</p>
</item>
<tag><c><![CDATA[{disconnectfun, fun(Reason:term()) -> _}]]></c></tag>
<item>
- <p>Provide a fun to implement your own logging when a user disconnects from the server.</p>
+ <p>Provides a fun to implement your own logging when a user disconnects from the server.</p>
</item>
</taglist>
</desc>
@@ -375,16 +436,16 @@
<name>shell(Host) -> </name>
<name>shell(Host, Option) -> </name>
<name>shell(Host, Port, Option) -> _</name>
- <fsummary> </fsummary>
+ <fsummary>Starts an interactive shell over an SSH server.</fsummary>
<type>
- <v> Host = string()</v>
- <v> Port = integer()</v>
- <v> Options - see ssh:connect/3</v>
+ <v>Host = string()</v>
+ <v>Port = integer()</v>
+ <v>Options - see ssh:connect/3</v>
</type>
<desc>
- <p>Starts an interactive shell via an SSH server on the
+ <p>Starts an interactive shell over an SSH server on the
given <c>Host</c>. The function waits for user input,
- and will not return until the remote shell is ended (i.e.
+ and does not return until the remote shell is ended (that is,
exit from the shell).
</p>
</desc>
@@ -393,28 +454,29 @@
<func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
- <fsummary>Starts the SSH application. </fsummary>
+ <fsummary>Starts the SSH application.</fsummary>
<type>
<v>Type = permanent | transient | temporary</v>
<v>Reason = term() </v>
</type>
<desc>
- <p>Utility function that starts crypto, public_key and the SSH
- application. Defult type is temporary.
- See also <seealso marker="kernel:application">application(3)</seealso>
- </p>
+ <p>Utility function that starts the applications <c>crypto</c>, <c>public_key</c>,
+ and <c>ssh</c>. Default type is <c>temporary</c>.
+ For more information, see the <seealso marker="kernel:application">application(3)</seealso>
+ manual page in <c>kernel</c>.</p>
</desc>
</func>
<func>
<name>stop() -> ok | {error, Reason}</name>
- <fsummary>Stops the SSH application.</fsummary>
+ <fsummary>Stops the <c>ssh</c> application.</fsummary>
<type>
<v>Reason = term()</v>
</type>
<desc>
- <p>Stops the SSH application. See also
- <seealso marker="kernel:application">application(3)</seealso></p>
+ <p>Stops the <c>ssh</c> application.
+ For more information, see the <seealso marker="kernel:application">application(3)</seealso>
+ manual page in <c>kernel</c>.</p>
</desc>
</func>
@@ -438,7 +500,7 @@
<name>stop_listener(DaemonRef) -> </name>
<name>stop_listener(Address, Port) -> ok </name>
<fsummary>Stops the listener, but leaves existing connections started
- by the listener up and running.</fsummary>
+ by the listener operational.</fsummary>
<type>
<v>DaemonRef = ssh_daemon_ref()</v>
<v>Address = ip_address()</v>
@@ -446,7 +508,7 @@
</type>
<desc>
<p>Stops the listener, but leaves existing connections started
- by the listener up and running.</p>
+ by the listener operational.</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml
index a1d2402790..1dfe68b17d 100644
--- a/lib/ssh/doc/src/ssh_app.xml
+++ b/lib/ssh/doc/src/ssh_app.xml
@@ -18,83 +18,103 @@
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
</legalnotice>
<title>SSH</title>
+ <prepared></prepared>
+ <docno></docno>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
<file>ssh_app.xml</file>
</header>
<app>SSH</app>
- <appsummary>The ssh application implements the SSH (Secure Shell) protocol and
- provides an SFTP (SSH File Transfer Protocol) client and server. </appsummary>
+ <appsummary>The ssh application implements the Secure Shell (SSH) protocol and
+ provides an SSH File Transfer Protocol (SFTP) client and server.</appsummary>
+ <description>
+ <p>The <c>ssh</c> application is an implementation of the SSH protocol in Erlang.
+ <c>ssh</c> offers API functions to write customized SSH clients and servers as well as
+ making the Erlang shell available over SSH. An SFTP client, <c>ssh_sftp</c>, and server,
+ <c>ssh_sftpd</c>, are also included.</p>
+ </description>
- <section>
+ <section>
<title>DEPENDENCIES</title>
- <p>The ssh application uses the Erlang applications public_key and
- crypto to handle public keys and encryption, hence these
- applications needs to be loaded for the ssh application to work. In
- an embedded environment that means they need to be started with
- application:start/[1,2] before the ssh application is started.
+ <p>The <c>ssh</c> application uses the applications <c>public_key</c> and
+ <c>crypto</c> to handle public keys and encryption. Hence, these
+ applications must be loaded for the <c>ssh</c> application to work. In
+ an embedded environment this means that they must be started with
+ <c>application:start/[1,2]</c> before the <c>ssh</c> application is started.
</p>
</section>
<section>
<title>CONFIGURATION</title>
- <p>The ssh application does not currently have an application
- specific configuration file as described in application(3),
- however it will by default use the following configuration files
- from openssh: known_hosts, authorized_keys, authorized_keys2,
- id_dsa and id_rsa, ssh_host_dsa_key and ssh_host_rsa_key. By
- default Erlang SSH will look for id_dsa, id_rsa, known_hosts
- and authorized_keys in ~/.ssh, and the host key files in /etc/ssh
- . These locations may be changed by the options user_dir and
- system_dir. Public key handling may also be customized by
- providing a callback module implementing the behaviors
- <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> and
- <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
- </p>
+ <p>The <c>ssh</c> application does not have an application-
+ specific configuration file, as described in <seealso marker="kernel:application">application(3)</seealso>.
+ However, by default it use the following configuration files
+ from OpenSSH:</p>
+ <list type="bulleted">
+ <item><c>known_hosts</c></item>
+ <item><c>authorized_keys</c></item>
+ <item><c>authorized_keys2</c></item>
+ <item><c>id_dsa</c></item>
+ <item><c>id_rsa</c></item>
+ <item><c>ssh_host_dsa_key</c></item>
+ <item><c>ssh_host_rsa_key</c></item>
+ </list>
+ <p>By default, <c>ssh</c> looks for <c>id_dsa</c>, <c>id_rsa</c>,
+ <c>known_hosts</c>, and <c>authorized_keys</c> in ~/.ssh,
+ and for the host key files in <c>/etc/ssh</c>. These locations can be changed
+ by the options <c>user_dir</c> and <c>system_dir</c>.
+ </p>
+ <p>Public key handling can also be customized through a callback module that
+ implements the behaviors
+ <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> and
+ <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
+ </p>
- <section>
- <title>PUBLIC KEYS</title>
- <p>
- id_dsa and id_rsa are the users private key files, note that
- the public key is part of the private key so the ssh
- application will not use the id_&lt;*>.pub files. These are
- for the users convenience when he/she needs to convey their
+ </section>
+ <section>
+ <title>Public Keys</title>
+ <p><c>id_dsa</c> and <c>id_rsa</c> are the users private key files.
+ Notice that the public key is part of the private key so the <c>ssh</c>
+ application does not use the <c>id_&lt;*>.pub</c> files. These are
+ for the user's convenience when it is needed to convey the user's
public key.
</p>
- </section>
-
- <section>
- <title>KNOW HOSTS</title>
- <p>The known_hosts file contains a list of approved servers and
- their public keys. Once a server is listed, it can be verified
+ </section>
+ <section>
+ <title>Known Hosts</title>
+ <p>The <c>known_hosts</c> file contains a list of approved servers and
+ their public keys. Once a server is listed, it can be verified
without user interaction.
</p>
- </section>
-
- <section>
- <title>AUTHORIZED KEYS</title>
- <p>The authorized key file keeps track of the user's authorized
+ </section>
+ <section>
+ <title>Authorized Keys</title>
+ <p>The <c>authorized_key</c> file keeps track of the user's authorized
public keys. The most common use of this file is to let users
- log in without entering their password which is supported by the
- Erlang SSH daemon.
+ log in without entering their password, which is supported by the
+ Erlang <c>ssh</c> daemon.
</p>
- </section>
-
- <section>
- <title>HOST KEYS</title>
- <p>Currently rsa and dsa host keys are supported and are
- expected to be found in files named ssh_host_rsa_key and
- ssh_host_dsa_key.
+ </section>
+ <section>
+ <title>Host Keys</title>
+ <p>RSA and DSA host keys are supported and are
+ expected to be found in files named <c>ssh_host_rsa_key</c> and
+ <c>ssh_host_dsa_key</c>.
</p>
- </section>
+ </section>
+ <section>
+ <title>ERROR LOGGER AND EVENT HANDLERS</title>
+ <p>The <c>ssh</c> application uses the default <seealso marker="kernel:error_logger">OTP error logger</seealso> to log unexpected errors or print information about special events.</p>
</section>
<section>
<title>SEE ALSO</title>
- <p>application(3)</p>
+ <p><seealso marker="kernel:application">application(3)</seealso></p>
</section>
</appref>
diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml
index a52a6a115e..429ef3c849 100644
--- a/lib/ssh/doc/src/ssh_channel.xml
+++ b/lib/ssh/doc/src/ssh_channel.xml
@@ -23,69 +23,84 @@
The Initial Developer of the Original Code is Ericsson AB.
</legalnotice>
<title>ssh_channel</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
</header>
<module>ssh_channel</module>
<modulesummary>-behaviour(ssh_channel).
</modulesummary>
<description>
<p>SSH services (clients and servers) are implemented as channels
- that are multiplexed over an SSH connection and communicates via
+ that are multiplexed over an SSH connection and communicates over
the <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH
Connection Protocol</url>. This module provides a callback API
- that takes care of generic channel aspects such as flow control
- and close messages and lets the callback functions take care of
+ that takes care of generic channel aspects, such as flow control
+ and close messages. It lets the callback functions take care of
the service (application) specific parts. This behavior also ensures
that the channel process honors the principal of an OTP-process so
that it can be part of a supervisor tree. This is a requirement of
channel processes implementing a subsystem that will be added to
- the SSH applications supervisor tree.
+ the <c>ssh</c> applications supervisor tree.
</p>
- <note> <p>When implementing a SSH subsystem use the
- <c>-behaviour(ssh_daemon_channel).</c> instead of <c>-behaviour(ssh_channel).</c>
- as the only relevant callback functions for subsystems are
- init/1, handle_ssh_msg/2, handle_msg/2 and terminate/2, so the ssh_daemon_channel
- behaviour is limited version of the ssh_channel behaviour.
- </p> </note>
+ <note><p>When implementing an <c>ssh</c> subsystem, use
+ <c>-behaviour(ssh_daemon_channel)</c> instead of <c>-behaviour(ssh_channel)</c>.
+ The reason is that the only relevant callback functions for subsystems are
+ <c>init/1</c>, <c>handle_ssh_msg/2</c>, <c>handle_msg/2</c>, and <c>terminate/2</c>.
+ So, the <c>ssh_daemon_channel</c> behaviour is a limited version of the
+ <c>ssh_channel</c> behaviour.
+ </p></note>
</description>
<section>
- <title>DATA TYPES </title>
+ <title>DATA TYPES</title>
- <p>Type definitions that are used more than once in this module
- and/or abstractions to indicate the intended use of the data
- type:</p>
+ <p>Type definitions that are used more than once in this module,
+ or abstractions to indicate the intended use of the data
+ type, or both:</p>
- <p><c>boolean() = true | false </c></p>
- <p><c>string() = list of ASCII characters</c></p>
- <p><c>timeout() = infinity | integer() - in milliseconds.</c></p>
- <p><c>ssh_connection_ref() - opaque to the user returned by
- ssh:connect/3 or sent to an SSH channel process</c></p>
- <p><c>ssh_channel_id() = integer() </c></p>
- <p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are
- currently valid values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</c></p>
+ <taglist>
+ <tag><c>boolean()</c></tag>
+ <item><p>= <c>true | false</c></p></item>
+ <tag><c>string()</c></tag>
+ <item><p>= list of ASCII characters</p></item>
+ <tag><c>timeout()</c></tag>
+ <item><p>= <c>infinity | integer()</c> in milliseconds</p></item>
+ <tag><c>ssh_connection_ref()</c></tag>
+ <item><p>Opaque to the user, returned by
+ <c>ssh:connect/3</c> or sent to an SSH channel process</p></item>
+ <tag><c>ssh_channel_id()</c></tag>
+ <item><p>= <c>integer()</c></p></item>
+ <tag><c>ssh_data_type_code()</c></tag>
+ <item><p>= <c>1</c> ("stderr") | <c>0</c> ("normal") are
+ the valid values,
+ see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>
+ Section 5.2</p></item>
+ </taglist>
</section>
<funcs>
<func>
<name>call(ChannelRef, Msg) -></name>
<name>call(ChannelRef, Msg, Timeout) -> Reply | {error, Reason}</name>
- <fsummary> Makes a synchronous call to a channel.</fsummary>
+ <fsummary>Makes a synchronous call to a channel.</fsummary>
<type>
<v>ChannelRef = pid() </v>
- <d>As returned by start_link/4 </d>
- <v>Msg = term() </v>
- <v>Timeout = timeout() </v>
- <v>Reply = term() </v>
- <v>Reason = closed | timeout </v>
+ <d>As returned by <c>start_link/4</c></d>
+ <v>Msg = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reply = term()</v>
+ <v>Reason = closed | timeout</v>
</type>
<desc>
<p>Makes a synchronous call to the channel process by sending
- a message and waiting until a reply arrives or a timeout
- occurs. The channel will call <seealso marker =
+ a message and waiting until a reply arrives, or a time-out
+ occurs. The channel calls <seealso marker =
"#Module:handle_call-3">Module:handle_call/3</seealso>
- to handle the message. If the channel process does not exist
+ to handle the message. If the channel process does not exist,
<c>{error, closed}</c> is returned.
</p>
</desc>
@@ -96,14 +111,14 @@
<fsummary>Sends an asynchronous message to the channel
ChannelRef and returns ok.</fsummary>
<type>
- <v>ChannelRef = pid() </v>
- <d>As returned by start_link/4 </d>
- <v>Msg = term() </v>
+ <v>ChannelRef = pid()</v>
+ <d>As returned by <c>start_link/4</c></d>
+ <v>Msg = term()</v>
</type>
<desc>
<p>Sends an asynchronous message to the channel process and
returns ok immediately, ignoring if the destination node or
- channel process does not exist. The channel will call
+ channel process does not exist. The channel calls
<seealso marker = "#Module:handle_cast-2">Module:handle_cast/2</seealso>
to handle the message.
</p>
@@ -112,31 +127,32 @@
<func>
<name>enter_loop(State) -> _ </name>
- <fsummary> Makes an existing process an ssh_channel process. </fsummary>
+ <fsummary>Makes an existing process an ssh_channel process.</fsummary>
<type>
- <v> State = term() - as returned by <seealso marker = "#init-1">ssh_channel:init/1</seealso></v>
+ <v>State = term() - as returned by
+ <seealso marker = "#init-1">ssh_channel:init/1</seealso></v>
</type>
<desc>
- <p> Makes an existing process an <c>ssh_channel</c>
- process. Does not return, instead the calling process will
- enter the <c>ssh_channel</c> process receive loop and become an
- <c>ssh_channel process.</c> The process must have been started using
- one of the start functions in proc_lib, see <seealso
- marker="stdlib:proc_lib">proc_lib(3)</seealso>. The
- user is responsible for any initialization of the process
- and needs to call <seealso marker = "#init-1">ssh_channel:init/1</seealso>
+ <p>Makes an existing process an <c>ssh_channel</c>
+ process. Does not return, instead the calling process
+ enters the <c>ssh_channel</c> process receive loop and become an
+ <c>ssh_channel process</c>. The process must have been started using
+ one of the start functions in <c>proc_lib</c>, see the <seealso
+ marker="stdlib:proc_lib">proc_lib(3)</seealso> manual page in <c>stdlib</c>.
+ The user is responsible for any initialization of the process
+ and must call <seealso marker = "#init-1">ssh_channel:init/1</seealso>.
</p>
</desc>
</func>
<func>
<name>init(Options) -> {ok, State} | {ok, State, Timeout} | {stop, Reason} </name>
- <fsummary> Initiates a ssh_channel process.</fsummary>
+ <fsummary>Initiates an <c>ssh_channel</c> process.</fsummary>
<type>
<v>Options = [{Option, Value}]</v>
<v>State = term()</v>
- <v>Timeout = timeout() </v>
- <v>Reason = term() </v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
</type>
<desc>
<p>
@@ -144,48 +160,47 @@
</p>
<taglist>
<tag><c><![CDATA[{channel_cb, atom()}]]></c></tag>
- <item>The module that implements the channel behaviour.</item>
+ <item><p>The module that implements the channel behaviour.</p></item>
<tag><c><![CDATA[{init_args(), list()}]]></c></tag>
- <item> The list of arguments to the callback module's
- init function.</item>
+ <item><p>The list of arguments to the <c>init</c> function of the callback module.</p></item>
<tag><c><![CDATA[{cm, connection_ref()}]]></c></tag>
- <item> Reference to the ssh connection as returned by <seealso
- marker="ssh#connect-3">ssh:connect/3</seealso></item>
+ <item><p>Reference to the <c>ssh</c> connection as returned by <seealso
+ marker="ssh#connect-3">ssh:connect/3</seealso></p></item>
<tag><c><![CDATA[{channel_id, channel_id()}]]></c></tag>
- <item> Id of the SSH channel.</item>
+ <item><p>Id of the <c>ssh</c> channel.</p></item>
</taglist>
<note><p>This function is normally not called by the
- user. The user only needs to call if for some reason the
+ user. The user only needs to call if the
channel process needs to be started with help of
<c>proc_lib</c> instead of calling
<c>ssh_channel:start/4</c> or
- <c>ssh_channel:start_link/4</c> </p>
+ <c>ssh_channel:start_link/4</c>.</p>
</note>
</desc>
</func>
<func>
<name>reply(Client, Reply) -> _</name>
- <fsummary>Send a reply to a client.</fsummary>
+ <fsummary>Sends a reply to a client.</fsummary>
<type>
<v>Client - opaque to the user, see explanation below</v>
<v>Reply = term()</v>
</type>
<desc>
- <p>This function can be used by a channel to explicitly send a
+ <p>This function can be used by a channel to send a
reply to a client that called <c>call/[2,3]</c> when the reply
cannot be defined in the return value of
<seealso marker ="#Module:handle_call-3">Module:handle_call/3</seealso>.</p>
<p><c>Client</c> must be the <c>From</c> argument provided to
the callback function <c>handle_call/3</c>.
<c>Reply</c> is an arbitrary term,
- which will be given back to the client as the return value of
- <seealso marker="#call-2">ssh_channel:call/[2,3].</seealso>></p>
+ which is given back to the client as the return value of
+ <seealso marker="#call-2">ssh_channel:call/[2,3].</seealso></p>
</desc>
</func>
@@ -193,24 +208,25 @@
<name>start(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> </name>
<name>start_link(SshConnection, ChannelId, ChannelCb, CbInitArgs) ->
{ok, ChannelRef} | {error, Reason}</name>
- <fsummary> Starts a processes that handles a SSH channel. </fsummary>
+ <fsummary>Starts a process that handles an SSH channel.</fsummary>
<type>
<v>SshConnection = ssh_connection_ref()</v>
- <v>ChannelId = ssh_channel_id() </v>
- <d> As returned by cannot be defined in the return value of
- <seealso marker ="ssh_connection#session_channel/2">ssh_connection:session_channel/[2,4]</seealso></d>
+ <v>ChannelId = ssh_channel_id()</v>
+ <d>As returned by
+ <seealso marker ="ssh_connection#session_channel/2">
+ ssh_connection:session_channel/[2,4]</seealso>.</d>
<v>ChannelCb = atom()</v>
- <d> The name of the module implementing the service specific parts
+ <d>Name of the module implementing the service-specific parts
of the channel.</d>
<v>CbInitArgs = [term()]</v>
- <d>Argument list for the init function in the callback module. </d>
+ <d>Argument list for the <c>init</c> function in the callback module.</d>
<v>ChannelRef = pid()</v>
</type>
<desc>
- <p>Starts a processes that handles an SSH channel. It will be
- called internally by the SSH daemon or explicitly by the SSH
- client implementations. The behavior will set the
- <c>trap_exit</c> flag to true.
+ <p>Starts a process that handles an SSH channel. It is
+ called internally, by the <c>ssh</c> daemon, or explicitly by the <c>ssh</c>
+ client implementations. The behavior sets the
+ <c>trap_exit</c> flag to <c>true</c>.
</p>
</desc>
</func>
@@ -219,19 +235,19 @@
<section>
<marker id="cb_timeouts"></marker>
- <title> CALLBACK TIMEOUTS</title>
+ <title>CALLBACK TIME-OUTS</title>
- <p>The timeout values that may be returned by the callback functions
- has the same semantics as in a <seealso marker="stdlib:gen_server">gen_server</seealso>
- If the timeout occurs <seealso marker="#Module:handle_msg-2">handle_msg/2</seealso>
- will be called as <c>handle_msg(timeout, State). </c></p>
+ <p>The time-out values that can be returned by the callback functions
+ have the same semantics as in a <seealso marker="stdlib:gen_server">gen_server</seealso>.
+ If the time-out occurs, <seealso marker="#Module:handle_msg-2">handle_msg/2</seealso>
+ is called as <c>handle_msg(timeout, State)</c>.</p>
</section>
<funcs>
<func>
<name>Module:code_change(OldVsn, State, Extra) -> {ok,
NewState}</name>
- <fsummary> Converts process state when code is changed.</fsummary>
+ <fsummary>Converts process state when code is changed.</fsummary>
<type>
<v>OldVsn = term()</v>
<d>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
@@ -241,31 +257,31 @@
<c>Module</c>. If no such attribute is defined, the version is
the checksum of the BEAM file.</d>
<v>State = term()</v>
- <d>The internal state of the channel.</d>
+ <d>Internal state of the channel.</d>
<v>Extra = term()</v>
- <d>Passed as-is from the <c>{advanced,Extra}</c>
+ <d>Passed “as-is” from the <c>{advanced,Extra}</c>
part of the update instruction.</d>
</type>
<desc>
- <p> Converts process state when code is changed.</p>
+ <p>Converts process state when code is changed.</p>
- <p>This function is called by a client side channel when it
- should update its internal state during a release
- upgrade/downgrade, i.e. when the instruction
- <c>{update,Module,Change,...}</c> where
- <c>Change={advanced,Extra}</c> is given in the <c>appup</c>
- file. See <seealso marker="doc/design_principles:release_handling#instr">OTP
- Design Principles</seealso> for more information.
+ <p>This function is called by a client-side channel when it
+ is to update its internal state during a release
+ upgrade or downgrade, that is, when the instruction
+ <c>{update,Module,Change,...}</c>, where
+ <c>Change={advanced,Extra}</c>, is given in the <c>appup</c>
+ file. For more information, refer to Section 9.11.6
+ Release Handling Instructions in the
+ <seealso marker="doc/design_principles:release_handling#instr">System Documentation</seealso>.
</p>
<note><p>Soft upgrade according to the OTP release concept
is not straight forward for the server side, as subsystem
- channel processes are spawned by the SSH application and
- hence added to its supervisor tree. It could be possible to
- upgrade the subsystem channels, when upgrading the user
- application, if the callback functions can handle two
- versions of the state, but this function can not be used in
- the normal way.</p>
+ channel processes are spawned by the <c>ssh</c> application and
+ hence added to its supervisor tree. The subsystem channels can
+ be upgraded when upgrading the user application, if the callback
+ functions can handle two versions of the state, but this function
+ cannot be used in the normal way.</p>
</note>
</desc>
@@ -274,30 +290,30 @@
<func>
<name>Module:init(Args) -> {ok, State} | {ok, State, timeout()} |
{stop, Reason}</name>
- <fsummary> Makes necessary initializations and returns the
+ <fsummary>Makes necessary initializations and returns the
initial channel state if the initializations succeed.</fsummary>
<type>
- <v> Args = term() </v>
- <d> Last argument to ssh_channel:start_link/4.</d>
- <v> State = term() </v>
- <v> Reason = term() </v>
+ <v>Args = term()</v>
+ <d>Last argument to <c>ssh_channel:start_link/4</c>.</d>
+ <v>State = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p> Makes necessary initializations and returns the initial channel
+ <p>Makes necessary initializations and returns the initial channel
state if the initializations succeed.
</p>
- <p>For more detailed information on timeouts see the section
- <seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p>
+ <p>For more detailed information on time-outs, see Section
+ <seealso marker="#cb_timeouts">CALLBACK TIME-OUTS</seealso>. </p>
</desc>
</func>
<func>
<name>Module:handle_call(Msg, From, State) -> Result</name>
- <fsummary> Handles messages sent by calling
- <c>ssh_channel:call/[2,3]</c></fsummary>
+ <fsummary>Handles messages sent by calling
+ <c>ssh_channel:call/[2,3]</c>.</fsummary>
<type>
<v>Msg = term()</v>
- <v>From = opaque to the user should be used as argument to
+ <v>From = Opaque to the user, is to be used as argument to
ssh_channel:reply/2</v>
<v>State = term()</v>
<v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, timeout()}
@@ -311,15 +327,15 @@
<p>Handles messages sent by calling
<seealso marker="#call-2">ssh_channel:call/[2,3]</seealso>
</p>
- <p>For more detailed information on timeouts see the section
- <seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p>
+ <p>For more detailed information on time-outs,, see Section
+ <seealso marker="#cb_timeouts">CALLBACK TIME-OUTS</seealso>.</p>
</desc>
</func>
<func>
<name>Module:handle_cast(Msg, State) -> Result</name>
- <fsummary> Handles messages sent by calling
- <c>ssh_channel:cact/2</c></fsummary>
+ <fsummary>Handles messages sent by calling
+ <c>ssh_channel:cact/2</c>.</fsummary>
<type>
<v>Msg = term()</v>
<v>State = term()</v>
@@ -329,11 +345,11 @@
<v>Reason = term()</v>
</type>
<desc>
- <p> Handles messages sent by calling
- <c>ssh_channel:cast/2</c>
+ <p>Handles messages sent by calling
+ <c>ssh_channel:cast/2</c>.
</p>
- <p>For more detailed information on timeouts see the section
- <seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p>
+ <p>For more detailed information on time-outs, see Section
+ <seealso marker="#cb_timeouts">CALLBACK TIME-OUTS</seealso>.</p>
</desc>
</func>
@@ -341,33 +357,33 @@
<name>Module:handle_msg(Msg, State) -> {ok, State} |
{stop, ChannelId, State}</name>
- <fsummary> Handle other messages than SSH connection protocol,
- call or cast messages sent to the channel.</fsummary>
+ <fsummary>Handles other messages than SSH connection protocol,
+ call, or cast messages sent to the channel.</fsummary>
<type>
<v>Msg = timeout | term()</v>
<v>ChannelId = ssh_channel_id()</v>
<v>State = term() </v>
</type>
<desc>
- <p>Handle other messages than ssh connection protocol, call or
+ <p>Handles other messages than SSH Connection Protocol, call, or
cast messages sent to the channel.
</p>
- <p> Possible erlang 'EXIT'-messages should be handled by this
- function and all channels should handle the following message.</p>
+ <p>Possible Erlang 'EXIT' messages is to be handled by this
+ function and all channels are to handle the following message.</p>
<taglist>
<tag><c><![CDATA[{ssh_channel_up, ssh_channel_id(),
ssh_connection_ref()}]]></c></tag>
- <item>This is the first messages that will be received by
- the channel, it is sent just before the <seealso
+ <item><p>This is the first message that the channel receives.
+ It is sent just before the <seealso
marker="#init-1">ssh_channel:init/1</seealso> function
- returns successfully. This is especially useful if the
+ returns successfully. This is especially useful if the
server wants to send a message to the client without first
receiving a message from it. If the message is not
- useful for your particular scenario just ignore it by
- immediately returning {ok, State}.
- </item>
+ useful for your particular scenario, ignore it by
+ immediately returning <c>{ok, State}</c>.
+ </p></item>
</taglist>
</desc>
</func>
@@ -375,42 +391,44 @@
<func>
<name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop,
ChannelId, State}</name>
- <fsummary> Handles ssh connection protocol messages. </fsummary>
+ <fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary>
<type>
- <v>Msg = <seealso marker="ssh_connection"> ssh_connection:event() </seealso> </v>
+ <v>Msg = ssh_connection:event()</v>
<v>ChannelId = ssh_channel_id()</v>
<v>State = term()</v>
</type>
<desc>
- <p> Handles SSH connection protocol messages that may need
- service specific attention.
+ <p>Handles SSH Connection Protocol messages that may need
+ service-specific attention. For details,
+ see <seealso marker="ssh_connection"> ssh_connection:event()</seealso>.
</p>
- <p> The following message is completely taken care of by the
- SSH channel behavior</p>
+ <p>The following message is taken care of by the
+ <c>ssh_channel</c> behavior.</p>
<taglist>
<tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag>
- <item> The channel behavior will send a close message to the
- other side if such a message has not already been sent and
- then terminate the channel with reason normal.</item>
+ <item><p>The channel behavior sends a close message to the
+ other side, if such a message has not already been sent.
+ Then it terminates the channel with reason <c>normal</c>.</p></item>
</taglist>
</desc>
</func>
<func>
<name>Module:terminate(Reason, State) -> _</name>
- <fsummary> </fsummary>
+ <fsummary>Does cleaning up before channel process termination.
+</fsummary>
<type>
<v>Reason = term()</v>
<v>State = term()</v>
</type>
<desc>
<p>This function is called by a channel process when it is
- about to terminate. Before this function is called <seealso
+ about to terminate. Before this function is called, <seealso
marker="ssh_connection#close-2"> ssh_connection:close/2
- </seealso> will be called if it has not been called earlier.
- This function should do any necessary cleaning
+ </seealso> is called, if it has not been called earlier.
+ This function does any necessary cleaning
up. When it returns, the channel process terminates with
reason <c>Reason</c>. The return value is ignored.
</p>
diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml
index f3d05a8980..a8dda042c9 100644
--- a/lib/ssh/doc/src/ssh_client_key_api.xml
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -23,102 +23,112 @@
The Initial Developer of the Original Code is Ericsson AB.
</legalnotice>
<title>ssh_client_key_api</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
</header>
<module>ssh_client_key_api</module>
<modulesummary>
-behaviour(ssh_client_key_api).
</modulesummary>
<description>
- <p> Behavior describing the API for an SSH client's public key handling.
- By implementing the callbacks defined.
- in this behavior it is possible to customize the SSH client's public key
- handling. By default the SSH application implements this behavior
- with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>. </p>
+ <p>Behavior describing the API for public key handling of an SSH client. By implementing
+ the callbacks defined in this behavior, the public key handling of an SSH client can
+ be customized. By default the <c>ssh</c> application implements this behavior
+ with help of the standard OpenSSH files,
+ see the <seealso marker="SSH_app"> ssh(6)</seealso> application manual.</p>
</description>
<section>
- <title>DATA TYPES </title>
+ <title>DATA TYPES</title>
- <p>Type definitions that are used more than once in this module
- and/or abstractions to indicate the intended use of the data
- type. For more details on public key data types
- see the <seealso marker="public_key:public_key_records"> public_key user's guide.</seealso>
+ <p>Type definitions that are used more than once in this module,
+ or abstractions to indicate the intended use of the data
+ type, or both. For more details on public key data types,
+ refer to Section 2 Public Key Records in the
+ <seealso marker="public_key:public_key_records"> public_key user's guide:</seealso>
</p>
-
- <p> boolean() = true | false</p>
- <p> string() = [byte()] </p>
- <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
- <p> private_key() = #'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</p>
- <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p>
-
+ <taglist>
+ <tag><c>boolean()</c></tag>
+ <item><p>= <c>true | false</c></p></item>
+ <tag><c>string()</c></tag>
+ <item><p>= <c>[byte()]</c></p></item>
+ <tag><c>public_key()</c></tag>
+ <item><p>= <c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <tag><c>private_key()</c></tag>
+ <item><p>= <c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <tag><c>public_key_algorithm()</c></tag>
+ <item><p>= <c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ </taglist>
</section>
<funcs>
<func>
<name>Module:add_host_key(HostNames, Key, ConnectOptions) -> ok | {error, Reason}</name>
- <fsummary>Adds a host key to the set of trusted host keys</fsummary>
+ <fsummary>Adds a host key to the set of trusted host keys.</fsummary>
<type>
<v>HostNames = string()</v>
- <d>Description of the host that owns the <c>PublicKey</c></d>
+ <d>Description of the host that owns the <c>PublicKey</c>.</d>
- <v>Key = public_key() </v>
- <d> Normally an RSA or DSA public key but handling of other public keys can be added</d>
+ <v>Key = public_key()</v>
+ <d>Normally an RSA or DSA public key, but handling of other public keys can be added.</d>
- <v>ConnectOptions = proplists:proplist() </v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
- <v>Reason = term() </v>
+ <v>ConnectOptions = proplists:proplist()</v>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <v>Reason = term().</v>
</type>
<desc>
- <p> Adds a host key to the set of trusted host keys</p>
+ <p>Adds a host key to the set of trusted host keys.</p>
</desc>
</func>
<func>
<name>Module:is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name>
- <fsummary>Checks if a host key is trusted</fsummary>
+ <fsummary>Checks if a host key is trusted.</fsummary>
<type>
<v>Key = public_key() </v>
- <d> Normally an RSA or DSA public key but handling of other public keys can be added</d>
+ <d>Normally an RSA or DSA public key, but handling of other public keys can be added.</d>
<v>Host = string()</v>
- <d>Description of the host</d>
+ <d>Description of the host.</d>
<v>Algorithm = public_key_algorithm()</v>
- <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms
+ <d>Host key algorithm. Is to support <c>'ssh-rsa'| 'ssh-dss'</c>, but more algorithms
can be handled.</d>
- <v> ConnectOptions = proplists:proplist() </v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <v>ConnectOptions = proplists:proplist() </v>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>.</d>
- <v> Result = boolean()</v>
+ <v>Result = boolean()</v>
</type>
<desc>
- <p>Checks if a host key is trusted</p>
+ <p>Checks if a host key is trusted.</p>
</desc>
</func>
<func>
<name>Module:user_key(Algorithm, ConnectOptions) ->
{ok, PrivateKey} | {error, Reason}</name>
- <fsummary>Fetches the users "public key" matching the <c>Algorithm</c>.</fsummary>
+ <fsummary>Fetches the users <em>public key</em> matching the <c>Algorithm</c>.</fsummary>
<type>
<v>Algorithm = public_key_algorithm()</v>
- <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms
+ <d>Host key algorithm. Is to support <c>'ssh-rsa'| 'ssh-dss'</c> but more algorithms
can be handled.</d>
- <v> ConnectOptions = proplists:proplist() </v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <v>ConnectOptions = proplists:proplist()</v>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
- <v> PrivateKey = private_key()</v>
- <d> The private key of the user matching the <c>Algorithm</c></d>
+ <v>PrivateKey = private_key()</v>
+ <d>Private key of the user matching the <c>Algorithm</c>.</d>
- <v>Reason = term() </v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>Fetches the users "public key" matching the <c>Algorithm</c>.
- <note><p>The private key contains the public key</p></note>
- </p>
+ <p>Fetches the users <em>public key</em> matching the <c>Algorithm</c>.</p>
+ <note><p>The private key contains the public key.</p></note>
+
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 5e2926dfa6..669a361db9 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -24,156 +24,174 @@
</legalnotice>
<title>ssh_connection</title>
+ <prepared></prepared>
+ <docno></docno>
<date></date>
+ <rev></rev>
</header>
<module>ssh_connection</module>
- <modulesummary>This module provides API functions to send <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH Connection Protocol </url>
+ <modulesummary>This module provides API functions to send
+ <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH Connection Protocol </url>
events to the other side of an SSH channel.
</modulesummary>
<description>
- <p>The SSH Connection Protocol is used by clients and servers
- (i.e. SSH channels) to communicate over the SSH connection. The
- API functions in this module sends SSH Connection Protocol events
- that are received as messages by the remote channel.
- In the case that the receiving channel is an Erlang process the
- message will be on the following format
- <c><![CDATA[{ssh_cm, ssh_connection_ref(), ssh_event_msg()}]]></c>. If the <seealso
- marker="ssh_channel">ssh_channel</seealso> behavior is used to
- implement the channel process these will be handled by
- <seealso
- marker="ssh_channel#Module:handle_ssh_msg-2">handle_ssh_msg/2 </seealso>.</p>
+ <p>The SSH Connection Protocol is used by clients and servers,
+ that is, SSH channels, to communicate over the SSH connection. The
+ API functions in this module send SSH Connection Protocol events,
+ which are received as messages by the remote channel.
+ If the receiving channel is an Erlang process, the
+ messages have the format
+ <c><![CDATA[{ssh_cm, ssh_connection_ref(), ssh_event_msg()}]]></c>.
+ If the <seealso marker="ssh_channel">ssh_channel</seealso> behavior is used to
+ implement the channel process, these messages are handled by
+ <seealso marker="ssh_channel#Module:handle_ssh_msg-2">handle_ssh_msg/2</seealso>.</p>
</description>
<section>
- <title>DATA TYPES </title>
-
- <p>Type definitions that are used more than once in this module and/or
- abstractions to indicate the intended use of the data type:</p>
-
- <p><c>boolean() = true | false </c></p>
- <p><c>string() = list of ASCII characters</c></p>
- <p><c>timeout() = infinity | integer() - in milliseconds.</c></p>
- <p><c>ssh_connection_ref() - opaque to the user returned by
- ssh:connect/3 or sent to an SSH channel processes</c></p>
- <p><c>ssh_channel_id() = integer() </c></p>
- <p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are
- currently valid values see</c> <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</p>
- <p><c>ssh_request_status() = success | failure</c></p>
- <p><c>event() = {ssh_cm, ssh_connection_ref(), ssh_event_msg()} </c></p>
- <p><c>ssh_event_msg() = data_events() | status_events() | terminal_events() </c></p>
- <p><c>reason() = timeout | closed </c></p>
+ <title>DATA TYPES</title>
+
+ <p>Type definitions that are used more than once in this module,
+ or abstractions to indicate the intended use of the data
+ type, or both:</p>
+
+ <taglist>
+ <tag><c>boolean()</c></tag>
+ <item><p>= <c>true | false </c></p></item>
+ <tag><c>string()</c></tag>
+ <item><p>= list of ASCII characters</p></item>
+ <tag><c>timeout()</c></tag>
+ <item><p>= <c>infinity | integer()</c> in milliseconds</p></item>
+ <tag><c>ssh_connection_ref()</c></tag>
+ <item><p>Opaque to the user, returned by
+ <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item>
+ <tag><c>ssh_channel_id()</c></tag>
+ <item><p>= <c>integer()</c></p></item>
+ <tag><c>ssh_data_type_code()</c></tag>
+ <item><p>= <c>1</c> ("stderr") | <c>0</c> ("normal") are
+ valid values, see
+ <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> Section 5.2.</p></item>
+ <tag><c>ssh_request_status() ssh_request_status()</c></tag>
+ <item><p>= <c>success | failure</c></p></item>
+ <tag><c>event()</c></tag>
+ <item><p>= <c>{ssh_cm, ssh_connection_ref(), ssh_event_msg()}</c></p></item>
+ <tag><c>ssh_event_msg()</c></tag>
+ <item><p>= <c>data_events() | status_events() | terminal_events()</c></p></item>
+ <tag><c>reason()</c></tag>
+ <item><p>= <c>timeout | closed</c></p></item>
+ </taglist>
<taglist>
- <tag><b>data_events()</b></tag>
+ <tag><em>data_events()</em></tag>
<item>
<taglist>
<tag><c><![CDATA[{data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}]]></c></tag>
- <item> Data has arrived on the channel. This event is sent as
- result of calling <seealso marker="ssh_connection#send-3"> ssh_connection:send/[3,4,5] </seealso></item>
+ <item><p>Data has arrived on the channel. This event is sent as a
+ result of calling <seealso marker="ssh_connection#send-3">
+ ssh_connection:send/[3,4,5]</seealso>.</p></item>
<tag><c><![CDATA[{eof, ssh_channel_id()}]]></c></tag>
- <item>Indicates that the other side will not send any more
- data. This event is sent as result of calling <seealso
- marker="ssh_connection#send_eof-2"> ssh_connection:send_eof/2</seealso>
- </item>
+ <item><p>Indicates that the other side sends no more data.
+ This event is sent as a result of calling <seealso
+ marker="ssh_connection#send_eof-2"> ssh_connection:send_eof/2</seealso>.
+ </p></item>
</taglist>
</item>
- <tag><b>status_events()</b></tag>
+ <tag><em>status_events()</em></tag>
<item>
<taglist>
<tag><c><![CDATA[{signal, ssh_channel_id(), ssh_signal()}]]></c></tag>
- <item>A signal can be delivered to the remote process/service
- using the following message. Some systems will not support
- signals, in which case they should ignore this message. There is
- currently no funtion to generate this event as the signals
- refered to are on OS-level and not something generated by an
- Erlang program.</item>
+ <item><p>A signal can be delivered to the remote process/service
+ using the following message. Some systems do not support
+ signals, in which case they are to ignore this message. There is
+ currently no function to generate this event as the signals
+ referred to are on OS-level and not something generated by an
+ Erlang program.</p></item>
<tag><c><![CDATA[{exit_signal, ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg,
string() = LanguageString}]]></c></tag>
- <item>A remote execution may terminate violently due to a signal
- then this message may be received. For details on valid string
- values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> section 6.10. Special case of the signals
- mentioned above.</item>
+ <item><p>A remote execution can terminate violently because of a signal.
+ Then this message can be received. For details on valid string
+ values, see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>
+ Section 6.10, which shows a special case of these signals.</p></item>
<tag><c><![CDATA[{exit_status, ssh_channel_id(), integer() = ExitStatus}]]></c></tag>
- <item> When the command running at the other end terminates, the
+ <item><p>When the command running at the other end terminates, the
following message can be sent to return the exit status of the
- command. A zero 'exit_status' usually means that the command
- terminated successfully. This event is sent as result of calling
+ command. A zero <c>exit_status</c> usually means that the command
+ terminated successfully. This event is sent as a result of calling
<seealso marker="ssh_connection#exit_status-3">
- ssh_connection:exit_status/3</seealso></item>
+ ssh_connection:exit_status/3</seealso>.</p></item>
<tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag>
- <item> This event is sent as result of calling
- <seealso marker="ssh_connection#close-2">ssh_connection:close/2</seealso> Both the handling of this
- event and sending of it will be taken care of by the
- <seealso marker="ssh_channel">ssh_channel</seealso> behavior.</item>
+ <item><p>This event is sent as a result of calling
+ <seealso marker="ssh_connection#close-2">ssh_connection:close/2</seealso>.
+ Both the handling of this event and sending it are taken care of by the
+ <seealso marker="ssh_channel">ssh_channel</seealso> behavior.</p></item>
</taglist>
</item>
- <tag><b>terminal_events()</b></tag>
+ <tag><em>terminal_events()</em></tag>
<item>
- <p> Channels implementing a shell and command execution on the
- server side should handle the following messages that may be sent by client channel processes. </p>
+ <p>Channels implementing a shell and command execution on the
+ server side are to handle the following messages that can be sent by client-
+ channel processes.</p>
- <note> <p>Events that includes a <c> WantReply</c> expects the event handling
- process to call <seealso marker="ssh_connection#reply_request-4">ssh_connection:reply_request/4</seealso>
- with the boolean value of <c> WantReply</c> as the second
- argument. </p></note>
+ <p>Events that include a <c>WantReply</c> expect the event handling
+ process to call <seealso marker="ssh_connection#reply_request-4">
+ ssh_connection:reply_request/4</seealso>
+ with the boolean value of <c>WantReply</c> as the second argument.</p>
<taglist>
<tag><c><![CDATA[{env, ssh_channel_id(), boolean() = WantReply,
string() = Var, string() = Value}]]></c></tag>
- <item> Environment variables may be passed to the shell/command
- to be started later. This event is sent as result of calling <seealso
- marker="ssh_connection#setenv-5"> ssh_connection:setenv/5</seealso>
- </item>
+ <item><p>Environment variables can be passed to the shell/command
+ to be started later. This event is sent as a result of calling <seealso
+ marker="ssh_connection#setenv-5"> ssh_connection:setenv/5</seealso>.
+ </p></item>
<tag><c><![CDATA[{pty, ssh_channel_id(),
boolean() = WantReply, {string() = Terminal, integer() = CharWidth,
integer() = RowHeight, integer() = PixelWidth, integer() = PixelHeight,
[{atom() | integer() = Opcode,
integer() = Value}] = TerminalModes}}]]></c></tag>
- <item>A pseudo-terminal has been requested for the
- session. Terminal is the value of the TERM environment
- variable value (e.g., vt100). Zero dimension parameters must
- be ignored. The character/row dimensions override the pixel
- dimensions (when nonzero). Pixel dimensions refer to the
- drawable area of the window. The <c>Opcode</c> in the
+ <item><p>A pseudo-terminal has been requested for the
+ session. <c>Terminal</c> is the value of the TERM environment
+ variable value, that is, <c>vt100</c>. Zero dimension parameters must
+ be ignored. The character/row dimensions override the pixel
+ dimensions (when non-zero). Pixel dimensions refer to the
+ drawable area of the window. <c>Opcode</c> in the
<c>TerminalModes</c> list is the mnemonic name, represented
- as an lowercase erlang atom, defined in
- <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8.
- It may also be an opcode if the mnemonic name is not listed in the
- RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom:
- echo</c>.This event is sent as result of calling <seealso
- marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso></item>
+ as a lowercase Erlang atom, defined in
+ <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>, Section 8.
+ It can also be an <c>Opcode</c> if the mnemonic name is not listed in the
+ RFC. Example: <c>OP code: 53, mnemonic name ECHO erlang atom:
+ echo</c>. This event is sent as a result of calling <seealso
+ marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso>.</p></item>
<tag><c><![CDATA[{shell, boolean() = WantReply}]]></c></tag>
- <item> This message will request that the user's default shell
- be started at the other end. This event is sent as result of calling <seealso
- marker="ssh_connection#shell-2"> ssh_connection:shell/2</seealso>
- </item>
+ <item><p>This message requests that the user default shell
+ is started at the other end. This event is sent as a result of calling
+ <seealso marker="ssh_connection#shell-2"> ssh_connection:shell/2</seealso>.
+ </p></item>
<tag><c><![CDATA[{window_change, ssh_channel_id(), integer() = CharWidth,
integer() = RowHeight, integer() = PixWidth, integer() = PixHeight}]]></c></tag>
- <item> When the window (terminal) size changes on the client
- side, it MAY send a message to the server side to inform it of
- the new dimensions. There is currently no API function to generate this
- event.</item>
+ <item><p>When the window (terminal) size changes on the client
+ side, it <em>can</em> send a message to the server side to inform it of
+ the new dimensions. No API function generates this event.</p></item>
<tag><c><![CDATA[{exec, ssh_channel_id(),
boolean() = WantReply, string() = Cmd}]]></c></tag>
- <item> This message will request that the server starts
- execution of the given command. This event is sent as result of calling <seealso
- marker="ssh_connection#exec-4">ssh_connection:exec/4 </seealso>
- </item>
+ <item><p>This message requests that the server starts
+ execution of the given command. This event is sent as a result of calling <seealso
+ marker="ssh_connection#exec-4">ssh_connection:exec/4 </seealso>.
+ </p></item>
</taglist>
</item>
</taglist>
@@ -183,80 +201,83 @@
<func>
<name>adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name>
- <fsummary>Adjusts the SSH flowcontrol window. </fsummary>
+ <fsummary>Adjusts the SSH flow control window.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id() </v>
- <v> NumOfBytes = integer()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>NumOfBytes = integer()</v>
</type>
<desc>
- <p>Adjusts the SSH flowcontrol window. This shall be done by both client and server side channel processes.</p>
+ <p>Adjusts the SSH flow control window. This is to be done by both the
+ client- and server-side channel processes.</p>
- <note><p>Channels implemented with the <seealso marker="ssh_channel"> ssh_channel
- behavior</seealso> will normaly not need to call this function as flow control
- will be handled by the behavior. The behavior will adjust the window every time
+ <note><p>Channels implemented with the <seealso marker="ssh_channel"> ssh_channel</seealso>
+ behavior do not normally need to call this function as flow control
+ is handled by the behavior. The behavior adjusts the window every time
the callback <seealso marker="ssh_channel#Module:handle_ssh_msg-2">
- handle_ssh_msg/2 </seealso> has returned after processing channel data</p> </note>
+ handle_ssh_msg/2</seealso> returns after processing channel data.</p></note>
</desc>
</func>
<func>
<name>close(ConnectionRef, ChannelId) -> ok</name>
- <fsummary>Sends a close message on the channel <c>ChannelId</c>. </fsummary>
+ <fsummary>Sends a close message on the channel <c>ChannelId</c>.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
</type>
<desc>
- <p>A server or client channel process can choose to close their session by sending a close event.
+ <p>A server- or client-channel process can choose to close their session by
+ sending a close event.
</p>
- <note><p>This function will be called by the ssh_channel
- behavior when the channel is terminated see <seealso
- marker="ssh_channel"> ssh_channel(3) </seealso> so channels implemented with the
- behavior should not call this function explicitly.</p></note>
+ <note><p>This function is called by the <c>ssh_channel</c>
+ behavior when the channel is terminated, see <seealso
+ marker="ssh_channel"> ssh_channel(3)</seealso>. Thus, channels implemented
+ with the behavior are not to call this function explicitly.</p></note>
</desc>
</func>
<func>
- <name>exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() | {error, reason()} </name>
- <fsummary>Request that the server start the execution of the given command. </fsummary>
+ <name>exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() |
+ {error, reason()}</name>
+ <fsummary>Requests that the server starts the execution of the given command.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
- <v> Command = string()</v>
- <v>Timeout = timeout() </v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>Command = string()</v>
+ <v>Timeout = timeout()</v>
</type>
<desc>
- <p>Should be called by a client channel process to request that the server starts execution of the
- given command, the result will be several messages according to the following pattern. Note
- that the last message will be a channel close message, as the exec request is a one time
- execution that closes the channel when it is done.</p>
+ <p>Is to be called by a client-channel process to request that the server starts
+ executing the given command. The result is several messages according to the
+ following pattern. The last message is a channel close message, as the <c>exec</c>
+ request is a one-time execution that closes the channel when it is done.</p>
<taglist>
- <tag><c> N x {ssh_cm, ssh_connection_ref(),
- {data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}} </c></tag>
- <item>The result of executing the command may be only one line
- or thousands of lines depending on the command.</item>
+ <tag><c>N x {ssh_cm, ssh_connection_ref(),
+ {data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}}</c></tag>
+ <item><p>The result of executing the command can be only one line
+ or thousands of lines depending on the command.</p></item>
<tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag>
- <item>Indicates that no more data will be sent.</item>
+ <item><p>Indicates that no more data is to be sent.</p></item>
<tag><c>0 or 1 x {ssh_cm,
ssh_connection_ref(), {exit_signal,
ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg, string() = LanguageString}}</c></tag>
- <item>Not all systems send signals. For details on valid string
- values see RFC 4254 section 6.10 </item>
+ <item><p>Not all systems send signals. For details on valid string
+ values, see RFC 4254, Section 6.10</p></item>
<tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {exit_status,
ssh_channel_id(), integer() = ExitStatus}}</c></tag>
- <item>It is recommended by the <c>ssh connection protocol</c> that this
- message shall be sent, but that may not always be the case.</item>
+ <item><p>It is recommended by the SSH Connection Protocol to send this
+ message, but that is not always the case.</p></item>
- <tag><c> 1 x {ssh_cm, ssh_connection_ref(),
+ <tag><c>1 x {ssh_cm, ssh_connection_ref(),
{closed, ssh_channel_id()}}</c></tag>
- <item>Indicates that the ssh channel started for the
- execution of the command has now been shutdown.</item>
+ <item><p>Indicates that the <c>ssh_channel</c> started for the
+ execution of the command has now been shut down.</p></item>
</taglist>
</desc>
</func>
@@ -265,78 +286,72 @@
<name>exit_status(ConnectionRef, ChannelId, Status) -> ok</name>
<fsummary>Sends the exit status of a command to the client.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
- <v> Status = integer()</v>
+ <v>ConnectionRef = ssh_connection_ref() </v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>Status = integer()</v>
</type>
<desc>
- <p>Should be called by a server channel process to sends the exit status of a command to the client.</p>
+ <p>Is to be called by a server-channel process to send the exit status of a command
+ to the client.</p>
</desc>
</func>
<func>
- <name>ptty_alloc(ConnectionRef, ChannelId, Options) -> </name>
- <name>ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> > ssh_request_status() | {error, reason()} </name>
- <fsummary>Send status replies to requests that want such replies. </fsummary>
+ <name>ptty_alloc(ConnectionRef, ChannelId, Options) -></name>
+ <name>ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> > ssh_request_status() |
+ {error, reason()}</name>
+ <fsummary>Sends an SSH Connection Protocol <c>pty_req</c>,
+ to allocate a pseudo-terminal.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
- <v> Options = proplists:proplist()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>Options = proplists:proplist()</v>
</type>
<desc>
- <p> Sends a SSH Connection Protocol pty_req, to allocate a pseudo tty.
- Should be called by a SSH client process.
- Options are:
- </p>
+ <p>Sends an SSH Connection Protocol <c>pty_req</c>, to allocate a pseudo-terminal.
+ Is to be called by an SSH client process.</p>
+ <p>Options:</p>
<taglist>
<tag>{term, string()}</tag>
- <item>
- Defaults to os:getenv("TERM") or "vt100" if it is undefined.
- </item>
+ <item><p>Defaults to <em>os:getenv("TERM")</em> or <em>vt100</em>
+ if it is undefined.</p></item>
+
<tag>{width, integer()}</tag>
- <item>
- Defaults to 80 if pixel_width is not defined.
- </item>
+ <item><p>Defaults to 80 if <c>pixel_width</c> is not defined.</p></item>
+
<tag>{height, integer()}</tag>
- <item>
- Defaults to 24 if pixel_height is not defined.
- </item>
+ <item><p>Defaults to 24 if <c>pixel_height</c> is not defined.</p></item>
+
<tag>{pixel_width, integer()}</tag>
- <item>
- Is disregarded if width is defined.
- </item>
+ <item><p>Is disregarded if <c>width</c> is defined.</p></item>
+
<tag>{pixel_height, integer()}</tag>
- <item>
- Is disregarded if height is defined.
- </item>
+ <item><p>Is disregarded if <c>height</c> is defined.</p></item>
+
<tag>{pty_opts, [{posix_atom(), integer()}]}</tag>
- <item>
- Option may be an empty list, otherwise
- see possible POSIX names in section 8 in <url href="http://www.ietf.org/rfc/rfc4254.txt"> RFC 4254</url>.
+ <item><p>Option can be an empty list. Otherwise, see possible <em>POSIX</em> names
+ in Section 8 in <url href="http://www.ietf.org/rfc/rfc4254.txt"> RFC 4254</url>.</p>
</item>
</taglist>
-
</desc>
</func>
- <func>
+ <func>
<name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name>
- <fsummary>Send status replies to requests that want such replies. </fsummary>
+ <fsummary>Sends status replies to requests that want such replies.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> WantReply = boolean()</v>
- <v> Status = ssh_request_status() </v>
- <v> ChannelId = ssh_channel_id()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>WantReply = boolean()</v>
+ <v>Status = ssh_request_status()</v>
+ <v>ChannelId = ssh_channel_id()</v>
</type>
<desc>
<p>Sends status replies to requests where the requester has
- stated that they want a status report e.i .<c> WantReply = true</c>,
- if <c> WantReply</c> is false calling this function will be a
- "noop". Should be called while handling an ssh connection
- protocol message containing a <c>WantReply</c> boolean
- value.
- </p>
+ stated that it wants a status report, that is, <c>WantReply = true</c>.
+ If <c>WantReply</c> is <c>false</c>, calling this function becomes a
+ "noop". Is to be called while handling an SSH Connection
+ Protocol message containing a <c>WantReply</c> boolean value.</p>
</desc>
</func>
@@ -346,98 +361,97 @@
<name>send(ConnectionRef, ChannelId, Type, Data) -></name>
<name>send(ConnectionRef, ChannelId, Type, Data, TimeOut) ->
ok | {error, timeout} | {error, closed}</name>
- <fsummary>Sends channel data </fsummary>
+ <fsummary>Sends channel data.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
- <v> Data = binary()</v>
- <v> Type = ssh_data_type_code()</v>
- <v> Timeout = timeout()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>Data = binary()</v>
+ <v>Type = ssh_data_type_code()</v>
+ <v>Timeout = timeout()</v>
</type>
<desc>
- <p>Should be called by client- and server channel processes to send data to each other.
+ <p>Is to be called by client- and server-channel processes to send data to each other.
</p>
</desc>
</func>
<func>
<name>send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name>
- <fsummary>Sends eof on the channel <c>ChannelId</c>. </fsummary>
+ <fsummary>Sends EOF on channel <c>ChannelId</c>.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
</type>
<desc>
- <p>Sends eof on the channel <c>ChannelId</c>.
- </p>
+ <p>Sends EOF on channel <c>ChannelId</c>.</p>
</desc>
</func>
<func>
- <name>session_channel(ConnectionRef, Timeout) -> </name>
+ <name>session_channel(ConnectionRef, Timeout) -></name>
<name>session_channel(ConnectionRef, InitialWindowSize,
MaxPacketSize, Timeout) -> {ok, ssh_channel_id()} | {error, reason()}</name>
- <fsummary>Opens a channel for a ssh session. </fsummary>
+ <fsummary>Opens a channel for an SSH session.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref()</v>
- <v> InitialWindowSize = integer() </v>
- <v> MaxPacketSize = integer() </v>
- <v> Timeout = timeout()</v>
- <v> Reason = term() </v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>InitialWindowSize = integer()</v>
+ <v>MaxPacketSize = integer()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
</type>
<desc>
<p>Opens a channel for an SSH session. The channel id returned from this function
- is the id used as input to the other funtions in this module.
- </p>
+ is the id used as input to the other functions in this module.</p>
</desc>
</func>
<func>
- <name>setenv(ConnectionRef, ChannelId, Var, Value, TimeOut) -> ssh_request_status() | {error, reason()} </name>
- <fsummary> Environment variables may be passed to the
+ <name>setenv(ConnectionRef, ChannelId, Var, Value, TimeOut) -> ssh_request_status() |
+ {error, reason()}</name>
+ <fsummary>Environment variables can be passed to the
shell/command to be started later.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
- <v> Var = string()</v>
- <v> Value = string()</v>
- <v> Timeout = timeout()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>Var = string()</v>
+ <v>Value = string()</v>
+ <v>Timeout = timeout()</v>
</type>
<desc>
- <p> Environment variables may be passed before starting the
- shell/command. Should be called by a client channel processes.
- </p>
+ <p>Environment variables can be passed before starting the
+ shell/command. Is to be called by a client channel processes.</p>
</desc>
</func>
<func>
<name>shell(ConnectionRef, ChannelId) -> ssh_request_status() | {error, closed}
</name>
- <fsummary> Requests that the user's default shell (typically
- defined in /etc/passwd in UNIX systems) shall be executed at the server
- end. </fsummary>
+ <fsummary>Requests that the user default shell (typically defined in
+ /etc/passwd in Unix systems) is to be executed at the server end.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
</type>
<desc>
- <p> Should be called by a client channel process to request that the user's default shell (typically
- defined in /etc/passwd in UNIX systems) shall be executed at the server end.
- </p>
+ <p>Is to be called by a client channel process to request that the user default
+ shell (typically defined in /etc/passwd in Unix systems) is executed
+ at the server end.</p>
</desc>
</func>
<func>
- <name>subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> ssh_request_status() | {error, reason()} </name>
- <fsummary> </fsummary>
+ <name>subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> ssh_request_status() |
+ {error, reason()}</name>
+ <fsummary>Requests to execute a predefined subsystem on the server.</fsummary>
<type>
- <v> ConnectionRef = ssh_connection_ref() </v>
- <v> ChannelId = ssh_channel_id()</v>
- <v> Subsystem = string()</v>
- <v> Timeout = timeout()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>ChannelId = ssh_channel_id()</v>
+ <v>Subsystem = string()</v>
+ <v>Timeout = timeout()</v>
</type>
<desc>
- <p> Should be called by a client channel process for requesting to execute a predefined subsystem on the server.
+ <p>Is to be called by a client-channel process for requesting to execute a predefined
+ subsystem on the server.
</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
index f7133e4ba5..34ce7f7660 100644
--- a/lib/ssh/doc/src/ssh_server_key_api.xml
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -23,68 +23,81 @@
The Initial Developer of the Original Code is Ericsson AB.
</legalnotice>
<title>ssh_server_key_api</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
</header>
<module>ssh_server_key_api</module>
<modulesummary>
-behaviour(ssh_server_key_api).
</modulesummary>
<description>
- <p> Behaviour describing the API for an SSH server's public key handling. By implementing the callbacks defined
- in this behavior it is possible to customize the SSH server's public key
- handling. By default the SSH application implements this behavior
- with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>.</p>
+ <p>Behaviour describing the API for public key handling of an SSH server. By implementing
+ the callbacks defined in this behavior, the public key handling of an SSH server can
+ be customized. By default the SSH application implements this behavior
+ with help of the standard OpenSSH files,
+ see the <seealso marker="SSH_app"> ssh(6)</seealso> application manual.</p>
</description>
<section>
- <title>DATA TYPES </title>
+ <title>DATA TYPES</title>
- <p>Type definitions that are used more than once in this module
- and/or abstractions to indicate the intended use of the data
- type. For more details on public key data types
- see the <seealso marker="public_key:public_key_records"> public_key user's guide.</seealso>
+ <p>Type definitions that are used more than once in this module,
+ or abstractions to indicate the intended use of the data
+ type, or both. For more details on public key data types,
+ refer to Section 2 Public Key Records in the
+ <seealso marker="public_key:public_key_records"> public_key user's guide</seealso>.
</p>
- <p> boolean() = true | false</p>
- <p> string() = [byte()]</p>
- <p> public_key() = #'RSAPublicKey'{} | {integer(), #'Dss-Parms'{}} | term()</p>
- <p> private_key() = #'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</p>
- <p> public_key_algorithm() = 'ssh-rsa' | 'ssh-dss' | atom()</p>
+<taglist>
+ <tag><c>boolean()</c></tag>
+ <item><p>= <c>true | false</c></p></item>
+ <tag><c>string()</c></tag>
+ <item><p>= <c>[byte()]</c></p></item>
+ <tag><c>public_key()</c></tag>
+ <item><p>= <c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <tag><c>private_key()</c></tag>
+ <item><p>= <c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <tag><c>public_key_algorithm()</c></tag>
+ <item><p>= <c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ </taglist>
</section>
<funcs>
<func>
<name>Module:host_key(Algorithm, DaemonOptions) ->
{ok, Key} | {error, Reason}</name>
- <fsummary>Fetches the hosts private key </fsummary>
+ <fsummary>Fetches the host’s private key.</fsummary>
<type>
<v>Algorithm = public_key_algorithm()</v>
- <d> Host key algorithm. Should support 'ssh-rsa' | 'ssh-dss' but additional algorithms
+ <d>Host key algorithm. Is to support <c>'ssh-rsa' | 'ssh-dss'</c>, but more algorithms
can be handled.</d>
- <v> DaemonOptions = proplists:proplist() </v>
- <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso></d>
- <v> Key = private_key()</v>
- <d> The private key of the host matching the <c>Algorithm</c></d>
- <v>Reason = term() </v>
+ <v>DaemonOptions = proplists:proplist()</v>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
+ <v>Key = private_key()</v>
+ <d>Private key of the host matching the <c>Algorithm</c>.</d>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>Fetches the hosts private key</p>
+ <p>Fetches the private key of the host.</p>
</desc>
</func>
<func>
<name>Module:is_auth_key(Key, User, DaemonOptions) -> Result</name>
- <fsummary> Checks if the user key is authorized</fsummary>
+ <fsummary>Checks if the user key is authorized.</fsummary>
<type>
- <v> Key = public_key() </v>
- <d> Normally an RSA or DSA public key but handling of other public keys can be added</d>
- <v> User = string()</v>
- <d> The user owning the public key</d>
- <v> DaemonOptions = proplists:proplist() </v>
- <d> Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso></d>
- <v> Result = boolean()</v>
+ <v>Key = public_key()</v>
+ <d>Normally an RSA or DSA public key, but handling of other public keys can be added</d>
+ <v>User = string()</v>
+ <d>User owning the public key.</d>
+ <v>DaemonOptions = proplists:proplist()</v>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
+ <v>Result = boolean()</v>
</type>
<desc>
- <p> Checks if the user key is authorized </p>
+ <p>Checks if the user key is authorized.</p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index ab111562f9..1a3705b341 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -23,131 +23,171 @@
<title>ssh_sftp</title>
<prepared>OTP</prepared>
+ <docno></docno>
<date>2005-09-22</date>
+ <rev></rev>
<file>ssh_sftp.sgml</file>
</header>
<module>ssh_sftp</module>
<modulesummary>SFTP client.</modulesummary>
<description>
- <p>This module implements an SFTP (SSH FTP) client. SFTP is a
+ <p>This module implements an SSH FTP (SFTP) client. SFTP is a
secure, encrypted file transfer service available for
SSH.</p>
</description>
<section>
- <title>DATA TYPES </title>
- <p>Type definitions that are used more than once in this module
- and/or abstractions to indicate the intended use of the data type:
+ <title>DATA TYPES</title>
+ <p>Type definitions that are used more than once in this module,
+ or abstractions to indicate the intended use of the data type, or both:
</p>
- <p><c>ssh_connection_ref() - opaque to the user
- returned by ssh:connect/3</c></p>
- <p><c>timeout() = infinity | integer() - in milliseconds.</c></p>
+
+ <taglist>
+ <tag><c>ssh_connection_ref()</c></tag>
+ <item><p>Opaque to the user, returned by <c>ssh:connect/3</c></p></item>
+ <tag><c>timeout()</c></tag>
+ <item><p>= <c>infinity | integer() in milliseconds. Default infinity.</c></p></item>
+ </taglist>
</section>
<section>
- <title>TIMEOUTS </title>
- <p>If the request functions for the SFTP channel return {error, timeout}
- it does not guarantee that the request did not reach the server and was
- not performed, it only means that we did not receive an answer from the
- server within the time that was expected.</p>
+ <title>Time-outs</title>
+ <p>If the request functions for the SFTP channel return <c>{error, timeout}</c>,
+ it does not guarantee that the request never reached the server and was
+ not performed. It only means that no answer was received from the
+ server within the expected time.</p>
</section>
<funcs>
+ <func>
+ <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, Error}</name>
+ <v>ChannelPid = pid()</v>
+ <v>Handle = term()</v>
+ <v>Position = integer()</v>
+ <v>Len = integer()</v>
+ <v>N = term()</v>
+ <v>Reason = term()</v>
+
+ <desc><p>The <c><![CDATA[apread]]></c> function reads from a specified position,
+ combining the <c><![CDATA[position]]></c> and <c><![CDATA[aread]]></c> functions.</p>
+ <p><seealso marker="#apread-3">ssh_sftp:apread/3</seealso></p> </desc>
+ </func>
+
+ <func>
+ <name>aread(ChannelPid, Handle, Len) -> {async, N} | {error, Error}</name>
+ <fsummary>Reads asynchronously from an open file.</fsummary>
+ <type>
+ <v>ChannelPid = pid()</v>
+ <v>Handle = term()</v>
+ <v>Position = integer()</v>
+ <v>Len = integer()</v>
+ <v>N = term()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Reads from an open file, without waiting for the result. If the
+ handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where <c>N</c>
+ is a term guaranteed to be unique between calls of <c><![CDATA[aread]]></c>.
+ The actual data is sent as a message to the calling process. This
+ message has the form <c><![CDATA[{async_reply, N, Result}]]></c>, where
+ <c><![CDATA[Result]]></c> is the result from the read, either <c><![CDATA[{ok, Data}]]></c>,
+ <c><![CDATA[eof]]></c>, or <c><![CDATA[{error, Error}]]></c>.</p>
+ </desc>
+ </func>
+
<func>
- <name>start_channel(ConnectionRef) -> </name>
- <name>start_channel(ConnectionRef, Options) -> </name>
- <name>start_channel(Host, Options) -></name>
- <name>start_channel(Host, Port, Options) -> {ok, Pid} | {ok, Pid, ConnectionRef} |
- {error, Reason}</name>
- <fsummary>Starts a SFTP client</fsummary>
+ <name>apwrite(ChannelPid, Handle, Position, Data) -> ok | {error, Reason}</name>
+ <fsummary>Writes asynchronously to an open file.</fsummary>
<type>
- <v>Host = string()</v>
- <v>ConnectionRef = ssh_connection_ref()</v>
- <v>Port = integer()</v>
- <v>Options = [{Option, Value}]</v>
+ <v>ChannelPid = pid()</v>
+ <v>Handle = term()</v>
+ <v>Position = integer()</v>
+ <v>Len = integer()</v>
+ <v>Data = binary()</v>
+ <v>Timeout = timeout()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>If no connection reference is provided, a connection is set
- up and the new connection is returned. An SSH channel process
- is started to handle the communication with the SFTP server.
- The returned pid for this process should be used as input to
- all other API functions in this module.</p>
+ <p><c><![CDATA[apwrite]]></c> writes on a specified position, combining
+ the <c><![CDATA[position]]></c> and <c><![CDATA[awrite]]></c> operations.</p>
+ <p><seealso marker="#awrite-3">ssh_sftp:awrite/3</seealso> </p></desc>
+ </func>
- <p>Options are:</p>
- <taglist>
- <tag><c><![CDATA[{timeout, timeout()}]]></c></tag>
- <item>
- <p>The timeout is passed to the ssh_channel start function,
- and defaults to infinity.</p>
- </item>
- <tag>
- <p><c><![CDATA[{sftp_vsn, integer()}]]></c></p>
- </tag>
- <item>
- <p>
- Desired SFTP protocol version.
- The actual version will be the minimum of
- the desired version and the maximum supported
- versions by the SFTP server.
- </p>
- </item>
- </taglist>
- <p>All other options are directly passed to
- <seealso marker="ssh">ssh:connect/3</seealso> or ignored if a
- connection is already provided. </p>
+ <func>
+ <name>awrite(ChannelPid, Handle, Data) -> ok | {error, Reason}</name>
+ <fsummary>Writes asynchronously to an open file.</fsummary>
+ <type>
+ <v>ChannelPid = pid()</v>
+ <v>Handle = term()</v>
+ <v>Position = integer()</v>
+ <v>Len = integer()</v>
+ <v>Data = binary()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Writes to an open file, without waiting for the result. If the
+ handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where <c>N</c>
+ is a term guaranteed to be unique between calls of
+ <c><![CDATA[awrite]]></c>. The result of the <c><![CDATA[write]]></c> operation is sent
+ as a message to the calling process. This message has the form
+ <c><![CDATA[{async_reply, N, Result}]]></c>, where <c><![CDATA[Result]]></c> is the result
+ from the write, either <c><![CDATA[ok]]></c>, or <c><![CDATA[{error, Error}]]></c>.</p>
</desc>
</func>
<func>
- <name>stop_channel(ChannelPid) -> ok</name>
- <fsummary>Stops the SFTP client channel.</fsummary>
+ <name>close(ChannelPid, Handle) -></name>
+ <name>close(ChannelPid, Handle, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Closes an open handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
+ <v>Handle = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>Stops an SFTP channel. Does not close the SSH connetion.
- Use <seealso marker="ssh">ssh:close/1</seealso> to close it.</p>
+ <p>Closes a handle to an open file or directory on the server.</p>
</desc>
</func>
-
+
<func>
- <name>read_file(ChannelPid, File) -> </name>
- <name>read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, Reason}</name>
- <fsummary>Read a file</fsummary>
+ <name>delete(ChannelPid, Name) -></name>
+ <name>delete(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Deletes a file.</fsummary>
<type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Data = binary()</v>
+ <v>ChannelPid = pid()</v>
+ <v>Name = string()</v>
<v>Timeout = timeout()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>Reads a file from the server, and returns the data in a binary,
- like <c><![CDATA[file:read_file/1]]></c>.</p>
+ <p>Deletes the file specified by <c><![CDATA[Name]]></c>, like
+ <seealso marker="file#delete-1">file:delete/1</seealso></p>
</desc>
</func>
+
<func>
- <name>write_file(ChannelPid, File, Iolist) -> </name>
- <name>write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Write a file</fsummary>
+ <name>del_dir(ChannelPid, Name) -></name>
+ <name>del_dir(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Deletes an empty directory.</fsummary>
<type>
<v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Iolist = iolist()</v>
+ <v>Name = string()</v>
<v>Timeout = timeout()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Writes a file to the server, like
- <c><![CDATA[file:write_file/2]]></c>. The file is created if
- it does not exist or is owerwritten if it does.</p>
+ <p>Deletes a directory specified by <c><![CDATA[Name]]></c>.
+ The directory must be empty before it can be successfully deleted.
+ </p>
</desc>
</func>
- <func>
- <name>list_dir(ChannelPid, Path) -> </name>
+
+ <func>
+ <name>list_dir(ChannelPid, Path) -></name>
<name>list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, Reason}</name>
- <fsummary>List directory</fsummary>
+ <fsummary>Lists the directory.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Path = string()</v>
@@ -161,10 +201,45 @@
filenames as a list of strings.</p>
</desc>
</func>
+
+ <func>
+ <name>make_dir(ChannelPid, Name) -></name>
+ <name>make_dir(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Creates a directory.</fsummary>
+ <type>
+ <v>ChannelPid = pid()</v>
+ <v>Name = string()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Creates a directory specified by <c><![CDATA[Name]]></c>. <c><![CDATA[Name]]></c>
+ must be a full path to a new directory. The directory can only be
+ created in an existing directory.</p>
+ </desc>
+ </func>
+
<func>
- <name>open(ChannelPid, File, Mode) -> </name>
+ <name>make_symlink(ChannelPid, Name, Target) -></name>
+ <name>make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Creates a symbolic link.</fsummary>
+ <type>
+ <v>ChannelPid = pid()</v>
+ <v>Name = string()</v>
+ <v>Target = string()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Creates a symbolic link pointing to <c><![CDATA[Target]]></c> with the
+ name <c><![CDATA[Name]]></c>, like
+ <seealso marker="file#make_symlink-2">file:make_symlink/2</seealso></p>
+ </desc>
+ </func>
+
+ <func>
+ <name>open(ChannelPid, File, Mode) -></name>
<name>open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, Reason}</name>
- <fsummary>Open a file and return a handle</fsummary>
+ <fsummary>Opens a file and returns a handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>File = string()</v>
@@ -175,14 +250,14 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Opens a file on the server, and returns a handle that
+ <p>Opens a file on the server and returns a handle, which
can be used for reading or writing.</p>
</desc>
</func>
<func>
- <name>opendir(ChannelPid, Path) -> </name>
+ <name>opendir(ChannelPid, Path) -></name>
<name>opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, Reason}</name>
- <fsummary>Open a directory and return a handle</fsummary>
+ <fsummary>Opens a directory and returns a handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Path = string()</v>
@@ -190,7 +265,7 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Opens a handle to a directory on the server, the handle
+ <p>Opens a handle to a directory on the server. The handle
can be used for reading directory contents.</p>
</desc>
</func>
@@ -198,14 +273,15 @@
<func>
<name>open_tar(ChannelPid, Path, Mode) -></name>
<name>open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, Reason}</name>
- <fsummary>Opens a tar file on the server to which <v>ChannelPid</v> is connected and returns a handle</fsummary>
+ <fsummary>Opens a tar file on the server to which <c>ChannelPid</c>
+ is connected and returns a handle.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Path = string()</v>
- <v>Mode = [read] | [write] | [read,EncryptOpt] | [write,DecryptOpt] </v>
+ <v>Mode = [read] | [write] | [read,EncryptOpt] | [write,DecryptOpt]</v>
<v>EncryptOpt = {crypto,{InitFun,EncryptFun,CloseFun}}</v>
<v>DecryptOpt = {crypto,{InitFun,DecryptFun}}</v>
- <v>InitFun = (fun() -> {ok,CryptoState}) | (fun() -> {ok,CryptoState,ChunkSize}) </v>
+ <v>InitFun = (fun() -> {ok,CryptoState}) | (fun() -> {ok,CryptoState,ChunkSize})</v>
<v>CryptoState = any()</v>
<v>ChunkSize = undefined | pos_integer()</v>
<v>EncryptFun = (fun(PlainBin,CryptoState) -> EncryptResult)</v>
@@ -219,113 +295,86 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Opens a handle to a tar file on the server associated with <c>ChannelPid</c>. The handle
- can be used for remote tar creation and extraction as defined by the
- <seealso marker="stdlib:erl_tar#init/3">erl_tar:init/3</seealso> function.
+ <p>Opens a handle to a tar file on the server, associated with <c>ChannelPid</c>.
+ The handle can be used for remote tar creation and extraction, as defined by the
+ <seealso marker="stdlib:erl_tar#init-3">erl_tar:init/3</seealso> function.
</p>
- <p>An example of writing and then reading a tar file:</p>
- <code type="none">
- {ok,HandleWrite} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [write]),
- ok = erl_tar:add(HandleWrite, .... ),
- ok = erl_tar:add(HandleWrite, .... ),
- ...
- ok = erl_tar:add(HandleWrite, .... ),
- ok = erl_tar:close(HandleWrite),
-
- %% And for reading
- {ok,HandleRead} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [read]),
- {ok,NameValueList} = erl_tar:extract(HandleRead,[memory]),
- ok = erl_tar:close(HandleRead),
- </code>
-
- <p>The <c>crypto</c> mode option is applied to the generated stream of bytes just prior to sending
- them to the sftp server. This is intended for encryption but could of course be used for other
+
+ <p> For code exampel see Section
+ <seealso marker="using_ssh">SFTP Client with TAR Compression and Encryption</seealso> in
+ the ssh Users Guide. </p>
+
+ <p>The <c>crypto</c> mode option is applied to the generated stream of bytes prior to sending
+ them to the SFTP server. This is intended for encryption but can be used for other
purposes.
</p>
<p>The <c>InitFun</c> is applied once
- prior to any other crypto operation. The returned <c>CryptoState</c> is then folded into
- repeated applications of the <c>EncryptFun</c> or <c>DecryptFun</c>. The binary returned
- from those Funs are sent further to the remote sftp server. Finally - if doing encryption
- - the <c>CloseFun</c> is applied to the last piece of data. The <c>CloseFun</c> is
+ prior to any other <c>crypto</c> operation. The returned <c>CryptoState</c> is then folded into
+ repeated applications of the <c>EncryptFun</c> or <c>DecryptFun</c>. The binary returned
+ from those funs are sent further to the remote SFTP server. Finally, if doing encryption,
+ the <c>CloseFun</c> is applied to the last piece of data. The <c>CloseFun</c> is
responsible for padding (if needed) and encryption of that last piece.
</p>
<p>The <c>ChunkSize</c> defines the size of the <c>PlainBin</c>s that <c>EncodeFun</c> is applied
- to. If the <c>ChunkSize</c> is <c>undefined</c> the size of the <c>PlainBin</c>s varies because
- this is inteded for stream crypto while a fixed <c>ChunkSize</c> is intended for block crypto. It
- is possible to change the <c>ChunkSize</c>s in the return from the <c>EncryptFun</c> or
- <c>DecryptFun</c>. It is in fact possible to change the value between <c>pos_integer()</c> and
- <c>undefined</c>.
+ to. If the <c>ChunkSize</c> is <c>undefined</c>, the size of the <c>PlainBin</c>s varies,
+ because this is intended for stream crypto, whereas a fixed <c>ChunkSize</c> is intended for block crypto.
+ <c>ChunkSize</c>s can be changed in the return from the <c>EncryptFun</c> or
+ <c>DecryptFun</c>. The value can be changed between <c>pos_integer()</c> and <c>undefined</c>.
</p>
- <p>The write and read example above can be extended with encryption and decryption:</p>
- <code type="none">
- %% First three parameters depending on which crypto type we select:
- Key = &lt;&lt;"This is a 256 bit key. abcdefghi">>,
- Ivec0 = crypto:rand_bytes(16),
- DataSize = 1024, % DataSize rem 16 = 0 for aes_cbc
-
- %% Initialization of the CryptoState, in this case it is the Ivector.
- InitFun = fun() -> {ok, Ivec0, DataSize} end,
-
- %% How to encrypt:
- EncryptFun =
- fun(PlainBin,Ivec) ->
- EncryptedBin = crypto:block_encrypt(aes_cbc256, Key, Ivec, PlainBin),
- {ok, EncryptedBin, crypto:next_iv(aes_cbc,EncryptedBin)}
- end,
-
- %% What to do with the very last block:
- CloseFun =
- fun(PlainBin, Ivec) ->
- EncryptedBin = crypto:block_encrypt(aes_cbc256, Key, Ivec,
- pad(16,PlainBin) %% Last chunk
- ),
- {ok, EncryptedBin}
- end,
-
- Cw = {InitFun,EncryptFun,CloseFun},
- {ok,HandleWrite} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [write,{crypto,Cw}]),
- ok = erl_tar:add(HandleWrite, .... ),
- ok = erl_tar:add(HandleWrite, .... ),
- ...
- ok = erl_tar:add(HandleWrite, .... ),
- ok = erl_tar:close(HandleWrite),
-
- %% And for decryption (in this crypto example we could use the same InitFun
- %% as for encryption):
- DecryptFun =
- fun(EncryptedBin,Ivec) ->
- PlainBin = crypto:block_decrypt(aes_cbc256, Key, Ivec, EncryptedBin),
- {ok, PlainBin, crypto:next_iv(aes_cbc,EncryptedBin)}
- end,
-
- Cr = {InitFun,DecryptFun},
- {ok,HandleRead} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [read,{crypto,Cw}]),
- {ok,NameValueList} = erl_tar:extract(HandleRead,[memory]),
- ok = erl_tar:close(HandleRead),
- </code>
+
</desc>
</func>
<func>
- <name>close(ChannelPid, Handle) -> </name>
- <name>close(ChannelPid, Handle, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Close an open handle</fsummary>
+ <name>position(ChannelPid, Handle, Location) -></name>
+ <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, Error}</name>
+ <fsummary>Sets the file position of a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Handle = term()</v>
+ <v>Location = Offset
+ | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v>
+ <v>Offset = integer()</v>
<v>Timeout = timeout()</v>
+ <v>NewPosition = integer()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Closes a handle to an open file or directory on the server.</p>
+ <p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>.
+ Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if
+ successful, otherwise <c><![CDATA[{error, Reason}]]></c>. <c><![CDATA[Location]]></c> is
+ one of the following:</p>
+ <taglist>
+ <tag><c><![CDATA[Offset]]></c></tag>
+ <item>
+ <p>The same as <c><![CDATA[{bof, Offset}]]></c>.</p>
+ </item>
+ <tag><c><![CDATA[{bof, Offset}]]></c></tag>
+ <item>
+ <p>Absolute offset.</p>
+ </item>
+ <tag><c><![CDATA[{cur, Offset}]]></c></tag>
+ <item>
+ <p>Offset from the current position.</p>
+ </item>
+ <tag><c><![CDATA[{eof, Offset}]]></c></tag>
+ <item>
+ <p>Offset from the end of file.</p>
+ </item>
+ <tag><c><![CDATA[bof | cur | eof]]></c></tag>
+ <item>
+ <p>The same as eariler with <c><![CDATA[Offset]]></c> 0,
+ that is, <c><![CDATA[{bof, 0} | {cur, 0} | {eof, 0}]]></c>.
+ </p>
+ </item>
+ </taglist>
</desc>
</func>
+
<func>
- <name>read(ChannelPid, Handle, Len) -> </name>
- <name>read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, Error}</name>
- <name>pread(ChannelPid, Handle, Position, Len) -> </name>
+ <name>pread(ChannelPid, Handle, Position, Len) -></name>
<name>pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, Error}</name>
- <fsummary>Read from an open file</fsummary>
+ <fsummary>Reads from an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Handle = term()</v>
@@ -336,47 +385,16 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by
- <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or
- <c><![CDATA[{error, Reason}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>,
- <c><![CDATA[Data]]></c> is a binary, otherwise it is a string.</p>
- <p>If the file is read past eof, only the remaining bytes
- will be read and returned. If no bytes are read, <c><![CDATA[eof]]></c>
- is returned.</p>
- <p>The <c><![CDATA[pread]]></c> function reads from a specified position,
- combining the <c><![CDATA[position]]></c> and <c><![CDATA[read]]></c> functions.</p>
- </desc>
- </func>
- <func>
- <name>aread(ChannelPid, Handle, Len) -> {async, N} | {error, Error}</name>
- <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, Error}</name>
- <fsummary>Read asynchronously from an open file</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>N = term()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Reads from an open file, without waiting for the result. If the
- handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where N
- is a term guaranteed to be unique between calls of <c><![CDATA[aread]]></c>.
- The actual data is sent as a message to the calling process. This
- message has the form <c><![CDATA[{async_reply, N, Result}]]></c>, where
- <c><![CDATA[Result]]></c> is the result from the read, either <c><![CDATA[{ok, Data}]]></c>,
- or <c><![CDATA[eof]]></c>, or <c><![CDATA[{error, Error}]]></c>.</p>
- <p>The <c><![CDATA[apread]]></c> function reads from a specified position,
- combining the <c><![CDATA[position]]></c> and <c><![CDATA[aread]]></c> functions.</p>
+ <p>The <c><![CDATA[pread]]></c> function reads from a specified position,
+ combining the <c><![CDATA[position]]></c> and <c><![CDATA[read]]></c> functions.</p>
+ <p><seealso marker="#read-4">ssh_sftp:read/4</seealso></p>
</desc>
</func>
+
<func>
- <name>write(ChannelPid, Handle, Data) -></name>
- <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, Error}</name>
- <name>pwrite(ChannelPid, Handle, Position, Data) -> ok </name>
+ <name>pwrite(ChannelPid, Handle, Position, Data) -> ok</name>
<name>pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, Error}</name>
- <fsummary>Write to an open file</fsummary>
+ <fsummary>Writes to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Handle = term()</v>
@@ -386,94 +404,59 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Writes<c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
- The file should be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c>
- flag. Returns <c><![CDATA[ok]]></c> if successful or S<c><![CDATA[{error, Reason}]]></c>
- otherwise.</p>
- <p>Typical error reasons are:</p>
- <taglist>
- <tag><c><![CDATA[ebadf]]></c></tag>
- <item>
- <p>The file is not opened for writing.</p>
- </item>
- <tag><c><![CDATA[enospc]]></c></tag>
- <item>
- <p>There is a no space left on the device.</p>
- </item>
- </taglist>
+ <p>The <c><![CDATA[pread]]></c> function writes to a specified position,
+ combining the <c><![CDATA[position]]></c> and <c><![CDATA[write]]></c> functions.</p>
+ <p><seealso marker="#write-3">ssh_sftp:write/3</seealso></p>
</desc>
</func>
- <func>
- <name>awrite(ChannelPid, Handle, Data) -> ok | {error, Reason} </name>
- <name>apwrite(ChannelPid, Handle, Position, Data) -> ok | {error, Reason}</name>
- <fsummary>Write asynchronously to an open file</fsummary>
+
+
+ <func>
+ <name>read(ChannelPid, Handle, Len) -></name>
+ <name>read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, Error}</name>
+ <fsummary>Reads from an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Handle = term()</v>
<v>Position = integer()</v>
<v>Len = integer()</v>
- <v>Data = binary()</v>
<v>Timeout = timeout()</v>
+ <v>Data = string() | binary()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Writes to an open file, without waiting for the result. If the
- handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where N
- is a term guaranteed to be unique between calls of
- <c><![CDATA[awrite]]></c>. The result of the <c><![CDATA[write]]></c> operation is sent
- as a message to the calling process. This message has the form
- <c><![CDATA[{async_reply, N, Result}]]></c>, where <c><![CDATA[Result]]></c> is the result
- from the write, either <c><![CDATA[ok]]></c>, or <c><![CDATA[{error, Error}]]></c>.</p>
- <p>The <c><![CDATA[apwrite]]></c> writes on a specified position, combining
- the <c><![CDATA[position]]></c> and <c><![CDATA[awrite]]></c> operations.</p>
+ <p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by
+ <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or
+ <c><![CDATA[{error, Reason}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>,
+ <c><![CDATA[Data]]></c> is a binary, otherwise it is a string.</p>
+ <p>If the file is read past <c>eof</c>, only the remaining bytes
+ are read and returned. If no bytes are read, <c><![CDATA[eof]]></c>
+ is returned.</p>
</desc>
</func>
- <func>
- <name>position(ChannelPid, Handle, Location) -> </name>
- <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, Error}</name>
- <fsummary>Seek position in open file</fsummary>
+
+ <func>
+ <name>read_file(ChannelPid, File) -></name>
+ <name>read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, Reason}</name>
+ <fsummary>Reads a file.</fsummary>
<type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Location = Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v>
- <v>Offset = integer()</v>
+ <v>ChannelPid = pid()</v>
+ <v>File = string()</v>
+ <v>Data = binary()</v>
<v>Timeout = timeout()</v>
- <v>NewPosition = integer()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>.
- Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if
- successful, otherwise <c><![CDATA[{error, Reason}]]></c>. <c><![CDATA[Location]]></c> is
- one of the following:</p>
- <taglist>
- <tag><c><![CDATA[Offset]]></c></tag>
- <item>
- <p>The same as <c><![CDATA[{bof, Offset}]]></c>.</p>
- </item>
- <tag><c><![CDATA[{bof, Offset}]]></c></tag>
- <item>
- <p>Absolute offset.</p>
- </item>
- <tag><c><![CDATA[{cur, Offset}]]></c></tag>
- <item>
- <p>Offset from the current position.</p>
- </item>
- <tag><c><![CDATA[{eof, Offset}]]></c></tag>
- <item>
- <p>Offset from the end of file.</p>
- </item>
- <tag><c><![CDATA[bof | cur | eof]]></c></tag>
- <item>
- <p>The same as above with <c><![CDATA[Offset]]></c> 0.</p>
- </item>
- </taglist>
+ <p>Reads a file from the server, and returns the data in a binary,
+ like
+ <seealso marker="file#read_file-1">file:read_file/1</seealso></p>
</desc>
</func>
- <func>
- <name>read_file_info(ChannelPid, Name) -> </name>
+
+ <func>
+ <name>read_file_info(ChannelPid, Name) -></name>
<name>read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, Reason}</name>
- <fsummary>Get information about a file</fsummary>
+ <fsummary>Gets information about a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
@@ -484,137 +467,190 @@
</type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the file specified by
- <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>, like <c><![CDATA[file:read_file_info/2]]></c>.</p>
+ <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>,
+ like <seealso marker="file#read_file_info-2">file:read_file_info/2</seealso></p>
</desc>
</func>
- <func>
- <name>read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, Reason}</name>
- <name>read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, Reason}</name>
- <fsummary>Get information about a symbolic link</fsummary>
+
+ <func>
+ <name>read_link(ChannelPid, Name) -></name>
+ <name>read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, Reason}</name>
+ <fsummary>Reads symbolic link.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- <v>FileInfo = record()</v>
+ <v>Target = string()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Returns a <c><![CDATA[file_info]]></c> record from the symbolic
- link specified by <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>, like
- <c><![CDATA[file:read_link_info/2]]></c>.</p>
+ <p>Reads the link target from the symbolic link specified
+ by <c><![CDATA[name]]></c>, like
+ <seealso marker="file#read_link-1">file:read_link/1</seealso></p>
</desc>
</func>
- <func>
- <name>write_file_info(ChannelPid, Name, Info) -> </name>
- <name>write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Write information for a file</fsummary>
+
+ <func>
+ <name>read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, Reason}</name>
+ <name>read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, Reason}</name>
+ <fsummary>Gets information about a symbolic link.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
- <v>Info = record()</v>
+ <v>Handle = term()</v>
<v>Timeout = timeout()</v>
+ <v>FileInfo = record()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Writes file information from a <c><![CDATA[file_info]]></c> record to the
- file specified by <c><![CDATA[Name]]></c>, like <c><![CDATA[file:write_file_info]]></c>.</p>
+ <p>Returns a <c><![CDATA[file_info]]></c> record from the symbolic
+ link specified by <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>, like
+ <seealso marker="file#read_link_info-2">file:read_link_info/2</seealso></p>
</desc>
</func>
+
<func>
- <name>read_link(ChannelPid, Name) -> </name>
- <name>read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, Reason}</name>
- <fsummary>Read symbolic link</fsummary>
+ <name>rename(ChannelPid, OldName, NewName) -> </name>
+ <name>rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Renames a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Target = string()</v>
+ <v>OldName = string()</v>
+ <v>NewName = string()</v>
+ <v>Timeout = timeout()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Reads the link target from the symbolic link specified
- by <c><![CDATA[name]]></c>, like <c><![CDATA[file:read_link/1]]></c>.</p>
+ <p>Renames a file named <c><![CDATA[OldName]]></c> and gives it the name
+ <c><![CDATA[NewName]]></c>, like
+ <seealso marker="file#rename-2">file:rename/2</seealso></p>
</desc>
</func>
+
<func>
- <name>make_symlink(ChannelPid, Name, Target) -> </name>
- <name>make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Create symbolic link</fsummary>
+ <name>start_channel(ConnectionRef) -></name>
+ <name>start_channel(ConnectionRef, Options) -></name>
+ <name>start_channel(Host, Options) -></name>
+ <name>start_channel(Host, Port, Options) -> {ok, Pid} | {ok, Pid, ConnectionRef} |
+ {error, Reason}</name>
+ <fsummary>Starts an SFTP client.</fsummary>
<type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Target = string()</v>
+ <v>Host = string()</v>
+ <v>ConnectionRef = ssh_connection_ref()</v>
+ <v>Port = integer()</v>
+ <v>Options = [{Option, Value}]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Creates a symbolic link pointing to <c><![CDATA[Target]]></c> with the
- name <c><![CDATA[Name]]></c>, like <c><![CDATA[file:make_symlink/2]]></c>.</p>
+ <p>If no connection reference is provided, a connection is set
+ up, and the new connection is returned. An SSH channel process
+ is started to handle the communication with the SFTP server.
+ The returned <c>pid</c> for this process is to be used as input to
+ all other API functions in this module.</p>
+
+ <p>Options:</p>
+ <taglist>
+ <tag><c><![CDATA[{timeout, timeout()}]]></c></tag>
+ <item>
+ <p>The time-out is passed to the <c>ssh_channel</c> start function,
+ and defaults to <c>infinity</c>.</p>
+ </item>
+ <tag>
+ <c><![CDATA[{sftp_vsn, integer()}]]></c>
+ </tag>
+ <item>
+ <p>
+ Desired SFTP protocol version.
+ The actual version is the minimum of
+ the desired version and the maximum supported
+ versions by the SFTP server.
+ </p>
+ </item>
+ </taglist>
+ <p>All other options are directly passed to
+ <seealso marker="ssh">ssh:connect/3</seealso> or ignored if a
+ connection is already provided.</p>
</desc>
</func>
- <func>
- <name>rename(ChannelPid, OldName, NewName) -> </name>
- <name>rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Rename a file</fsummary>
+
+ <func>
+ <name>stop_channel(ChannelPid) -> ok</name>
+ <fsummary>Stops the SFTP client channel.</fsummary>
<type>
<v>ChannelPid = pid()</v>
- <v>OldName = string()</v>
- <v>NewName = string()</v>
- <v>Timeout = timeout()</v>
- <v>Reason = term()</v>
</type>
<desc>
- <p>Renames a file named <c><![CDATA[OldName]]></c>, and gives it the name
- <c><![CDATA[NewName]]></c>, like <c><![CDATA[file:rename/2]]></c></p>
+ <p>Stops an SFTP channel. Does not close the SSH connection.
+ Use <seealso marker="ssh#close-1">ssh:close/1</seealso> to close it.</p>
</desc>
</func>
+
<func>
- <name>delete(ChannelPid, Name) -> </name>
- <name>delete(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Delete a file</fsummary>
+ <name>write_file(ChannelPid, File, Iolist) -></name>
+ <name>write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Writes a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
- <v>Name = string()</v>
+ <v>File = string()</v>
+ <v>Iolist = iolist()</v>
<v>Timeout = timeout()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Deletes the file specified by <c><![CDATA[Name]]></c>, like
- <c><![CDATA[file:delete/1]]></c></p>
+ <p>Writes a file to the server, like <seealso
+ marker="file#write_file-2">file:write_file/2</seealso> The
+ file is created if it does not exist. The file is overwritten
+ if it exists.</p>
</desc>
</func>
+
<func>
- <name>make_dir(ChannelPid, Name) -> </name>
- <name>make_dir(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Create a directory</fsummary>
+ <name>write(ChannelPid, Handle, Data) -></name>
+ <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, Error}</name>
+ <fsummary>Writes to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
- <v>Name = string()</v>
+ <v>Handle = term()</v>
+ <v>Position = integer()</v>
+ <v>Data = iolist()</v>
<v>Timeout = timeout()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Creates a directory specified by <c><![CDATA[Name]]></c>. <c><![CDATA[Name]]></c> should
- be a full path to a new directory. The directory can only be
- created in an existing directory.</p>
+ <p>Writes <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
+ The file is to be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c>
+ flag. Returns <c><![CDATA[ok]]></c> if successful or <c><![CDATA[{error, Reason}]]></c>
+ otherwise.</p>
+ <p>Typical error reasons:</p>
+ <taglist>
+ <tag><c><![CDATA[ebadf]]></c></tag>
+ <item>
+ <p>File is not opened for writing.</p>
+ </item>
+ <tag><c><![CDATA[enospc]]></c></tag>
+ <item>
+ <p>No space is left on the device.</p>
+ </item>
+ </taglist>
</desc>
</func>
+
<func>
- <name>del_dir(ChannelPid, Name) -> </name>
- <name>del_dir(ChannelPid, Name, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Delete an empty directory</fsummary>
+ <name>write_file_info(ChannelPid, Name, Info) -></name>
+ <name>write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Writes information for a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
<v>Name = string()</v>
+ <v>Info = record()</v>
<v>Timeout = timeout()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Deletes a directory specified by <c><![CDATA[Name]]></c>.
- Note that the directory must be empty before it can be successfully deleted
- </p>
+ <p>Writes file information from a <c><![CDATA[file_info]]></c> record to the
+ file specified by <c><![CDATA[Name]]></c>, like
+ <seealso marker="file#write_file_info-2">file:write_file_info/[2,3]</seealso></p>
</desc>
</func>
-
</funcs>
</erlref>
diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml
index 81c2acc575..bc2660f595 100644
--- a/lib/ssh/doc/src/ssh_sftpd.xml
+++ b/lib/ssh/doc/src/ssh_sftpd.xml
@@ -22,67 +22,73 @@
</legalnotice>
<title>ssh_sftpd</title>
+ <prepared></prepared>
+ <docno></docno>
<date>2005-09-22</date>
+ <rev></rev>
<file>ssh_sftpd.sgml</file>
</header>
<module>ssh_sftpd</module>
- <modulesummary>Specifies the channel process to handle an sftp subsystem.</modulesummary>
+ <modulesummary>Specifies the channel process to handle an SFTP subsystem.</modulesummary>
<description>
- <p>Specifies a channel process to handle a sftp subsystem.</p>
+ <p>Specifies a channel process to handle an SFTP subsystem.</p>
</description>
<section>
- <title>DATA TYPES </title>
- <p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p>
- <p><c>subsystem_name() = "sftp"</c></p>
- <p><c>channel_callback() = atom()</c> - Name of the erlang module implementing the
- subsystem using the ssh_channel behavior see
- <seealso marker="ssh_channel">ssh_channel(3)</seealso></p>
- <p><c> channel_init_args() = list() - The one given as argument to function
- subsystem_spec/1.</c></p>
+ <title>DATA TYPES</title>
+ <taglist>
+ <tag><c>subsystem_spec()</c></tag>
+ <item><p>= <c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item>
+ <tag><c>subsystem_name()</c></tag>
+ <item><p>= <c>"sftp"</c></p></item>
+ <tag><c>channel_callback()</c></tag>
+ <item><p>= <c>atom()</c> - Name of the Erlang module implementing the subsystem using the
+ <c>ssh_channel</c> behavior, see the
+ <seealso marker="ssh_channel">ssh_channel(3)</seealso> manual page.</p></item>
+ <tag><c>channel_init_args()</c></tag>
+ <item><p>= <c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item>
+ </taglist>
</section>
<funcs>
<func>
<name>subsystem_spec(Options) -> subsystem_spec()</name>
- <fsummary>Returns the subsystem specification that allows an ssh daemon to handle the subsystem "sftp".</fsummary>
+ <fsummary>Returns the subsystem specification that allows an SSH daemon to handle the subsystem "sftp".</fsummary>
<type>
<v>Options = [{Option, Value}]</v>
</type>
<desc>
- <p>Should be used together with ssh:daemon/[1,2,3]</p>
- <p>Options are:</p>
+ <p>Is to be used together with <c>ssh:daemon/[1,2,3]</c></p>
+ <p>Options:</p>
<taglist>
<tag><c><![CDATA[{cwd, String}]]></c></tag>
<item>
- <p>Sets the initial current working directory for the
- server.</p>
+ <p>Sets the initial current working directory for the server.</p>
</item>
<tag><c><![CDATA[{file_handler, CallbackModule}]]></c></tag>
<item>
<p>Determines which module to call for accessing
- the file server. The default value is <c>ssh_sftpd_file</c> that uses the
- <seealso marker="kernel:file">file</seealso> and <seealso marker="stdlib:filelib">filelib</seealso> API:s to access the standard OTP file
- server. This option may be used to plug in
+ the file server. The default value is <c>ssh_sftpd_file</c>, which uses the
+ <seealso marker="kernel:file">file</seealso> and <seealso marker="stdlib:filelib">filelib</seealso>
+ APIs to access the standard OTP file server. This option can be used to plug in
other file servers.</p>
</item>
<tag><c><![CDATA[{max_files, Integer}]]></c></tag>
<item>
<p>The default value is <c>0</c>, which means that there is no upper limit.
- If supplied, the number of filenames returned to the sftp client per <c>READDIR</c>
+ If supplied, the number of filenames returned to the SFTP client per <c>READDIR</c>
request is limited to at most the given value.</p>
</item>
<tag><c><![CDATA[{root, String}]]></c></tag>
<item>
- <p>Sets the sftp root directory. The user will then not be
- able to see any files above this root. If for instance
- the root is set to <c>/tmp</c> the user will see this
- directory as <c>/</c> and if the user does cd <c>/etc</c>
- the user will end up in <c>/tmp/etc</c>.
+ <p>Sets the SFTP root directory. Then the user cannot see any files
+ above this root. If, for example, the root directory is set to <c>/tmp</c>,
+ then the user sees this directory as <c>/</c>. If the user then writes
+ <c>cd /etc</c>, the user moves to <c>/tmp/etc</c>.
</p>
</item>
<tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag>
<item>
- <p>Sets the sftp version to use, defaults to 5. Version 6 is under
+ <p>Sets the SFTP version to use. Defaults to 5. Version 6 is under
development and limited.</p>
</item>
</taglist>
diff --git a/lib/ssh/doc/src/usersguide.xml b/lib/ssh/doc/src/usersguide.xml
index 8ab14c2945..a9ed5fe21e 100644
--- a/lib/ssh/doc/src/usersguide.xml
+++ b/lib/ssh/doc/src/usersguide.xml
@@ -23,15 +23,16 @@
<title>SSH User's Guide</title>
<prepared>OTP Team</prepared>
+ <docno></docno>
<date>2012-10-11</date>
+ <rev></rev>
<file>usersguide.xml</file>
</header>
<description>
- <p>The <em>SSH</em> application implements the SSH (Secure Shell) protocol and
- provides an SFTP (Secret File Transfer Protocol) client and server.
+ <p>The Erlang Secure Shell (SSH) application, <c>ssh</c>, implements the SSH Transport Layer Protocol and
+ provides SSH File Transfer Protocol (SFTP) clients and servers.
</p>
</description>
<xi:include href="introduction.xml"/>
- <xi:include href="ssh_protocol.xml"/>
<xi:include href="using_ssh.xml"/>
</part>
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
index 46178d4018..cd7b64ac43 100644
--- a/lib/ssh/doc/src/using_ssh.xml
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -22,64 +22,70 @@
</legalnotice>
- <title>Getting started</title>
+ <title>Getting Started</title>
+ <prepared></prepared>
+ <docno></docno>
+ <approved></approved>
+ <date></date>
+ <rev></rev>
<file>using_ssh.xml</file>
</header>
<section>
- <title> General information</title>
- <p>The examples in the following sections use the utility function
- <seealso marker="ssh#start-0"> ssh:start/0 </seealso> that starts
- all needed applications (crypto, public_key and ssh). All examples
- are run in an Erlang shell, or in a bash shell using openssh to
- illustrate how the erlang ssh application can be used. The
- examples are run as the user otptest on a local network where the
- user is authorized to login in over ssh to the host "tarlop". If
- nothing else is stated it is persumed that the otptest user has an
- entry in tarlop's authorized_keys file (may log in via ssh without
- entering a password). Also tarlop is a known host in the user
- otptest's known_hosts file so that host verification can be done
- without user interaction.
+ <title>General Information</title>
+ <p>The following examples use the utility function
+ <seealso marker="ssh#start-0"> ssh:start/0</seealso> to start
+ all needed applications (<c>crypto</c>, <c>public_key</c>, and <c>ssh</c>).
+ All examples are run in an Erlang shell, or in a bash shell, using <em>openssh</em>
+ to illustrate how the <c>ssh</c> application can be used. The
+ examples are run as the user <c>otptest</c> on a local network where the
+ user is authorized to log in over <c>ssh</c> to the host <em>tarlop</em>.
+ </p>
+ <p>If nothing else is stated, it is presumed that the <c>otptest</c> user
+ has an entry in the <em>authorized_keys</em> file of <em>tarlop</em>
+ (allowed to log in over <c>ssh</c> without entering a password).
+ Also, <em>tarlop</em> is a known host in the <c>known_hosts</c>
+ file of the user <c>otptest</c>. This means that host-verification
+ can be done without user-interaction.
</p>
</section>
<section>
- <title>Using the Erlang SSH Terminal Client</title>
+ <title>Using the Erlang ssh Terminal Client</title>
- <p>The user otptest, that has bash as default shell, uses the
- ssh:shell/1 client to connect to the openssh daemon running on a
- host called tarlop. Note that currently this client is very simple
- and you should not be expected to be as fancy as the openssh
- client.</p>
+ <p>The user <c>otptest</c>, which has bash as default shell, uses the
+ <c>ssh:shell/1</c> client to connect to the <em>openssh</em> daemon running on a
+ host called <em>tarlop</em>:</p>
<code type="erl" >
1> ssh:start().
ok
2> {ok, S} = ssh:shell("tarlop").
- >pwd
+ otptest@tarlop:> pwd
/home/otptest
- >exit
+ otptest@tarlop:> exit
logout
3>
</code>
</section>
<section>
- <title>Running an Erlang SSH Daemon </title>
+ <marker id="Running an Erlang ssh Daemon"></marker>
+ <title>Running an Erlang ssh Daemon</title>
- <p> The option system_dir must be a directory containing a host
- key file and it defaults to /etc/ssh. For details see section
+ <p>The <c>system_dir</c> option must be a directory containing a host
+ key file and it defaults to <c>/etc/ssh</c>. For details, see Section
Configuration Files in <seealso
marker="SSH_app">ssh(6)</seealso>.
</p>
- <note><p>Normally the /etc/ssh directory is only readable by root. </p>
+ <note><p>Normally, the <c>/etc/ssh</c> directory is only readable by root.</p>
</note>
- <p> The option user_dir defaults to the users ~/.ssh directory</p>
+ <p>The option <c>user_dir</c> defaults to directory <c>users ~/.ssh</c>.</p>
- <p>In the following example we generate new keys and host keys as
- to be able to run the example without having root privileges</p>
+ <p><em>Step 1.</em> To run the example without root privileges,
+ generate new keys and host keys:</p>
<code>
$bash> ssh-keygen -t rsa -f /tmp/ssh_daemon/ssh_host_rsa_key
@@ -88,19 +94,22 @@
[...]
</code>
- <p>Create the file /tmp/otptest_user/.ssh/authorized_keys and add the content
- of /tmp/otptest_user/.ssh/id_rsa.pub Now we can do</p>
+ <p><em>Step 2.</em> Create the file <c>/tmp/otptest_user/.ssh/authorized_keys</c>
+ and add the content of <c>/tmp/otptest_user/.ssh/id_rsa.pub</c>.</p>
+
+ <p><em>Step 3.</em> Start the Erlang <c>ssh</c> daemon:</p>
<code type="erl">
1> ssh:start().
ok
- 2> {ok, Sshd} = ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
- {user_dir, "/tmp/otptest_user/.ssh"}]).
+ 2> {ok, Sshd} = ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
+ {user_dir, "/tmp/otptest_user/.ssh"}]).
{ok,&lt;0.54.0>}
3>
</code>
- <p>Use the openssh client from a shell to connect to the Erlang ssh daemon.</p>
+ <p><em>Step 4.</em> Use the <em>openssh</em> client from a shell to connect
+ to the Erlang <c>ssh</c> daemon:</p>
<code>
$bash> ssh tarlop -p 8989 -i /tmp/otptest_user/.ssh/id_rsa\
@@ -113,9 +122,12 @@
1>
</code>
- <p>There are two ways of shutting down an SSH daemon</p>
+ <p>There are two ways of shutting down an <c>ssh</c> daemon,
+ see <em>Step 5a</em> and <em>Step 5b</em>.</p>
- <p>1: Stops the listener, but leaves existing connections started by the listener up and running.</p>
+ <p><em>Step 5a.</em> Shut down the Erlang <c>ssh</c> daemon so that it
+ stops the listener but leaves existing connections, started by the listener,
+ operational:</p>
<code type="erl">
3> ssh:stop_listener(Sshd).
@@ -123,7 +135,8 @@
4>
</code>
- <p>2: Stops the listener and all connections started by the listener.</p>
+ <p><em>Step 5b.</em> Shut down the Erlang <c>ssh</c> daemon so that it
+ stops the listener and all connections started by the listener:</p>
<code type="erl">
3> ssh:stop_daemon(Sshd)
@@ -134,17 +147,18 @@
</section>
<section>
- <title>One Time Execution</title>
+ <title>One-Time Execution</title>
- <p>In the following example the Erlang shell is the client process
- that receives the channel replies. </p>
+ <p>In the following example, the Erlang shell is the client process
+ that receives the channel replies.</p>
- <note><p> If you run this example
- in your environment you may get fewer or more messages back as
- this depends on the OS and shell on the machine running the ssh
- daemon. See also <seealso marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso>
+ <note><p>The number of received messages in this example depends on which OS
+ and which shell that is used on the machine running the <c>ssh</c> daemon.
+ See also <seealso marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso>.
</p></note>
+ <p>Do a one-time execution of a remote command over <c>ssh</c>:</p>
+
<code type="erl" >
1> ssh:start().
ok
@@ -162,7 +176,8 @@
6>
</code>
- <p>Note only the channel is closed the connection is still up and can handle other channels</p>
+ <p>Notice that only the channel is closed. The connection is still up and can
+ handle other channels:</p>
<code type="erl" >
6> {ok, NewChannelId} = ssh_connection:session_channel(ConnectionRef, infinity).
@@ -172,19 +187,22 @@
</section>
<section>
- <title>SFTP (SSH File Transport Protocol) server</title>
+ <title>SFTP Server</title>
+
+ <p>Start the Erlang <c>ssh</c> daemon with the SFTP subsystem:</p>
<code type="erl" >
1> ssh:start().
ok
- 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
- {user_dir, "/tmp/otptest_user/.ssh"},
- {subsystems, [ssh_sftpd:subsystem_spec([{cwd, "/tmp/sftp/example"}])]}]).
+ 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
+ {user_dir, "/tmp/otptest_user/.ssh"},
+ {subsystems, [ssh_sftpd:subsystem_spec([{cwd, "/tmp/sftp/example"}])
+ ]}]).
{ok,&lt;0.54.0>}
3>
</code>
- <p> Run the openssh sftp client</p>
+ <p>Run the OpenSSH SFTP client:</p>
<code type="erl">
$bash> sftp -oPort=8989 -o IdentityFile=/tmp/otptest_user/.ssh/id_rsa\
@@ -197,7 +215,9 @@
</section>
<section>
- <title>SFTP (SSH File Transport Protocol) client</title>
+ <title>SFTP Client</title>
+
+ <p>Fetch a file with the Erlang SFTP client:</p>
<code type="erl" >
1> ssh:start().
@@ -210,10 +230,77 @@
</section>
<section>
- <title>Creating a subsystem</title>
+ <title>SFTP Client with TAR Compression and Encryption</title>
+
+ <p>Example of writing and then reading a tar file follows:</p>
+ <code type="erlang">
+ {ok,HandleWrite} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [write]),
+ ok = erl_tar:add(HandleWrite, .... ),
+ ok = erl_tar:add(HandleWrite, .... ),
+ ...
+ ok = erl_tar:add(HandleWrite, .... ),
+ ok = erl_tar:close(HandleWrite),
+
+ %% And for reading
+ {ok,HandleRead} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [read]),
+ {ok,NameValueList} = erl_tar:extract(HandleRead,[memory]),
+ ok = erl_tar:close(HandleRead),
+ </code>
+
+ <p>The previous write and read example can be extended with encryption and decryption as follows:</p>
+ <code type="erlang">
+%% First three parameters depending on which crypto type we select:
+Key = &lt;&lt;"This is a 256 bit key. abcdefghi">>,
+Ivec0 = crypto:rand_bytes(16),
+DataSize = 1024, % DataSize rem 16 = 0 for aes_cbc
+
+%% Initialization of the CryptoState, in this case it is the Ivector.
+InitFun = fun() -> {ok, Ivec0, DataSize} end,
+
+%% How to encrypt:
+EncryptFun =
+ fun(PlainBin,Ivec) ->
+ EncryptedBin = crypto:block_encrypt(aes_cbc256, Key, Ivec, PlainBin),
+ {ok, EncryptedBin, crypto:next_iv(aes_cbc,EncryptedBin)}
+ end,
+
+%% What to do with the very last block:
+CloseFun =
+ fun(PlainBin, Ivec) ->
+ EncryptedBin = crypto:block_encrypt(aes_cbc256, Key, Ivec,
+ pad(16,PlainBin) %% Last chunk
+ ),
+ {ok, EncryptedBin}
+ end,
+
+Cw = {InitFun,EncryptFun,CloseFun},
+{ok,HandleWrite} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [write,{crypto,Cw}]),
+ok = erl_tar:add(HandleWrite, .... ),
+ok = erl_tar:add(HandleWrite, .... ),
+...
+ok = erl_tar:add(HandleWrite, .... ),
+ok = erl_tar:close(HandleWrite),
+
+%% And for decryption (in this crypto example we could use the same InitFun
+%% as for encryption):
+DecryptFun =
+ fun(EncryptedBin,Ivec) ->
+ PlainBin = crypto:block_decrypt(aes_cbc256, Key, Ivec, EncryptedBin),
+ {ok, PlainBin, crypto:next_iv(aes_cbc,EncryptedBin)}
+ end,
+
+Cr = {InitFun,DecryptFun},
+{ok,HandleRead} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [read,{crypto,Cw}]),
+{ok,NameValueList} = erl_tar:extract(HandleRead,[memory]),
+ok = erl_tar:close(HandleRead),
+ </code>
+ </section>
+
+ <section>
+ <title>Creating a Subsystem</title>
- <p>A very small SSH subsystem that echos N bytes could be implemented like this.
- See also <seealso marker="ssh_channel"> ssh_channel(3)</seealso> </p>
+ <p>A small <c>ssh</c> subsystem that echoes N bytes can be implemented as shown
+ in the following example:</p>
<code type="erl" >
-module(ssh_echo_server).
@@ -267,14 +354,16 @@ terminate(_Reason, _State) ->
ok.
</code>
- <p>And run like this on the host tarlop with the keys generated in section 3.3</p>
+ <p>The subsystem can be run on the host <em>tarlop</em> with the generated keys,
+ as described in Section <seealso marker="#Running an Erlang ssh Daemon">
+ Running an Erlang ssh Daemon</seealso>:</p>
<code type="erl" >
1> ssh:start().
ok
- 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
- {user_dir, "/tmp/otptest_user/.ssh"}
- {subsystems, [{"echo_n", {ssh_echo_server, [10]}}]}]).
+ 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"},
+ {user_dir, "/tmp/otptest_user/.ssh"}
+ {subsystems, [{"echo_n", {ssh_echo_server, [10]}}]}]).
{ok,&lt;0.54.0>}
3>
</code>
@@ -293,6 +382,7 @@ terminate(_Reason, _State) ->
{ssh_msg, &lt;0.57.0>, {closed, 0}}
7> {error, closed} = ssh_connection:send(ConnectionRef, ChannelId, "10", infinity).
</code>
+<p>See also <seealso marker="ssh_channel"> ssh_channel(3)</seealso>.</p>
</section>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 51ad691ba2..d4b02a024e 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -347,6 +347,8 @@ handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([{minimal_remote_max_packet_size, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{id_string, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -439,6 +441,10 @@ handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 ->
Opt;
handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
Opt;
+handle_ssh_option({id_string, random}) ->
+ {id_string, {random,2,5}}; %% 2 - 5 random characters
+handle_ssh_option({id_string, ID} = Opt) when is_list(ID) ->
+ Opt;
handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 76fa776113..8669be570e 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -44,12 +44,34 @@
versions(client, Options)->
Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION),
- Version = format_version(Vsn),
- {Vsn, Version};
+ {Vsn, format_version(Vsn, software_version(Options))};
versions(server, Options) ->
Vsn = proplists:get_value(vsn, Options, ?DEFAULT_SERVER_VERSION),
- Version = format_version(Vsn),
- {Vsn, Version}.
+ {Vsn, format_version(Vsn, software_version(Options))}.
+
+software_version(Options) ->
+ case proplists:get_value(id_string, Options) of
+ undefined ->
+ "Erlang"++ssh_vsn();
+ {random,Nlo,Nup} ->
+ random_id(Nlo,Nup);
+ ID ->
+ ID
+ end.
+
+ssh_vsn() ->
+ try {ok,L} = application:get_all_key(ssh),
+ proplists:get_value(vsn,L,"")
+ of
+ "" -> "";
+ VSN when is_list(VSN) -> "/" ++ VSN;
+ _ -> ""
+ catch
+ _:_ -> ""
+ end.
+
+random_id(Nlo, Nup) ->
+ [crypto:rand_uniform($a,$z+1) || _<- lists:duplicate(crypto:rand_uniform(Nlo,Nup+1),x) ].
hello_version_msg(Data) ->
[Data,"\r\n"].
@@ -77,9 +99,9 @@ is_valid_mac(Mac, Data, #ssh{recv_mac = Algorithm,
yes_no(Ssh, Prompt) ->
(Ssh#ssh.io_cb):yes_no(Prompt, Ssh).
-format_version({Major,Minor}) ->
+format_version({Major,Minor}, SoftwareVersion) ->
"SSH-" ++ integer_to_list(Major) ++ "." ++
- integer_to_list(Minor) ++ "-Erlang".
+ integer_to_list(Minor) ++ "-" ++ SoftwareVersion.
handle_hello_version(Version) ->
try
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 3331038450..bd029ad420 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -52,6 +52,12 @@ all() ->
ssh_connect_arg4_timeout,
packet_size_zero,
ssh_daemon_minimal_remote_max_packet_size_option,
+ id_string_no_opt_client,
+ id_string_own_string_client,
+ id_string_random_client,
+ id_string_no_opt_server,
+ id_string_own_string_server,
+ id_string_random_server,
{group, hardening_tests}
].
@@ -816,6 +822,66 @@ ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
ssh:stop_daemon(Server).
%%--------------------------------------------------------------------
+id_string_no_opt_client(Config) ->
+ {Server, Host, Port} = fake_daemon(Config),
+ {error,_} = ssh:connect(Host, Port, []),
+ receive
+ {id,Server,"SSH-2.0-Erlang/"++Vsn} ->
+ true = expected_ssh_vsn(Vsn);
+ {id,Server,Other} ->
+ ct:fail("Unexpected id: ~s.",[Other])
+ end.
+
+%%--------------------------------------------------------------------
+id_string_own_string_client(Config) ->
+ {Server, Host, Port} = fake_daemon(Config),
+ {error,_} = ssh:connect(Host, Port, [{id_string,"Pelle"}]),
+ receive
+ {id,Server,"SSH-2.0-Pelle\r\n"} ->
+ ok;
+ {id,Server,Other} ->
+ ct:fail("Unexpected id: ~s.",[Other])
+ end.
+
+%%--------------------------------------------------------------------
+id_string_random_client(Config) ->
+ {Server, Host, Port} = fake_daemon(Config),
+ {error,_} = ssh:connect(Host, Port, [{id_string,random}]),
+ receive
+ {id,Server,Id="SSH-2.0-Erlang"++_} ->
+ ct:fail("Unexpected id: ~s.",[Id]);
+ {id,Server,Rnd="SSH-2.0-"++_} ->
+ ct:log("Got ~s.",[Rnd]);
+ {id,Server,Id} ->
+ ct:fail("Unexpected id: ~s.",[Id])
+ end.
+
+%%--------------------------------------------------------------------
+id_string_no_opt_server(Config) ->
+ {_Server, Host, Port} = std_daemon(Config, []),
+ {ok,S1}=gen_tcp:connect(Host,Port,[{active,false}]),
+ {ok,"SSH-2.0-Erlang/"++Vsn} = gen_tcp:recv(S1, 0, 2000),
+ true = expected_ssh_vsn(Vsn).
+
+%%--------------------------------------------------------------------
+id_string_own_string_server(Config) ->
+ {_Server, Host, Port} = std_daemon(Config, [{id_string,"Olle"}]),
+ {ok,S1}=gen_tcp:connect(Host,Port,[{active,false}]),
+ {ok,"SSH-2.0-Olle\r\n"} = gen_tcp:recv(S1, 0, 2000).
+
+%%--------------------------------------------------------------------
+id_string_random_server(Config) ->
+ {_Server, Host, Port} = std_daemon(Config, [{id_string,random}]),
+ {ok,S1}=gen_tcp:connect(Host,Port,[{active,false}]),
+ {ok,"SSH-2.0-"++Rnd} = gen_tcp:recv(S1, 0, 2000),
+ case Rnd of
+ "Erlang"++_ -> ct:log("Id=~p",[Rnd]),
+ {fail,got_default_id};
+ "Olle\r\n" -> {fail,got_previous_tests_value};
+ _ -> ct:log("Got ~s.",[Rnd])
+ end.
+
+%%--------------------------------------------------------------------
ssh_connect_negtimeout_parallel(Config) -> ssh_connect_negtimeout(Config,true).
ssh_connect_negtimeout_sequential(Config) -> ssh_connect_negtimeout(Config,false).
@@ -1094,3 +1160,46 @@ do_shell(IO, Shell) ->
%% {'EXIT', Shell, killed} ->
%% ok
%% end.
+
+
+std_daemon(Config, ExtraOpts) ->
+ SystemDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ {_Server, _Host, _Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2} | ExtraOpts]).
+
+expected_ssh_vsn(Str) ->
+ try
+ {ok,L} = application:get_all_key(ssh),
+ proplists:get_value(vsn,L,"")++"\r\n"
+ of
+ Str -> true;
+ "\r\n" -> true;
+ _ -> false
+ catch
+ _:_ -> true %% ssh not started so we dont't know
+ end.
+
+
+fake_daemon(_Config) ->
+ Parent = self(),
+ %% start the server
+ Server = spawn(fun() ->
+ {ok,Sl} = gen_tcp:listen(0,[]),
+ {ok,{Host,Port}} = inet:sockname(Sl),
+ Parent ! {sockname,self(),Host,Port},
+ Rsa = gen_tcp:accept(Sl),
+ ct:log("Server gen_tcp:accept got ~p",[Rsa]),
+ {ok,S} = Rsa,
+ receive
+ {tcp, S, Id} -> Parent ! {id,self(),Id}
+ end
+ end),
+ %% Get listening host and port
+ receive
+ {sockname,Server,ServerHost,ServerPort} -> {Server, ServerHost, ServerPort}
+ end.
+
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index fec8dacab7..b2b85a717f 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 3.2.1
+SSH_VSN = 3.2.2
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index f29aa00a60..b538fefe53 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -578,11 +578,10 @@ prf({3,_N}, Secret, Label, Seed, WantedLength) ->
%%--------------------------------------------------------------------
select_hashsign(_, undefined, _Version) ->
{null, anon};
-select_hashsign(undefined, Cert, Version) ->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- select_hashsign_algs(undefined, Algo, Version);
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, Version) ->
+%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
+%% negotiated a lower version.
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, {Major, Minor} = Version)
+ when Major >= 3 andalso Minor >= 3 ->
#'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
DefaultHashSign = {_, Sign} = select_hashsign_algs(undefined, Algo, Version),
@@ -600,7 +599,11 @@ select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, Version) ->
DefaultHashSign;
[HashSign| _] ->
HashSign
- end.
+ end;
+select_hashsign(_, Cert, Version) ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+ select_hashsign_algs(undefined, Algo, Version).
%%--------------------------------------------------------------------
-spec select_hashsign_algs(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version()) ->
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 8dca733526..d4433393a1 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,47 @@ all() -> [decode_hello_handshake,
encode_single_hello_sni_extension_correctly,
decode_single_hello_sni_extension_correctly,
decode_empty_server_sni_correctly,
- select_proper_tls_1_2_rsa_default_hashsign].
+ select_proper_tls_1_2_rsa_default_hashsign,
+ ignore_hassign_extension_pre_tls_1_2].
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_,Config) ->
+ Config.
+
+init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ case is_supported(sha512) of
+ true ->
+ ssl:start(),
+ %% make rsa certs using oppenssl
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ ct:log("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config0);
+ false ->
+ {skip, "Crypto did not support sha512"}
+ end
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end;
+init_per_testcase(_, Config0) ->
+ Config0.
+
+end_per_testcase(ignore_hassign_extension_pre_tls_1_2, _) ->
+ crypto:stop();
+end_per_testcase(_TestCase, Config) ->
+ Config.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
@@ -121,3 +161,18 @@ select_proper_tls_1_2_rsa_default_hashsign(_Config) ->
{md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,2}),
{md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,0}).
+
+ignore_hassign_extension_pre_tls_1_2(Config) ->
+ Opts = ?config(server_opts, Config),
+ CertFile = proplists:get_value(certfile, Opts),
+ [{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}]},
+ {sha512, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,3}),
+ %%% Ignore
+ {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,2}),
+ {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,0}).
+
+is_supported(Hash) ->
+ Algos = crypto:supports(),
+ Hashs = proplists:get_value(hashs, Algos),
+ lists:member(Hash, Hashs).
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 27ee07ffc6..94426a3061 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -291,7 +291,7 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost" ++ workaround_openssl_s_client(),
+ " -host localhost" ++ workaround_openssl_s_clinent(),
ct:log("openssl cmd: ~p~n", [Cmd]),
@@ -1658,7 +1658,7 @@ supports_sslv2(Port) ->
true
end.
-workaround_openssl_s_client() ->
+workaround_openssl_s_clinent() ->
%% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159
%% https://bugs.archlinux.org/task/33919
%% Bug seems to manifests it self if TLS version is not
@@ -1672,8 +1672,6 @@ workaround_openssl_s_client() ->
" -no_tls1_2 ";
"OpenSSL 1.0.1f" ++ _ ->
" -no_tls1_2 ";
- "OpenSSL 1.0.1l" ++ _ ->
- " -cipher AES256-SHA";
- _ ->
+ _ ->
""
end.
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 64a00acd88..dc74d611a3 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -384,21 +384,11 @@ expr({call,Line,{tuple,_,[{atom,_,erlang},{atom,_,is_record}]},
expr({call,Line,{atom,_La,N}=Atom,As0}, St0) ->
{As,St1} = expr_list(As0, St0),
Ar = length(As),
- case erl_internal:bif(N, Ar) of
- true ->
- {{call,Line,Atom,As},St1};
- false ->
- case imported(N, Ar, St1) of
- {yes,_Mod} ->
- {{call,Line,Atom,As},St1};
- no ->
- case {N,Ar} of
- {record_info,2} ->
- record_info_call(Line, As, St1);
- _ ->
- {{call,Line,Atom,As},St1}
- end
- end
+ case {N,Ar} =:= {record_info,2} andalso not imported(N, Ar, St1) of
+ true ->
+ record_info_call(Line, As, St1);
+ false ->
+ {{call,Line,Atom,As},St1}
end;
expr({call,Line,{remote,Lr,M,F},As0}, St0) ->
{[M1,F1 | As1],St1} = expr_list([M,F | As0], St0),
@@ -832,10 +822,7 @@ add_imports(Mod, [F | Fs], Is) ->
add_imports(_, [], Is) -> Is.
imported(F, A, St) ->
- case orddict:find({F,A}, St#exprec.imports) of
- {ok,Mod} -> {yes,Mod};
- error -> no
- end.
+ orddict:is_key({F,A}, St#exprec.imports).
%%%
%%% Replace is_record/3 in guards with matching if possible.
diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl
index c98d78b34d..af5d917840 100644
--- a/lib/stdlib/src/orddict.erl
+++ b/lib/stdlib/src/orddict.erl
@@ -115,8 +115,8 @@ erase(_, []) -> [].
Orddict1 :: orddict(),
Orddict2 :: orddict().
-store(Key, New, [{K,_}=E|Dict]) when Key < K ->
- [{Key,New},E|Dict];
+store(Key, New, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,New}|Dict];
store(Key, New, [{K,_}=E|Dict]) when Key > K ->
[E|store(Key, New, Dict)];
store(Key, New, [{_K,_Old}|Dict]) -> %Key == K
@@ -129,8 +129,8 @@ store(Key, New, []) -> [{Key,New}].
Orddict1 :: orddict(),
Orddict2 :: orddict().
-append(Key, New, [{K,_}=E|Dict]) when Key < K ->
- [{Key,[New]},E|Dict];
+append(Key, New, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,[New]}|Dict];
append(Key, New, [{K,_}=E|Dict]) when Key > K ->
[E|append(Key, New, Dict)];
append(Key, New, [{_K,Old}|Dict]) -> %Key == K
@@ -143,8 +143,8 @@ append(Key, New, []) -> [{Key,[New]}].
Orddict1 :: orddict(),
Orddict2 :: orddict().
-append_list(Key, NewList, [{K,_}=E|Dict]) when Key < K ->
- [{Key,NewList},E|Dict];
+append_list(Key, NewList, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,NewList}|Dict];
append_list(Key, NewList, [{K,_}=E|Dict]) when Key > K ->
[E|append_list(Key, NewList, Dict)];
append_list(Key, NewList, [{_K,Old}|Dict]) -> %Key == K
@@ -170,8 +170,8 @@ update(Key, Fun, [{K,Val}|Dict]) when Key == K ->
Orddict1 :: orddict(),
Orddict2 :: orddict().
-update(Key, _, Init, [{K,_}=E|Dict]) when Key < K ->
- [{Key,Init},E|Dict];
+update(Key, _, Init, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,Init}|Dict];
update(Key, Fun, Init, [{K,_}=E|Dict]) when Key > K ->
[E|update(Key, Fun, Init, Dict)];
update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K
@@ -184,8 +184,8 @@ update(Key, _, Init, []) -> [{Key,Init}].
Orddict1 :: orddict(),
Orddict2 :: orddict().
-update_counter(Key, Incr, [{K,_}=E|Dict]) when Key < K ->
- [{Key,Incr},E|Dict];
+update_counter(Key, Incr, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,Incr}|Dict];
update_counter(Key, Incr, [{K,_}=E|Dict]) when Key > K ->
[E|update_counter(Key, Incr, Dict)];
update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 540c1cac9c..0fb6974426 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -596,7 +596,7 @@ obsolete_1(core_lib, is_literal_list, 1) ->
obsolete_1(core_lib, literal_value, 1) ->
{deprecated,{core_lib,concrete,1}};
obsolete_1(ssl, negotiated_next_protocol, 1) ->
- {deprecated,{ssl,negotiated_protocol}};
+ {deprecated,{ssl,negotiated_protocol,1}};
obsolete_1(_, _, _) ->
no.
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 0c003bab39..3610356355 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -2444,7 +2444,10 @@ This is automagically called by the user level function `indent-region'."
;; Parse the Erlang code from the beginning of the clause to
;; the beginning of the region.
(while (< (point) indent-point)
- (setq state (erlang-partial-parse (point) indent-point state)))
+ (let ((pt (point)))
+ (setq state (erlang-partial-parse pt indent-point state))
+ (if (= pt (point))
+ (error "Illegal syntax"))))
;; Indent every line in the region
(while continue
(goto-char indent-point)
@@ -2480,8 +2483,11 @@ This is automagically called by the user level function `indent-region'."
(if (>= from-end (- (point-max) indent-point))
(setq continue nil)
(while (< (point) indent-point)
- (setq state (erlang-partial-parse
- (point) indent-point state))))))))
+ (let ((pt (point)))
+ (setq state (erlang-partial-parse
+ pt indent-point state))
+ (if (= pt (point))
+ (error "Illegal syntax")))))))))
(defun erlang-indent-current-buffer ()
@@ -2528,7 +2534,10 @@ Return nil if line starts inside string, t if in a comment."
(goto-char parse-start)
(erlang-beginning-of-clause))
(while (< (point) indent-point)
- (setq state (erlang-partial-parse (point) indent-point state)))
+ (let ((pt (point)))
+ (setq state (erlang-partial-parse pt indent-point state))
+ (if (= pt (point))
+ (error "Illegal syntax"))))
(erlang-calculate-stack-indent indent-point state))))
(defun erlang-show-syntactic-information ()
@@ -2698,12 +2707,13 @@ Value is list (stack token-start token-type in-what)."
(erlang-push (list '|| token (current-column)) stack)
(forward-char 2))
- ;; Bit-syntax open paren
- ((looking-at "<<")
+ ;; Bit-syntax open. Note that map syntax allows "<<" to follow ":="
+ ;; or "=>" without intervening whitespace, so handle that case here
+ ((looking-at "\\(:=\\|=>\\)?<<")
(erlang-push (list '<< token (current-column)) stack)
- (forward-char 2))
+ (forward-char (- (match-end 0) (match-beginning 0))))
- ;; Bbit-syntax close paren
+ ;; Bit-syntax close
((looking-at ">>")
(while (memq (car (car stack)) '(|| ->))
(erlang-pop stack))
@@ -4188,7 +4198,10 @@ This function is designed to be a member of a criteria list."
;; Do not return `stop' when inside a list comprehension
;; construction. (The point must be after `||').
(while (< (point) orig-point)
- (setq state (erlang-partial-parse (point) orig-point state)))
+ (let ((pt (point)))
+ (setq state (erlang-partial-parse pt orig-point state))
+ (if (= pt (point))
+ (error "Illegal syntax"))))
(if (and (car state) (eq (car (car (car state))) '||))
nil
'stop)))
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index 1c1086ca58..abb05fd59b 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -32,6 +32,14 @@
-module(test).
-compile(export_all).
+%% Used to cause an "Unbalanced parentheses" error.
+foo(M) ->
+ M#{a :=<<"a">>
+ ,b:=1}.
+foo() ->
+ #{a =><<"a">>
+ ,b=>1}.
+
%% Module attributes should be highlighted
-export([t/1]).
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index a9d09000d2..3d8f29fe18 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -32,6 +32,14 @@
-module(test).
-compile(export_all).
+%% Used to cause an "Unbalanced parentheses" error.
+foo(M) ->
+M#{a :=<<"a">>
+,b:=1}.
+foo() ->
+#{a =><<"a">>
+,b=>1}.
+
%% Module attributes should be highlighted
-export([t/1]).
diff --git a/otp_versions.table b/otp_versions.table
index a82f535ea1..4bf6cb93b2 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-17.5.2 : inets-5.10.7 ssh-3.2.2 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :
OTP-17.5.1 : ssh-3.2.1 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :
OTP-17.5 : asn1-3.0.4 common_test-1.10 compiler-5.0.4 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 eldap-1.1.1 erts-6.4 hipe-3.11.3 inets-5.10.6 kernel-3.2 mnesia-4.12.5 observer-2.0.4 os_mon-2.3.1 public_key-0.23 runtime_tools-1.8.16 ssh-3.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 wx-1.3.3 # cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 edoc-0.7.16 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 ic-4.3.6 jinterface-1.5.12 megaco-3.17.3 odbc-2.10.22 orber-3.7.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 reltool-0.6.6 sasl-2.4.1 snmp-5.1.1 typer-0.9.8 webtool-0.8.10 xmerl-1.3.7 :
OTP-17.4.1 : erts-6.3.1 inets-5.10.5 # asn1-3.0.3 common_test-1.9 compiler-5.0.3 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.2 debugger-4.0.2 dialyzer-2.7.3 diameter-1.8 edoc-0.7.16 eldap-1.1 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.2 ic-4.3.6 jinterface-1.5.12 kernel-3.1 megaco-3.17.3 mnesia-4.12.4 observer-2.0.3 odbc-2.10.22 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.15 sasl-2.4.1 snmp-5.1.1 ssh-3.1 ssl-5.3.8 stdlib-2.3 syntax_tools-1.6.17 test_server-3.7.2 tools-2.7.1 typer-0.9.8 webtool-0.8.10 wx-1.3.2 xmerl-1.3.7 :