aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/src/asn1ct_check.erl46
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Constraints.py40
-rw-r--r--lib/asn1/test/testConstraints.erl18
-rw-r--r--lib/compiler/test/compilation_SUITE.erl6
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl17
-rw-r--r--lib/inets/doc/src/ftp.xml14
-rw-r--r--lib/inets/src/ftp/ftp.erl449
-rw-r--r--lib/inets/src/ftp/ftp_response.erl2
-rw-r--r--lib/inets/test/Makefile15
-rw-r--r--lib/inets/test/ftp_SUITE.erl843
-rw-r--r--lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel18
-rw-r--r--lib/inets/test/ftp_SUITE_data/vsftpd.conf26
-rw-r--r--lib/inets/test/ftp_freebsd_x86_test.erl160
-rw-r--r--lib/inets/test/ftp_linux_ppc_test.erl158
-rw-r--r--lib/inets/test/ftp_linux_x86_test.erl160
-rw-r--r--lib/inets/test/ftp_macosx_ppc_test.erl159
-rw-r--r--lib/inets/test/ftp_macosx_x86_test.erl159
-rw-r--r--lib/inets/test/ftp_netbsd_x86_test.erl159
-rw-r--r--lib/inets/test/ftp_openbsd_x86_test.erl158
-rw-r--r--lib/inets/test/ftp_solaris10_sparc_test.erl161
-rw-r--r--lib/inets/test/ftp_solaris10_x86_test.erl162
-rw-r--r--lib/inets/test/ftp_solaris8_sparc_test.erl159
-rw-r--r--lib/inets/test/ftp_solaris9_sparc_test.erl158
-rw-r--r--lib/inets/test/ftp_suite_lib.erl1675
-rw-r--r--lib/inets/test/ftp_ticket_test.erl61
-rw-r--r--lib/inets/test/ftp_windows_2003_server_test.erl167
-rw-r--r--lib/inets/test/ftp_windows_xp_test.erl157
-rw-r--r--lib/inets/test/httpc_SUITE.erl23
-rw-r--r--lib/kernel/doc/src/inet.xml92
-rw-r--r--lib/kernel/doc/src/os.xml11
-rw-r--r--lib/kernel/doc/src/rpc.xml2
-rw-r--r--lib/kernel/src/inet.erl39
-rw-r--r--lib/kernel/src/inet_int.hrl2
-rw-r--r--lib/kernel/src/os.erl8
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl27
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl333
-rw-r--r--lib/kernel/test/zlib_SUITE.erl8
-rw-r--r--lib/observer/doc/src/ttb_ug.xml2
-rw-r--r--lib/odbc/configure.in5
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn112
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl11
-rw-r--r--lib/public_key/test/public_key_SUITE.erl38
-rw-r--r--lib/runtime_tools/src/observer_backend.erl4
-rw-r--r--lib/snmp/doc/src/notes.xml121
-rw-r--r--lib/snmp/doc/src/snmp.xml80
-rw-r--r--lib/snmp/doc/src/snmp_app.xml7
-rw-r--r--lib/snmp/doc/src/snmp_config.xml9
-rw-r--r--lib/snmp/doc/src/snmpa.xml48
-rw-r--r--lib/snmp/doc/src/snmpm.xml49
-rw-r--r--lib/snmp/doc/src/snmpm_user.xml187
-rw-r--r--lib/snmp/examples/ex2/snmp_ex2_manager.erl9
-rw-r--r--lib/snmp/src/agent/snmpa.erl198
-rw-r--r--lib/snmp/src/agent/snmpa_local_db.erl8
-rw-r--r--lib/snmp/src/agent/snmpa_mpd.erl4
-rw-r--r--lib/snmp/src/agent/snmpa_supervisor.erl2
-rw-r--r--lib/snmp/src/agent/snmpa_symbolic_store.erl8
-rw-r--r--lib/snmp/src/agent/snmpa_usm.erl10
-rw-r--r--lib/snmp/src/app/Makefile4
-rw-r--r--lib/snmp/src/app/snmp.appup.src43
-rw-r--r--lib/snmp/src/app/snmp.erl119
-rw-r--r--lib/snmp/src/app/snmp_internal.hrl4
-rw-r--r--lib/snmp/src/manager/snmpm.erl218
-rw-r--r--lib/snmp/src/manager/snmpm_config.erl31
-rw-r--r--lib/snmp/src/manager/snmpm_server.erl184
-rw-r--r--lib/snmp/src/manager/snmpm_user.erl173
-rw-r--r--lib/snmp/src/manager/snmpm_usm.erl14
-rw-r--r--lib/snmp/src/misc/snmp_config.erl187
-rw-r--r--lib/snmp/src/misc/snmp_log.erl188
-rw-r--r--lib/snmp/src/misc/snmp_usm.erl23
-rw-r--r--lib/snmp/src/misc/snmp_verbosity.erl2
-rw-r--r--lib/snmp/test/snmp_agent_test.erl72
-rw-r--r--lib/snmp/test/snmp_log_test.erl77
-rw-r--r--lib/snmp/test/snmp_manager_config_test.erl43
-rw-r--r--lib/snmp/test/snmp_manager_test.erl66
-rw-r--r--lib/snmp/test/snmp_manager_user.erl17
-rw-r--r--lib/snmp/test/snmp_test_manager.erl20
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/ssh.xml66
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml6
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml4
-rw-r--r--lib/ssh/src/Makefile3
-rw-r--r--lib/ssh/src/ssh.app.src3
-rw-r--r--lib/ssh/src/ssh.erl177
-rw-r--r--lib/ssh/src/ssh.hrl4
-rw-r--r--lib/ssh/src/ssh_acceptor.erl19
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl6
-rw-r--r--lib/ssh/src/ssh_auth.erl99
-rw-r--r--lib/ssh/src/ssh_bits.erl334
-rw-r--r--lib/ssh/src/ssh_channel.erl2
-rw-r--r--lib/ssh/src/ssh_channel_sup.erl4
-rw-r--r--lib/ssh/src/ssh_cli.erl110
-rw-r--r--lib/ssh/src/ssh_connect.hrl5
-rw-r--r--lib/ssh/src/ssh_connection.erl653
-rw-r--r--lib/ssh/src/ssh_connection_controler.erl137
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl1292
-rw-r--r--lib/ssh/src/ssh_connection_manager.erl916
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl87
-rw-r--r--lib/ssh/src/ssh_io.erl5
-rw-r--r--lib/ssh/src/ssh_message.erl529
-rw-r--r--lib/ssh/src/ssh_no_io.erl43
-rw-r--r--lib/ssh/src/ssh_sftpd.erl2
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl16
-rw-r--r--lib/ssh/src/ssh_sup.erl15
-rw-r--r--lib/ssh/src/ssh_system_sup.erl20
-rw-r--r--lib/ssh/src/ssh_transport.erl203
-rw-r--r--lib/ssh/src/ssh_userreg.erl141
-rw-r--r--lib/ssh/src/ssh_xfer.erl4
-rw-r--r--lib/ssh/src/sshc_sup.erl6
-rw-r--r--lib/ssh/src/sshd_sup.erl11
-rw-r--r--lib/ssh/test/Makefile3
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl107
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl33
-rw-r--r--lib/ssh/test/ssh_peername_sockname_server.erl8
-rw-r--r--lib/ssh/test/ssh_test_cli.erl81
-rw-r--r--lib/ssl/doc/src/ssl.xml2
115 files changed, 5498 insertions, 7809 deletions
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 04227fd23b..0a13801e08 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -4194,9 +4194,10 @@ constraint_union(S,C) when is_list(C) ->
constraint_union(_S,C) ->
[C].
-constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) ->
- AunionB = constraint_union_vr([A,B]),
- constraint_union1(S, AunionB++Rest, Acc);
+constraint_union1(S, [{'ValueRange',{Lb1,Ub1}},union,
+ {'ValueRange',{Lb2,Ub2}}|Rest], Acc) ->
+ AunionB = {'ValueRange',{c_min(Lb1, Lb2),max(Ub1, Ub2)}},
+ constraint_union1(S, [AunionB|Rest], Acc);
constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) ->
AunionB = constraint_union_sv(S,[A,B]),
constraint_union1(S,Rest,Acc ++ AunionB);
@@ -4220,42 +4221,9 @@ constraint_union_sv(_S,SV) ->
[N] -> [{'SingleValue',N}];
L -> [{'SingleValue',L}]
end.
-
-%% REMOVE????
-%%constraint_union(S,VR,'ValueRange') ->
-%% constraint_union_vr(VR).
-
-%% constraint_union_vr(VR)
-%% VR = [{'ValueRange',{Lb,Ub}},...]
-%% Lb = 'MIN' | integer()
-%% Ub = 'MAX' | integer()
-%% Returns if possible only one ValueRange tuple with a range that
-%% is a union of all ranges in VR.
-constraint_union_vr(VR) ->
- %% Sort VR by Lb in first hand and by Ub in second hand
- Fun=fun({_,{'MIN',_B1}},{_,{A2,_B2}}) when is_integer(A2)->true;
- ({_,{A1,_B1}},{_,{'MAX',_B2}}) when is_integer(A1) -> true;
- ({_,{A1,_B1}},{_,{A2,_B2}}) when is_integer(A1),is_integer(A2),A1<A2 -> true;
- ({_,{A,B1}},{_,{A,B2}}) when B1=<B2->true;
- (_,_)->false end,
- SortedVR = lists:usort(Fun,VR),
- constraint_union_vr(SortedVR, []).
-
-constraint_union_vr([],Acc) ->
- lists:reverse(Acc);
-constraint_union_vr([C|Rest],[]) ->
- constraint_union_vr(Rest,[C]);
-constraint_union_vr([{_,{Lb,Ub2}}|Rest],[{_,{Lb,_Ub1}}|Acc]) -> %Ub2 > Ub1
- constraint_union_vr(Rest,[{'ValueRange',{Lb,Ub2}}|Acc]);
-constraint_union_vr([{_,{_,Ub}}|Rest],A=[{_,{_,Ub}}|_Acc]) ->
- constraint_union_vr(Rest,A);
-constraint_union_vr([{_,{Lb2,Ub2}}|Rest], [{_,{Lb1,Ub1}}|Acc])
- when Ub1 =< Lb2, Ub1 < Ub2 ->
- constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]);
-constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1->
- constraint_union_vr(Rest,A);
-constraint_union_vr([VR|Rest],Acc) ->
- constraint_union_vr(Rest,[VR|Acc]).
+c_min('MIN', _) -> 'MIN';
+c_min(_, 'MIN') -> 'MIN';
+c_min(A, B) -> min(A, B).
union_sv_vr(_S,{'SingleValue',SV},VR)
when is_integer(SV) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py
index 581ec2f467..864a471b88 100644
--- a/lib/asn1/test/asn1_SUITE_data/Constraints.py
+++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py
@@ -99,4 +99,44 @@ pdf OBJECT IDENTIFIER ::= {1,2,3,4,5}
ShorterExt ::= IA5String (SIZE (5, ...))
+SeqOverlapping ::= SEQUENCE {
+ v Overlapping
+}
+
+SeqNonOverlapping ::= SEQUENCE {
+ v NonOverlapping
+}
+
+Overlapping ::= INTEGER (7280..7560 |
+7580..7680 |
+7910..8210 |
+8600..8940 |
+9250..9600 |
+14759..15109 |
+15250..15590 |
+18050..18800 |
+19300..19950 |
+21100..21700 |
+26200..26900 |
+18500..19900 |
+20100..20250 |
+21100..21700 |
+23000..24000 |
+24960..26900)
+
+-- The same intervals, but merged and sorted --
+NonOverlapping ::= INTEGER (7280..7560 |
+7580..7680 |
+7910..8210 |
+8600..8940 |
+9250..9600 |
+14759..15109 |
+15250..15590 |
+18050..19950 |
+20100..20250 |
+21100..21700 |
+23000..24000 |
+24960..26900)
+
+
END
diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl
index 34fbbcf6cc..23322f5e88 100644
--- a/lib/asn1/test/testConstraints.erl
+++ b/lib/asn1/test/testConstraints.erl
@@ -218,6 +218,15 @@ int_constraints(Rules) ->
roundtrip('ShorterExt', "abcde"),
roundtrip('ShorterExt', "abcdef"),
+ %%==========================================================
+ %% Unions of INTEGER constraints
+ %%==========================================================
+ seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 7580),
+ seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 9600),
+ seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 18050),
+ seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 19000),
+ seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 26900),
+
ok.
%% PER: Ensure that if the lower bound is Lb, Lb+16#80 is encoded
@@ -297,3 +306,12 @@ range_error(Per, Type, Value) when Per =:= per; Per =:= uper ->
%% on encode.
{error,_} = 'Constraints':encode(Type, Value),
ok.
+
+seq_roundtrip(Rules, Seq1, Seq2, Val) ->
+ Enc = roundtrip(Seq1, {Seq1,Val}),
+ case Rules of
+ ber ->
+ roundtrip(Seq2, {Seq2,Val});
+ _ ->
+ roundtrip_enc(Seq2, {Seq2,Val}, Enc)
+ end.
diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl
index 8e7b7292cc..d517029b1b 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -50,7 +50,7 @@ groups() ->
trycatch_4,opt_crash,otp_5404,otp_5436,otp_5481,
otp_5553,otp_5632,otp_5714,otp_5872,otp_6121,
otp_6121a,otp_6121b,otp_7202,otp_7345,on_load,
- string_table,otp_8949_a,otp_8949_a,split_cases,
+ string_table,otp_8949_a,otp_8949_b,split_cases,
beam_utils_liveopt]}].
init_per_suite(Config) ->
@@ -630,13 +630,13 @@ string_table(Config) when is_list(Config) ->
ok.
otp_8949_a(Config) when is_list(Config) ->
- value = otp_8949_a(),
+ value = do_otp_8949_a(),
ok.
-record(cs, {exs,keys = [],flags = 1}).
-record(exs, {children = []}).
-otp_8949_a() ->
+do_otp_8949_a() ->
case id([#cs{}]) of
[#cs{}=Cs] ->
SomeVar = id(value),
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index 127d753b92..bf5fa83c3c 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -28,10 +28,19 @@
-define(TIMEOUT, 120000). % 2 min
init_per_suite(Config) ->
- ssl:start(),
- chk_config(ldap_server, {"localhost",9876},
- chk_config(ldaps_server, {"localhost",9877},
- Config)).
+ StartSsl = try ssl:start()
+ catch
+ Error:Reason ->
+ {skip, lists:flatten(io_lib:format("eldap init_per_suite failed to start ssl Error=~p Reason=~p", [Error, Reason]))}
+ end,
+ case StartSsl of
+ ok ->
+ chk_config(ldap_server, {"localhost",9876},
+ chk_config(ldaps_server, {"localhost",9877},
+ Config));
+ _ ->
+ StartSsl
+ end.
end_per_suite(_Config) ->
ok.
diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml
index f8f11ec705..4d559817c4 100644
--- a/lib/inets/doc/src/ftp.xml
+++ b/lib/inets/doc/src/ftp.xml
@@ -547,15 +547,14 @@
<v>Opts = options()</v>
<v>options() = [option()]</v>
<v>option() = start_option() | open_option()</v>
- <!-- <v>start_options() = [start_option()]</v> -->
<v>start_option() = {verbose, verbose()} | {debug, debug()}</v>
<v>verbose() = boolean() (defaults to false)</v>
<v>debug() = disable | debug | trace (defaults to disable)</v>
- <!-- <v>open_options() = [open_option()]</v> -->
- <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v>
+ <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v>
<v>ipfamily() = inet | inet6 | inet6fb4 (defaults to inet)</v>
<v>port() = integer() > 0 (defaults to 21)</v>
<v>mode() = active | passive (defaults to passive)</v>
+ <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v>
<v>timeout() = integer() > 0 (defaults to 60000 milliseconds)</v>
<v>dtimeout() = integer() > 0 | infinity (defaults to infinity)</v>
<v>pogress() = ignore | {module(), function(), initial_data()} (defaults to ignore)</v>
@@ -570,6 +569,10 @@
(without the inets service framework) and
open a session with the FTP server at <c>Host</c>. </p>
+ <p>If the option <c>{tls, tls_options()}</c> is present, the ftp session will be transported over tls (ftps, see
+<url href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</url>). The list <c>tls_options()</c> may be empty. The function <seealso marker="ssl:ssl#connect/3"><c>ssl:connect/3</c></seealso> is used for securing both the control connection and the data sessions.
+ </p>
+
<p>A session opened in this way, is closed using the
<seealso marker="#close">close</seealso> function. </p>
@@ -815,8 +818,7 @@
<p>Sets the file transfer type to <c>ascii</c> or <c>binary</c>. When
an ftp session is opened, the default transfer type of the
server is used, most often <c>ascii</c>, which is the default
- according to RFC 959.</p>
-
+ according to <url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>.</p>
<marker id="user3"></marker>
</desc>
</func>
@@ -943,7 +945,7 @@
<section>
<title>SEE ALSO</title>
<p>file, filename, J. Postel and J. Reynolds: File Transfer Protocol
- (RFC 959).
+ (<url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>).
</p>
</section>
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index 5d9887a9a4..86ef9280ad 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
%%
%%
%% Description: This module implements an ftp client, RFC 959.
-%% It also supports ipv6 RFC 2428.
+%% It also supports ipv6 RFC 2428 and starttls RFC 4217.
-module(ftp).
@@ -39,7 +39,8 @@
send_chunk_start/2, send_chunk/2, send_chunk_end/1,
type/2, user/3, user/4, account/2,
append/3, append/2, append_bin/3,
- append_chunk/2, append_chunk_end/1, append_chunk_start/2, info/1]).
+ append_chunk/2, append_chunk_end/1, append_chunk_start/2,
+ info/1, latest_ctrl_response/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
@@ -54,7 +55,7 @@
-include("ftp_internal.hrl").
-%% Constante used in internal state definition
+%% Constants used in internal state definition
-define(CONNECTION_TIMEOUT, 60*1000).
-define(DATA_ACCEPT_TIMEOUT, infinity).
-define(DEFAULT_MODE, passive).
@@ -67,7 +68,8 @@
%% Internal state
-record(state, {
csock = undefined, % socket() - Control connection socket
- dsock = undefined, % socket() - Data connection socket
+ dsock = undefined, % socket() - Data connection socket
+ tls_options = undefined, % list()
verbose = false, % boolean()
ldir = undefined, % string() - Current local directory
type = ftp_server_default, % atom() - binary | ascii
@@ -83,6 +85,7 @@
%% and hence the end of the response is reached!
ctrl_data = {<<>>, [], start}, % {binary(), [bytes()], LineStatus}
%% pid() - Client pid (note not the same as "From")
+ latest_ctrl_response = "",
owner = undefined,
client = undefined, % "From" to be used in gen_server:reply/2
%% Function that activated a connection and maybe some
@@ -90,7 +93,8 @@
caller = undefined, % term()
ipfamily, % inet | inet6 | inet6fb4
progress = ignore, % ignore | pid()
- dtimeout = ?DATA_ACCEPT_TIMEOUT % non_neg_integer() | infinity
+ dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity
+ tls_upgrading_data_connection = false
}).
@@ -99,6 +103,8 @@
-type common_reason() :: 'econn' | 'eclosed' | term().
-type file_write_error_reason() :: term(). % See file:write for more info
+-define(DBG(F,A), 'n/a').
+%%-define(DBG(F,A), io:format(F,A)).
%%%=========================================================================
%%% API - CLIENT FUNCTIONS
@@ -154,8 +160,7 @@ open(Host, Opts) when is_list(Opts) ->
?fcrt("open", [{open_options, OpenOptions}]),
case start_link(StartOptions, []) of
{ok, Pid} ->
- ?fcrt("open - ok", [{pid, Pid}]),
- call(Pid, {open, ip_comm, OpenOptions}, plain);
+ do_open(Pid, OpenOptions, tls_options(Opts));
Error1 ->
?fcrt("open - error", [{error1, Error1}]),
Error1
@@ -166,7 +171,13 @@ open(Host, Opts) when is_list(Opts) ->
Error2
end.
-
+do_open(Pid, OpenOptions, TLSOpts) ->
+ case call(Pid, {open, ip_comm, OpenOptions}, plain) of
+ {ok, Pid} ->
+ maybe_tls_upgrade(Pid, TLSOpts);
+ Error ->
+ Error
+ end.
%%--------------------------------------------------------------------------
%% user(Pid, User, Pass, <Acc>) -> ok | {error, euser} | {error, econn}
%% | {error, eacct}
@@ -744,6 +755,18 @@ info(Pid) ->
call(Pid, info, list).
+%%--------------------------------------------------------------------------
+%% latest_ctrl_response(Pid) -> string()
+%% Pid = pid()
+%%
+%% Description: The latest received response from the server
+%%--------------------------------------------------------------------------
+
+-spec latest_ctrl_response(Pid :: pid()) -> string().
+
+latest_ctrl_response(Pid) ->
+ call(Pid, latest_ctrl_response, string).
+
%%%========================================================================
%%% Behavior callbacks
%%%========================================================================
@@ -905,6 +928,10 @@ open_options(Options) ->
{progress, ValidateProgress, false, ?PROGRESS_DEFAULT}],
validate_options(Options, ValidOptions, []).
+tls_options(Options) ->
+ %% Options will be validated by ssl application
+ proplists:get_value(tls, Options, undefined).
+
validate_options([], [], Acc) ->
?fcrt("validate_options -> done", [{acc, Acc}]),
{ok, lists:reverse(Acc)};
@@ -1021,8 +1048,8 @@ handle_call({_, info}, _, #state{verbose = Verbose,
ipfamily = IpFamily,
csock = Socket,
progress = Progress} = State) ->
- {ok, {_, LocalPort}} = inet:sockname(Socket),
- {ok, {Address, Port}} = inet:peername(Socket),
+ {ok, {_, LocalPort}} = sockname(Socket),
+ {ok, {Address, Port}} = peername(Socket),
Options = [{verbose, Verbose},
{ipfamily, IpFamily},
{mode, Mode},
@@ -1033,6 +1060,9 @@ handle_call({_, info}, _, #state{verbose = Verbose,
{progress, Progress}],
{reply, {ok, Options}, State};
+handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = State) ->
+ {reply, {ok,Resp}, State};
+
%% But everything else must come from the owner
handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid ->
{reply, {error, not_connection_owner}, State};
@@ -1091,6 +1121,11 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
{stop, normal, State2#state{client = undefined}}
end;
+handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) ->
+ send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{client = From, caller = open, tls_options = TLSOptions}};
+
handle_call({_, {user, User, Password}}, From,
#state{csock = CSock} = State) when (CSock =/= undefined) ->
handle_user(User, Password, "", State#state{client = From});
@@ -1196,8 +1231,8 @@ handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false}
handle_call({_, recv_chunk}, _, #state{chunk = false} = State) ->
{reply, {error, "ftp:recv_chunk_start/2 not called"}, State};
-handle_call({_, recv_chunk}, From, #state{chunk = true} = State) ->
- activate_data_connection(State),
+handle_call({_, recv_chunk}, From, #state{chunk = true} = State0) ->
+ State = activate_data_connection(State0),
{noreply, State#state{client = From, caller = recv_chunk}};
handle_call({_, {send, LocalFile, RemoteFile}}, From,
@@ -1299,71 +1334,77 @@ handle_info(timeout, State) ->
{noreply, State};
%%% Data socket messages %%%
-handle_info({tcp, Socket, Data},
- #state{dsock = Socket,
- caller = {recv_file, Fd}} = State) ->
+handle_info({Trpt, Socket, Data},
+ #state{dsock = {Trpt,Socket},
+ caller = {recv_file, Fd}} = State0) when Trpt==tcp;Trpt==ssl ->
+ ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
file_write(binary_to_list(Data), Fd),
- progress_report({binary, Data}, State),
- activate_data_connection(State),
+ progress_report({binary, Data}, State0),
+ State = activate_data_connection(State0),
{noreply, State};
-handle_info({tcp, Socket, Data}, #state{dsock = Socket, client = From,
+handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}, client = From,
caller = recv_chunk}
- = State) ->
+ = State) when Trpt==tcp;Trpt==ssl ->
+ ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]),
gen_server:reply(From, {ok, Data}),
{noreply, State#state{client = undefined, data = <<>>}};
-handle_info({tcp, Socket, Data}, #state{dsock = Socket} = State) ->
- activate_data_connection(State),
+handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when Trpt==tcp;Trpt==ssl ->
+ ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
+ State = activate_data_connection(State0),
{noreply, State#state{data = <<(State#state.data)/binary,
Data/binary>>}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket,
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
caller = {recv_file, Fd}}
- = State) ->
+ = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
file_close(Fd),
progress_report({transfer_size, 0}, State),
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined, data = <<>>}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket, client = From,
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, client = From,
caller = recv_chunk}
- = State) ->
+ = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
gen_server:reply(From, ok),
{noreply, State#state{dsock = undefined, client = undefined,
data = <<>>, caller = undefined,
chunk = false}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket, caller = recv_bin,
- data = Data} = State) ->
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin,
+ data = Data} = State)
+ when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined, data = <<>>,
caller = {recv_bin, Data}}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket, data = Data,
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data,
caller = {handle_dir_result, Dir}}
- = State) ->
+ = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined,
caller = {handle_dir_result, Dir, Data},
% data = <<?CR,?LF>>}};
data = <<>>}};
-
-handle_info({tcp_error, Socket, Reason}, #state{dsock = Socket,
- client = From} = State) ->
+
+handle_info({Err, Socket, Reason}, #state{dsock = {Trpt,Socket},
+ client = From} = State)
+ when {Err,Trpt}=={tcp_error,tcp} ; {Err,Trpt}=={ssl_error,ssl} ->
gen_server:reply(From, {error, Reason}),
close_data_connection(State),
{noreply, State#state{dsock = undefined, client = undefined,
data = <<>>, caller = undefined, chunk = false}};
%%% Ctrl socket messages %%%
-handle_info({tcp, Socket, Data}, #state{csock = Socket,
- verbose = Verbose,
- caller = Caller,
- client = From,
- ctrl_data = {CtrlData, AccLines,
- LineStatus}}
+handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
+ verbose = Verbose,
+ caller = Caller,
+ client = From,
+ ctrl_data = {CtrlData, AccLines,
+ LineStatus}}
= State) ->
+ ?DBG('--ctrl ~p ----> ~s~p~n',[Socket,<<CtrlData/binary, Data/binary>>,State]),
case ftp_response:parse_lines(<<CtrlData/binary, Data/binary>>,
AccLines, LineStatus) of
{ok, Lines, NextMsgData} ->
@@ -1374,27 +1415,32 @@ handle_info({tcp, Socket, Data}, #state{csock = Socket,
gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])),
{noreply, State#state{client = undefined,
caller = undefined,
+ latest_ctrl_response = Lines,
ctrl_data = {NextMsgData, [],
start}}};
_ ->
+ ?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]),
handle_ctrl_result(CtrlResult,
- State#state{ctrl_data =
- {NextMsgData, [], start}})
+ State#state{latest_ctrl_response = Lines,
+ ctrl_data =
+ {NextMsgData, [], start}})
end;
{continue, NewCtrlData} ->
+ ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
activate_ctrl_connection(State),
{noreply, State#state{ctrl_data = NewCtrlData}}
end;
-handle_info({tcp_closed, Socket}, #state{csock = Socket}) ->
- %% If the server closes the control channel it is
- %% the expected behavior that connection process terminates.
+%% If the server closes the control channel it is
+%% the expected behavior that connection process terminates.
+handle_info({Cls, Socket}, #state{csock = {Trpt, Socket}})
+ when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
exit(normal); %% User will get error message from terminate/2
-handle_info({tcp_error, Socket, Reason}, _) ->
+handle_info({Err, Socket, Reason}, _) when Err==tcp_error ; Err==ssl_error ->
Report =
- io_lib:format("tcp_error on socket: ~p for reason: ~p~n",
- [Socket, Reason]),
+ io_lib:format("~p on socket: ~p for reason: ~p~n",
+ [Err, Socket, Reason]),
error_logger:error_report(Report),
%% If tcp does not work the only option is to terminate,
%% this is the expected behavior under these circumstances.
@@ -1425,8 +1471,8 @@ handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) ->
%% so we do not want to crash, but we make a log entry as it is an
%% unwanted behaviour.)
handle_info(Info, State) ->
- Report = io_lib:format("ftp : ~p : Unexpected message: ~p\n",
- [self(), Info]),
+ Report = io_lib:format("ftp : ~p : Unexpected message: ~p~nState: ~p~n",
+ [self(), Info, State]),
error_logger:info_report(Report),
{noreply, State}.
@@ -1566,8 +1612,37 @@ handle_user_account(Acc, State) ->
%%--------------------------------------------------------------------------
%% handle_ctrl_result
%%--------------------------------------------------------------------------
-%%--------------------------------------------------------------------------
-%% Handling of control connection setup
+handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
+ tls_options = TLSOptions,
+ timeout = Timeout,
+ caller = open, client = From}
+ = State0) ->
+ ?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
+ case ssl:connect(Socket, TLSOptions, Timeout) of
+ {ok, TLSSocket} ->
+ State = State0#state{csock = {ssl,TLSSocket}},
+ send_ctrl_message(State, mk_cmd("PBSZ 0", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} };
+ {error, _} = Error ->
+ gen_server:reply(From, {Error, self()}),
+ {stop, normal, State0#state{client = undefined,
+ caller = undefined,
+ tls_upgrading_data_connection = false}}
+ end;
+
+handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) ->
+ send_ctrl_message(State, mk_cmd("PROT P", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{tls_upgrading_data_connection = {true, prot}}};
+
+handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot},
+ client = From} = State) ->
+ gen_server:reply(From, {ok, self()}),
+ {noreply, State#state{client = undefined,
+ caller = undefined,
+ tls_upgrading_data_connection = false}};
+
handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From}
= State) ->
gen_server:reply(From, {ok, self()}),
@@ -1601,10 +1676,10 @@ handle_ctrl_result({pos_compl, Lines},
timeout = Timeout}
= State) ->
[_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
- {ok, {IP, _}} = inet:peername(CSock),
+ {ok, {IP, _}} = peername(CSock),
case connect(IP, list_to_integer(PortStr), Timeout, State) of
{ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = Socket});
+ handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
{error, _Reason} = Error ->
gen_server:reply(From, Error),
{noreply, State#state{client = undefined, caller = undefined}}
@@ -1614,7 +1689,7 @@ handle_ctrl_result({pos_compl, Lines},
#state{mode = passive,
ipfamily = inet,
client = From,
- caller = {setup_data_connection, Caller},
+ caller = {setup_data_connection, Caller},
timeout = Timeout} = State) ->
{_, [?LEFT_PAREN | Rest]} =
@@ -1626,9 +1701,11 @@ handle_ctrl_result({pos_compl, Lines},
string:tokens(NewPortAddr, [$,])),
IP = {A1, A2, A3, A4},
Port = (P1 * 256) + P2,
+
+ ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]),
case connect(IP, Port, Timeout, State) of
- {ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = Socket});
+ {ok, _, Socket} ->
+ handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}});
{error, _Reason} = Error ->
gen_server:reply(From, Error),
{noreply,State#state{client = undefined, caller = undefined}}
@@ -1669,18 +1746,18 @@ handle_ctrl_result({pos_compl, Lines},
%%--------------------------------------------------------------------------
%% Directory listing
-handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- activate_data_connection(NewState),
- {noreply, NewState#state{caller = {handle_dir_result, Dir}}};
+handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = activate_data_connection(State1),
+ {noreply, State#state{caller = {handle_dir_result, Dir}}};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1778,18 +1855,18 @@ handle_ctrl_result({Status, _},
%%--------------------------------------------------------------------------
%% File handling - recv_bin
-handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- activate_data_connection(NewState),
- {noreply, NewState};
+handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = activate_data_connection(State1),
+ {noreply, State};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1812,36 +1889,35 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_bin, _}} = State) ->
%% File handling - start_chunk_transfer
handle_ctrl_result({pos_prel, _}, #state{client = From,
caller = start_chunk_transfer}
- = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- gen_server:reply(From, ok),
- {noreply, NewState#state{chunk = true, client = undefined,
- caller = undefined}};
+ = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = start_chunk(State1),
+ {noreply, State};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
%%--------------------------------------------------------------------------
%% File handling - recv_file
-handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- activate_data_connection(NewState),
- {noreply, NewState};
+handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = activate_data_connection(State1),
+ {noreply, State};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1853,36 +1929,32 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) ->
%%--------------------------------------------------------------------------
%% File handling - transfer_*
handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_file, Fd}}
- = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- send_file(Fd, NewState);
+ = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ send_file(State1, Fd);
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}}
- = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- send_data_message(NewState, Bin),
- close_data_connection(NewState),
- activate_ctrl_connection(NewState),
- {noreply, NewState#state{caller = transfer_data_second_phase,
- dsock = undefined}};
+ = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State} ->
+ send_bin(State, Bin);
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1975,7 +2047,7 @@ setup_ctrl_connection(Host, Port, Timeout, State) ->
MsTime = millisec_time(),
case connect(Host, Port, Timeout, State) of
{ok, IpFam, CSock} ->
- NewState = State#state{csock = CSock, ipfamily = IpFam},
+ NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam},
activate_ctrl_connection(NewState),
case Timeout - (millisec_time() - MsTime) of
Timeout2 when (Timeout2 >= 0) ->
@@ -1991,12 +2063,12 @@ setup_ctrl_connection(Host, Port, Timeout, State) ->
setup_data_connection(#state{mode = active,
caller = Caller,
csock = CSock} = State) ->
- case (catch inet:sockname(CSock)) of
+ case (catch sockname(CSock)) of
{ok, {{_, _, _, _, _, _, _, _} = IP, _}} ->
{ok, LSock} =
gen_tcp:listen(0, [{ip, IP}, {active, false},
inet6, binary, {packet, 0}]),
- {ok, Port} = inet:port(LSock),
+ {ok, {_, Port}} = sockname(LSock),
IpAddress = inet_parse:ntoa(IP),
Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]),
send_ctrl_message(State, Cmd),
@@ -2029,20 +2101,6 @@ setup_data_connection(#state{mode = passive, ipfamily = inet,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection, Caller}}}.
-
-%% setup_data_connection(#state{mode = passive, ip_v6_disabled = false,
-%% caller = Caller} = State) ->
-%% send_ctrl_message(State, mk_cmd("EPSV", [])),
-%% activate_ctrl_connection(State),
-%% {noreply, State#state{caller = {setup_data_connection, Caller}}};
-
-%% setup_data_connection(#state{mode = passive, ip_v6_disabled = true,
-%% caller = Caller} = State) ->
-%% send_ctrl_message(State, mk_cmd("PASV", [])),
-%% activate_ctrl_connection(State),
-%% {noreply, State#state{caller = {setup_data_connection, Caller}}}.
-
-
connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) ->
connect2(Host, Port, IpFam, Timeout);
@@ -2088,75 +2146,101 @@ connect2(Host, Port, IpFam, Timeout) ->
accept_data_connection(#state{mode = active,
dtimeout = DTimeout,
- dsock = {lsock, LSock}} = State) ->
+ tls_options = TLSOptions,
+ dsock = {lsock, LSock}} = State0) ->
case gen_tcp:accept(LSock, DTimeout) of
+ {ok, Socket} when is_list(TLSOptions) ->
+ gen_tcp:close(LSock),
+ ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
+ case ssl:connect(Socket, TLSOptions, DTimeout) of
+ {ok, TLSSocket} ->
+ {ok, State0#state{dsock={ssl,TLSSocket}}};
+ {error, Reason} ->
+ {error, {ssl_connect_failed, Reason}}
+ end;
{ok, Socket} ->
gen_tcp:close(LSock),
- {ok, State#state{dsock = Socket}};
+ {ok, State0#state{dsock={tcp,Socket}}};
{error, Reason} ->
{error, {data_connect_failed, Reason}}
end;
+accept_data_connection(#state{mode = passive,
+ dtimeout = DTimeout,
+ dsock = {tcp,Socket},
+ tls_options = TLSOptions} = State) when is_list(TLSOptions) ->
+ ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]),
+ case ssl:connect(Socket, TLSOptions, DTimeout) of
+ {ok, TLSSocket} ->
+ {ok, State#state{dsock={ssl,TLSSocket}}};
+ {error, Reason} ->
+ {error, {ssl_connect_failed, Reason}}
+ end;
accept_data_connection(#state{mode = passive} = State) ->
- {ok, State}.
+ {ok,State}.
+
-send_ctrl_message(#state{csock = Socket, verbose = Verbose}, Message) ->
- %% io:format("send control message: ~n~p~n", [lists:flatten(Message)]),
+send_ctrl_message(_S=#state{csock = Socket, verbose = Verbose}, Message) ->
verbose(lists:flatten(Message),Verbose,send),
+ ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,_S]),
send_message(Socket, Message).
-send_data_message(#state{dsock = Socket}, Message) ->
- send_message(Socket, Message).
-
-send_message(Socket, Message) ->
- case gen_tcp:send(Socket, Message) of
+send_data_message(_S=#state{dsock = Socket}, Message) ->
+ ?DBG('<==data ~p ==== ~s~n~p~n',[Socket,Message,_S]),
+ case send_message(Socket, Message) of
ok ->
ok;
{error, Reason} ->
- Report = io_lib:format("gen_tcp:send/2 failed for "
- "reason ~p~n", [Reason]),
+ Report = io_lib:format("send/2 for socket ~p failed with "
+ "reason ~p~n", [Socket, Reason]),
error_logger:error_report(Report),
- %% If tcp does not work the only option is to terminate,
+ %% If tcp/ssl does not work the only option is to terminate,
%% this is the expected behavior under these circumstances.
exit(normal) %% User will get error message from terminate/2
end.
+send_message({tcp, Socket}, Message) ->
+ gen_tcp:send(Socket, Message);
+send_message({ssl, Socket}, Message) ->
+ ssl:send(Socket, Message).
+
activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) ->
activate_connection(Socket);
activate_ctrl_connection(#state{csock = Socket}) ->
%% We have already received at least part of the next control message,
%% that has been saved in ctrl_data, process this first.
- self() ! {tcp, Socket, <<>>}.
+ self() ! {tcp, unwrap_socket(Socket), <<>>}.
-activate_data_connection(#state{dsock = Socket}) ->
- activate_connection(Socket).
+unwrap_socket({tcp,Socket}) -> Socket;
+unwrap_socket({ssl,Socket}) -> Socket;
+unwrap_socket(Socket) -> Socket.
+
-activate_connection(Socket) ->
- inet:setopts(Socket, [{active, once}]).
+activate_data_connection(#state{dsock = Socket} = State) ->
+ activate_connection(Socket),
+ State.
-close_ctrl_connection(#state{csock = undefined}) ->
- ok;
-close_ctrl_connection(#state{csock = Socket}) ->
- close_connection(Socket).
+activate_connection({tcp, Socket}) -> inet:setopts(Socket, [{active, once}]);
+activate_connection({ssl, Socket}) -> ssl:setopts(Socket, [{active, once}]).
-close_data_connection(#state{dsock = undefined}) ->
- ok;
-close_data_connection(#state{dsock = {lsock, Socket}}) ->
- close_connection(Socket);
-close_data_connection(#state{dsock = Socket}) ->
- close_connection(Socket).
+close_ctrl_connection(#state{csock = undefined}) -> ok;
+close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket).
-close_connection(Socket) ->
- gen_tcp:close(Socket).
+close_data_connection(#state{dsock = undefined}) -> ok;
+close_data_connection(#state{dsock = Socket}) -> close_connection(Socket).
-%% ------------ FILE HANDELING ----------------------------------------
+close_connection({tcp, Socket}) -> gen_tcp:close(Socket);
+close_connection({ssl, Socket}) -> ssl:close(Socket).
-send_file(Fd, State) ->
+%% ------------ FILE HANDELING ----------------------------------------
+send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) ->
+ {noreply, State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_file, Fd}}};
+send_file(State, Fd) ->
case file_read(Fd) of
{ok, N, Bin} when N > 0->
send_data_message(State, Bin),
progress_report({binary, Bin}, State),
- send_file(Fd, State);
+ send_file(State, Fd);
{ok, _, _} ->
file_close(Fd),
close_data_connection(State),
@@ -2206,6 +2290,15 @@ call(GenServer, Msg, Format, Timeout) ->
cast(GenServer, Msg) ->
gen_server:cast(GenServer, {self(), Msg}).
+send_bin(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Bin) ->
+ State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_bin, Bin}};
+send_bin(State, Bin) ->
+ send_data_message(State, Bin),
+ close_data_connection(State),
+ activate_ctrl_connection(State),
+ {noreply, State#state{caller = transfer_data_second_phase,
+ dsock = undefined}}.
+
mk_cmd(Fmt, Args) ->
[io_lib:format(Fmt, Args)| [?CR, ?LF]]. % Deep list ok.
@@ -2216,20 +2309,6 @@ pwd_result(Lines) ->
lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Rest),
Dir.
-%% is_verbose(Params) ->
-%% check_param(verbose, Params).
-
-%% is_debug(Flags) ->
-%% check_param(debug, Flags).
-
-%% is_trace(Flags) ->
-%% check_param(trace, Flags).
-
-%% is_ipv6_disabled(Flags) ->
-%% check_param(ip_v6_disabled, Flags).
-
-%% check_param(Param, Params) ->
-%% lists:member(Param, Params).
key_search(Key, List, Default) ->
case lists:keysearch(Key, 1, List) of
@@ -2239,14 +2318,6 @@ key_search(Key, List, Default) ->
Default
end.
-%% check_option(Pred, Value, Default) ->
-%% case Pred(Value) of
-%% true ->
-%% Value;
-%% false ->
-%% Default
-%% end.
-
verbose(Lines, true, Direction) ->
DirStr =
case Direction of
@@ -2276,3 +2347,23 @@ progress_report(Report, #state{progress = ProgressPid}) ->
millisec_time() ->
{A,B,C} = erlang:now(),
A*1000000000+B*1000+(C div 1000).
+
+peername({tcp, Socket}) -> inet:peername(Socket);
+peername({ssl, Socket}) -> ssl:peername(Socket).
+
+sockname({tcp, Socket}) -> inet:peername(Socket);
+sockname({ssl, Socket}) -> ssl:peername(Socket).
+
+maybe_tls_upgrade(Pid, undefined) ->
+ {ok, Pid};
+maybe_tls_upgrade(Pid, TLSOptions) ->
+ catch ssl:start(),
+ call(Pid, {open, tls_upgrade, TLSOptions}, plain).
+
+start_chunk(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) ->
+ State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, start_chunk, undefined}};
+start_chunk(#state{client = From} = State) ->
+ gen_server:reply(From, ok),
+ State#state{chunk = true,
+ client = undefined,
+ caller = undefined}.
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl
index 4bf788e946..dfe180ff18 100644
--- a/lib/inets/src/ftp/ftp_response.erl
+++ b/lib/inets/src/ftp/ftp_response.erl
@@ -175,6 +175,8 @@ error_string(Reason) ->
%% Positive Preleminary Reply
interpret_status(?POS_PREL,_,_) -> pos_prel;
+%%FIXME ??? 3??? interpret_status(?POS_COMPL, ?AUTH_ACC, 3) -> tls_upgrade;
+interpret_status(?POS_COMPL, ?AUTH_ACC, 4) -> tls_upgrade;
%% Positive Completion Reply
interpret_status(?POS_COMPL,_,_) -> pos_compl;
%% Positive Intermediate Reply nedd account
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 2f2f6ec16e..73070ac57e 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -152,21 +152,6 @@ MODULES = \
erl_make_certs \
ftp_SUITE \
ftp_format_SUITE \
- ftp_solaris8_sparc_test \
- ftp_solaris9_sparc_test \
- ftp_solaris10_sparc_test \
- ftp_solaris10_x86_test \
- ftp_linux_x86_test \
- ftp_linux_ppc_test \
- ftp_macosx_x86_test \
- ftp_macosx_ppc_test \
- ftp_openbsd_x86_test \
- ftp_freebsd_x86_test \
- ftp_netbsd_x86_test \
- ftp_windows_xp_test \
- ftp_windows_2003_server_test \
- ftp_suite_lib \
- ftp_ticket_test \
http_format_SUITE \
httpc_SUITE \
httpc_cookie_SUITE \
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl
index 17e5f6777e..e39f9f1eb6 100644
--- a/lib/inets/test/ftp_SUITE.erl
+++ b/lib/inets/test/ftp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,110 +18,803 @@
%%
%%
+%%
+%% ct:run("../inets_test", ftp_SUITE).
+%%
+
-module(ftp_SUITE).
+-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
+-include("inets_test_lib.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-% -export([init_per_testcase/2, end_per_testcase/2]).
--export([init_per_suite/1, end_per_suite/1]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-define(FTP_USER, "anonymous").
--define(FTP_PASS, passwd()).
--define(FTP_PORT, 21).
+-define(FTP_PASS(Cmnt), (fun({ok,__H}) -> "ftp_SUITE_"++Cmnt++"@" ++ __H;
+ (_) -> "ftp_SUITE_"++Cmnt++"@localhost"
+ end)(inet:gethostname())
+ ).
-define(BAD_HOST, "badhostname").
-define(BAD_USER, "baduser").
-define(BAD_DIR, "baddirectory").
--ifdef(ftp_debug_client).
--define(ftp_open(Host, Flags), do_ftp_open(Host, [debug] ++ Flags)).
--else.
--ifdef(ftp_trace_client).
--define(ftp_open(Host, Flags), do_ftp_open(Host, [trace] ++ Flags)).
--else.
--define(ftp_open(Host, Flags), do_ftp_open(Host, [verbose] ++ Flags)).
--endif.
--endif.
-
+go() -> ct:run_test([{suite,"ftp_SUITE"}, {logdir,"LOG"}]).
+gos() -> ct:run_test([{suite,"ftp_SUITE"}, {group,ftps_passive}, {logdir,"LOG"}]).
%%--------------------------------------------------------------------
-%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
-%% Arg - doc | suite
-%% Doc - string()
-%% Case - atom()
-%% Name of a test case function.
-%% Comment - string()
-%% Description: Returns documentation/test cases in this test suite
-%% or a skip tuple if the platform is not supported.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() -> [{ct_hooks, [ts_install_cth]}].
+all() ->
+ [
+ {group, ftp_passive},
+ {group, ftp_active},
+ {group, ftps_passive},
+ {group, ftps_active}
+ ].
-all() ->
+groups() ->
[
- {group, solaris8_test},
- {group, solaris9_test},
- {group, solaris10_test},
- {group, linux_x86_test},
- {group, linux_ppc_test},
- {group, macosx_x86_test},
- {group, macosx_ppc_test},
- {group, openbsd_test},
- {group, freebsd_test},
- {group, netbsd_test},
- {group, windows_xp_test},
- {group, windows_2003_server_test},
- {group, ticket_tests}
+ {ftp_passive, [], ftp_tests()},
+ {ftp_active, [], ftp_tests()},
+ {ftps_passive, [], ftp_tests()},
+ {ftps_active, [], ftp_tests()}
].
-groups() ->
+ftp_tests()->
[
- {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]},
- {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]},
- {solaris10_test, [], [{ftp_solaris10_sparc_test, all},
- {ftp_solaris10_x86_test, all}]},
- {linux_x86_test, [], [{ftp_linux_x86_test, all}]},
- {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]},
- {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]},
- {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]},
- {openbsd_test, [], [{ftp_openbsd_x86_test, all}]},
- {freebsd_test, [], [{ftp_freebsd_x86_test, all}]},
- {netbsd_test, [], [{ftp_netbsd_x86_test, all}]},
- {windows_xp_test, [], [{ftp_windows_xp_test, all}]},
- {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]},
- {ticket_tests, [], [{ftp_ticket_test, all}]}
+ user,
+ bad_user,
+ pwd,
+ cd,
+ lcd,
+ ls,
+ nlist,
+ rename,
+ delete,
+ mkdir,
+ rmdir,
+ send,
+ send_3,
+ send_bin,
+ send_chunk,
+ append,
+ append_bin,
+ append_chunk,
+ recv,
+ recv_3,
+ recv_bin,
+ recv_chunk,
+ type,
+ quote,
+ ip_v6_disabled
].
-init_per_group(_GroupName, Config) ->
- Config.
+%%--------------------------------------------------------------------
+
+%%% Config
+%%% key meaning
+%%% ................................................................
+%%% ftpservers list of servers to check if they are available
+%%% The element is:
+%%% {Name, % string(). The os command name
+%%% StartCommand, % fun()->{ok,start_result()} | {error,string()}.
+%%% % The command to start the daemon with.
+%%% ChkUp, % fun(start_result()) -> string(). Os command to check
+%%% % if the server is running. [] if not running.
+%%% % The string in string() is suitable for logging.
+%%% StopCommand, % fun(start_result()) -> void(). The command to stop the daemon with.
+%%% AugmentFun, % fun(config()) -> config() Adds two funs for transforming names of files
+%%% % and directories to the form they are returned from this server
+%%% ServerHost, % string(). Mostly "localhost"
+%%% ServerPort % pos_integer()
+%%% }
+%%%
+
+-define(default_ftp_servers,
+ [{"vsftpd",
+ fun(__CONF__) ->
+ DataDir = ?config(data_dir,__CONF__),
+ ConfFile = filename:join(DataDir, "vsftpd.conf"),
+ PrivDir = ?config(priv_dir,__CONF__),
+ AnonRoot = PrivDir,
+ Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"),
+ " -oftpd_banner=erlang_otp_testing",
+ " -oanon_root=\"",AnonRoot,"\"",
+ " -orsa_cert_file=\"",filename:join(DataDir,"server-cert.pem"),"\"",
+ " -orsa_private_key_file=\"",filename:join(DataDir,"server-key.pem"),"\""
+ ],
+ Result = os:cmd(Cmd),
+ ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p",
+ [case file:read_file(ConfFile) of
+ {ok,X} -> X;
+ _ -> ""
+ end,
+ Cmd, Result
+ ]),
+ case Result of
+ [] -> {ok,'dont care'};
+ [Msg] -> {error,Msg}
+ end
+ end,
+ fun(_StartResult) -> os:cmd("ps ax | grep erlang_otp_testing | grep -v grep")
+ end,
+ fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`")
+ end,
+ fun(__CONF__) ->
+ AnonRoot = ?config(priv_dir,__CONF__),
+ [{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end},
+ {id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__]
+ end,
+ "localhost",
+ 9999
+ }
+ ]
+ ).
-end_per_group(_GroupName, Config) ->
- Config.
+init_per_suite(Config) ->
+ case find_executable(Config) of
+ false ->
+ {skip, "No ftp server found"};
+ {ok,Data} ->
+ TstDir = filename:join(?config(priv_dir,Config), "test"),
+ file:make_dir(TstDir),
+ make_cert_files(dsa, rsa, "server-", ?config(data_dir,Config)),
+ start_ftpd([{test_dir,TstDir},
+ {ftpd_data,Data}
+ | Config])
+ end.
+
+end_per_suite(Config) ->
+ ps_ftpd(Config),
+ stop_ftpd(Config),
+ ps_ftpd(Config),
+ ok.
+
+%%--------------------------------------------------------------------
+init_per_group(_Group, Config) -> Config.
+
+end_per_group(_Group, Config) -> Config.
+%%--------------------------------------------------------------------
+init_per_testcase(Case, Config0) ->
+ Group = proplists:get_value(name,?config(tc_group_properties,Config0)),
+ try ?MODULE:Case(doc) of
+ Msg -> ct:comment(Msg)
+ catch
+ _:_-> ok
+ end,
+ TLS = [{tls,[{reuse_sessions,true}]}],
+ ACTIVE = [{mode,active}],
+ PASSIVE = [{mode,passive}],
+ ExtraOpts = [verbose],
+ Config =
+ case Group of
+ ftp_active -> ftp__open(Config0, ACTIVE ++ExtraOpts);
+ ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ExtraOpts);
+ ftp_passive -> ftp__open(Config0, PASSIVE ++ExtraOpts);
+ ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ExtraOpts)
+ end,
+ case Case of
+ user -> Config;
+ bad_user -> Config;
+ _ ->
+ Pid = ?config(ftp,Config),
+ ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ),
+ ok = ftp:cd(Pid, ?config(priv_dir,Config)),
+ Config
+ end.
+
+end_per_testcase(user, _Config) -> ok;
+end_per_testcase(bad_user, _Config) -> ok;
+end_per_testcase(_Case, Config) ->
+ case ?config(tc_status,Config) of
+ ok -> ok;
+ _ ->
+ try ftp:latest_ctrl_response(?config(ftp,Config))
+ of
+ {ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S])
+ catch
+ _:_ -> ok
+ end
+ end,
+ ftp__close(Config).
%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+user(doc) -> ["Open an ftp connection to a host, and logon as anonymous ftp, then logoff"];
+user(Config) ->
+ Pid = ?config(ftp, Config),
+ ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon
+ ok = ftp:close(Pid), % logoff
+ {error,eclosed} = ftp:pwd(Pid), % check logoff result
+ ok.
+
+%%-------------------------------------------------------------------------
+bad_user(doc) -> ["Open an ftp connection to a host, and logon with bad user."];
+bad_user(Config) ->
+ Pid = ?config(ftp, Config),
+ {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")),
+ ok.
+
+%%-------------------------------------------------------------------------
+pwd(doc) -> ["Test ftp:pwd/1 & ftp:lpwd/1"];
+pwd(Config0) ->
+ Config = set_state([reset], Config0),
+ Pid = ?config(ftp, Config),
+ {ok, PWD} = ftp:pwd(Pid),
+ {ok, PathLpwd} = ftp:lpwd(Pid),
+ PWD = id2ftp_result("", Config),
+ PathLpwd = id2ftp_result("", Config).
+
+%%-------------------------------------------------------------------------
+cd(doc) -> ["Open an ftp connection, log on as anonymous ftp, and cd to a"
+ "directory and to a non-existent directory."];
+cd(Config0) ->
+ Dir = "test",
+ Config = set_state([reset,{mkdir,Dir}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:cd(Pid, id2ftp(Dir,Config)),
+ {ok, PWD} = ftp:pwd(Pid),
+ ExpectedPWD = id2ftp_result(Dir, Config),
+ PWD = ExpectedPWD,
+ {error, epath} = ftp:cd(Pid, ?BAD_DIR).
+
+%%-------------------------------------------------------------------------
+lcd(doc) ->
+ ["Test api function ftp:lcd/2"];
+lcd(Config0) ->
+ Dir = "test",
+ Config = set_state([reset,{mkdir,Dir}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:lcd(Pid, id2ftp(Dir,Config)),
+ {ok, PWD} = ftp:lpwd(Pid),
+ ExpectedPWD = id2ftp_result(Dir, Config),
+ PWD = ExpectedPWD,
+ {error, epath} = ftp:lcd(Pid, ?BAD_DIR).
+
+%%-------------------------------------------------------------------------
+ls(doc) -> ["Open an ftp connection; ls the current directory, and the "
+ "\"test\" directory. We assume that ls never fails, since "
+ "it's output is meant to be read by humans. "];
+ls(Config0) ->
+ Config = set_state([reset,{mkdir,"test"}], Config0),
+ Pid = ?config(ftp, Config),
+ {ok, _R1} = ftp:ls(Pid),
+ {ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)),
+ %% neither nlist nor ls operates on a directory
+ %% they operate on a pathname, which *can* be a
+ %% directory, but can also be a filename or a group
+ %% of files (including wildcards).
+ case ?config(wildcard_support, Config) of
+ true ->
+ {ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config));
+ _ ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+nlist(doc) -> ["Open an ftp connection; nlist the current directory, and the "
+ "\"test\" directory. Nlist does not behave consistenly over "
+ "operating systems. On some it is an error to have an empty "
+ "directory."];
+nlist(Config0) ->
+ Config = set_state([reset,{mkdir,"test"}], Config0),
+ Pid = ?config(ftp, Config),
+ {ok, _R1} = ftp:nlist(Pid),
+ {ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)),
+ %% neither nlist nor ls operates on a directory
+ %% they operate on a pathname, which *can* be a
+ %% directory, but can also be a filename or a group
+ %% of files (including wildcards).
+ case ?config(wildcard_support, Config) of
+ true ->
+ {ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config));
+ _ ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+rename(doc) -> ["Rename a file."];
+rename(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ OldFile = "old.txt",
+ NewFile = "new.txt",
+ Config = set_state([reset,{mkfile,OldFile,Contents}], Config0),
+ Pid = ?config(ftp, Config),
+
+ ok = ftp:rename(Pid,
+ id2ftp(OldFile,Config),
+ id2ftp(NewFile,Config)),
+
+ true = (chk_file(NewFile,Contents,Config)
+ and chk_no_file([OldFile],Config)).
+
+
+%%-------------------------------------------------------------------------
+send(doc) -> ["Transfer a file with ftp using send/2."];
+send(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ SrcDir = "data",
+ File = "file.txt",
+ Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0),
+ Pid = ?config(ftp, Config),
+
+chk_no_file([File],Config),
+chk_file([SrcDir,File],Contents,Config),
+
+ ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)),
+ ok = ftp:cd(Pid, id2ftp("",Config)),
+ ok = ftp:send(Pid, File),
+
+ chk_file(File, Contents, Config).
+
+%%-------------------------------------------------------------------------
+send_3(doc) -> ["Transfer a file with ftp using send/3."];
+send_3(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ Dir = "incoming",
+ File = "file.txt",
+ RemoteFile = "remfile.txt",
+ Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0),
+ Pid = ?config(ftp, Config),
+
+ ok = ftp:cd(Pid, id2ftp(Dir,Config)),
+ ok = ftp:lcd(Pid, id2ftp("",Config)),
+ ok = ftp:send(Pid, File, RemoteFile),
+
+ chk_file([Dir,RemoteFile], Contents, Config).
+
+%%-------------------------------------------------------------------------
+send_bin(doc) -> ["Send a binary."];
+send_bin(Config0) ->
+ BinContents = <<"ftp_SUITE test ...">>,
+ File = "file.txt",
+ Config = set_state([reset], Config0),
+ Pid = ?config(ftp, Config),
+ {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)),
+ ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)),
+ chk_file(File, BinContents, Config).
+
+%%-------------------------------------------------------------------------
+send_chunk(doc) -> ["Send a binary using chunks."];
+send_chunk(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ File = "file.txt",
+ Config = set_state([reset,{mkdir,"incoming"}], Config0),
+ Pid = ?config(ftp, Config),
+
+ ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)),
+ {error, echunk} = ftp:cd(Pid, "incoming"),
+ {error, enotbinary} = ftp:send_chunk(Pid, "some string"),
+ ok = ftp:send_chunk(Pid, Contents),
+ ok = ftp:send_chunk(Pid, Contents),
+ ok = ftp:send_chunk_end(Pid),
+ chk_file(File, <<Contents/binary,Contents/binary>>, Config).
+
+%%-------------------------------------------------------------------------
+delete(doc) -> ["Delete a file."];
+delete(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ File = "file.txt",
+ Config = set_state([reset,{mkfile,File,Contents}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:delete(Pid, id2ftp(File,Config)),
+ chk_no_file([File], Config).
+
+%%-------------------------------------------------------------------------
+mkdir(doc) -> ["Make a remote directory."];
+mkdir(Config0) ->
+ NewDir = "new_dir",
+ Config = set_state([reset], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)),
+ chk_dir([NewDir], Config).
+
+%%-------------------------------------------------------------------------
+rmdir(doc) -> ["Remove a directory."];
+rmdir(Config0) ->
+ Dir = "dir",
+ Config = set_state([reset,{mkdir,Dir}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:rmdir(Pid, id2ftp(Dir,Config)),
+ chk_no_dir([Dir], Config).
+
+%%-------------------------------------------------------------------------
+append(doc) -> ["Append a local file twice to a remote file"];
+append(Config0) ->
+ SrcFile = "f_src.txt",
+ DstFile = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
+ ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
+ chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config).
+
+%%-------------------------------------------------------------------------
+append_bin(doc) -> ["Append a local file twice to a remote file using append_bin"];
+append_bin(Config0) ->
+ DstFile = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
+ ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
+ chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config).
+
+%%-------------------------------------------------------------------------
+append_chunk(doc) -> ["Append chunks."];
+append_chunk(Config0) ->
+ File = "f_dst.txt",
+ Contents = [<<"ER">>,<<"LE">>,<<"RL">>],
+ Config = set_state([reset], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)),
+ {error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))),
+ ok = ftp:append_chunk(Pid,lists:nth(1,Contents)),
+ ok = ftp:append_chunk(Pid,lists:nth(2,Contents)),
+ ok = ftp:append_chunk(Pid,lists:nth(3,Contents)),
+ ok = ftp:append_chunk_end(Pid),
+ chk_file(File, <<"ERLERL">>, Config).
+
+%%-------------------------------------------------------------------------
+recv(doc) -> ["Receive a file using recv/2"];
+recv(Config0) ->
+ File = "f_dst.txt",
+ SrcDir = "a_dir",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,[SrcDir,File],Contents}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:cd(Pid, id2ftp(SrcDir,Config)),
+ ok = ftp:lcd(Pid, id2ftp("",Config)),
+ ok = ftp:recv(Pid, File),
+ chk_file(File, Contents, Config).
+
+%%-------------------------------------------------------------------------
+recv_3(doc) -> ["Receive a file using recv/3"];
+recv_3(Config0) ->
+ DstFile = "f_src.txt",
+ SrcFile = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0),
+ Pid = ?config(ftp, Config),
+ ok = ftp:cd(Pid, id2ftp("",Config)),
+ ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)),
+ chk_file(DstFile, Contents, Config).
+
+%%-------------------------------------------------------------------------
+recv_bin(doc) -> ["Receive a file as a binary."];
+recv_bin(Config0) ->
+ File = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,File,Contents}], Config0),
+ Pid = ?config(ftp, Config),
+ {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)),
+ find_diff(Received, Contents).
+
+%%-------------------------------------------------------------------------
+recv_chunk(doc) -> ["Receive a file using chunk-wise."];
+recv_chunk(Config0) ->
+ File = "big_file.txt",
+ Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
+ Config = set_state([reset, {mkfile,File,Contents}], Config0),
+ Pid = ?config(ftp, Config),
+ {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)),
+ {ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>),
+ find_diff(ReceivedContents, Contents).
+
+recv_chunk(Pid, Acc) -> recv_chunk(Pid, Acc, 0).
+
+recv_chunk(Pid, Acc, N) ->
+ case ftp:recv_chunk(Pid) of
+ ok -> {ok, Acc, N};
+ {ok, Bin} -> recv_chunk(Pid, <<Acc/binary, Bin/binary>>, N+1);
+ Error -> {Error, N}
+ end.
+
+%%-------------------------------------------------------------------------
+type(doc) -> ["Test that we can change btween ASCCI and binary transfer mode"];
+type(Config) ->
+ Pid = ?config(ftp, Config),
+ ok = ftp:type(Pid, ascii),
+ ok = ftp:type(Pid, binary),
+ ok = ftp:type(Pid, ascii),
+ {error, etype} = ftp:type(Pid, foobar).
+
+%%-------------------------------------------------------------------------
+quote(doc) -> [""];
+quote(Config) ->
+ Pid = ?config(ftp, Config),
+ ["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
+ [_| _] = ftp:quote(Pid, "help"),
+ %% This negativ test causes some ftp servers to hang. This test
+ %% is not important for the client, so we skip it for now.
+ %%["425 Can't build data connection: Connection refused."]
+ %% = ftp:quote(Pid, "list"),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+ip_v6_disabled(doc) -> ["Test ipv4 command PORT"];
+ip_v6_disabled(_Config) ->
+ %%% FIXME!!!! What is this???
+ ok.%% send(Config).
+
+%%-------------------------------------------------------------------------
+%% big_one(doc) ->
+%% ["Create a local file and transfer it to the remote host into the "
+%% "the \"incoming\" directory, remove "
+%% "the local file. Then open a new connection; cd to \"incoming\", "
+%% "lcd to the private directory; receive the file; delete the "
+%% "remote file; close connection; check that received file is in "
+%% "the correct directory; cleanup." ];
+%% big_one(Config) ->
+%% Pid = ?config(ftp, Config),
+%% do_recv(Pid, Config).
+
+%% do_recv(Pid, Config) ->
+%% PrivDir = ?config(priv_dir, Config),
+%% File = ?config(file, Config),
+%% Newfile = ?config(new_file, Config),
+%% AbsFile = filename:absname(File, PrivDir),
+%% Contents = "ftp_SUITE:recv test ...",
+%% ok = file:write_file(AbsFile, list_to_binary(Contents)),
+%% ok = ftp:cd(Pid, "incoming"),
+%% ftp:delete(Pid, File), % reset
+%% ftp:lcd(Pid, PrivDir),
+%% ok = ftp:send(Pid, File),
+%% ok = file:delete(AbsFile), % cleanup
+%% test_server:sleep(100),
+%% ok = ftp:lcd(Pid, PrivDir),
+%% ok = ftp:recv(Pid, File),
+%% {ok, Files} = file:list_dir(PrivDir),
+%% true = lists:member(File, Files),
+%% ok = file:delete(AbsFile), % cleanup
+%% ok = ftp:recv(Pid, File, Newfile),
+%% ok = ftp:delete(Pid, File), % cleanup
+%% ok.
+
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+
+make_cert_files(Alg1, Alg2, Prefix, Dir) ->
+ CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg1}]),
+ {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg2},{issuer,CaInfo}]),
+ CaCertFile = filename:join(Dir, Prefix++"cacerts.pem"),
+ CertFile = filename:join(Dir, Prefix++"cert.pem"),
+ KeyFile = filename:join(Dir, Prefix++"key.pem"),
+ der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
+ der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
+ der_to_pem(KeyFile, [CertKey]),
+ ok.
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
+
+%%--------------------------------------------------------------------
+chk_file(Path=[C|_], ExpectedContents, Config) when 0<C,C=<255 ->
+ chk_file([Path], ExpectedContents, Config);
+
+chk_file(PathList, ExpectedContents, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file(AbsPath) of
+ {ok,ExpectedContents} ->
+ true;
+ {ok,ReadContents} ->
+ {error,{diff,Pos,RC,LC}} = find_diff(ReadContents, ExpectedContents, 1),
+ ct:log("Bad contents of ~p.~nGot:~n~p~nExpected:~n~p~nDiff at pos ~p ~nRead: ~p~nExp : ~p",
+ [AbsPath,ReadContents,ExpectedContents,Pos,RC,LC]),
+ ct:fail("Bad contents of ~p", [Path]);
+ {error,Error} ->
+ try begin
+ {ok,CWD} = file:get_cwd(),
+ ct:log("file:get_cwd()=~p~nfiles:~n~p",[CWD,file:list_dir(CWD)])
+ end
+ of _ -> ok
+ catch _:_ ->ok
+ end,
+ ct:fail("Error reading ~p: ~p",[Path,Error])
+ end.
+
+
+chk_no_file(Path=[C|_], Config) when 0<C,C=<255 ->
+ chk_no_file([Path], Config);
+
+chk_no_file(PathList, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file(AbsPath) of
+ {error,enoent} ->
+ true;
+ {ok,Contents} ->
+ ct:log("File ~p exists although it shouldn't. Contents:~n~p",
+ [AbsPath,Contents]),
+ ct:fail("File exists: ~p", [Path]);
+ {error,Error} ->
+ ct:fail("Unexpected error reading ~p: ~p",[Path,Error])
+ end.
+
+
+chk_dir(Path=[C|_], Config) when 0<C,C=<255 ->
+ chk_dir([Path], Config);
+
+chk_dir(PathList, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file_info(AbsPath) of
+ {ok, #file_info{type=directory}} ->
+ true;
+ {ok, #file_info{type=Type}} ->
+ ct:fail("Expected dir ~p is a ~p",[Path,Type]);
+ {error,Error} ->
+ ct:fail("Expected dir ~p: ~p",[Path,Error])
+ end.
+
+chk_no_dir(PathList, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file_info(AbsPath) of
+ {error,enoent} ->
+ true;
+ {ok, #file_info{type=directory}} ->
+ ct:fail("Dir ~p erroneously exists",[Path]);
+ {ok, #file_info{type=Type}} ->
+ ct:fail("~p ~p erroneously exists",[Type,Path]);
+ {error,Error} ->
+ ct:fail("Unexpected error for ~p: ~p",[Path,Error])
+ end.
+
+
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+%% find a suitable ftpd
%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
+find_executable(Config) ->
+ FTPservers = case ?config(ftpservers,Config) of
+ undefined -> ?default_ftp_servers;
+ L -> L
+ end,
+ case lists:dropwhile(fun not_available/1, FTPservers) of
+ [] -> false;
+ [FTPD_data|_] -> {ok, FTPD_data}
+ end.
+
+not_available({Name,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}) ->
+ os:find_executable(Name) == false.
+
%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- inets:start(),
+%% start/stop of ftpd
+%%
+start_ftpd(Config) ->
+ {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config),
+ case StartCmd(Config) of
+ {ok,StartResult} ->
+ [{ftpd_host,Host},
+ {ftpd_port,Port},
+ {ftpd_start_result,StartResult} | ConfigRewrite(Config)];
+ {error,Msg} ->
+ {skip, [Name," not started: ",Msg]}
+ end.
+
+stop_ftpd(Config) ->
+ {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config),
+ StopCommand(?config(ftpd_start_result,Config)).
+
+ps_ftpd(Config) ->
+ {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config),
+ ct:log( ChkUp(?config(ftpd_start_result,Config)) ).
+
+
+ftpd_running(Config) ->
+ {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config),
+ ChkUp(?config(ftpd_start_result,Config)).
+
+%%--------------------------------------------------------------------
+%% start/stop of ftpc
+%%
+ftp__open(Config, Options) ->
+ Host = ?config(ftpd_host,Config),
+ Port = ?config(ftpd_port,Config),
+ ct:log("Host=~p, Port=~p",[Host,Port]),
+ {ok,Pid} = ftp:open(Host, [{port,Port} | Options]),
+ [{ftp,Pid}|Config].
+
+ftp__close(Config) ->
+ ok = ftp:close(?config(ftp,Config)),
Config.
%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
+%%
+split(Cs) -> string:tokens(Cs, "\r\n").
+
+%%--------------------------------------------------------------------
+%%
+find_diff(Bin1, Bin2) ->
+ case find_diff(Bin1, Bin2, 1) of
+ {error, {diff,Pos,RC,LC}} ->
+ ct:log("Contents differ at position ~p.~nOp1: ~p~nOp2: ~p",[Pos,RC,LC]),
+ ct:fail("Contents differ at pos ~p",[Pos]);
+ Other ->
+ Other
+ end.
+
+find_diff(A, A, _) -> true;
+find_diff(<<H,T1/binary>>, <<H,T2/binary>>, Pos) -> find_diff(T1, T2, Pos+1);
+find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}.
%%--------------------------------------------------------------------
-end_per_suite(_Config) ->
- inets:stop(),
- ok.
+%%
+set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops);
+
+set_state(reset, Config) ->
+ rm('*', id2abs("",Config)),
+ PrivDir = ?config(priv_dir,Config),
+ file:set_cwd(PrivDir),
+ ftp:lcd(?config(ftp,Config),PrivDir),
+ set_state({mkdir,""},Config);
+set_state({mkdir,Id}, Config) ->
+ Abs = id2abs(Id, Config),
+ mk_path(Abs),
+ file:make_dir(Abs),
+ Config;
+set_state({mkfile,Id,Contents}, Config) ->
+ Abs = id2abs(Id, Config),
+ mk_path(Abs),
+ ok = file:write_file(Abs, Contents),
+ Config.
+
+mk_path(Abs) -> lists:foldl(fun mk_path/2, [], filename:split(filename:dirname(Abs))).
+
+mk_path(F, Pfx) ->
+ case file:read_file_info(AbsName=filename:join(Pfx,F)) of
+ {ok,#file_info{type=directory}} ->
+ AbsName;
+ {error,eexist} ->
+ AbsName;
+ {error,enoent} ->
+ ok = file:make_dir(AbsName),
+ AbsName
+ end.
+
+
+rm('*', Pfx) ->
+ {ok,Fs} = file:list_dir(Pfx),
+ lists:foreach(fun(F) -> rm(F, Pfx) end, Fs);
+rm(F, Pfx) ->
+ case file:read_file_info(AbsName=filename:join(Pfx,F)) of
+ {ok,#file_info{type=directory}} ->
+ {ok,Fs} = file:list_dir(AbsName),
+ lists:foreach(fun(F1) -> rm(F1,AbsName) end, Fs),
+ ok = file:del_dir(AbsName);
+
+ {ok,#file_info{type=regular}} ->
+ ok = file:delete(AbsName);
+
+ {error,enoent} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%%
+
+id2abs(Id, Conf) -> filename:join(?config(priv_dir,Conf),ids(Id)).
+id2ftp(Id, Conf) -> (?config(id2ftp,Conf))(ids(Id)).
+id2ftp_result(Id, Conf) -> (?config(id2ftp_result,Conf))(ids(Id)).
+
+ids([[_|_]|_]=Ids) -> filename:join(Ids);
+ids(Id) -> Id.
+
+
+is_expected_absName(Id, File, Conf) -> File = (?config(id2abs,Conf))(Id).
+is_expected_ftpInName(Id, File, Conf) -> File = (?config(id2ftp,Conf))(Id).
+is_expected_ftpOutName(Id, File, Conf) -> File = (?config(id2ftp_result,Conf))(Id).
diff --git a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
deleted file mode 100644
index 75096ce687..0000000000
--- a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
+++ /dev/null
@@ -1,18 +0,0 @@
-%% Add a host name in the appropriate list
-%% Each "platform" contains a list of hostnames (a string) that can
-%% be used for testing the ftp client.
-%% The definition below are an example!!
-[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]},
- {solaris9_sparc, ["solaris9_sparc_dummy1"]},
- {solaris10_sparc, ["solaris10_sparc_dummy1"]},
- {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]},
- {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]},
- {linux_ppc, ["linux_ppc_dummy1"]},
- {macosx_ppc, ["macosx_ppc_dummy1"]},
- {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]},
- {openbsd_x86, []},
- {freebsd_x86, ["freebsd_x86_dummy1"]},
- {netbsd_x86, []},
- {windows_xp, []},
- {windows_2003_server, ["win2003_dummy1"]},
- {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}].
diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf
new file mode 100644
index 0000000000..a5584f5916
--- /dev/null
+++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf
@@ -0,0 +1,26 @@
+
+###
+### Some parameters are given in the vsftpd start command.
+###
+### Typical command-line paramters are such that has a file path
+### component like cert files.
+###
+
+
+listen=YES
+listen_port=9999
+run_as_launching_user=YES
+ssl_enable=YES
+allow_anon_ssl=YES
+
+background=YES
+
+write_enable=YES
+anonymous_enable=YES
+anon_upload_enable=YES
+anon_mkdir_write_enable=YES
+anon_other_write_enable=YES
+anon_world_readable_only=NO
+
+### Shouldn't be necessary....
+require_ssl_reuse=NO
diff --git a/lib/inets/test/ftp_freebsd_x86_test.erl b/lib/inets/test/ftp_freebsd_x86_test.erl
deleted file mode 100644
index 1d66779882..0000000000
--- a/lib/inets/test/ftp_freebsd_x86_test.erl
+++ /dev/null
@@ -1,160 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_freebsd_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Freebsd x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(freebsd_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_linux_ppc_test.erl b/lib/inets/test/ftp_linux_ppc_test.erl
deleted file mode 100644
index bba97237f1..0000000000
--- a/lib/inets/test/ftp_linux_ppc_test.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_linux_ppc_test).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Linux ppc ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(linux_ppc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
diff --git a/lib/inets/test/ftp_linux_x86_test.erl b/lib/inets/test/ftp_linux_x86_test.erl
deleted file mode 100644
index bbefd8231e..0000000000
--- a/lib/inets/test/ftp_linux_x86_test.erl
+++ /dev/null
@@ -1,160 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_linux_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Linux x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(linux_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_macosx_ppc_test.erl b/lib/inets/test/ftp_macosx_ppc_test.erl
deleted file mode 100644
index c9f33b8beb..0000000000
--- a/lib/inets/test/ftp_macosx_ppc_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_macosx_ppc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Macosx ppc ").
-
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(macosx_ppc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
-[open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(_X) -> {skipped,"unknown error"}.%?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(_X) -> {skipped,"unknown error"}.%%?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_macosx_x86_test.erl b/lib/inets/test/ftp_macosx_x86_test.erl
deleted file mode 100644
index 17b7160b95..0000000000
--- a/lib/inets/test/ftp_macosx_x86_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_macosx_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Macosx x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(macosx_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
-[open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist([{wildcard_support, false} | X]).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist([{wildcard_support, false} | X]).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl
deleted file mode 100644
index bb474852c5..0000000000
--- a/lib/inets/test/ftp_netbsd_x86_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_netbsd_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Netbsd x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(netbsd_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_openbsd_x86_test.erl b/lib/inets/test/ftp_openbsd_x86_test.erl
deleted file mode 100644
index 54fce702a0..0000000000
--- a/lib/inets/test/ftp_openbsd_x86_test.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_openbsd_x86_test).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Openbsd x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(openbsd_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
diff --git a/lib/inets/test/ftp_solaris10_sparc_test.erl b/lib/inets/test/ftp_solaris10_sparc_test.erl
deleted file mode 100644
index 0da50dc91b..0000000000
--- a/lib/inets/test/ftp_solaris10_sparc_test.erl
+++ /dev/null
@@ -1,161 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_solaris10_sparc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 10 sparc ").
-
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris10_sparc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_solaris10_x86_test.erl b/lib/inets/test/ftp_solaris10_x86_test.erl
deleted file mode 100644
index 3e7045bb4d..0000000000
--- a/lib/inets/test/ftp_solaris10_x86_test.erl
+++ /dev/null
@@ -1,162 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010. 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(ftp_solaris10_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD, ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_), ?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM, "Solaris 10 x86 ").
-
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris10_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_solaris8_sparc_test.erl b/lib/inets/test/ftp_solaris8_sparc_test.erl
deleted file mode 100644
index 23dbfc8fe3..0000000000
--- a/lib/inets/test/ftp_solaris8_sparc_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_solaris8_sparc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 8 sparc ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris8_sparc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_solaris9_sparc_test.erl b/lib/inets/test/ftp_solaris9_sparc_test.erl
deleted file mode 100644
index 896e2f497f..0000000000
--- a/lib/inets/test/ftp_solaris9_sparc_test.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_solaris9_sparc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 9 sparc ").
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris9_sparc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
deleted file mode 100644
index 35f21cc74d..0000000000
--- a/lib/inets/test/ftp_suite_lib.erl
+++ /dev/null
@@ -1,1675 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% 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(ftp_suite_lib).
-
-
--include_lib("test_server/include/test_server.hrl").
--include_lib("test_server/include/test_server_line.hrl").
--include("inets_test_lib.hrl").
-
-%% Test server specific exports
-% -export([init_per_testcase/2, end_per_testcase/2]).
-
--compile(export_all).
-
-
--record(progress, {
- current = 0,
- total
- }).
-
-
-
--define(FTP_USER, "anonymous").
--define(FTP_PASS, passwd()).
--define(FTP_PORT, 21).
-
--define(BAD_HOST, "badhostname").
--define(BAD_USER, "baduser").
--define(BAD_DIR, "baddirectory").
-
--ifdef(ftp_debug_client).
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{debug, debug},
- {timeout, timer:seconds(15)} | Flags])).
--else.
--ifdef(ftp_trace_client).
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{debug, trace},
- {timeout, timer:seconds(15)} | Flags])).
--else.
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{verbose, true},
- {timeout, timer:seconds(15)} | Flags])).
--endif.
--endif.
-
-%% -- Tickets --
-
-tickets(doc) ->
- "Test cases for reported bugs";
-tickets(suite) ->
- [ticket_6035].
-
-%% --
-
-ftpd_init(FtpdTag, Config) ->
- %% Get the host name(s) of FTP server
- Hosts =
- case ct:get_config(ftpd_hosts) of
- undefined ->
- ftpd_hosts(data_dir(Config));
- H ->
- H
- end,
- p("ftpd_init -> "
- "~n Hosts: ~p"
- "~n Config: ~p"
- "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]),
- %% Get the first host that actually have a running FTP server
- case lists:keysearch(FtpdTag, 1, Hosts) of
- {value, {_, TagHosts}} when is_list(TagHosts) ->
- inets:start(),
- case (catch get_ftpd_host(TagHosts)) of
- {ok, Host} ->
- inets:stop(),
- [{ftp_remote_host, Host}|Config];
- _ ->
- inets:stop(),
- Reason = lists:flatten(
- io_lib:format("Could not find a valid "
- "FTP server for ~p (~p)",
- [FtpdTag, TagHosts])),
- {skip, Reason}
- end;
- _ ->
- Reason = lists:flatten(
- io_lib:format("No host(s) running FTPD server "
- "for ~p", [FtpdTag])),
- {skip, Reason}
- end.
-
-ftpd_fin(Config) ->
- lists:keydelete(ftp_remote_host, 1, Config).
-
-get_ftpd_host([]) ->
- {error, no_host};
-get_ftpd_host([Host|Hosts]) ->
- p("get_ftpd_host -> entry with"
- "~n Host: ~p"
- "~n", [Host]),
- case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of
- {ok, Pid} ->
- (catch ftp:close(Pid)),
- {ok, Host};
- _ ->
- get_ftpd_host(Hosts)
- end.
-
-
-%%--------------------------------------------------------------------
-
-dirty_select_ftpd_host(Config) ->
- Hosts =
- case ct:get_config(ftpd_hosts) of
- undefined ->
- ftpd_hosts(data_dir(Config));
- H ->
- H
- end,
- dirty_select_ftpd_host2(Hosts).
-
-dirty_select_ftpd_host2([]) ->
- throw({error, not_found});
-dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) ->
- case dirty_select_ftpd_host3(Hosts) of
- none ->
- dirty_select_ftpd_host2(PlatformHosts);
- {ok, Host} ->
- {PlatformTag, Host}
- end.
-
-dirty_select_ftpd_host3([]) ->
- none;
-dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) ->
- case dirty_select_ftpd_host4(Host) of
- true ->
- {ok, Host};
- false ->
- dirty_select_ftpd_host3(Hosts)
- end;
-dirty_select_ftpd_host3([_|Hosts]) ->
- dirty_select_ftpd_host3(Hosts).
-
-%% This is a very simple and dirty test that there is a
-%% (FTP) deamon on the other end.
-dirty_select_ftpd_host4(Host) ->
- Port = 21,
- IpFam = inet,
- Opts = [IpFam, binary, {packet, 0}, {active, false}],
- Timeout = ?SECS(5),
- case gen_tcp:connect(Host, Port, Opts, Timeout) of
- {ok, Sock} ->
- gen_tcp:close(Sock),
- true;
- _Error ->
- false
- end.
-
-
-%%--------------------------------------------------------------------
-
-test_filenames() ->
- {ok, Host} = inet:gethostname(),
- File = Host ++ "_ftp_test.txt",
- NewFile = "new_" ++ File,
- {File, NewFile}.
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(Case, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config)
- when (Case =:= open) orelse
- (Case =:= open_port) ->
- put(ftp_testcase, Case),
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = data_dir(Config),
- watch_dog(NewConfig);
-
-init_per_testcase(Case, Config) ->
- put(ftp_testcase, Case),
- do_init_per_testcase(Case, Config).
-
-do_init_per_testcase(Case, Config)
- when (Case =:= passive_user) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [{mode, passive}])) of
- {ok, Pid} ->
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config)
- when (Case =:= active_user) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [{mode, active}])) of
- {ok, Pid} ->
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config)
- when (Case =:= progress_report_send) orelse
- (Case =:= progress_report_recv) ->
- inets:start(),
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- Opts = [{port, ?FTP_PORT},
- {verbose, true},
- {progress, {?MODULE, progress, #progress{}}}],
- case ftp:open(Host, Opts) of
- {ok, Pid} ->
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config) ->
- io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- Opts1 =
- if
- ((Case =:= passive_ip_v6_disabled) orelse
- (Case =:= active_ip_v6_disabled)) ->
- [{ipfamily, inet}];
- true ->
- []
- end,
- Opts2 =
- case string:tokens(atom_to_list(Case), [$_]) of
- ["active" | _] ->
- [{mode, active} | Opts1];
- _ ->
- [{mode, passive} | Opts1]
- end,
- case (catch ?ftp_open(Host, Opts2)) of
- {ok, Pid} ->
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end.
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(Case, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(_, Config) ->
- NewConfig = close_connection(Config),
- Dog = ?config(watchdog, NewConfig),
- inets:stop(),
- test_server:timetrap_cancel(Dog),
- ok.
-
-
-%%-------------------------------------------------------------------------
-%% Suites similar for all hosts.
-%%-------------------------------------------------------------------------
-
-passive(suite) ->
- [
- passive_user,
- passive_pwd,
- passive_cd,
- passive_lcd,
- passive_ls,
- passive_nlist,
- passive_rename,
- passive_delete,
- passive_mkdir,
- passive_send,
- passive_send_bin,
- passive_send_chunk,
- passive_append,
- passive_append_bin,
- passive_append_chunk,
- passive_recv,
- passive_recv_bin,
- passive_recv_chunk,
- passive_type,
- passive_quote,
- passive_ip_v6_disabled
- ].
-
-active(suite) ->
- [
- active_user,
- active_pwd,
- active_cd,
- active_lcd,
- active_ls,
- active_nlist,
- active_rename,
- active_delete,
- active_mkdir,
- active_send,
- active_send_bin,
- active_send_chunk,
- active_append,
- active_append_bin,
- active_append_chunk,
- active_recv,
- active_recv_bin,
- active_recv_chunk,
- active_type,
- active_quote,
- active_ip_v6_disabled
- ].
-
-
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-open(doc) ->
- ["Open an ftp connection to a host and close the connection."
- "Also check that !-messages does not disturbe the connection"];
-open(suite) ->
- [];
-open(Config) when is_list(Config) ->
- Host = ftp_host(Config),
- (catch tc_open(Host)).
-
-
-tc_open(Host) ->
- p("tc_open -> entry with"
- "~n Host: ~p", [Host]),
- {ok, Pid} = ?ftp_open(Host, []),
- ok = ftp:close(Pid),
- p("tc_open -> try (ok) open 1"),
- {ok, Pid1} =
- ftp:open({option_list, [{host,Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {timeout, 30000}]}),
- ok = ftp:close(Pid1),
-
- p("tc_open -> try (fail) open 2"),
- {error, ehost} =
- ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}),
- {ok, Pid2} = ftp:open(Host),
- ok = ftp:close(Pid2),
-
- p("tc_open -> try (ok) open 3"),
- {ok, NewHost} = inet:getaddr(Host, inet),
- {ok, Pid3} = ftp:open(NewHost),
- ftp:user(Pid3, ?FTP_USER, ?FTP_PASS),
- Pid3 ! foobar,
- test_server:sleep(5000),
- {message_queue_len, 0} = process_info(self(), message_queue_len),
- ["200" ++ _] = ftp:quote(Pid3, "NOOP"),
- ok = ftp:close(Pid3),
-
- %% Bad input that has default values are ignored and the defult
- %% is used.
- p("tc_open -> try (ok) open 4"),
- {ok, Pid4} =
- ftp:open({option_list, [{host, Host},
- {port, badarg},
- {flags, [verbose]},
- {timeout, 30000}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid4),
-
- p("tc_open -> try (ok) open 5"),
- {ok, Pid5} =
- ftp:open({option_list, [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {timeout, -42}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid5),
-
- p("tc_open -> try (ok) open 6"),
- {ok, Pid6} =
- ftp:open({option_list, [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {mode, cool}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid6),
-
- p("tc_open -> try (ok) open 7"),
- {ok, Pid7} =
- ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]),
- ok = ftp:close(Pid7),
-
- p("tc_open -> try (ok) open 8"),
- {ok, Pid8} =
- ftp:open(Host, ?FTP_PORT),
- ok = ftp:close(Pid8),
-
- p("tc_open -> try (ok) open 9"),
- {ok, Pid9} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, -99}]),
- ok = ftp:close(Pid9),
-
- p("tc_open -> try (ok) open 10"),
- {ok, Pid10} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, "foobar"}]),
- ok = ftp:close(Pid10),
-
- p("tc_open -> try (ok) open 11"),
- {ok, Pid11} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, 1}]),
- ok = ftp:close(Pid11),
-
- p("tc_open -> done"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-open_port(doc) ->
- ["Open an ftp connection to a host with given port number "
- "and close the connection."]; % See also OTP-3892
-open_port(suite) ->
- [];
-open_port(Config) when is_list(Config) ->
- Host = ftp_host(Config),
- {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]),
- ok = ftp:close(Pid),
- {error, ehost} = ftp:open(?BAD_HOST, []),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-passive_user(doc) ->
- ["Open an ftp connection to a host, and logon as anonymous ftp."];
-passive_user(suite) ->
- [];
-passive_user(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- p("Pid: ~p",[Pid]),
- do_user(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_pwd(doc) ->
- ["Test ftp:pwd/1 & ftp:lpwd/1"];
-passive_pwd(suite) ->
- [];
-passive_pwd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_pwd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_cd(doc) ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to the"
- "directory \"/pub\" and the to the non-existent directory."];
-passive_cd(suite) ->
- [];
-passive_cd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_cd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_lcd(doc) ->
- ["Test api function ftp:lcd/2"];
-passive_lcd(suite) ->
- [];
-passive_lcd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- PrivDir = ?config(priv_dir, Config),
- do_lcd(Pid, PrivDir).
-
-
-%%-------------------------------------------------------------------------
-
-passive_ls(doc) ->
- ["Open an ftp connection; ls the current directory, and the "
- "\"incoming\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
-passive_ls(suite) ->
- [];
-passive_ls(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_ls(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_nlist(doc) ->
- ["Open an ftp connection; nlist the current directory, and the "
- "\"incoming\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."];
-passive_nlist(suite) ->
- [];
-passive_nlist(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- WildcardSupport = ?config(wildcard_support, Config),
- do_nlist(Pid, WildcardSupport).
-
-
-%%-------------------------------------------------------------------------
-
-passive_rename(doc) ->
- ["Transfer a file to the server, and rename it; then remove it."];
-passive_rename(suite) ->
- [];
-passive_rename(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_rename(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_delete(doc) ->
- ["Transfer a file to the server, and then delete it"];
-passive_delete(suite) ->
- [];
-passive_delete(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_delete(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_mkdir(doc) ->
- ["Make a remote directory, cd to it, go to parent directory, and "
- "remove the directory."];
-passive_mkdir(suite) ->
- [];
-passive_mkdir(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_mkdir(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; send the file; get a directory listing and check that "
- "the file is on the list;, delete the remote file; get another listing "
- "and check that the file is not on the list; close the session; "
- "delete the local file."];
-passive_send(suite) ->
- [];
-passive_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_append(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; append the file to a file at the remote side that not exits"
- "this will create the file at the remote side. Then it append the file "
- "again. When this is done it recive the remote file and control that"
- "the content is doubled in it.After that it will remove the files"];
-passive_append(suite) ->
- [];
-passive_append(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send a binary; remove file; close the connection."];
-passive_send_bin(suite) ->
- [];
-passive_send_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_bin(Pid, Config).
-
-%%-------------------------------------------------------------------------
-
-passive_append_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append a binary twice; get the file and compare the content"
- "remove file; close the connection."];
-passive_append_bin(suite) ->
- [];
-passive_append_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send chunks; remove file; close the connection."];
-passive_send_chunk(suite) ->
- [];
-passive_send_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_append_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append chunks;control content remove file; close the connection."];
-passive_append_chunk(suite) ->
- [];
-passive_append_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv(doc) ->
- ["Create a local file and transfer it to the remote host into the "
- "the \"incoming\" directory, remove "
- "the local file. Then open a new connection; cd to \"incoming\", "
- "lcd to the private directory; receive the file; delete the "
- "remote file; close connection; check that received file is in "
- "the correct directory; cleanup." ];
-passive_recv(suite) ->
- [];
-passive_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv_bin(doc) ->
- ["Send a binary to the remote host; and retreive "
- "the file; then remove the file."];
-passive_recv_bin(suite) ->
- [];
-passive_recv_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv_chunk(doc) ->
- ["Send a binary to the remote host; Connect again, and retreive "
- "the file; then remove the file."];
-passive_recv_chunk(suite) ->
- [];
-passive_recv_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_type(doc) ->
- ["Test that we can change btween ASCCI and binary transfer mode"];
-passive_type(suite) ->
- [];
-passive_type(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_type(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_quote(doc) ->
- [""];
-passive_quote(suite) ->
- [];
-passive_quote(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_quote(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_ip_v6_disabled(doc) ->
- ["Test ipv4 command PASV"];
-passive_ip_v6_disabled(suite) ->
- [];
-passive_ip_v6_disabled(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_user(doc) ->
- ["Open an ftp connection to a host, and logon as anonymous ftp."];
-active_user(suite) ->
- [];
-active_user(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_user(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_pwd(doc) ->
- ["Test ftp:pwd/1 & ftp:lpwd/1"];
-active_pwd(suite) ->
- [];
-active_pwd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_pwd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_cd(doc) ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to the"
- "directory \"/pub\" and to a non-existent directory."];
-active_cd(suite) ->
- [];
-active_cd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_cd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_lcd(doc) ->
- ["Test api function ftp:lcd/2"];
-active_lcd(suite) ->
- [];
-active_lcd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- PrivDir = ?config(priv_dir, Config),
- do_lcd(Pid, PrivDir).
-
-
-%%-------------------------------------------------------------------------
-
-active_ls(doc) ->
- ["Open an ftp connection; ls the current directory, and the "
- "\"incoming\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
-active_ls(suite) ->
- [];
-active_ls(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_ls(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_nlist(doc) ->
- ["Open an ftp connection; nlist the current directory, and the "
- "\"incoming\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."];
-active_nlist(suite) ->
- [];
-active_nlist(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- WildcardSupport = ?config(wildcard_support, Config),
- do_nlist(Pid, WildcardSupport).
-
-
-%%-------------------------------------------------------------------------
-
-active_rename(doc) ->
- ["Transfer a file to the server, and rename it; then remove it."];
-active_rename(suite) ->
- [];
-active_rename(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_rename(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_delete(doc) ->
- ["Transfer a file to the server, and then delete it"];
-active_delete(suite) ->
- [];
-active_delete(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_delete(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_mkdir(doc) ->
- ["Make a remote directory, cd to it, go to parent directory, and "
- "remove the directory."];
-active_mkdir(suite) ->
- [];
-active_mkdir(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_mkdir(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_send(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; send the file; get a directory listing and check that "
- "the file is on the list;, delete the remote file; get another listing "
- "and check that the file is not on the list; close the session; "
- "delete the local file."];
-active_send(suite) ->
- [];
-active_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; append the file to a file at the remote side that not exits"
- "this will create the file at the remote side. Then it append the file "
- "again. When this is done it recive the remote file and control that"
- "the content is doubled in it.After that it will remove the files"];
-active_append(suite) ->
- [];
-active_append(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_send_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send a binary; remove file; close the connection."];
-active_send_bin(suite) ->
- [];
-active_send_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append a binary twice; get the file and compare the content"
- "remove file; close the connection."];
-active_append_bin(suite) ->
- [];
-active_append_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_send_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send chunks; remove file; close the connection."];
-active_send_chunk(suite) ->
- [];
-active_send_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append chunks;control content remove file; close the connection."];
-active_append_chunk(suite) ->
- [];
-active_append_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv(doc) ->
- ["Create a local file and transfer it to the remote host into the "
- "the \"incoming\" directory, remove "
- "the local file. Then open a new connection; cd to \"incoming\", "
- "lcd to the private directory; receive the file; delete the "
- "remote file; close connection; check that received file is in "
- "the correct directory; cleanup." ];
-active_recv(suite) ->
- [];
-active_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv_bin(doc) ->
- ["Send a binary to the remote host; and retreive "
- "the file; then remove the file."];
-active_recv_bin(suite) ->
- [];
-active_recv_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv_chunk(doc) ->
- ["Send a binary to the remote host; Connect again, and retreive "
- "the file; then remove the file."];
-active_recv_chunk(suite) ->
- [];
-active_recv_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_type(doc) ->
- ["Test that we can change btween ASCCI and binary transfer mode"];
-active_type(suite) ->
- [];
-active_type(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_type(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_quote(doc) ->
- [""];
-active_quote(suite) ->
- [];
-active_quote(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_quote(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_ip_v6_disabled(doc) ->
- ["Test ipv4 command PORT"];
-active_ip_v6_disabled(suite) ->
- [];
-active_ip_v6_disabled(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-api_missuse(doc)->
- ["Test that behaviour of the ftp process if the api is abused"];
-api_missuse(suite) -> [];
-api_missuse(Config) when is_list(Config) ->
- p("api_missuse -> entry"),
- Flag = process_flag(trap_exit, true),
- Pid = ?config(ftp, Config),
- Host = ftp_host(Config),
-
- %% Serious programming fault, connetion will be shut down
- p("api_missuse -> verify bad call termination (~p)", [Pid]),
- case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
- {error, {connection_terminated, 'API_violation'}} ->
- ok;
- Unexpected1 ->
- exit({unexpected_result, Unexpected1})
- end,
- test_server:sleep(500),
- undefined = process_info(Pid, status),
-
- p("api_missuse -> start new client"),
- {ok, Pid2} = ?ftp_open(Host, []),
- %% Serious programming fault, connetion will be shut down
- p("api_missuse -> verify bad cast termination"),
- gen_server:cast(Pid2, {self(), foobar, 10}),
- test_server:sleep(500),
- undefined = process_info(Pid2, status),
-
- p("api_missuse -> start new client"),
- {ok, Pid3} = ?ftp_open(Host, []),
- %% Could be an innocent misstake the connection lives.
- p("api_missuse -> verify bad bang"),
- Pid3 ! foobar,
- test_server:sleep(500),
- {status, _} = process_info(Pid3, status),
- process_flag(trap_exit, Flag),
- p("api_missuse -> done"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-not_owner(doc) ->
- ["Test what happens if a process that not owns the connection tries "
- "to use it"];
-not_owner(suite) ->
- [];
-not_owner(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]),
-
- receive
- {OtherPid, ok} ->
- {ok, _} = ftp:pwd(Pid)
- end,
- ok.
-
-not_owner(FtpPid, Pid) ->
- {error, not_connection_owner} = ftp:pwd(FtpPid),
- ftp:close(FtpPid),
- test_server:sleep(100),
- Pid ! {self(), ok}.
-
-
-%%-------------------------------------------------------------------------
-
-
-progress_report(doc) ->
- ["Solaris 8 sparc test the option progress."];
-progress_report(suite) ->
- [progress_report_send, progress_report_recv].
-
-
-%% --
-
-progress_report_send(doc) ->
- ["Test the option progress for ftp:send/[2,3]"];
-progress_report_send(suite) ->
- [];
-progress_report_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
- do_send(Pid, Config),
- receive
- {ReportPid, ok} ->
- ok
- end.
-
-
-%% --
-
-progress_report_recv(doc) ->
- ["Test the option progress for ftp:recv/[2,3]"];
-progress_report_recv(suite) ->
- [];
-progress_report_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
- do_recv(Pid, Config),
- receive
- {ReportPid, ok} ->
- ok
- end,
- ok.
-
-progress(#progress{} = Progress , _File, {file_size, Total}) ->
- progress_report_receiver ! start,
- Progress#progress{total = Total};
-progress(#progress{total = Total, current = Current}
- = Progress, _File, {transfer_size, 0}) ->
- progress_report_receiver ! finish,
- case Total of
- unknown ->
- ok;
- Current ->
- ok;
- _ ->
- test_server:fail({error, {progress, {total, Total},
- {current, Current}}})
- end,
- Progress;
-progress(#progress{current = Current} = Progress, _File,
- {transfer_size, Size}) ->
- progress_report_receiver ! update,
- Progress#progress{current = Current + Size}.
-
-progress_report_receiver_init(Pid, N) ->
- register(progress_report_receiver, self()),
- receive
- start ->
- ok
- end,
- progress_report_receiver_loop(Pid, N-1).
-
-progress_report_receiver_loop(Pid, N) ->
- receive
- update ->
- progress_report_receiver_loop(Pid, N);
- finish when N =:= 0 ->
- Pid ! {self(), ok};
- finish ->
- Pid ! {self(), ok},
- receive
- start ->
- ok
- end,
- progress_report_receiver_loop(Pid, N-1)
- end.
-
-
-%%-------------------------------------------------------------------------
-%% Ticket test cases
-%%-------------------------------------------------------------------------
-
-ticket_6035(doc) -> ["Test that owning process that exits with reason "
- "'shutdown' does not cause an error message."];
-ticket_6035(suite) -> [];
-ticket_6035(Config) ->
- p("ticket_6035 -> entry with"
- "~n Config: ~p", [Config]),
- PrivDir = ?config(priv_dir, Config),
- LogFile = filename:join([PrivDir,"ticket_6035.log"]),
- try
- begin
- p("ticket_6035 -> select ftpd host"),
- Host = dirty_select_ftpd_host(Config),
- p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]),
- Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
- p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)",
- [Pid, LogFile]),
- error_logger:logfile({open, LogFile}),
- p("ticket_6035 -> error logfile open => now kill waiter process"),
- true = kill_ftp_proc_6035(Pid, LogFile),
- p("ticket_6035 -> waiter process killed => now close error logfile"),
- error_logger:logfile(close),
- p("ticket_6035 -> done", []),
- ok
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
- end.
-
-kill_ftp_proc_6035(Pid, LogFile) ->
- p("kill_ftp_proc_6035 -> entry"),
- receive
- open ->
- p("kill_ftp_proc_6035 -> received open => now issue shutdown"),
- exit(Pid, shutdown),
- kill_ftp_proc_6035(Pid, LogFile);
- {open_failed, Reason} ->
- p("kill_ftp_proc_6035 -> received open_failed"
- "~n Reason: ~p", [Reason]),
- exit({skip, {failed_openening_server_connection, Reason}})
- after
- 5000 ->
- p("kill_ftp_proc_6035 -> timeout"),
- is_error_report_6035(LogFile)
- end.
-
-open_wait_6035({Tag, FtpServer}, From) ->
- p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]),
- case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of
- {ok, Pid} ->
- p("open_wait_6035 -> connected (~p), now login", [Pid]),
- LoginResult = ftp:user(Pid,"anonymous","kldjf"),
- p("open_wait_6035 -> login result: ~p", [LoginResult]),
- From ! open,
- receive
- dummy ->
- p("open_wait_6035 -> received dummy"),
- ok
- after
- 10000 ->
- p("open_wait_6035 -> timeout"),
- ok
- end,
- p("open_wait_6035 -> done(ok)"),
- ok;
- {error, Reason} ->
- p("open_wait_6035 -> open failed"
- "~n Reason: ~p", [Reason]),
- From ! {open_failed, {Reason, FtpServer}},
- p("open_wait_6035 -> done(error)"),
- ok
- end.
-
-is_error_report_6035(LogFile) ->
- p("is_error_report_6035 -> entry"),
- Res =
- case file:read_file(LogFile) of
- {ok, Bin} ->
- Txt = binary_to_list(Bin),
- p("is_error_report_6035 -> logfile read: ~n~p", [Txt]),
- read_log_6035(Txt);
- _ ->
- false
- end,
- p("is_error_report_6035 -> logfile read result: "
- "~n ~p", [Res]),
- %% file:delete(LogFile),
- Res.
-
-read_log_6035("=ERROR REPORT===="++_Rest) ->
- p("read_log_6035 -> ERROR REPORT detected"),
- true;
-read_log_6035([H|T]) ->
- p("read_log_6035 -> OTHER: "
- "~p", [H]),
- read_log_6035(T);
-read_log_6035([]) ->
- p("read_log_6035 -> done"),
- false.
-
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
-do_user(Pid) ->
- {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS),
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- ok.
-
-do_pwd(Pid) ->
- {ok, "/"} = ftp:pwd(Pid),
- {ok, Path} = ftp:lpwd(Pid),
- {ok, Path} = file:get_cwd(),
- ok.
-
-do_cd(Pid) ->
- ok = ftp:cd(Pid, "/pub"),
- {error, epath} = ftp:cd(Pid, ?BAD_DIR),
- ok.
-
-do_lcd(Pid, Dir) ->
- ok = ftp:lcd(Pid, Dir),
- {error, epath} = ftp:lcd(Pid, ?BAD_DIR),
- ok.
-
-
-do_ls(Pid) ->
- {ok, _} = ftp:ls(Pid),
- {ok, _} = ftp:ls(Pid, "incoming"),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- {ok, _} = ftp:ls(Pid, "incom*"),
- ok.
-
-do_nlist(Pid, WildcardSupport) ->
- {ok, _} = ftp:nlist(Pid),
- {ok, _} = ftp:nlist(Pid, "incoming"),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- case WildcardSupport of
- true ->
- {ok, _} = ftp:nlist(Pid, "incom*"),
- ok;
- _ ->
- ok
- end.
-
-do_rename(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- NewLFile = ?config(new_file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ftp:delete(Pid, LFile), % reset
- ftp:delete(Pid, NewLFile), % reset
- ok = ftp:send(Pid, LFile),
- {error, epath} = ftp:rename(Pid, NewLFile, LFile),
- ok = ftp:rename(Pid, LFile, NewLFile),
- ftp:delete(Pid, LFile), % cleanup
- ftp:delete(Pid, NewLFile), % cleanup
- ok.
-
-do_delete(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ftp:delete(Pid,LFile), % reset
- ok = ftp:send(Pid, LFile),
- ok = ftp:delete(Pid,LFile),
- ok.
-
-do_mkdir(Pid) ->
- {A, B, C} = erlang:now(),
- NewDir = "nisse_" ++ integer_to_list(A) ++ "_" ++
- integer_to_list(B) ++ "_" ++ integer_to_list(C),
- ok = ftp:cd(Pid, "incoming"),
- {ok, CurrDir} = ftp:pwd(Pid),
- ok = ftp:mkdir(Pid, NewDir),
- ok = ftp:cd(Pid, NewDir),
- ok = ftp:cd(Pid, CurrDir),
- ok = ftp:rmdir(Pid, NewDir),
- ok.
-
-do_send(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- RFile = LFile ++ ".remote",
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ok = ftp:send(Pid, LFile, RFile),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(RFile, RFiles),
- ok = ftp:delete(Pid, RFile),
- case ftp:nlist(Pid) of
- {error, epath} ->
- ok; % No files
- {ok, RFilesString1} ->
- RFiles1 = split(RFilesString1),
- false = lists:member(RFile, RFiles1)
- end,
- ok = file:delete(AbsLFile).
-
-do_append(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- RFile = ?config(new_file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test:appending\r\n",
-
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
-
- %% remove files from earlier failed test case
- ftp:delete(Pid, RFile),
- ftp:delete(Pid, LFile),
-
- ok = ftp:append(Pid, LFile, RFile),
- ok = ftp:append(Pid, LFile, RFile),
- ok = ftp:append(Pid, LFile),
-
- %% Control the contents of the file
- {ok, Bin1} = ftp:recv_bin(Pid, RFile),
- ok = ftp:delete(Pid, RFile),
- ok = file:delete(AbsLFile),
- ok = check_content(binary_to_list(Bin1), Contents, double),
-
- {ok, Bin2} = ftp:recv_bin(Pid, LFile),
- ok = ftp:delete(Pid, LFile),
- ok = check_content(binary_to_list(Bin2), Contents, singel),
- ok.
-
-do_send_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_bin(Pid, Contents, File),
- ok = ftp:send_bin(Pid, Bin, File),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(File, RFiles),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_append_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:append_bin(Pid, Contents, File),
- ok = ftp:append_bin(Pid, Bin, File),
- ok = ftp:append_bin(Pid, Bin, File),
- %% Control the contents of the file
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = ftp:delete(Pid,File),
- ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double).
-
-do_send_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:send_chunk_start(Pid, File),
- {error, echunk} = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_chunk(Pid, Contents),
- ok = ftp:send_chunk(Pid, Bin),
- ok = ftp:send_chunk(Pid, Bin),
- ok = ftp:send_chunk_end(Pid),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(File, RFiles),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_append_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Contents = ["ER","LE","RL"],
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:append_chunk_start(Pid, File),
- {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))),
- ok = ftp:append_chunk_end(Pid),
- %%Control the contents of the file
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = check_content(binary_to_list(Bin2),"ERL", double),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_recv(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- File = ?config(file, Config),
- Newfile = ?config(new_file, Config),
- AbsFile = filename:absname(File, PrivDir),
- Contents = "ftp_SUITE:recv test ...",
- ok = file:write_file(AbsFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ftp:delete(Pid, File), % reset
- ftp:lcd(Pid, PrivDir),
- ok = ftp:send(Pid, File),
- ok = file:delete(AbsFile), % cleanup
- test_server:sleep(100),
- ok = ftp:lcd(Pid, PrivDir),
- ok = ftp:recv(Pid, File),
- {ok, Files} = file:list_dir(PrivDir),
- true = lists:member(File, Files),
- ok = file:delete(AbsFile), % cleanup
- ok = ftp:recv(Pid, File, Newfile),
- ok = ftp:delete(Pid, File), % cleanup
- ok.
-
-do_recv_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents1 = "ftp_SUITE test ...",
- Bin1 = list_to_binary(Contents1),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:send_bin(Pid, Bin1, File),
- test_server:sleep(100),
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = ftp:delete(Pid, File), % cleanup
- Contents2 = binary_to_list(Bin2),
- Contents1 = Contents2,
- ok.
-
-do_recv_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
- "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
- "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
- "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
- "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
- "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
- "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
- "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
- "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII",
-
- Contents1 = lists:flatten(lists:duplicate(10, Data)),
- Bin1 = list_to_binary(Contents1),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:type(Pid, binary),
- ok = ftp:send_bin(Pid, Bin1, File),
- test_server:sleep(100),
- {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>),
- ok = ftp:recv_chunk_start(Pid, File),
- {ok, Contents2} = recv_chunk(Pid, <<>>),
- ok = ftp:delete(Pid, File), % cleanup
- ok = find_diff(Contents2, Contents1, 1),
- ok.
-
-do_type(Pid) ->
- ok = ftp:type(Pid, ascii),
- ok = ftp:type(Pid, binary),
- ok = ftp:type(Pid, ascii),
- {error, etype} = ftp:type(Pid, foobar),
- ok.
-
-do_quote(Pid) ->
- ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
- [_| _] = ftp:quote(Pid, "help"),
- %% This negativ test causes some ftp servers to hang. This test
- %% is not important for the client, so we skip it for now.
- %%["425 Can't build data connection: Connection refused."]
- %% = ftp:quote(Pid, "list"),
- ok.
-
- watch_dog(Config) ->
- Dog = test_server:timetrap(inets_test_lib:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
-
- close_connection(Config) ->
- case ?config(ftp, Config) of
- Pid when is_pid(Pid) ->
- ok = ftp:close(Pid),
- lists:delete({ftp, Pid}, Config);
- _ ->
- Config
- end.
-
-ftp_host(Config) ->
- case ?config(ftp_remote_host, Config) of
- undefined ->
- exit({skip, "No host specified"});
- Host ->
- Host
- end.
-
-check_content(RContent, LContent, Amount) ->
- LContent2 = case Amount of
- double ->
- LContent ++ LContent;
- singel ->
- LContent
- end,
- case string:equal(RContent, LContent2) of
- true ->
- ok;
- false ->
- %% Find where the diff is
- Where = find_diff(RContent, LContent2, 1),
- Where
- end.
-
-find_diff(A, A, _) ->
- ok;
-find_diff([H|T1], [H|T2], Pos) ->
- find_diff(T1, T2, Pos+1);
-find_diff(RC, LC, Pos) ->
- {error, {diff, Pos, RC, LC}}.
-
-recv_chunk(Pid, Acc) ->
- case ftp:recv_chunk(Pid) of
- ok ->
- {ok, binary_to_list(Acc)};
- {ok, Bin} ->
- recv_chunk(Pid, <<Acc/binary, Bin/binary>>);
- Error ->
- Error
- end.
-
-split(Cs) ->
- split(Cs, [], []).
-
-split([$\r, $\n| Cs], I, Is) ->
- split(Cs, [], [lists:reverse(I)| Is]);
-split([C| Cs], I, Is) ->
- split(Cs, [C| I], Is);
-split([], I, Is) ->
- lists:reverse([lists:reverse(I)| Is]).
-
-do_ftp_open(Host, Opts) ->
- p("do_ftp_open -> entry with"
- "~n Host: ~p"
- "~n Opts: ~p", [Host, Opts]),
- case ftp:open(Host, Opts) of
- {ok, _} = OK ->
- OK;
- {error, Reason} ->
- Str =
- lists:flatten(
- io_lib:format("Unable to reach test FTP server ~p (~p)",
- [Host, Reason])),
- throw({skip, Str})
- end.
-
-
-passwd() ->
- Host =
- case inet:gethostname() of
- {ok, H} ->
- H;
- _ ->
- "localhost"
- end,
- "ftp_SUITE@" ++ Host.
-
-ftpd_hosts(Config) ->
- DataDir = ?config(data_dir, Config),
- FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]),
- p("FileName: ~p", [FileName]),
- case file:consult(FileName) of
- {ok, [Hosts]} when is_list(Hosts) ->
- Hosts;
- _ ->
- []
- end.
-
-wrapper(Prefix,doc,Func) ->
- Prefix++Func(doc);
-wrapper(_,X,Func) ->
- Func(X).
-
-data_dir(Config) ->
- case ?config(data_dir, Config) of
- List when (length(List) > 0) ->
- PathList = filename:split(List),
- {NewPathList,_} = lists:split((length(PathList)-1), PathList),
- DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]),
- NewConfig =
- lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}),
- NewConfig;
- _ -> Config
- end.
-
-
-
-p(F) ->
- p(F, []).
-
-p(F, A) ->
- case get(ftp_testcase) of
- undefined ->
- io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]);
- TC when is_atom(TC) ->
- io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A])
- end.
diff --git a/lib/inets/test/ftp_ticket_test.erl b/lib/inets/test/ftp_ticket_test.erl
deleted file mode 100644
index fe4ab35728..0000000000
--- a/lib/inets/test/ftp_ticket_test.erl
+++ /dev/null
@@ -1,61 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. 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(ftp_ticket_test).
-
--compile(export_all).
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 8 sparc ").
-
-
-%% Test server callbacks
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-
-all() ->
- tickets().
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_suite(Config) ->
- ?LIB_MOD:ftpd_init(ticket_test, Config).
-
-tickets() ->
- [ticket_6035].
-
-
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-ticket_6035(X) -> ?LIB_MOD:ticket_6035(X).
diff --git a/lib/inets/test/ftp_windows_2003_server_test.erl b/lib/inets/test/ftp_windows_2003_server_test.erl
deleted file mode 100644
index 32f25713f8..0000000000
--- a/lib/inets/test/ftp_windows_2003_server_test.erl
+++ /dev/null
@@ -1,167 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_windows_2003_server_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Windows 2003 server ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(windows_2003_server, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [
- open,
- open_port,
- {group, passive},
- {group, active},
- api_missuse,
- not_owner,
- {group, progress_report}
- ].
-
-groups() ->
- [
- {passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [], ftp_suite_lib:progress_report(suite)}
- ].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_windows_xp_test.erl b/lib/inets/test/ftp_windows_xp_test.erl
deleted file mode 100644
index 06d919ba00..0000000000
--- a/lib/inets/test/ftp_windows_xp_test.erl
+++ /dev/null
@@ -1,157 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(ftp_windows_xp_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Windows xp ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(windows_xp, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 818edc12ac..fe6edd504e 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -145,6 +145,22 @@ init_per_group(misc = Group, Config) ->
ok = httpc:set_options([{ipfamily, Inet}]),
Config;
+init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https->
+ start_apps(Group),
+ StartSsl = try ssl:start()
+ catch
+ Error:Reason ->
+ {skip, lists:flatten(io_lib:format("Failed to start apps for https Error=~p Reason=~p", [Error, Reason]))}
+ end,
+ case StartSsl of
+ {error, {already_started, _}} ->
+ do_init_per_group(Group, Config0);
+ ok ->
+ do_init_per_group(Group, Config0);
+ _ ->
+ StartSsl
+ end;
+
init_per_group(Group, Config0) ->
start_apps(Group),
Config = proplists:delete(port, Config0),
@@ -153,7 +169,10 @@ init_per_group(Group, Config0) ->
end_per_group(_, _Config) ->
ok.
-
+do_init_per_group(Group, Config0) ->
+ Config = proplists:delete(port, Config0),
+ Port = server_start(Group, server_config(Group, Config)),
+ [{port, Port} | Config].
%%--------------------------------------------------------------------
init_per_testcase(pipeline, Config) ->
inets:start(httpc, [{profile, pipeline}]),
@@ -1069,6 +1088,8 @@ server_config(_, _) ->
start_apps(https) ->
inets_test_lib:start_apps([crypto, public_key, ssl]);
+start_apps(sim_https) ->
+ inets_test_lib:start_apps([crypto, public_key, ssl]);
start_apps(_) ->
ok.
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index fd62f778a2..bc4c68230e 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -430,8 +430,56 @@ fe80::204:acff:fe17:bf38
<name name="peername" arity="1"/>
<fsummary>Return the address and port for the other end of a connection</fsummary>
<desc>
- <p>Returns the address and port for the other end of a
- connection.</p>
+ <p>
+ Returns the address and port for the other end of a
+ connection.
+ </p>
+ <p>
+ Note that for SCTP sockets this function only returns
+ one of the socket's peer addresses. The function
+ <seealso marker="#peernames/1">peernames/1,2</seealso>
+ returns all.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="peernames" arity="1"/>
+ <fsummary>
+ Return all address/port numbers for the other end of a connection
+ </fsummary>
+ <desc>
+ <p>
+ Equivalent to
+ <seealso marker="#peernames/2"><c>peernames(<anno>Socket</anno>, 0)</c></seealso>.
+ Note that this function's behaviour for an SCTP
+ one-to-many style socket is not defined by the
+ <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="peernames" arity="2"/>
+ <fsummary>
+ Return all address/port numbers for the other end of a connection
+ </fsummary>
+ <desc>
+ <p>
+ Returns a list of all address/port number pairs for the other end
+ of a socket's association <c><anno>Assoc</anno></c>.
+ </p>
+ <p>
+ This function can return multiple addresses for multihomed
+ sockets such as SCTP sockets. For other sockets it
+ returns a one element list.
+ </p>
+ <p>
+ Note that the <c><anno>Assoc</anno></c> parameter is by the
+ <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>
+ defined to be ignored for
+ one-to-one style sockets. What the special value <c>0</c>
+ means hence its behaviour for one-to-many style sockets
+ is unfortunately not defined.
+ </p>
</desc>
</func>
<func>
@@ -446,6 +494,46 @@ fe80::204:acff:fe17:bf38
<fsummary>Return the local address and port number for a socket</fsummary>
<desc>
<p>Returns the local address and port number for a socket.</p>
+ <p>
+ Note that for SCTP sockets this function only returns
+ one of the socket addresses. The function
+ <seealso marker="#socknames/1">socknames/1,2</seealso>
+ returns all.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="socknames" arity="1"/>
+ <fsummary>Return all local address/port numbers for a socket</fsummary>
+ <desc>
+ <p>
+ Equivalent to
+ <seealso marker="#socknames/2"><c>socknames(<anno>Socket</anno>, 0)</c></seealso>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="socknames" arity="2"/>
+ <fsummary>Return all local address/port numbers for a socket</fsummary>
+ <desc>
+ <p>
+ Returns a list of all local address/port number pairs for a socket
+ for the given association <c><anno>Assoc</anno></c>.
+ </p>
+ <p>
+ This function can return multiple addresses for multihomed
+ sockets such as SCTP sockets. For other sockets it
+ returns a one element list.
+ </p>
+ <p>
+ Note that the <c><anno>Assoc</anno></c> parameter is by the
+ <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>
+ defined to be ignored for one-to-one style sockets.
+ For one-to-many style sockets the special value <c>0</c>
+ is defined to mean that the returned addresses shall be
+ without regard to any particular association.
+ How different SCTP implementations interprets this varies somewhat.
+ </p>
</desc>
</func>
<func>
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index 5e182de41d..9122267c40 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -177,6 +177,17 @@ format_utc_timestamp() ->
</desc>
</func>
<func>
+ <name name="unsetenv" arity="1"/>
+ <fsummary>Delete an environment variable</fsummary>
+ <desc>
+ <p>Deletes the environment variable <c><anno>VarName</anno></c>.</p>
+ <p>If Unicode filename encoding is in effect (see the <seealso
+ marker="erts:erl#file_name_encoding">erl manual
+ page</seealso>), the string (<c><anno>VarName</anno></c>) may
+ contain characters with codepoints > 255.</p>
+ </desc>
+ </func>
+ <func>
<name name="version" arity="0"/>
<fsummary>Return the Operating System version</fsummary>
<desc>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index b01ff16c85..67fdccb734 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -185,7 +185,7 @@
{Mod, Bin, File} = code:get_object_code(Mod),
%% and load it on all nodes including this one
-{ResL, _} = rpc:multicall(code, load_binary, [Mod, Bin, File,]),
+{ResL, _} = rpc:multicall(code, load_binary, [Mod, File, Bin]),
%% and then maybe check the ResL list.</code>
</desc>
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index d4c78505da..b1c9d56c2d 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -24,6 +24,7 @@
%% socket
-export([peername/1, sockname/1, port/1, send/2,
+ peernames/1, peernames/2, socknames/1, socknames/2,
setopts/2, getopts/2,
getifaddrs/0, getifaddrs/1,
getif/1, getif/0, getiflist/0, getiflist/1,
@@ -157,6 +158,7 @@ close(Socket) ->
ok
end.
+
-spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when
Socket :: socket(),
Address :: ip_address(),
@@ -173,6 +175,24 @@ setpeername(Socket, {IP,Port}) ->
setpeername(Socket, undefined) ->
prim_inet:setpeername(Socket, undefined).
+-spec peernames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+peernames(Socket) ->
+ prim_inet:peernames(Socket).
+
+-spec peernames(Socket, Assoc) ->
+ {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Assoc :: #sctp_assoc_change{} | gen_sctp:assoc_id(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+peernames(Socket, Assoc) ->
+ prim_inet:peernames(Socket, Assoc).
+
-spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when
Socket :: socket(),
@@ -190,6 +210,25 @@ setsockname(Socket, {IP,Port}) ->
setsockname(Socket, undefined) ->
prim_inet:setsockname(Socket, undefined).
+-spec socknames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+socknames(Socket) ->
+ prim_inet:socknames(Socket).
+
+-spec socknames(Socket, Assoc) ->
+ {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Assoc :: #sctp_assoc_change{} | gen_sctp:assoc_id(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+socknames(Socket, Assoc) ->
+ prim_inet:socknames(Socket, Assoc).
+
+
-spec port(Socket) -> {'ok', Port} | {'error', any()} when
Socket :: socket(),
Port :: port_number().
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 18a4a61b2f..641a8dc0ca 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -86,6 +86,8 @@
-define(INET_REQ_ACCEPT, 26).
-define(INET_REQ_LISTEN, 27).
-define(INET_REQ_IGNOREFD, 28).
+-define(INET_REQ_GETLADDRS, 29).
+-define(INET_REQ_GETPADDRS, 30).
%% TCP requests
%%-define(TCP_REQ_ACCEPT, 40). MOVED
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index ded03361ee..9415593485 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -26,7 +26,7 @@
%%% BIFs
--export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0]).
+-export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0, unsetenv/1]).
-spec getenv() -> [string()].
@@ -58,6 +58,12 @@ putenv(_, _) ->
timestamp() ->
erlang:nif_error(undef).
+-spec unsetenv(VarName) -> true when
+ VarName :: string().
+
+unsetenv(_) ->
+ erlang:nif_error(undef).
+
%%% End of BIFs
-spec type() -> {Osfamily, Osname} when
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index 35502a1d27..b2ca3bdbc2 100644
--- a/lib/kernel/test/erl_prim_loader_SUITE.erl
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([get_path/1, set_path/1, get_file/1,
+-export([get_path/1, set_path/1, get_file/1, normalize_and_backslash/1,
inet_existing/1, inet_coming_up/1, inet_disconnects/1,
multiple_slaves/1, file_requests/1,
local_archive/1, remote_archive/1,
@@ -39,7 +39,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [get_path, set_path, get_file, inet_existing,
+ [get_path, set_path, get_file,
+ normalize_and_backslash, inet_existing,
inet_coming_up, inet_disconnects, multiple_slaves,
file_requests, local_archive, remote_archive,
primary_archive, virtual_dir_in_archive].
@@ -107,6 +108,26 @@ get_file(Config) when is_list(Config) ->
?line error = erl_prim_loader:get_file({dummy}),
ok.
+normalize_and_backslash(Config) ->
+ %% Test OTP-11170
+ case os:type() of
+ {win32,_} ->
+ {skip, "not on windows"};
+ _ ->
+ test_normalize_and_backslash(Config)
+ end.
+test_normalize_and_backslash(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ Dir = filename:join(PrivDir,"\\"),
+ File = filename:join(Dir,"file-OTP-11170"),
+ ok = file:make_dir(Dir),
+ ok = file:write_file(File,"a file to test OTP-11170"),
+ {ok,["file-OTP-11170"]} = file:list_dir(Dir),
+ {ok,["file-OTP-11170"]} = erl_prim_loader:list_dir(Dir),
+ ok = file:delete(File),
+ ok = file:del_dir(Dir),
+ ok.
+
inet_existing(doc) -> ["Start a node using the 'inet' loading method, ",
"from an already started boot server."];
inet_existing(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index e89cb44797..d2de96b269 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1,4 +1,4 @@
-%%
+%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
@@ -36,7 +36,9 @@
open_multihoming_ipv6_socket/1,
open_multihoming_ipv4_and_ipv6_socket/1,
basic_stream/1, xfer_stream_min/1, peeloff_active_once/1,
- peeloff_active_true/1, buffers/1]).
+ peeloff_active_true/1, buffers/1,
+ names_unihoming_ipv4/1, names_unihoming_ipv6/1,
+ names_multihoming_ipv4/1, names_multihoming_ipv6/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -48,7 +50,9 @@ all() ->
open_multihoming_ipv6_socket,
open_multihoming_ipv4_and_ipv6_socket,
basic_stream, xfer_stream_min, peeloff_active_once,
- peeloff_active_true, buffers].
+ peeloff_active_true, buffers,
+ names_unihoming_ipv4, names_unihoming_ipv6,
+ names_multihoming_ipv4, names_multihoming_ipv6].
groups() ->
[].
@@ -107,7 +111,7 @@ xfer_min(Config) when is_list(Config) ->
?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
?line {ok,Pb} = inet:port(Sb),
?line ok = gen_sctp:listen(Sb, true),
-
+
?line {ok,Sa} = gen_sctp:open(),
?line {ok,Pa} = inet:port(Sa),
?line {ok,#sctp_assoc_change{state=comm_up,
@@ -118,18 +122,18 @@ xfer_min(Config) when is_list(Config) ->
gen_sctp:connect(Sa, Loopback, Pb, []),
?line {SbAssocId,SaOutboundStreams,SaInboundStreams} =
case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
- {Loopback,Pa,
- #sctp_assoc_change{state=comm_up,
- error=0,
- outbound_streams=SbOutboundStreams,
- inbound_streams=SbInboundStreams,
- assoc_id=AssocId}} ->
- {AssocId,SbInboundStreams,SbOutboundStreams};
- {Loopback,Pa,
- #sctp_paddr_change{state=addr_confirmed,
- addr={Loopback,Pa},
- error=0,
- assoc_id=AssocId}} ->
+ {Loopback,Pa,
+ #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SbOutboundStreams,
+ inbound_streams=SbInboundStreams,
+ assoc_id=AssocId}} ->
+ {AssocId,SbInboundStreams,SbOutboundStreams};
+ {Loopback,Pa,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=AssocId}} ->
{Loopback,Pa,
#sctp_assoc_change{state=comm_up,
error=0,
@@ -148,17 +152,20 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}],
Data} -> ok;
Event1 ->
- {Loopback,Pa,
- #sctp_paddr_change{addr = {Loopback,_},
- state = addr_available,
- error = 0,
- assoc_id = SbAssocId}} =
- recv_event(Event1),
- {ok,{Loopback,
- Pa,
- [#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} = gen_sctp:recv(Sb, infinity)
+ case recv_event(Event1) of
+ {Loopback,Pa,
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = State,
+ error = 0,
+ assoc_id = SbAssocId}}
+ when State =:= addr_available;
+ State =:= addr_confirmed ->
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} = log_ok(gen_sctp:recv(Sb, infinity))
+ end
end,
?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
?line case log_ok(gen_sctp:recv(Sa, infinity)) of
@@ -197,7 +204,7 @@ xfer_min(Config) when is_list(Config) ->
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
-
+
?line receive
Msg -> test_server:fail({received,Msg})
after 17 -> ok
@@ -216,7 +223,7 @@ xfer_active(Config) when is_list(Config) ->
?line {ok,Sb} = gen_sctp:open([{active,true}]),
?line {ok,Pb} = inet:port(Sb),
?line ok = gen_sctp:listen(Sb, true),
-
+
?line {ok,Sa} = gen_sctp:open([{active,true}]),
?line {ok,Pa} = inet:port(Sa),
?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
@@ -348,7 +355,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
%%
?line S1 =
log_ok(gen_sctp:open(
- 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
+ 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
?LOGVAR(S1),
?line P1 =
log_ok(inet:port(S1)),
@@ -455,18 +462,22 @@ def_sndrcvinfo(Config) when is_list(Config) ->
stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
<<"3: ",Data/binary>>} -> ok;
Event2 ->
- {Loopback,P2,
- #sctp_paddr_change{
- addr={Loopback,_}, state=addr_available,
- error=0, assoc_id=S1AssocId}} =
- recv_event(Event2),
- ?line case log_ok(gen_sctp:recv(S1)) of
- {Loopback,P2,
- [#sctp_sndrcvinfo{
- stream=1, ppid=0, context=0,
- assoc_id=S1AssocId}],
- <<"3: ",Data/binary>>} -> ok
- end
+ case recv_event(Event2) of
+ {Loopback,P2,
+ #sctp_paddr_change{
+ addr={Loopback,_},
+ state=State,
+ error=0, assoc_id=S1AssocId}}
+ when State =:= addr_available;
+ State =:= addr_confirmed ->
+ ?line case log_ok(gen_sctp:recv(S1)) of
+ {Loopback,P2,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=0, context=0,
+ assoc_id=S1AssocId}],
+ <<"3: ",Data/binary>>} -> ok
+ end
+ end
end,
?line ok =
do_from_other_process(
@@ -509,6 +520,13 @@ log_ok(X) -> log(ok(X)).
ok({ok,X}) -> X.
+err([], Result) ->
+ erlang:error(Result);
+err([Reason|_], {error,Reason}) ->
+ ok;
+err([_|Reasons], Result) ->
+ err(Reasons, Result).
+
log(X) ->
io:format("LOG[~w]: ~p~n", [self(),X]),
X.
@@ -529,57 +547,57 @@ api_open_close(Config) when is_list(Config) ->
?line {ok,S1} = gen_sctp:open(0),
?line {ok,P} = inet:port(S1),
?line ok = gen_sctp:close(S1),
-
+
?line {ok,S2} = gen_sctp:open(P),
?line {ok,P} = inet:port(S2),
?line ok = gen_sctp:close(S2),
-
+
?line {ok,S3} = gen_sctp:open([{port,P}]),
?line {ok,P} = inet:port(S3),
?line ok = gen_sctp:close(S3),
-
+
?line {ok,S4} = gen_sctp:open(P, []),
?line {ok,P} = inet:port(S4),
?line ok = gen_sctp:close(S4),
-
+
?line {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]),
?line {ok,P} = inet:port(S5),
?line ok = gen_sctp:close(S5),
?line ok = gen_sctp:close(S5),
-
+
?line try gen_sctp:close(0)
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open({})
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(-1)
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(65536)
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(make_ref(), [])
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(0, {})
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(0, [make_ref()])
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open([{invalid_option,0}])
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(0, [{mode,invalid_mode}])
catch error:badarg -> ok
end,
@@ -591,11 +609,11 @@ api_listen(suite) ->
[];
api_listen(Config) when is_list(Config) ->
?line Localhost = {127,0,0,1},
-
+
?line try gen_sctp:listen(0, true)
catch error:badarg -> ok
end,
-
+
?line {ok,S} = gen_sctp:open(),
?line {ok,Pb} = inet:port(S),
?line try gen_sctp:listen(S, not_allowed_for_listen)
@@ -603,7 +621,7 @@ api_listen(Config) when is_list(Config) ->
end,
?line ok = gen_sctp:close(S),
?line {error,closed} = gen_sctp:listen(S, true),
-
+
?line {ok,Sb} = gen_sctp:open(Pb),
?line {ok,Sa} = gen_sctp:open(),
?line case gen_sctp:connect(Sa, localhost, Pb, []) of
@@ -615,8 +633,8 @@ api_listen(Config) when is_list(Config) ->
gen_sctp:recv(Sa, infinity);
{error,#sctp_assoc_change{state=cant_assoc}} ->
ok%;
- %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
- %% ok
+ %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
+ %% ok
end,
?line ok = gen_sctp:listen(Sb, true),
?line {ok,#sctp_assoc_change{state=comm_up,
@@ -840,23 +858,36 @@ xfer_stream_min(Config) when is_list(Config) ->
?line SbOutboundStreams = SaInboundStreams,
?line ?LOGVAR(SbOutboundStreams),
?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
- ?line case gen_sctp:recv(Sb, infinity) of
- {ok,{Loopback,
+ ?line case log_ok(gen_sctp:recv(Sb, infinity)) of
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} -> ok;
+ {Loopback,
+ Pa,[],
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = addr_available,
+ error = 0,
+ assoc_id = SbAssocId}} ->
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} = log_ok(gen_sctp:recv(Sb, infinity));
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = addr_confirmed,
+ error = 0,
+ assoc_id = SbAssocId}} ->
+ {Loopback,
Pa,
[#sctp_sndrcvinfo{stream=Stream,
assoc_id=SbAssocId}],
- Data}} -> ok;
- {ok,{Loopback,
- Pa,[],
- #sctp_paddr_change{addr = {Loopback,_},
- state = addr_available,
- error = 0,
- assoc_id = SbAssocId}}} ->
- {ok,{Loopback,
- Pa,
- [#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} = gen_sctp:recv(Sb, infinity)
+ Data} = log_ok(gen_sctp:recv(Sb, infinity))
end,
?line ok =
do_from_other_process(
@@ -972,10 +1003,10 @@ peeloff(Config, SockOpts) when is_list(Config) ->
?line ?LOGVAR(S2Ai),
?line S1Ai =
receive
- {S1,{Addr,P2,
- #sctp_assoc_change{
- state=comm_up,
- assoc_id=AssocId1}}} -> AssocId1
+ {S1,{Addr,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
socket_bailout([S1,S2])
end,
@@ -1003,8 +1034,8 @@ peeloff(Config, SockOpts) when is_list(Config) ->
?line P3 = case P3_X of 0 -> P1; _ -> P3_X end,
?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
socket_call(S3,
- {getopts,[{sctp_get_peer_addr_info,
- #sctp_paddrinfo{address={Addr,P2}}}]}),
+ {getopts,[{sctp_get_peer_addr_info,
+ #sctp_paddrinfo{address={Addr,P2}}}]}),
%%?line S3Ai = S1Ai,
?line ?LOGVAR(S3Ai),
%%
@@ -1087,9 +1118,9 @@ buffers(Config) when is_list(Config) ->
%%
?line socket_call(S1, {setopts,[{recbuf,Limit}]}),
?line Recbuf =
- case socket_call(S1, {getopts,[recbuf]}) of
- [{recbuf,RB1}] when RB1 >= Limit -> RB1
- end,
+ case socket_call(S1, {getopts,[recbuf]}) of
+ [{recbuf,RB1}] when RB1 >= Limit -> RB1
+ end,
?line Data = mk_data(Recbuf+Limit),
?line socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}),
?line socket_call(S2, {send,S2Ai,Stream,Data}),
@@ -1190,6 +1221,93 @@ open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
{skip, Reason}
end.
+names_unihoming_ipv4(doc) ->
+ "Test inet:socknames/peernames on unihoming IPv4 sockets";
+names_unihoming_ipv4(suite) ->
+ [];
+names_unihoming_ipv4(Config) when is_list(Config) ->
+ ?line do_names(Config, inet, 1).
+
+names_unihoming_ipv6(doc) ->
+ "Test inet:socknames/peernames on unihoming IPv6 sockets";
+names_unihoming_ipv6(suite) ->
+ [];
+names_unihoming_ipv6(Config) when is_list(Config) ->
+ ?line do_names(Config, inet6, 1).
+
+names_multihoming_ipv4(doc) ->
+ "Test inet:socknames/peernames on multihoming IPv4 sockets";
+names_multihoming_ipv4(suite) ->
+ [];
+names_multihoming_ipv4(Config) when is_list(Config) ->
+ ?line do_names(Config, inet, 2).
+
+names_multihoming_ipv6(doc) ->
+ "Test inet:socknames/peernames on multihoming IPv6 sockets";
+names_multihoming_ipv6(suite) ->
+ [];
+names_multihoming_ipv6(Config) when is_list(Config) ->
+ ?line do_names(Config, inet6, 2).
+
+
+
+do_names(_, FamilySpec, AddressCount) ->
+ Fun =
+ fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) ->
+ ?line ServerSocknamesNoassoc =
+ lists:sort(ok(inet:socknames(ServerSocket))),
+ ?line ?LOGVAR(ServerSocknamesNoassoc),
+ ?line ServerSocknames =
+ lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))),
+ ?line ?LOGVAR(ServerSocknames),
+ ?line [_|_] =
+ ordsets:intersection
+ (ServerSocknamesNoassoc, ServerSocknames),
+ ?line ClientSocknamesNoassoc =
+ lists:sort(ok(inet:socknames(ClientSocket))),
+ ?line ?LOGVAR(ClientSocknamesNoassoc),
+ ?line ClientSocknames =
+ lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))),
+ ?line ?LOGVAR(ClientSocknames),
+ ?line [_|_] =
+ ordsets:intersection
+ (ClientSocknamesNoassoc, ClientSocknames),
+ ?line err([einval,enotconn], inet:peernames(ServerSocket)),
+ ?line ServerPeernames =
+ lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))),
+ ?line ?LOGVAR(ServerPeernames),
+ ?line err([einval,enotconn], inet:peernames(ClientSocket)),
+ ?line ClientPeernames =
+ lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))),
+ ?line ?LOGVAR(ClientPeernames),
+ ?line ServerSocknames = ClientPeernames,
+ ?line ClientSocknames = ServerPeernames,
+ ?line {ok,Socket} =
+ gen_sctp:peeloff(ServerSocket, ServerAssoc),
+ ?line SocknamesNoassoc =
+ lists:sort(ok(inet:socknames(Socket))),
+ ?line ?LOGVAR(SocknamesNoassoc),
+ ?line Socknames =
+ lists:sort(ok(inet:socknames(Socket, ServerAssoc))),
+ ?line ?LOGVAR(Socknames),
+ ?line true =
+ ordsets:is_subset(SocknamesNoassoc, Socknames),
+ ?line Peernames =
+ lists:sort(ok(inet:peernames(Socket, ServerAssoc))),
+ ?line ?LOGVAR(Peernames),
+ ?line ok = gen_sctp:close(Socket),
+ ?line Socknames = ClientPeernames,
+ ?line ClientSocknames = Peernames,
+ ok
+ end,
+ ?line case get_addrs_by_family(FamilySpec, AddressCount) of
+ {ok, Addresses} when length(Addresses) =:= AddressCount ->
+ ?line do_open_and_connect(Addresses, hd(Addresses), Fun);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
get_addrs_by_family(Family, NumAddrs) ->
case os:type() of
@@ -1274,6 +1392,10 @@ f(F, A) ->
lists:flatten(io_lib:format(F, A)).
do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
+ ?line Fun = fun (_, _, _, _, _, _) -> ok end,
+ ?line do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun).
+%%
+do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) ->
?line ServerFamily = get_family_by_addrs(ServerAddresses),
?line io:format("Serving ~p addresses: ~p~n",
[ServerFamily, ServerAddresses]),
@@ -1284,14 +1406,26 @@ do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
?line ClientFamily = get_family_by_addr(AddressToConnectTo),
?line io:format("Connecting to ~p ~p~n",
[ClientFamily, AddressToConnectTo]),
- ?line S2 = ok(gen_sctp:open(0, [ClientFamily])),
+ ?line ClientOpts =
+ [ClientFamily |
+ case ClientFamily of
+ inet6 ->
+ [{ipv6_v6only,true}];
+ _ ->
+ []
+ end],
+ ?line S2 = ok(gen_sctp:open(0, ClientOpts)),
+ log(open),
%% Verify client can connect
- ?line #sctp_assoc_change{state=comm_up} =
+ ?line #sctp_assoc_change{state=comm_up} = S2Assoc =
ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
+ log(comm_up),
%% verify server side also receives comm_up from client
- ?line recv_comm_up_eventually(S1),
+ ?line S1Assoc = recv_comm_up_eventually(S1),
+ ?line Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc),
?line ok = gen_sctp:close(S2),
- ?line ok = gen_sctp:close(S1).
+ ?line ok = gen_sctp:close(S1),
+ Result.
%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
get_family_by_addrs(Addresses) ->
@@ -1306,9 +1440,11 @@ get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6.
recv_comm_up_eventually(S) ->
?line case ok(gen_sctp:recv(S)) of
- {_Addr, _Port, _Info, #sctp_assoc_change{state=comm_up}} ->
- ok;
- {_Addr, _Port, _Info, _OtherSctpMsg} ->
+ {_Addr, _Port, _Info,
+ #sctp_assoc_change{state=comm_up} = Assoc} ->
+ Assoc;
+ {_Addr, _Port, _Info, _OtherSctpMsg} = Msg ->
+ ?line log({unexpected,Msg}),
?line recv_comm_up_eventually(S)
end.
@@ -1367,10 +1503,10 @@ socket_bailout([]) ->
socket_history({State,Flush}) ->
{lists:keysort(
- 2,
- lists:flatten(
- [[{Key,Val} || Val <- Vals]
- || {Key,Vals} <- gb_trees:to_list(State)])),
+ 2,
+ lists:flatten(
+ [[{Key,Val} || Val <- Vals]
+ || {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
s_handler(Socket) ->
@@ -1453,7 +1589,7 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
#sctp_assoc_change{
state=comm_up,
inbound_streams=Is}}}|_]
- when 0 =< Stream, Stream < Is-> ok;
+ when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
@@ -1473,7 +1609,7 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
case {gb_get(Key, State),St} of
{[],_} -> ok;
{[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
- when St =:= comm_lost; St =:= shutdown_comp -> ok
+ when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
NewState = gb_push(Key, Val, State),
Parent ! {self(),{Addr,Port,SAC}},
@@ -1489,8 +1625,9 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],
- addr_available} -> ok;
+ {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ when St =:= addr_available;
+ St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},
diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl
index bd237cb513..e91f6f18d4 100644
--- a/lib/kernel/test/zlib_SUITE.erl
+++ b/lib/kernel/test/zlib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -565,8 +565,8 @@ intro(Config) when is_list(Config) ->
large_deflate(doc) -> "Test deflate large file, which had a bug reported on erlang-bugs";
large_deflate(suite) -> [];
large_deflate(Config) when is_list(Config) ->
- large_deflate().
-large_deflate() ->
+ large_deflate_do().
+large_deflate_do() ->
?line Z = zlib:open(),
?line Plain = rand_bytes(zlib:getBufSize(Z)*5),
?line ok = zlib:deflateInit(Z),
@@ -899,7 +899,7 @@ worker(Seed, FnATpl, Parent) ->
Parent ! self().
worker_loop(0, _FnATpl) ->
- large_deflate(), % the time consuming one as finale
+ large_deflate_do(), % the time consuming one as finale
ok;
worker_loop(N, FnATpl) ->
{F,A} = element(random:uniform(size(FnATpl)),FnATpl),
diff --git a/lib/observer/doc/src/ttb_ug.xml b/lib/observer/doc/src/ttb_ug.xml
index 08093a9451..402d079c2c 100644
--- a/lib/observer/doc/src/ttb_ug.xml
+++ b/lib/observer/doc/src/ttb_ug.xml
@@ -697,7 +697,7 @@ f3() ->
(tiger@durin)6>ttb:stop({format, {handler, ttb:get_et_handler()}}).
</code>
- <p>This shoud render a result similar to the
+ <p>This should render a result similar to the
following:
</p>
<p></p>
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index 83f7a47434..531ad84fb9 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -105,6 +105,7 @@ AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"]))
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h winsock2.h])
+AC_CHECK_HEADERS([sql.h, sqlext.h], [odbc_required_headers=yes], [odbc_required_headers=no])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -203,6 +204,10 @@ AC_SUBST(TARGET_FLAGS)
;;
esac
+if test $odbc_required_headers = no; then
+ AC_MSG_WARN(["ODBC library - header check failed"])
+ echo "ODBC library - header check failed" > $ERL_TOP/lib/odbc/SKIP
+fi
if test $odbc_lib_link_success = no; then
AC_MSG_WARN(["ODBC library - link check failed"])
echo "ODBC library - link check failed" > $ERL_TOP/lib/odbc/SKIP
diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index 911a156d6c..8d3c76adf5 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -252,7 +252,17 @@ domainComponent ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
emailAddress ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
ID id-emailAddress
- TYPE EmailAddress }
+ TYPE EmailAddress } -- this is currently not used when decoding
+ -- The decoding and mapping between ID and Type is done in the code
+ -- in module publickey_cert_records via the function attribute_type
+ -- To be more forgiving and compatible with other SSL implementations
+ -- regarding how to handle and sometimes accept incorrect certificates
+ -- we define and use the type below instead of emailAddress
+
+ OTP-emailAddress ::= CHOICE {
+ ia5String IA5String (SIZE (1..255)),
+ utf8String UTF8String (SIZE (1..255))
+}
--
-- Signature and Public Key Algorithms
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index 0449129809..fdd89aa70d 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -67,6 +67,15 @@ transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
{ok, {printableString, ASCCI}} ->
{ok, ASCCI}
end;
+ 'EmailAddress' when Func == decode ->
+ %% Workaround that some certificates break the ASN-1 spec
+ %% and encode emailAddress as utf8
+ case 'OTP-PUB-KEY':Func('OTP-emailAddress', Value0) of
+ {ok, {utf8String, Utf8Value}} ->
+ {ok, unicode:characters_to_list(Utf8Value)};
+ {ok, {ia5String, Ia5Value}} ->
+ {ok, Ia5Value}
+ end;
Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
_UnknownType -> {ok, Value0}
end,
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index f8d167e770..d3e9bf7cf6 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -41,7 +41,7 @@ all() ->
{group, ssh_public_key_decode_encode},
encrypt_decrypt,
{group, sign_verify},
- pkix, pkix_countryname, pkix_path_validation,
+ pkix, pkix_countryname, pkix_emailaddress, pkix_path_validation,
pkix_iso_rsa_oid, pkix_iso_dsa_oid].
groups() ->
@@ -586,9 +586,9 @@ pkix(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
pkix_countryname() ->
- [{doc, "Test workaround for certs that code x509countryname as utf8"}].
+ [{doc, "Test workaround for certs that code x509countryname as utf8"}].
pkix_countryname(Config) when is_list(Config) ->
- Cert = incorrect_pkix_cert(),
+ Cert = incorrect_countryname_pkix_cert(),
OTPCert = public_key:pkix_decode_cert(Cert, otp),
TBSCert = OTPCert#'OTPCertificate'.tbsCertificate,
Issuer = TBSCert#'OTPTBSCertificate'.issuer,
@@ -597,6 +597,18 @@ pkix_countryname(Config) when is_list(Config) ->
check_countryname(Subj).
%%--------------------------------------------------------------------
+pkix_emailaddress() ->
+ [{doc, "Test workaround for certs that code emailAddress as utf8"}].
+pkix_emailaddress(Config) when is_list(Config) ->
+ Cert = incorrect_emailaddress_pkix_cert(),
+ OTPCert = public_key:pkix_decode_cert(Cert, otp),
+ TBSCert = OTPCert#'OTPCertificate'.tbsCertificate,
+ Issuer = TBSCert#'OTPTBSCertificate'.issuer,
+ Subj = TBSCert#'OTPTBSCertificate'.subject,
+ check_emailaddress(Issuer),
+ check_emailaddress(Subj).
+
+%%--------------------------------------------------------------------
pkix_path_validation() ->
[{doc, "Test PKIX path validation"}].
pkix_path_validation(Config) when is_list(Config) ->
@@ -710,10 +722,23 @@ do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName',
ok;
do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName',
value = Value}|_]) ->
- ct:fail({incorrect_cuntry_name, Value});
+ ct:fail({incorrect_country_name, Value});
do_check_countryname([_| Rest]) ->
do_check_countryname(Rest).
+check_emailaddress({rdnSequence,DirName}) ->
+ do_check_emailaddress(DirName).
+do_check_emailaddress([]) ->
+ ok;
+do_check_emailaddress([#'AttributeTypeAndValue'{type = ?'id-emailAddress',
+ value = "[email protected]"}|_]) ->
+ ok;
+do_check_emailaddress([#'AttributeTypeAndValue'{type = ?'id-emailAddress',
+ value = Value}|_]) ->
+ ct:fail({incorrect_email_address, Value});
+do_check_emailaddress([_| Rest]) ->
+ do_check_emailaddress(Rest).
+
check_entry_type(#'DSAPrivateKey'{}, 'DSAPrivateKey') ->
true;
check_entry_type(#'RSAPrivateKey'{}, 'RSAPrivateKey') ->
@@ -732,5 +757,8 @@ check_entry_type(_,_) ->
strip_ending_newlines(Bin) ->
string:strip(binary_to_list(Bin), right, 10).
-incorrect_pkix_cert() ->
+incorrect_countryname_pkix_cert() ->
<<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>.
+
+incorrect_emailaddress_pkix_cert() ->
+ <<48,130,3,74,48,130,2,50,2,9,0,133,49,203,25,198,156,252,230,48,13,6,9,42,134, 72,134,247,13,1,1,5,5,0,48,103,49,11,48,9,6,3,85,4,6,19,2,65,85,49,19,48,17, 6,3,85,4,8,12,10,83,111,109,101,45,83,116,97,116,101,49,33,48,31,6,3,85,4,10, 12,24,73,110,116,101,114,110,101,116,32,87,105,100,103,105,116,115,32,80,116, 121,32,76,116,100,49,32,48,30,6,9,42,134,72,134,247,13,1,9,1,12,17,105,110, 118,97,108,105,100,64,101,109,97,105,108,46,99,111,109,48,30,23,13,49,51,49, 49,48,55,50,48,53,54,49,56,90,23,13,49,52,49,49,48,55,50,48,53,54,49,56,90, 48,103,49,11,48,9,6,3,85,4,6,19,2,65,85,49,19,48,17,6,3,85,4,8,12,10,83,111, 109,101,45,83,116,97,116,101,49,33,48,31,6,3,85,4,10,12,24,73,110,116,101, 114,110,101,116,32,87,105,100,103,105,116,115,32,80,116,121,32,76,116,100,49, 32,48,30,6,9,42,134,72,134,247,13,1,9,1,12,17,105,110,118,97,108,105,100,64, 101,109,97,105,108,46,99,111,109,48,130,1,34,48,13,6,9,42,134,72,134,247,13, 1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,190,243,49,213,219,60,232,105, 1,127,126,9,130,15,60,190,78,100,148,235,246,223,21,91,238,200,251,84,55,212, 78,32,120,61,85,172,0,144,248,5,165,29,143,79,64,178,51,153,203,76,115,238, 192,49,173,37,121,203,89,62,157,13,181,166,30,112,154,40,202,140,104,211,157, 73,244,9,78,236,70,153,195,158,233,141,42,238,2,143,160,225,249,27,30,140, 151,176,43,211,87,114,164,108,69,47,39,195,123,185,179,219,28,218,122,53,83, 77,48,81,184,14,91,243,12,62,146,86,210,248,228,171,146,225,87,51,146,155, 116,112,238,212,36,111,58,41,67,27,6,61,61,3,84,150,126,214,121,57,38,12,87, 121,67,244,37,45,145,234,131,115,134,58,194,5,36,166,52,59,229,32,47,152,80, 237,190,58,182,248,98,7,165,198,211,5,31,231,152,116,31,108,71,218,64,188, 178,143,27,167,79,15,112,196,103,116,212,65,197,94,37,4,132,103,91,217,73, 223,207,185,7,153,221,240,232,31,44,102,108,82,83,56,242,210,214,74,71,246, 177,217,148,227,220,230,4,176,226,74,194,37,2,3,1,0,1,48,13,6,9,42,134,72, 134,247,13,1,1,5,5,0,3,130,1,1,0,89,247,141,154,173,123,123,203,143,85,28,79, 73,37,164,6,17,89,171,224,149,22,134,17,198,146,158,192,241,41,253,58,230, 133,71,189,43,66,123,88,15,242,119,227,249,99,137,61,200,54,161,0,177,167, 169,114,80,148,90,22,97,78,162,181,75,93,209,116,245,46,81,232,64,157,93,136, 52,57,229,113,197,218,113,93,42,161,213,104,205,137,30,144,183,58,10,98,47, 227,177,96,40,233,98,150,209,217,68,22,221,133,27,161,152,237,46,36,179,59, 172,97,134,194,205,101,137,71,192,57,153,20,114,27,173,233,166,45,56,0,61, 205,45,202,139,7,132,103,248,193,157,184,123,43,62,172,236,110,49,62,209,78, 249,83,219,133,1,213,143,73,174,16,113,143,189,41,84,60,128,222,30,177,104, 134,220,52,239,171,76,59,176,36,113,176,214,118,16,44,235,21,167,199,216,200, 76,219,142,248,13,70,145,205,216,230,226,148,97,223,216,179,68,209,222,63, 140,137,24,164,192,149,194,79,119,247,75,159,49,116,70,241,70,116,11,40,119, 176,157,36,160,102,140,255,34,248,25,231,136,59>>.
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 670e216d97..202129c61a 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -77,8 +77,8 @@ sys_info() ->
| MemInfo].
alloc_info() ->
- {_,_,AllocTypes,_} = erlang:system_info(allocator),
- try erlang:system_info({allocator_sizes,AllocTypes}) of
+ AlcuAllocs = erlang:system_info(alloc_util_allocators),
+ try erlang:system_info({allocator_sizes, AlcuAllocs}) of
Allocators -> Allocators
catch _:_ -> []
end.
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 7514c52dda..d213b67052 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,6 +34,125 @@
<section>
+ <title>SNMP Development Toolkit 4.25</title>
+ <p>Version 4.25 supports code replacement in runtime from/to
+ version 4.24.2, 4.24.1, 4.24, 4.23.1 and 4.23. </p>
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[agent] Enable SNMP to create missing database directories. </p>
+ <p>Add
+ <seealso marker="snmp_app#db_init_error">
+ {db_init_error, create_db_and_dir}</seealso> option to SNMP
+ <seealso marker="snmp_app#manager_opts_and_types">manager</seealso>
+ and
+ <seealso marker="snmp_app#agent_opts_and_types">agent</seealso>.
+ This allows them to create any missing parent directories for
+ <c>db_dir</c>, rather than treating any missing directories
+ as a fatal error.
+ The default for <c>db_init_error</c>, which is <c>terminate</c>,
+ is unchanged. </p>
+ <p>Steve Vinoski</p>
+ <p>Own Id: OTP-11352</p>
+ </item>
+
+ <item>
+ <p>[manager] Improved handling of unexpected return values from
+ <seealso marker="snmpm_user">snmpm_user</seealso>
+ callback functions. </p>
+ <p>Violations of the documented API (crashes or invalid return
+ values) will now result in an error message. </p>
+ <p>Own Id: OTP-11307</p>
+ </item>
+
+ <item>
+ <p>Add (atl) log conversion block option. </p>
+ <p>It is now possible to request that the Audit Trail Log should
+ be blocked during conversion (<c>log_to_txt</c> or <c>log_to_io</c>).
+ This could be usefull when coverting an entire large log (when
+ there is a chance it may otherwise wrap during conversion). </p>
+ <p>See
+ agent
+ <seealso marker="snmpa#log_to_txt">log_to_txt</seealso> and
+ <seealso marker="snmpa#log_to_io">log_to_io</seealso> and also
+ manager
+ <seealso marker="snmpm#log_to_txt">log_to_txt</seealso> and
+ <seealso marker="snmpm#log_to_io">log_to_io</seealso>
+ for details. </p>
+ <p>Own Id: OTP-11396</p>
+ <p>Own Id: seq12433</p>
+ </item>
+
+ <item>
+ <p>When converting an Audit Trail Log to text, a corrupt
+ log entry could cause the entire conversion to fail. </p>
+ <p>Also, for a log with sequence numbers, failing to
+ decode a log entry would cause the conversion to fail
+ (not because of the failed decode, but because of the
+ failure to write the error message). </p>
+ <p>Own Id: OTP-111453</p>
+ <p>Aux Id: Seq 12459</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Wrong block cypher type used for AES ('aes_cbf128'
+ instead of 'aes_cfb128') when performing AES block
+ encrypt/decrypt which breaks SNMP usmAesCfb128Protocol
+ in agent and manager. </p>
+ <p>Own Id: OTP-11412</p>
+ </item>
+
+ <item>
+ <p>[manager] When performing the AES encryption, invalid values for
+ the EngineBoots and EngineTime was used. </p>
+ <p>The values of the local agent was used, which would have produced
+ "some" values if an agent was actually running.
+ If not it would have caused a crash. </p>
+ <p>Own Id: OTP-11413</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>[manager] The old Addr-and-Port based API functions, previously
+ long deprecated and marked for deletion in R16B, has now been
+ removed. </p>
+ <p>Own Id: OTP-10027</p>
+ </item>
+
+ </list>
+-->
+ </section>
+
+ </section> <!-- 4.25 -->
+
+
+ <section>
<title>SNMP Development Toolkit 4.24.2</title>
<p>Version 4.24.2 supports code replacement in runtime from/to
version 4.24.1, 4.24, 4.23.1 and 4.23. </p>
@@ -96,7 +215,7 @@
</list>
-->
</section>
-
+
</section> <!-- 4.24.2 -->
diff --git a/lib/snmp/doc/src/snmp.xml b/lib/snmp/doc/src/snmp.xml
index 3e6610891f..97b479385c 100644
--- a/lib/snmp/doc/src/snmp.xml
+++ b/lib/snmp/doc/src/snmp.xml
@@ -341,8 +341,9 @@
<func>
<name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Block | Stop) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop, Block) -> ok | {error, Reason}</name>
<fsummary>Convert an Audit Trail Log to text format</fsummary>
<type>
<v>LogDir = string()</v>
@@ -352,53 +353,56 @@
<v>LogName = string()</v>
<v>LogFile = string()</v>
<v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v>
+ <v>Block = boolean()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Converts an Audit Trail Log to a readable text file, where
- each item has a trailing TAB character, and any TAB
- character in the body of an item has been replaced by ESC
- TAB.
- </p>
+ <p>Converts an Audit Trail Log to a readable text file, where
+ each item has a trailing TAB character, and any TAB
+ character in the body of an item has been replaced by ESC
+ TAB. </p>
<p>The function can be used on a running system, or by copying
- the entire log directory and calling this function. SNMP
- must be running in order to provide MIB information.
- </p>
+ the entire log directory and calling this function. SNMP
+ must be running in order to provide MIB information. </p>
<p><c>LogDir</c> is the name of the directory where the audit
- trail log is stored.
- <c>Mibs</c> is a list of Mibs to be used. The function uses
- the information in the Mibs to convert for example object
- identifiers to their symbolic name.
- <c>OutFile</c> is the name of the generated text-file.
- <c>LogName</c> is the name of the log,
- <c>LogFile</c> is the name of the log file.
- <c>Start</c> is the start (first) date and time from which
- log events will be converted and
- <c>Stop</c> is the stop (last) date and time to which log
- events will be converted.
- </p>
- <p>The format of an audit trail log text item is as follows:
- </p>
- <p><c>Tag Addr - Community [TimeStamp] Vsn</c><br></br>
- <c>PDU</c></p>
- <p>where <c>Tag</c> is <c>request</c>, <c>response</c>,
- <c>report</c>, <c>trap</c> or <c>inform</c>; Addr is
- <c>IP:Port</c> (or comma space separated list of such);
- <c>Community</c> is the community parameter (SNMP version
- v1 and v2), or <c>SecLevel:"AuthEngineID":"UserName"</c>
- (SNMP v3); <c>TimeStamp</c> is a date and time stamp,
- and <c>Vsn</c> is the SNMP version. <c>PDU</c> is a textual
- version of the protocol data unit. There is a new line
- between <c>Vsn</c> and <c>PDU</c>.</p>
-
+ trail log is stored.
+ <c>Mibs</c> is a list of Mibs to be used. The function uses
+ the information in the Mibs to convert for example object
+ identifiers to their symbolic name.
+ <c>OutFile</c> is the name of the generated text-file.
+ <c>LogName</c> is the name of the log,
+ <c>LogFile</c> is the name of the log file.
+ <c>Start</c> is the start (first) date and time from which
+ log events will be converted and
+ <c>Stop</c> is the stop (last) date and time to which log
+ events will be converted.
+ The <c>Block</c> argument indicates if the log should be blocked
+ during conversion. This could be usefull when converting large
+ logs (when otherwise the log could wrap during conversion).
+ Defaults to <c>true</c>.
+ </p>
+ <p>The format of an audit trail log text item is as follows: </p>
+ <p><c>Tag Addr - Community [TimeStamp] Vsn</c><br></br>
+ <c>PDU</c></p>
+ <p>where <c>Tag</c> is <c>request</c>, <c>response</c>,
+ <c>report</c>, <c>trap</c> or <c>inform</c>; Addr is
+ <c>IP:Port</c> (or comma space separated list of such);
+ <c>Community</c> is the community parameter (SNMP version
+ v1 and v2), or <c>SecLevel:"AuthEngineID":"UserName"</c>
+ (SNMP v3); <c>TimeStamp</c> is a date and time stamp,
+ and <c>Vsn</c> is the SNMP version. <c>PDU</c> is a textual
+ version of the protocol data unit. There is a new line
+ between <c>Vsn</c> and <c>PDU</c>.</p>
+
<marker id="log_to_io"></marker>
</desc>
</func>
<func>
<name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Block | Stop) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop, Block) -> ok | {error, Reason}</name>
<fsummary>Convert an Audit Trail Log to text format</fsummary>
<type>
<v>LogDir = string()</v>
diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml
index e5a05342c1..9ede75b943 100644
--- a/lib/snmp/doc/src/snmp_app.xml
+++ b/lib/snmp/doc/src/snmp_app.xml
@@ -763,12 +763,15 @@
</item>
<marker id="db_init_error"></marker>
- <tag><c>db_init_error() = terminate | create</c></tag>
+ <tag><c>db_init_error() = terminate | create | create_db_and_dir</c></tag>
<item>
<p>Defines what to do if the agent or manager is unable to open an
existing database file. <c>terminate</c> means that the
agent/manager will terminate and <c>create</c> means that the
- agent/manager will remove the faulty file(s) and create new ones.</p>
+ agent/manager will remove the faulty file(s) and create new ones,
+ and <c>create_db_and_dir</c> means that the agent/manager will
+ create the database file along with any missing parent directories
+ for the database file.</p>
<p>Default is <c>terminate</c>.</p>
</item>
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml
index 61ee7f00ee..30b46e6aa8 100644
--- a/lib/snmp/doc/src/snmp_config.xml
+++ b/lib/snmp/doc/src/snmp_config.xml
@@ -792,12 +792,15 @@ in so far as it will be converted to the new format if found.
</item>
<marker id="db_init_error"></marker>
- <tag><c>db_init_error() = terminate | create</c></tag>
+ <tag><c>db_init_error() = terminate | create | create_db_and_dir</c></tag>
<item>
<p>Defines what to do if the agent is unable to open an
existing database file. <c>terminate</c> means that the
- agent/manager will terminate and <c>create</c> means that the
- agent/manager will remove the faulty file(s) and create new ones.</p>
+ agent/manager will terminate, <c>create</c> means that the
+ agent/manager will remove the faulty file(s) and create new ones,
+ and <c>create_db_and_dir</c> means that the agent/manager will
+ create the database file along with any missing parent directories
+ for the database file.</p>
<p>Default is <c>terminate</c>.</p>
</item>
diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml
index 77146f3a89..cc8681e5c8 100644
--- a/lib/snmp/doc/src/snmpa.xml
+++ b/lib/snmp/doc/src/snmpa.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -557,32 +557,39 @@ notification_delivery_info() = #snmpa_notification_delivery_info{}
<func>
<name>log_to_txt(LogDir)</name>
- <name>log_to_txt(LogDir, Mibs)</name>
- <name>log_to_txt(LogDir, Mibs, OutFile) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Block | Mibs)</name>
+ <name>log_to_txt(LogDir, Mibs, Block | OutFile) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, Block | LogName) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, Block | LogFile) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start) -> ok | {error, Reason}</name>
<name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) -> ok | {error, Reason}</name>
<fsummary>Convert an Audit Trail Log to text format</fsummary>
<type>
<v>LogDir = string()</v>
<v>Mibs = [MibName]</v>
<v>MibName = string()</v>
+ <v>Block = boolean()</v>
<v>OutFile = string()</v>
<v>LogName = string()</v>
<v>LogFile = string()</v>
- <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v>
+ <v>Start = Stop = null | calendar:datetime() | {local_time, calendar:datetime()} | {universal_time, calendar:datetime()} </v>
<v>Reason = disk_log_open_error() | file_open_error() | term()</v>
<v>disk_log_open_error() = {LogName, term()}</v>
<v>file_open_error() = {OutFile, term()}</v>
</type>
<desc>
<p>Converts an Audit Trail Log to a readable text file.
- <c>OutFile</c> defaults to "./snmpa_log.txt".
- <c>LogName</c> defaults to "snmpa_log".
- <c>LogFile</c> defaults to "snmpa.log".
- See <seealso marker="snmp#log_to_txt">snmp:log_to_txt</seealso>
- for more info.</p>
+ <c>OutFile</c> defaults to "./snmpa_log.txt".
+ <c>LogName</c> defaults to "snmpa_log".
+ <c>LogFile</c> defaults to "snmpa.log". </p>
+ <p>The <c>Block</c> option indicates if the log should be blocked
+ during conversion. This could be usefull when converting large
+ logs (when otherwise the log could wrap during conversion).
+ Defaults to <c>true</c>. </p>
+ <p>See <seealso marker="snmp#log_to_txt">snmp:log_to_txt</seealso>
+ for more info.</p>
<marker id="log_to_io"></marker>
</desc>
@@ -590,19 +597,22 @@ notification_delivery_info() = #snmpa_notification_delivery_info{}
<func>
<name>log_to_io(LogDir) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Block | Mibs) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, Block | LogName) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, Block | LogFile) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start) -> ok | {error, Reason}</name>
<name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) -> ok | {error, Reason}</name>
<fsummary>Convert an Audit Trail Log to text format</fsummary>
<type>
<v>LogDir = string()</v>
<v>Mibs = [MibName]</v>
<v>MibName = string()</v>
+ <v>Block = boolean()</v>
<v>LogName = string()</v>
<v>LogFile = string()</v>
- <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v>
+ <v>Start = Stop = null | calendar:datetime() | {local_time, calendar:datetime()} | {universal_time, calendar:datetime()} </v>
<v>Reason = disk_log_open_error() | file_open_error() | term()</v>
<v>disk_log_open_error() = {LogName, term()}</v>
<v>file_open_error() = {OutFile, term()}</v>
@@ -612,6 +622,10 @@ notification_delivery_info() = #snmpa_notification_delivery_info{}
prints it on stdio.
<c>LogName</c> defaults to "snmpa_log".
<c>LogFile</c> defaults to "snmpa.log".
+ <p>The <c>Block</c> option indicates if the log should be blocked
+ during conversion. This could be usefull when converting large
+ logs (when otherwise the log could wrap during conversion).
+ Defaults to <c>true</c>. </p>
See <seealso marker="snmp#log_to_io">snmp:log_to_io</seealso>
for more info.</p>
diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml
index 07fdd208ff..a0a1b5716d 100644
--- a/lib/snmp/doc/src/snmpm.xml
+++ b/lib/snmp/doc/src/snmpm.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1209,32 +1209,40 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1
</func>
<func>
- <name>log_to_txt(LogDir, Mibs)</name>
- <name>log_to_txt(LogDir, Mibs, OutFile) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) -> ok | {error, Reason}</name>
- <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir)</name>
+ <name>log_to_txt(LogDir, Block | Mibs)</name>
+ <name>log_to_txt(LogDir, Mibs, Block | OutFile) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, Block | LogName) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, Block | LogFile) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block | Start) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start) -> ok | {error, Reason}</name>
<name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name>
+ <name>log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) -> ok | {error, Reason}</name>
<fsummary>Convert an Audit Trail Log to text format</fsummary>
<type>
<v>LogDir = string()</v>
<v>Mibs = [MibName]</v>
<v>MibName = string()</v>
+ <v>Block = boolean()</v>
<v>OutFile = string()</v>
<v>LogName = string()</v>
<v>LogFile = string()</v>
- <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v>
+ <v>Start = Stop = null | calendar:datetime() | {local_time, calendar:datetime()} | {universal_time, calendar:datetime()} </v>
<v>Reason = disk_log_open_error() | file_open_error() | term()</v>
<v>disk_log_open_error() = {LogName, term()}</v>
<v>file_open_error() = {OutFile, term()}</v>
</type>
<desc>
<p>Converts an Audit Trail Log to a readable text file.
- <c>OutFile</c> defaults to "./snmpm_log.txt".
- <c>LogName</c> defaults to "snmpm_log".
- <c>LogFile</c> defaults to "snmpm.log".
- See <seealso marker="snmp#log_to_txt">snmp:log_to_txt</seealso>
- for more info.</p>
+ <c>OutFile</c> defaults to "./snmpm_log.txt".
+ <c>LogName</c> defaults to "snmpm_log".
+ <c>LogFile</c> defaults to "snmpm.log".
+ <p>The <c>Block</c> argument indicates if the log should be blocked
+ during conversion. This could be usefull when converting large
+ logs (when otherwise the log could wrap during conversion).
+ Defaults to <c>true</c>. </p>
+ See <seealso marker="snmp#log_to_txt">snmp:log_to_txt</seealso>
+ for more info.</p>
<marker id="log_to_io"></marker>
</desc>
@@ -1242,20 +1250,23 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1
<func>
<name>log_to_io(LogDir) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Block | Mibs) -> ok | {error, Reason}</name>
<name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {error, Reason}</name>
- <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, Block | LogName) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, Block | LogFile) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block | Start) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start) -> ok | {error, Reason}</name>
<name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name>
+ <name>log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) -> ok | {error, Reason}</name>
<fsummary>Convert an Audit Trail Log to text format</fsummary>
<type>
<v>LogDir = string()</v>
<v>Mibs = [MibName]</v>
<v>MibName = string()</v>
+ <v>Block = boolean()</v>
<v>LogName = string()</v>
<v>LogFile = string()</v>
- <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v>
+ <v>Start = Stop = null | calendar:datetime() | {local_time, calendar:datetime()} | {universal_time, calendar:datetime()} </v>
<v>Reason = disk_log_open_error() | file_open_error() | term()</v>
<v>disk_log_open_error() = {LogName, term()}</v>
<v>file_open_error() = {OutFile, term()}</v>
@@ -1265,6 +1276,10 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1
prints it on stdio.
<c>LogName</c> defaults to "snmpm_log".
<c>LogFile</c> defaults to "snmpm.log".
+ <p>The <c>Block</c> argument indicates if the log should be blocked
+ during conversion. This could be usefull when converting large
+ logs (when otherwise the log could wrap during conversion).
+ Defaults to <c>true</c>. </p>
See <seealso marker="snmp#log_to_io">snmp:log_to_io</seealso>
for more info.</p>
diff --git a/lib/snmp/doc/src/snmpm_user.xml b/lib/snmp/doc/src/snmpm_user.xml
index 1823e0c815..cb2deab976 100644
--- a/lib/snmp/doc/src/snmpm_user.xml
+++ b/lib/snmp/doc/src/snmpm_user.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -56,40 +56,59 @@
<item>
<p>handle_report/3</p>
</item>
+ <item>
+ <p>handle_invalid_result/2</p>
+ </item>
</list>
<p>The semantics of them and their exact signatures are explained
- below. </p>
- <p>Note that if an agent is registered using the old, no longer
- documented, functions (using Addr and Port), the old variant of the
- callback functions, handle_pdu, handle_trap, handle_inform and
- handle_report, will be called. </p>
+ below. </p>
+ <p>Some of the function has no defined return value (<c>void()</c>),
+ they can ofcourse return anythyng. But the functions that do have
+ specified return value(s) <em>must</em> adhere to this. None of the
+ functions can use exit of throw to return. </p>
- <marker id="handle_error"></marker>
+ <marker id="types"></marker>
</description>
+
+ <section>
+ <title>DATA TYPES</title>
+ <code type="none"><![CDATA[
+snmp_gen_info() = {ErrorStatus :: atom(),
+ ErrorIndex :: pos_integer(),
+ Varbinds :: [snmp:varbind()]}
+snmp_v1_trap_info() :: {Enteprise :: snmp:oid(),
+ Generic :: integer(),
+ Spec :: integer(),
+ Timestamp :: integer(),
+ Varbinds :: [snmp:varbind()]}
+ ]]></code>
+ <marker id="handle_error"></marker>
+ </section>
+
<funcs>
<func>
- <name>handle_error(ReqId, Reason, UserData) -> Reply</name>
+ <name>handle_error(ReqId, Reason, UserData) -> void()</name>
<fsummary>Handle error</fsummary>
<type>
<v>ReqId = integer()</v>
<v>Reason = {unexpected_pdu, SnmpInfo} | {invalid_sec_info, SecInfo, SnmpInfo} | {empty_message, Addr, Port} | term()</v>
+ <v>SnmpInfo = snmp_gen_info()</v>
+ <v>SecInfo = term()</v>
<v>Addr = ip_address()</v>
<v>Port = integer()</v>
<v>UserData = term()</v>
- <v>Reply = ignore</v>
</type>
<desc>
<p>This function is called when the manager needs to
- communicate an "asynchronous" error, to the user:
- e.g. failure to send an asynchronous message (i.e. encoding
- error), a received message was discarded due to security
- error, the manager failed to generate a response message to
- a received inform-request, or when receiving an unexpected
- PDU from an agent (could be an expired async request). </p>
- <p>If <c>ReqId</c> is less then 0, it means that this
- information was not available to the manager (that info was
- never retrieved before the message was discarded).
- </p>
+ communicate an "asynchronous" error to the user:
+ e.g. failure to send an asynchronous message (i.e. encoding
+ error), a received message was discarded due to security
+ error, the manager failed to generate a response message to
+ a received inform-request, or when receiving an unexpected
+ PDU from an agent (could be an expired async request). </p>
+ <p>If <c>ReqId</c> is less then 0, it means that this
+ information was not available to the manager (that info was
+ never retrieved before the message was discarded). </p>
<p>For <c>SnmpInfo</c> see handle_agent below.</p>
<marker id="handle_agent"></marker>
@@ -104,22 +123,22 @@
<v>Port = integer()</v>
<v>Type = pdu | trap | report | inform</v>
<v>SnmpInfo = SnmpPduInfo | SnmpTrapInfo | SnmpReportInfo | SnmpInformInfo</v>
- <v>ErrorStatus = atom()</v>
- <v>ErrorIndex = integer()</v>
- <v>Varbinds = [varbind()]</v>
- <v>varbind() = #varbind</v>
+ <v>SnmpPduInfo = snmp_gen_info()</v>
+ <v>SnmpTrapInfo = snmp_v1_trap_info()</v>
+ <v>SnmpReportInfo = snmp_gen_info()</v>
+ <v>SnmpInformInfo = snmp_gen_info()</v>
<v>UserData = term()</v>
- <v>Reply = ignore | {register, UserId, TargetName, agent_info()}</v>
+ <v>Reply = ignore | {register, UserId, TargetName, AgentConfig}</v>
<v>UserId = term()</v>
<v>TargetName = target_name()</v>
- <v>agent_info() = [{agent_info_item(), agent_info_value()}]</v>
+ <v>AgentConfig = [agent_config()]</v>
</type>
<desc>
- <p>This function is called when a message is received from an
- unknown agent.</p>
+ <p>This function is called when a message is received from an
+ unknown agent.</p>
<p>Note that this will always be the default user that is called.</p>
- <p>For more info about the <c>agent_info()</c>, see
- <seealso marker="snmpm#register_agent">register_agent</seealso>.</p>
+ <p>For more info about the <c>agent_config()</c>, see
+ <seealso marker="snmpm#register_agent">register_agent</seealso>.</p>
<p>The arguments <c>Type</c> and <c>SnmpInfo</c> relates in the
following way: </p>
@@ -148,7 +167,7 @@
</list>
<p>The only user which would return
- <c>{register, UserId, TargetName, agent_info()}</c> is the
+ <c>{register, UserId, TargetName, AgentConfig}</c> is the
<em>default user</em>.</p>
<marker id="handle_pdu"></marker>
@@ -156,18 +175,13 @@
</func>
<func>
- <name>handle_pdu(TargetName, ReqId, SnmpPduInfo, UserData) -> Reply</name>
+ <name>handle_pdu(TargetName, ReqId, SnmpPduInfo, UserData) -> void()</name>
<fsummary>Handle the reply to an asynchronous request</fsummary>
<type>
<v>TargetName = target_name()</v>
<v>ReqId = term()</v>
- <v>SnmpPduInfo = {ErrorStatus, ErrorIndex, Varbinds}</v>
- <v>ErrorStatus = atom()</v>
- <v>ErrorIndex = integer()</v>
- <v>Varbinds = [varbind()]</v>
- <v>varbind() = #varbind</v>
+ <v>SnmpPduInfo = snmp_gen_info()</v>
<v>UserData = term()</v>
- <v>Reply = ignore</v>
</type>
<desc>
<p>Handle the reply to an asynchronous request, such as
@@ -186,27 +200,19 @@
<fsummary>Handle a trap/notification message</fsummary>
<type>
<v>TargetName = TargetName2 = target_name()</v>
- <v>SnmpTrapInfo = {Enteprise, Generic, Spec, Timestamp, Varbinds} | {ErrorStatus, ErrorIndex, Varbinds}</v>
- <v>Enterprise = oid()</v>
- <v>Generic = integer()</v>
- <v>Spec = integer()</v>
- <v>Timestamp = integer()</v>
- <v>ErrorStatus = atom()</v>
- <v>ErrorIndex = integer()</v>
- <v>Varbinds = [varbind()]</v>
- <v>varbind() = #varbind</v>
+ <v>SnmpTrapInfo = snmp_v1_trap_info() | snmp_gen_info()</v>
<v>UserData = term()</v>
- <v>Reply = ignore | unregister | {register, UserId, TargetName2, agent_info()}</v>
+ <v>Reply = ignore | unregister | {register, UserId, TargetName2, AgentConfig}</v>
<v>UserId = term()</v>
- <v>agent_info() = [{agent_info_item(), agent_info_value()}]</v>
+ <v>AgentConfig = [agent_config()]</v>
</type>
<desc>
<p>Handle a trap/notification message from an agent.</p>
- <p>For more info about the <c>agent_info()</c>, see
- <seealso marker="snmpm#register_agent">register_agent</seealso></p>
+ <p>For more info about the <c>agent_config()</c>, see
+ <seealso marker="snmpm#register_agent">register_agent</seealso></p>
<p>The only user which would return
- <c>{register, UserId, TargetName2, agent_info()}</c> is the
- <em>default user</em>.</p>
+ <c>{register, UserId, TargetName2, agent_info()}</c> is the
+ <em>default user</em>.</p>
<marker id="handle_inform"></marker>
</desc>
@@ -217,29 +223,25 @@
<fsummary>Handle a inform message</fsummary>
<type>
<v>TargetName = TargetName2 = target_name()</v>
- <v>SnmpInformInfo = {ErrorStatus, ErrorIndex, Varbinds}</v>
- <v>ErrorStatus = atom()</v>
- <v>ErrorIndex = integer()</v>
- <v>Varbinds = [varbind()]</v>
- <v>varbind() = #varbind</v>
+ <v>SnmpInformInfo = snmp_gen_info()</v>
<v>UserData = term()</v>
- <v>Reply = ignore | unregister | {register, UserId, TargetName2, agent_info()}</v>
+ <v>Reply = ignore | no_reply | unregister | {register, UserId, TargetName2, AgentConfig}</v>
<v>UserId = term()</v>
- <v>agent_info() = [{agent_info_item(), agent_info_value()}]</v>
+ <v>AgentConfig = [agent_config()]</v>
</type>
<desc>
<p>Handle a inform message.</p>
- <p>For more info about the <c>agent_info()</c>, see
- <seealso marker="snmpm#register_agent">register_agent</seealso></p>
+ <p>For more info about the <c>agent_config()</c>, see
+ <seealso marker="snmpm#register_agent">register_agent</seealso></p>
<p>The only user which would return
- <c>{register, UserId, TargetName2, agent_info()}</c> is the
- <em>default user</em>.</p>
- <p>If the
- <seealso marker="snmp_app">inform request behaviour</seealso>
- configuration option is set to <c>user</c> or
- <c>{user, integer()}</c>, the response (acknowledgment) to this
- inform-request will be sent when this function returns.</p>
-
+ <c>{register, UserId, TargetName2, AgentConfig}</c> is the
+ <em>default user</em>.</p>
+ <p>If the
+ <seealso marker="snmp_app">inform request behaviour</seealso>
+ configuration option is set to <c>user</c> or
+ <c>{user, integer()}</c>, the response (acknowledgment) to this
+ inform-request will be sent when this function returns.</p>
+
<marker id="handle_report"></marker>
</desc>
</func>
@@ -251,23 +253,46 @@
<v>TargetName = TargetName2 = target_name()</v>
<v>Addr = ip_address()</v>
<v>Port = integer()</v>
- <v>SnmpReportInfo = {ErrorStatus, ErrorIndex, Varbinds}</v>
- <v>ErrorStatus = atom()</v>
- <v>ErrorIndex = integer()</v>
- <v>Varbinds = [varbind()]</v>
- <v>varbind() = #varbind</v>
+ <v>SnmpReportInfo = snmp_gen_info()</v>
<v>UserData = term()</v>
- <v>Reply = ignore | unregister | {register, UserId, TargetName2, agent_info()}</v>
+ <v>Reply = ignore | unregister | {register, UserId, TargetName2, AgentConfig}</v>
<v>UserId = term()</v>
- <v>agent_info() = [{agent_info_item(), agent_info_value()}]</v>
+ <v>AgentConfig = [agent_config()]</v>
</type>
<desc>
<p>Handle a report message.</p>
- <p>For more info about the <c>agent_info()</c>, see
- <seealso marker="snmpm#register_agent">register_agent</seealso></p>
+ <p>For more info about the <c>agent_config()</c>, see
+ <seealso marker="snmpm#register_agent">register_agent</seealso></p>
<p>The only user which would return
- <c>{register, UserId, TargetName2, agent_info()}</c> is the
- <em>default user</em>.</p>
+ <c>{register, UserId, TargetName2, AgentConfig}</c> is the
+ <em>default user</em>.</p>
+
+ <marker id="handle_invalid_result"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>handle_invalid_result(IN, OUT) -> void()</name>
+ <fsummary>Handle a report message</fsummary>
+ <type>
+ <v>IN = {Func, Args}</v>
+ <v>Func = atom()</v>
+ <v>Args = list()</v>
+ <v>OUT = {crash, CrashInfo} | {result, InvalidResult}</v>
+ <v>CrashInfo = {ErrorType, Error, Stacktrace}</v>
+ <v>ErrorType = atom()</v>
+ <v>Error = term()</v>
+ <v>Stacktrace = list()</v>
+ <v>InvalidResult = term()</v>
+ </type>
+ <desc>
+ <p>If <em>any</em> of the <em>other</em> callback functions crashes
+ (exit, throw or a plain crash) or return an invalid result (if a valid
+ return has been specified), this function is called.
+ The purpose is to allow the user handle this
+ error (for instance to issue an error report).</p>
+ <p><c>IN</c> reprecents the function called (and its arguments).
+ <c>OUT</c> represents the unexpected/invalid result. </p>
</desc>
</func>
</funcs>
diff --git a/lib/snmp/examples/ex2/snmp_ex2_manager.erl b/lib/snmp/examples/ex2/snmp_ex2_manager.erl
index 1b247d713d..a9dcc09b77 100644
--- a/lib/snmp/examples/ex2/snmp_ex2_manager.erl
+++ b/lib/snmp/examples/ex2/snmp_ex2_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,7 +43,8 @@
handle_pdu/4,
handle_trap/3,
handle_inform/3,
- handle_report/3]).
+ handle_report/3,
+ handle_invalid_result/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -406,5 +407,9 @@ handle_report(TargetName, SnmpReport, Server) when is_pid(Server) ->
report_callback(Server, handle_inform, {TargetName, SnmpReport}),
ok.
+handle_invalid_result(In, Out, Server) when is_pid(Server) ->
+ report_callback(Server, handle_invalid_result, {In, Out}),
+ ok.
+
report_callback(Pid, Tag, Info) ->
Pid ! {snmp_callback, Tag, Info}.
diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl
index a95e41ea42..aea63effe6 100644
--- a/lib/snmp/src/agent/snmpa.erl
+++ b/lib/snmp/src/agent/snmpa.erl
@@ -85,11 +85,10 @@
-export([add_agent_caps/2, del_agent_caps/1, get_agent_caps/0]).
%% Audit Trail Log functions
--export([log_to_txt/1,
- log_to_txt/2, log_to_txt/3, log_to_txt/4,
- log_to_txt/5, log_to_txt/6, log_to_txt/7,
- log_to_io/1, log_to_io/2, log_to_io/3,
- log_to_io/4, log_to_io/5, log_to_io/6,
+-export([log_to_txt/1, log_to_txt/2, log_to_txt/3, log_to_txt/4,
+ log_to_txt/5, log_to_txt/6, log_to_txt/7, log_to_txt/8,
+ log_to_io/1, log_to_io/2, log_to_io/3, log_to_io/4,
+ log_to_io/5, log_to_io/6, log_to_io/7,
log_info/0,
change_log_size/1,
get_log_type/0, get_log_type/1,
@@ -130,7 +129,8 @@
-include("snmpa_internal.hrl").
-include_lib("snmp/include/snmp_types.hrl"). % type of me needed.
--define(DISCO_EXTRA_INFO, undefined).
+-define(DISCO_EXTRA_INFO, undefined).
+-define(ATL_BLOCK_DEFAULT, true).
%%-----------------------------------------------------------------
@@ -872,43 +872,207 @@ get_agent_caps() ->
%%% Audit Trail Log functions
%%%-----------------------------------------------------------------
+-spec log_to_txt(LogDir :: snmp:dir()) ->
+ snmp:void().
+
log_to_txt(LogDir) ->
log_to_txt(LogDir, []).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()]) ->
+ snmp:void().
+
+log_to_txt(LogDir, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Mibs = [],
+ OutFile = "snmpa_log.txt",
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
+
log_to_txt(LogDir, Mibs) ->
+ Block = ?ATL_BLOCK_DEFAULT,
OutFile = "snmpa_log.txt",
LogName = ?audit_trail_log_name,
LogFile = ?audit_trail_log_file,
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ OutFile = "snmpa_log.txt",
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile) ->
+ Block = ?ATL_BLOCK_DEFAULT,
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
LogName = ?audit_trail_log_name,
LogFile = ?audit_trail_log_file,
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile, LogName) ->
+ Block = ?ATL_BLOCK_DEFAULT,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
LogFile = ?audit_trail_log_file,
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) ->
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Start :: snmp_log:log_time()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) ->
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Block :: boolean(),
+ Start :: snmp_log:log_time()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Start :: snmp_log:log_time(),
+ Stop :: snmp_log:log_time()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start);
+
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) ->
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Block :: boolean(),
+ Start :: snmp_log:log_time(),
+ Stop :: snmp_log:log_time()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
log_to_io(LogDir) ->
log_to_io(LogDir, []).
+
+log_to_io(LogDir, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Mibs = [],
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs) ->
+ Block = ?ATL_BLOCK_DEFAULT,
LogName = ?audit_trail_log_name,
LogFile = ?audit_trail_log_file,
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile).
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block).
+
+log_to_io(LogDir, Mibs, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs, LogName) ->
+ Block = ?ATL_BLOCK_DEFAULT,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block).
+
+log_to_io(LogDir, Mibs, LogName, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
LogFile = ?audit_trail_log_file,
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile).
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs, LogName, LogFile) ->
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs, LogName, LogFile, Start) ->
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start);
log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) ->
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) ->
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
log_info() ->
diff --git a/lib/snmp/src/agent/snmpa_local_db.erl b/lib/snmp/src/agent/snmpa_local_db.erl
index 5198c6ec4e..f991244287 100644
--- a/lib/snmp/src/agent/snmpa_local_db.erl
+++ b/lib/snmp/src/agent/snmpa_local_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -191,6 +191,12 @@ dets_open(DbDir, DbInitError, Opts) ->
end
end;
_ ->
+ case DbInitError of
+ create_db_and_dir ->
+ ok = filelib:ensure_dir(Filename);
+ _ ->
+ ok
+ end,
case do_dets_open(Name, Filename, Opts) of
{ok, Dets} ->
?vdebug("dets open done",[]),
diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl
index 2d37ea56f0..11ae806866 100644
--- a/lib/snmp/src/agent/snmpa_mpd.erl
+++ b/lib/snmp/src/agent/snmpa_mpd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -657,7 +657,7 @@ generate_response_msg(Vsn, RePdu, Type,
?SEC_USM ->
snmpa_usm
end,
- SecEngineID = LocalEngineID,
+ SecEngineID = LocalEngineID, % 3.1.1a
?vtrace("generate_response_msg -> SecEngineID: ~w", [SecEngineID]),
case (catch SecModule:generate_outgoing_msg(Message,
SecEngineID,
diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl
index aebcdbaa84..77ed54bee4 100644
--- a/lib/snmp/src/agent/snmpa_supervisor.erl
+++ b/lib/snmp/src/agent/snmpa_supervisor.erl
@@ -356,7 +356,7 @@ init([AgentType, Opts]) ->
SymStoreSpec =
worker_spec(snmpa_symbolic_store, SymStoreArgs, Restart, 2000),
- LdbArgs = [Prio, DbDir, LdbOpts],
+ LdbArgs = [Prio, DbDir, DbInitError, LdbOpts],
LocalDbSpec =
worker_spec(snmpa_local_db, LdbArgs, Restart, 5000),
diff --git a/lib/snmp/src/agent/snmpa_symbolic_store.erl b/lib/snmp/src/agent/snmpa_symbolic_store.erl
index 00178f4bcd..a922d62ba8 100644
--- a/lib/snmp/src/agent/snmpa_symbolic_store.erl
+++ b/lib/snmp/src/agent/snmpa_symbolic_store.erl
@@ -642,10 +642,10 @@ code_change(_Vsn, S, _Extra) ->
{ok, S}.
-stop_backup_server(undefined) ->
- ok;
-stop_backup_server({Pid, _}) when is_pid(Pid) ->
- exit(Pid, kill).
+%% stop_backup_server(undefined) ->
+%% ok;
+%% stop_backup_server({Pid, _}) when is_pid(Pid) ->
+%% exit(Pid, kill).
diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl
index 6f54307f9f..719ea4e356 100644
--- a/lib/snmp/src/agent/snmpa_usm.erl
+++ b/lib/snmp/src/agent/snmpa_usm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,6 +16,9 @@
%%
%% %CopyrightEnd%
%%
+%% AES: RFC 3826
+%%
+
-module(snmpa_usm).
%% Avoid warning for local function error/1 clashing with autoimported BIF.
@@ -652,7 +655,10 @@ get_des_salt() ->
[?i32(EngineBoots), ?i32(SaltInt)].
aes_encrypt(PrivKey, Data) ->
- snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0).
+ EngineBoots = snmp_framework_mib:get_engine_boots(),
+ EngineTime = snmp_framework_mib:get_engine_time(),
+ snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0,
+ EngineBoots, EngineTime).
aes_decrypt(PrivKey, UsmSecParams, EncData) ->
#usmSecurityParameters{msgPrivacyParameters = PrivParams,
diff --git a/lib/snmp/src/app/Makefile b/lib/snmp/src/app/Makefile
index 716add8b9e..b8cc4b8754 100644
--- a/lib/snmp/src/app/Makefile
+++ b/lib/snmp/src/app/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2012. All Rights Reserved.
+# Copyright Ericsson AB 2003-2013. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -79,6 +79,8 @@ endif
# FLAGS
# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/snmp/ebin
+
ifeq ($(WARN_UNUSED_VARS),true)
ERL_COMPILE_FLAGS += +warn_unused_vars
endif
diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index 6edcf7e833..fa4b72ab68 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -27,26 +27,10 @@
%% {load_module, snmp_pdus, soft_purge, soft_purge, []}
%% {update, snmpa_local_db, soft, soft_purge, soft_purge, []}
%% {add_module, snmpm_net_if_mt}
-
[
- {"4.24.1",
- [
- {load_module, snmpa, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.24",
- [
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []}
- ]
- },
+ {"4.24.2", [{restart_application, snmp}]},
+ {"4.24.1", [{restart_application, snmp}]},
+ {"4.24", [{restart_application, snmp}]},
{"4.23.1", [{restart_application, snmp}]},
{"4.23", [{restart_application, snmp}]}
],
@@ -57,24 +41,9 @@
%% {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
[
- {"4.24.1",
- [
- {load_module, snmpa, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.24",
- [
- {load_module, snmp_conf, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
- [snmp_conf]},
- {load_module, snmpa, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_local_db, soft, soft_purge, soft_purge, []},
- {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_agent]},
- {update, snmpa_mib, soft, soft_purge, soft_purge, []}
- ]
- },
+ {"4.24.2", [{restart_application, snmp}]},
+ {"4.24.1", [{restart_application, snmp}]},
+ {"4.24", [{restart_application, snmp}]},
{"4.23.1", [{restart_application, snmp}]},
{"4.23", [{restart_application, snmp}]}
]
diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl
index 1bb562654a..8b3a8af77d 100644
--- a/lib/snmp/src/app/snmp.erl
+++ b/lib/snmp/src/app/snmp.erl
@@ -49,8 +49,8 @@
read_mib/1,
- log_to_txt/5, log_to_txt/6, log_to_txt/7,
- log_to_io/4, log_to_io/5, log_to_io/6,
+ log_to_txt/5, log_to_txt/6, log_to_txt/7, log_to_txt/8,
+ log_to_io/4, log_to_io/5, log_to_io/6, log_to_io/7,
change_log_size/2,
octet_string_to_bits/1, bits_to_octet_string/1,
@@ -91,7 +91,31 @@
]).
-export_type([
+ dir/0,
+ snmp_timer/0,
+
+ engine_id/0,
+ tdomain/0,
+ community/0,
+ mms/0,
+ version/0,
+ sec_model/0,
+ sec_name/0,
+ sec_level/0,
+
oid/0,
+ varbind/0,
+ ivarbind/0,
+ asn1_type/0,
+ table_info/0,
+ variable_info/0,
+ me/0,
+ trap/0,
+ notification/0,
+ pdu/0,
+ trappdu/0,
+ mib/0,
+ mib_name/0,
void/0
]).
@@ -148,15 +172,42 @@
-define(APPLICATION, snmp).
+-define(ATL_BLOCK_DEFAULT, true).
+-include_lib("snmp/include/snmp_types.hrl").
%%-----------------------------------------------------------------
%% Types
%%-----------------------------------------------------------------
--type oid() :: [non_neg_integer()].
--type void() :: term().
+-type dir() :: string().
+-type snmp_timer() :: #snmp_incr_timer{}.
+
+-type engine_id() :: string().
+-type tdomain() :: transportDomainUdpIpv4 | transportDomainUdpIpv6.
+-type community() :: string().
+-type mms() :: non_neg_integer().
+-type version() :: v1 | v2 | v3.
+-type sec_model() :: any | v1 | v2c | usm.
+-type sec_name() :: string().
+-type sec_level() :: noAuthNoPriv | authNoPriv | authPriv.
+
+-type oid() :: [non_neg_integer()].
+-type varbind() :: #varbind{}.
+-type ivarbind() :: #ivarbind{}.
+-type asn1_type() :: #asn1_type{}.
+-type table_info() :: #table_info{}.
+-type variable_info() :: #variable_info{}.
+-type me() :: #me{}.
+-type trap() :: #trap{}.
+-type notification() :: #notification{}.
+-type mib() :: #mib{}.
+-type mib_name() :: string().
+-type pdu() :: #pdu{}.
+-type trappdu() :: #trappdu{}.
+
+-type void() :: term().
%%-----------------------------------------------------------------
@@ -854,18 +905,60 @@ read_mib(FileName) ->
%%%-----------------------------------------------------------------
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) ->
- snmp_log:log_to_txt(LogName, LogFile, LogDir, Mibs, OutFile).
+ Block = ?ATL_BLOCK_DEFAULT,
+ Start = null,
+ Stop = null,
+ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Start = null,
+ Stop = null,
+ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) ->
- snmp_log:log_to_txt(LogName, LogFile, LogDir, Mibs, OutFile, Start).
+ Block = ?ATL_BLOCK_DEFAULT,
+ Stop = null,
+ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Stop = null,
+ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) ->
- snmp_log:log_to_txt(LogName, LogFile, LogDir, Mibs, OutFile, Start, Stop).
+ Block = ?ATL_BLOCK_DEFAULT,
+ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) ->
+ snmp_log:log_to_txt(LogName, Block, LogFile, LogDir, Mibs, OutFile,
+ Start, Stop).
+
log_to_io(LogDir, Mibs, LogName, LogFile) ->
- snmp_log:log_to_io(LogName, LogFile, LogDir, Mibs).
+ Block = ?ATL_BLOCK_DEFAULT,
+ Start = null,
+ Stop = null,
+ log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Start = null,
+ Stop = null,
+ log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop);
log_to_io(LogDir, Mibs, LogName, LogFile, Start) ->
- snmp_log:log_to_io(LogName, LogFile, LogDir, Mibs, Start).
+ Block = ?ATL_BLOCK_DEFAULT,
+ Stop = null,
+ log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Stop = null,
+ log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop);
log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) ->
- snmp_log:log_to_io(LogName, LogFile, LogDir, Mibs, Start, Stop).
+ Block = ?ATL_BLOCK_DEFAULT,
+ log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) ->
+ snmp_log:log_to_io(LogName, Block, LogFile, LogDir, Mibs, Start, Stop).
change_log_size(LogName, NewSize) ->
snmp_log:change_size(LogName, NewSize).
@@ -878,12 +971,12 @@ change_log_size(LogName, NewSize) ->
%% Usage: erl -s snmp str_apply '{Mod,Func,ArgList}'
str_apply([Atom]) ->
Str = atom_to_list(Atom),
- {Mod,Func,Args} = to_erlang_term(Str),
- apply(Mod,Func,Args).
+ {Mod, Func, Args} = to_erlang_term(Str),
+ apply(Mod, Func, Args).
to_erlang_term(String) ->
{ok, Tokens, _} = erl_scan:string(lists:append([String, ". "])),
- {ok,Term} = erl_parse:parse_term(Tokens),
+ {ok, Term} = erl_parse:parse_term(Tokens),
Term.
diff --git a/lib/snmp/src/app/snmp_internal.hrl b/lib/snmp/src/app/snmp_internal.hrl
index 5ff715e0b7..f04fa4dd53 100644
--- a/lib/snmp/src/app/snmp_internal.hrl
+++ b/lib/snmp/src/app/snmp_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,8 @@
-define(APPLICATION, snmp).
-endif.
+-define(STACK(), erlang:get_stacktrace()).
+
-define(snmp_info(C, F, A), ?snmp_msg(info_msg, C, F, A)).
-define(snmp_warning(C, F, A), ?snmp_msg(warning_msg, C, F, A)).
-define(snmp_error(C, F, A), ?snmp_msg(error_msg, C, F, A)).
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 6ac0115dad..c97b635fc6 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -75,11 +75,10 @@
%%
%% Logging
- log_to_txt/1,
- log_to_txt/2, log_to_txt/3, log_to_txt/4,
- log_to_txt/5, log_to_txt/6, log_to_txt/7,
- log_to_io/1, log_to_io/2, log_to_io/3,
- log_to_io/4, log_to_io/5, log_to_io/6,
+ log_to_txt/1, log_to_txt/2, log_to_txt/3, log_to_txt/4,
+ log_to_txt/5, log_to_txt/6, log_to_txt/7, log_to_txt/8,
+ log_to_io/1, log_to_io/2, log_to_io/3, log_to_io/4,
+ log_to_io/5, log_to_io/6, log_to_io/7,
change_log_size/1,
get_log_type/0,
set_log_type/1,
@@ -111,6 +110,12 @@
-export([start_link/3, snmpm_start_verify/2, snmpm_start_verify/3]).
-export([target_name/1, target_name/2]).
+-export_type([
+ register_timeout/0,
+ agent_config/0,
+ target_name/0
+ ]).
+
-include_lib("snmp/src/misc/snmp_debug.hrl").
-include_lib("snmp/include/snmp_types.hrl").
@@ -119,6 +124,26 @@
-include("snmp_verbosity.hrl").
-define(DEFAULT_AGENT_PORT, 161).
+-define(ATL_BLOCK_DEFAULT, true).
+
+
+%%-----------------------------------------------------------------
+%% Types
+%%-----------------------------------------------------------------
+
+-type register_timeout() :: pos_integer() | snmp:snmp_timer().
+-type agent_config() :: {engine_id, snmp:engine_id()} | % Mandatory
+ {address, inet:ip_address()} | % Mandatory
+ {port, inet:port_number()} | % Optional
+ {tdomain, snmp:tdomain()} | % Optional
+ {community, snmp:community()} | % Optional
+ {timeout, register_timeout()} | % Optional
+ {max_message_size, snmp:mms()} | % Optional
+ {version, snmp:version()} | % Optional
+ {sec_moduel, snmp:sec_model()} | % Optional
+ {sec_name, snmp:sec_name()} | % Optional
+ {sec_level, snmp:sec_level()}. % Optional
+-type target_name() :: string().
%% This function is called when the snmp application
@@ -762,43 +787,204 @@ cancel_async_request(UserId, ReqId) ->
%%% Audit Trail Log functions (for backward compatibility)
%%%-----------------------------------------------------------------
+-spec log_to_txt(LogDir :: snmp:dir()) ->
+ snmp:void().
+
log_to_txt(LogDir) ->
log_to_txt(LogDir, []).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()]) ->
+ snmp:void().
+
+log_to_txt(LogDir, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Mibs = [],
+ OutFile = "snmpm_log.txt",
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs) ->
+ Block = ?ATL_BLOCK_DEFAULT,
+ OutFile = "snmpm_log.txt",
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
OutFile = "snmpm_log.txt",
LogName = ?audit_trail_log_name,
LogFile = ?audit_trail_log_file,
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile) ->
+ Block = ?ATL_BLOCK_DEFAULT,
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
LogName = ?audit_trail_log_name,
LogFile = ?audit_trail_log_file,
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile, LogName) ->
+ Block = ?ATL_BLOCK_DEFAULT,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
LogFile = ?audit_trail_log_file,
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile) ->
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Block :: boolean()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Start :: snmp_log:log_time()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) ->
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Block :: boolean(),
+ Start :: snmp_log:log_time()) ->
+ snmp:void();
+ (LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Start :: snmp_log:log_time(),
+ Stop :: snmp_log:log_time()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start);
log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) ->
- snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
+
+-spec log_to_txt(LogDir :: snmp:dir(),
+ Mibs :: [snmp:mib_name()],
+ OutFile :: file:filename(),
+ LogName :: string(),
+ LogFile :: string(),
+ Block :: boolean(),
+ Start :: snmp_log:log_time(),
+ Stop :: snmp_log:log_time()) ->
+ snmp:void().
+
+log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop) ->
+ snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Block, Start, Stop).
log_to_io(LogDir) ->
log_to_io(LogDir, []).
+
+log_to_io(LogDir, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ Mibs = [],
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs) ->
LogName = ?audit_trail_log_name,
LogFile = ?audit_trail_log_file,
snmp:log_to_io(LogDir, Mibs, LogName, LogFile).
+
+log_to_io(LogDir, Mibs, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ LogName = ?audit_trail_log_name,
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs, LogName) ->
+ Block = ?ATL_BLOCK_DEFAULT,
LogFile = ?audit_trail_log_file,
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile).
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block).
+
+log_to_io(LogDir, Mibs, LogName, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ LogFile = ?audit_trail_log_file,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs, LogName, LogFile) ->
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block);
log_to_io(LogDir, Mibs, LogName, LogFile, Start) ->
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start);
log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) ->
- snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop).
+ Block = ?ATL_BLOCK_DEFAULT,
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
+
+log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop) ->
+ snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Block, Start, Stop).
change_log_size(NewSize) ->
diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl
index 736debe544..2101ad46e1 100644
--- a/lib/snmp/src/manager/snmpm_config.erl
+++ b/lib/snmp/src/manager/snmpm_config.erl
@@ -1215,6 +1215,12 @@ dets_open(Dir, DbInitError, Repair, AutoSave) ->
end
end;
_ ->
+ case DbInitError of
+ create_db_and_dir ->
+ ok = filelib:ensure_dir(Filename);
+ _ ->
+ ok
+ end,
case do_dets_open(Name, Filename, Repair, AutoSave) of
{ok, _Dets} ->
ok;
@@ -1316,7 +1322,14 @@ verify_option({server, ServerOpts}) ->
verify_server_opts(ServerOpts);
verify_option({note_store, NoteStoreOpts}) ->
verify_note_store_opts(NoteStoreOpts);
-verify_option({config, ConfOpts}) ->
+verify_option({config, ConfOpts0}) ->
+ %% Make sure any db_dir option is first in the options list to make it
+ %% easier to check if the db_init_error option specifies that a missing
+ %% db_dir should be created.
+ ConfOpts = case lists:keytake(db_dir, 1, ConfOpts0) of
+ false -> ConfOpts0;
+ {value, Result, OtherOpts} -> [Result|OtherOpts]
+ end,
verify_config_opts(ConfOpts);
verify_option({versions, Vsns}) ->
verify_versions(Vsns);
@@ -1365,7 +1378,12 @@ verify_config_opts([{dir, Dir}|Opts]) ->
verify_conf_dir(Dir),
verify_config_opts(Opts);
verify_config_opts([{db_dir, Dir}|Opts]) ->
- verify_conf_db_dir(Dir),
+ case lists:keyfind(db_init_error, 1, Opts) of
+ {db_init_error, create_db_and_dir} ->
+ verify_conf_db_dir(Dir, false);
+ _ ->
+ verify_conf_db_dir(Dir, true)
+ end,
verify_config_opts(Opts);
verify_config_opts([{db_init_error, DbInitErr}|Opts]) ->
verify_conf_db_init_error(DbInitErr),
@@ -1443,7 +1461,7 @@ verify_conf_dir(Dir) ->
error({invalid_conf_dir, Dir})
end.
-verify_conf_db_dir(Dir) ->
+verify_conf_db_dir(Dir, true) ->
case (catch verify_dir(Dir)) of
ok ->
ok;
@@ -1451,13 +1469,16 @@ verify_conf_db_dir(Dir) ->
error({invalid_conf_db_dir, Dir, Reason});
_ ->
error({invalid_conf_db_dir, Dir})
- end.
-
+ end;
+verify_conf_db_dir(_Dir, false) ->
+ ok.
verify_conf_db_init_error(terminate) ->
ok;
verify_conf_db_init_error(create) ->
ok;
+verify_conf_db_init_error(create_db_and_dir) ->
+ ok;
verify_conf_db_init_error(InvalidDbInitError) ->
error({invalid_conf_db_init_error, InvalidDbInitError}).
diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl
index 61d22362cc..9c79df2748 100644
--- a/lib/snmp/src/manager/snmpm_server.erl
+++ b/lib/snmp/src/manager/snmpm_server.erl
@@ -488,7 +488,7 @@ cancel_async_request(UserId, ReqId) ->
%% discovery(UserId, BAddr, Port, Config, Expire, ExtraInfo) ->
%% call({discovery, self(), UserId, BAddr, Port, Config, Expire, ExtraInfo}).
-
+
verbosity(Verbosity) ->
case ?vvalidate(Verbosity) of
Verbosity ->
@@ -1851,7 +1851,17 @@ handle_snmp_error(Addr, Port, ReqId, Reason, State) ->
handle_error(_UserId, Mod, Reason, ReqId, Data, _State) ->
?vtrace("handle_error -> entry when"
"~n Mod: ~p", [Mod]),
- F = fun() -> (catch Mod:handle_error(ReqId, Reason, Data)) end,
+ F = fun() ->
+ try
+ begin
+ Mod:handle_error(ReqId, Reason, Data)
+ end
+ catch
+ T:E ->
+ CallbackArgs = [ReqId, Reason, Data],
+ handle_invalid_result(handle_error, CallbackArgs, T, E)
+ end
+ end,
handle_callback(F),
ok.
@@ -2031,7 +2041,15 @@ handle_pdu(_UserId, Mod, target_name = _RegType, TargetName, _Addr, _Port,
?vtrace("handle_pdu(target_name) -> entry when"
"~n Mod: ~p", [Mod]),
F = fun() ->
- (catch Mod:handle_pdu(TargetName, ReqId, SnmpResponse, Data))
+ try
+ begin
+ Mod:handle_pdu(TargetName, ReqId, SnmpResponse, Data)
+ end
+ catch
+ T:E ->
+ CallbackArgs = [TargetName, ReqId, SnmpResponse, Data],
+ handle_invalid_result(handle_pdu, CallbackArgs, T, E)
+ end
end,
handle_callback(F),
ok;
@@ -2064,8 +2082,37 @@ do_handle_agent(DefUserId, DefMod,
SnmpInfo, DefData, State) ->
?vdebug("do_handle_agent -> entry when"
"~n DefUserId: ~p", [DefUserId]),
- case (catch DefMod:handle_agent(Addr, Port, Type, SnmpInfo, DefData)) of
- {'EXIT', {undef, _}} when Type =:= pdu ->
+ try DefMod:handle_agent(Addr, Port, Type, SnmpInfo, DefData) of
+ {register, UserId2, TargetName, Config} ->
+ ?vtrace("do_handle_agent -> register: "
+ "~n UserId2: ~p"
+ "~n TargetName: ~p"
+ "~n Config: ~p",
+ [UserId2, TargetName, Config]),
+ Config2 = ensure_present([{address, Addr}, {port, Port}], Config),
+ Config3 = [{reg_type, target_name} | Config2],
+ case snmpm_config:register_agent(UserId2,
+ TargetName, Config3) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ error_msg("failed registering agent - "
+ "handling agent "
+ "~p <~p,~p>: ~n~w",
+ [TargetName, Addr, Port, Reason]),
+ ok
+ end;
+
+ ignore ->
+ ?vdebug("do_handle_agent -> ignore", []),
+ ok;
+
+ InvalidResult ->
+ CallbackArgs = [Addr, Port, Type, SnmpInfo, DefData],
+ handle_invalid_result(handle_agent, CallbackArgs, InvalidResult)
+
+ catch
+ error:{undef, _} when Type =:= pdu ->
%% Maybe, still on the old API
?vdebug("do_handle_agent -> maybe still on the old api", []),
case (catch DefMod:handle_agent(Addr, Port, SnmpInfo, DefData)) of
@@ -2113,10 +2160,10 @@ do_handle_agent(DefUserId, DefMod,
ok
end;
- {'EXIT', {undef, _}} ->
+ error:{undef, _} ->
%% If the user does not implement the new API (but the
%% old), then this clause catches all non-pdu handle_agent
- %% calls. These calls was previously never made,so we make
+ %% calls. These calls was previously never made, so we make
%% a best-effert call (using reg-type target_name) to the
%% various callback functions, and leave it to the user to
%% figure out
@@ -2148,31 +2195,11 @@ do_handle_agent(DefUserId, DefMod,
"regarding agent "
"<~p,~p>: ~n~w", [Type, Addr, Port, SnmpInfo])
end;
-
- {register, UserId2, TargetName, Config} ->
- ?vtrace("do_handle_agent -> register: "
- "~n UserId2: ~p"
- "~n TargetName: ~p"
- "~n Config: ~p",
- [UserId2, TargetName, Config]),
- Config2 = ensure_present([{address, Addr}, {port, Port}], Config),
- Config3 = [{reg_type, target_name} | Config2],
- case snmpm_config:register_agent(UserId2,
- TargetName, Config3) of
- ok ->
- ok;
- {error, Reason} ->
- error_msg("failed registering agent - "
- "handling agent "
- "~p <~p,~p>: ~n~w",
- [TargetName, Addr, Port, Reason]),
- ok
- end;
- _Ignore ->
- ?vdebug("do_handle_agent -> ignore", []),
- ok
-
+ T:E ->
+ CallbackArgs = [Addr, Port, Type, SnmpInfo, DefData],
+ handle_invalid_result(handle_agent, CallbackArgs, T, E)
+
end.
ensure_present([], Config) ->
@@ -2305,15 +2332,17 @@ do_handle_trap(UserId, Mod,
RegType, Target, Addr, Port, SnmpTrapInfo, Data, _State) ->
?vdebug("do_handle_trap -> entry with"
"~n UserId: ~p", [UserId]),
- HandleTrap =
+ {HandleTrap, CallbackArgs} =
case RegType of
target_name ->
- fun() -> Mod:handle_trap(Target, SnmpTrapInfo, Data) end;
+ {fun() -> Mod:handle_trap(Target, SnmpTrapInfo, Data) end,
+ [Target, SnmpTrapInfo, Data]};
addr_port ->
- fun() -> Mod:handle_trap(Addr, Port, SnmpTrapInfo, Data) end
+ {fun() -> Mod:handle_trap(Addr, Port, SnmpTrapInfo, Data) end,
+ [Addr, Port, SnmpTrapInfo, Data]}
end,
- case (catch HandleTrap()) of
+ try HandleTrap() of
{register, UserId2, Config} ->
?vtrace("do_handle_trap -> register: "
"~n UserId2: ~p"
@@ -2362,9 +2391,17 @@ do_handle_trap(UserId, Mod,
[Addr, Port, Reason]),
ok
end;
- _Ignore ->
+ ignore ->
?vtrace("do_handle_trap -> ignore", []),
- ok
+ ok;
+
+ InvalidResult ->
+ handle_invalid_result(handle_trap, CallbackArgs, InvalidResult)
+
+ catch
+ T:E ->
+ handle_invalid_result(handle_trap, CallbackArgs, T, E)
+
end.
@@ -2465,16 +2502,18 @@ do_handle_inform(UserId, Mod, Ref,
RegType, Target, Addr, Port, SnmpInform, Data, State) ->
?vdebug("do_handle_inform -> entry with"
"~n UserId: ~p", [UserId]),
- HandleInform =
+ {HandleInform, CallbackArgs} =
case RegType of
target_name ->
- fun() -> Mod:handle_inform(Target, SnmpInform, Data) end;
+ {fun() -> Mod:handle_inform(Target, SnmpInform, Data) end,
+ [Target, SnmpInform, Data]};
addr_port ->
- fun() -> Mod:handle_inform(Addr, Port, SnmpInform, Data) end
+ {fun() -> Mod:handle_inform(Addr, Port, SnmpInform, Data) end,
+ [Addr, Port, SnmpInform, Data]}
end,
Rep =
- case (catch HandleInform()) of
+ try HandleInform() of
{register, UserId2, Config} ->
?vtrace("do_handle_inform -> register: "
"~n UserId2: ~p"
@@ -2494,6 +2533,7 @@ do_handle_inform(UserId, Mod, Ref,
[Target2, Addr, Port, Reason]),
reply
end;
+
{register, UserId2, Target2, Config} ->
?vtrace("do_handle_inform -> register: "
"~n UserId2: ~p"
@@ -2512,6 +2552,7 @@ do_handle_inform(UserId, Mod, Ref,
[Target2, Addr, Port, Reason]),
reply
end;
+
unregister ->
?vtrace("do_handle_inform -> unregister", []),
case snmpm_config:unregister_agent(UserId,
@@ -2525,12 +2566,25 @@ do_handle_inform(UserId, Mod, Ref,
[Addr, Port, Reason]),
reply
end;
+
no_reply ->
?vtrace("do_handle_inform -> no_reply", []),
no_reply;
- _Ignore ->
+
+ ignore ->
?vtrace("do_handle_inform -> ignore", []),
+ reply;
+
+ InvalidResult ->
+ handle_invalid_result(handle_inform, CallbackArgs,
+ InvalidResult),
reply
+
+ catch
+ T:E ->
+ handle_invalid_result(handle_inform, CallbackArgs, T, E),
+ reply
+
end,
handle_inform_response(Rep, Ref, Addr, Port, State),
ok.
@@ -2760,15 +2814,17 @@ do_handle_report(UserId, Mod,
RegType, Target, Addr, Port, SnmpReport, Data, _State) ->
?vdebug("do_handle_report -> entry with"
"~n UserId: ~p", [UserId]),
- HandleReport =
+ {HandleReport, CallbackArgs} =
case RegType of
target_name ->
- fun() -> Mod:handle_report(Target, SnmpReport, Data) end;
+ {fun() -> Mod:handle_report(Target, SnmpReport, Data) end,
+ [Target, SnmpReport, Data]};
addr_port ->
- fun() -> Mod:handle_report(Addr, Port, SnmpReport, Data) end
+ {fun() -> Mod:handle_report(Addr, Port, SnmpReport, Data) end,
+ [Addr, Port, SnmpReport, Data]}
end,
- case (catch HandleReport()) of
+ try HandleReport() of
{register, UserId2, Config} ->
?vtrace("do_handle_report -> register: "
"~n UserId2: ~p"
@@ -2788,6 +2844,7 @@ do_handle_report(UserId, Mod,
[Addr, Port, Reason]),
ok
end;
+
{register, UserId2, Target2, Config} ->
?vtrace("do_handle_report -> register: "
"~n UserId2: ~p"
@@ -2806,6 +2863,7 @@ do_handle_report(UserId, Mod,
[Target2, Addr, Port, Reason]),
reply
end;
+
unregister ->
?vtrace("do_handle_trap -> unregister", []),
case snmpm_config:unregister_agent(UserId,
@@ -2819,9 +2877,20 @@ do_handle_report(UserId, Mod,
[Addr, Port, Reason]),
ok
end;
- _Ignore ->
+
+ ignore ->
?vtrace("do_handle_report -> ignore", []),
- ok
+ ok;
+
+ InvalidResult ->
+ handle_invalid_result(handle_report, CallbackArgs, InvalidResult),
+ reply
+
+ catch
+ T:E ->
+ handle_invalid_result(handle_report, CallbackArgs, T, E),
+ reply
+
end.
@@ -2835,6 +2904,25 @@ handle_callback(F) ->
end).
+
+handle_invalid_result(Func, Args, T, E) ->
+ Stacktrace = ?STACK(),
+ error_msg("Callback function failed: "
+ "~n Function: ~p"
+ "~n Args: ~p"
+ "~n Error Type: ~p"
+ "~n Error: ~p"
+ "~n Stacktrace: ~p",
+ [Func, Args, T, E, Stacktrace]).
+
+handle_invalid_result(Func, Args, InvalidResult) ->
+ error_msg("Callback function returned invalid result: "
+ "~n Function: ~p"
+ "~n Args: ~p"
+ "~n Invalid result: ~p",
+ [Func, Args, InvalidResult]).
+
+
handle_down(MonRef) ->
(catch do_handle_down(MonRef)).
diff --git a/lib/snmp/src/manager/snmpm_user.erl b/lib/snmp/src/manager/snmpm_user.erl
index 78aa560b2e..e6b0b6943e 100644
--- a/lib/snmp/src/manager/snmpm_user.erl
+++ b/lib/snmp/src/manager/snmpm_user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,79 +19,100 @@
-module(snmpm_user).
--export([behaviour_info/1]).
-
-behaviour_info(callbacks) ->
- [{handle_error, 3},
- {handle_agent, 5},
- {handle_pdu, 4},
- {handle_trap, 3},
- {handle_inform, 3},
- {handle_report, 3}];
-behaviour_info(_) ->
- undefined.
-
-
-%% handle_error(ReqId, Reason, UserData) -> Reply
-%% ReqId -> integer()
-%% Reason -> term()
-%% UserData -> term() (supplied when the user register)
-%% Reply -> ignore
-
-%% handle_agent(Addr, Port, Type, SnmpInfo, UserData) -> Reply
-%% Addr -> term()
-%% Port -> integer()
-%% Type -> pdu | trap | inform | report
-%% SnmpInfo -> {ErrorStatus, ErrorIndex, Varbinds}
-%% UserId -> term()
-%% ErrorStatus -> atom()
-%% ErrorIndex -> integer()
-%% Varbinds -> [varbind()]
-%% UserData -> term() (supplied when the user register)
-%% Reply -> ignore | {register, UserId, agent_info()}
-%% agent_info() -> [{agent_info_item(), agent_info_value()}]
-%% This is the same info as in update_agent_info/4
-
-%% handle_pdu(TargetName, ReqId, SnmpResponse, UserData) -> Reply
-%% TargetName -> target_name()
-%% ReqId -> term() (returned when calling ag(...), ...)
-%% SnmpResponse -> {ErrorStatus, ErrorIndex, Varbinds}
-%% ErrorStatus -> atom()
-%% ErrorIndex -> integer()
-%% Varbinds -> [varbind()]
-%% UserData -> term() (supplied when the user register)
-%% Reply -> ignore
-
-%% handle_trap(TargetName, SnmpTrapInfo, UserData) -> Reply
-%% TargetName -> target_name()
-%% SnmpTrapInfo -> {Enteprise, Generic, Spec, Timestamp, Varbinds} |
-%% {ErrorStatus, ErrorIndex, Varbinds}
-%% Enteprise -> oid()
-%% Generic -> integer()
-%% Spec -> integer()
-%% Timestamp -> integer()
-%% ErrorStatus -> atom()
-%% ErrorIndex -> integer()
-%% Varbinds -> [varbind()]
-%% UserData -> term() (supplied when the user register)
-%% Reply -> ignore | unregister | {register, UserId, agent_info()}
-
-%% handle_inform(TargetName, SnmpInform, UserData) -> Reply
-%% TargetName -> target_name()
-%% SnmpInform -> {ErrorStatus, ErrorIndex, Varbinds}
-%% ErrorStatus -> atom()
-%% ErrorIndex -> integer()
-%% Varbinds -> [varbind()]
-%% UserData -> term() (supplied when the user register)
-%% Reply -> ignore | unregister | {register, UserId, agent_info()}
-%%
-
-%% handle_report(TargetName, SnmpReport, UserData) -> Reply
-%% TargetName -> target_name()
-%% SnmpReport -> {ErrorStatus, ErrorIndex, Varbinds}
-%% ErrorStatus -> integer()
-%% ErrorIndex -> integer()
-%% Varbinds -> [varbind()]
-%% UserData -> term() (supplied when the user register)
-%% Reply -> ignore | unregister | {register, UserId, agent_info()}
+-export_type([
+ snmp_gen_info/0,
+ snmp_v1_trap_info/0
+ ]).
+
+-type snmp_gen_info() :: {ErrorStatus :: atom(),
+ ErrorIndex :: pos_integer(),
+ Varbinds :: [snmp:varbind()]}.
+-type snmp_v1_trap_info() :: {Enteprise :: snmp:oid(),
+ Generic :: integer(),
+ Spec :: integer(),
+ Timestamp :: integer(),
+ Varbinds :: [snmp:varbind()]}.
+-type ip_address() :: inet:ip_address().
+-type port_number() :: inet:port_number().
+
+
+%% *** handle_error ***
+%% An "asynchronous" error has been detected
+
+-callback handle_error(ReqId :: integer(),
+ Reason :: {unexpected_pdu, SnmpInfo :: snmp_gen_info()} |
+ {invalid_sec_info, SecInfo :: term(), SnmpInfo :: snmp_gen_info()} |
+ {empty_message, Addr :: ip_address(), Port :: port_number()} |
+ term(),
+ UserData :: term()) ->
+ snmp:void().
+
+
+%% *** handle_agent ***
+%% A message was received from an unknown agent
+
+-callback handle_agent(Addr :: term(),
+ Port :: pos_integer(),
+ Type :: pdu | trap | inform | report,
+ SnmpInfo :: snmp_gen_info() | snmp_v1_trap_info(),
+ UserData :: term()) ->
+ Reply :: ignore |
+ {register,
+ UserId :: term(),
+ RTargetName :: snmpm:target_name(),
+ AgentConfig :: [snmpm:agent_config()]}.
+
+
+%% *** handle_pdu ***
+%% Handle the reply to an async request (such as get, get-next and set).
+
+-callback handle_pdu(TargetName :: snmpm:target_name(),
+ ReqId :: term(),
+ SnmpResponse :: snmp_gen_info(),
+ UserData :: term()) ->
+ snmp:void().
+
+
+%% *** handle_trap ***
+%% Handle a trap/notification message received from an agent
+
+-callback handle_trap(TargetName :: snmpm:target_name(),
+ SnmpTrapInfo :: snmp_gen_info() | snmp_v1_trap_info(),
+ UserData :: term()) ->
+ Reply :: ignore |
+ unregister |
+ {register,
+ UserId :: term(),
+ RTargetName :: snmpm:target_name(),
+ AgentConfig :: [snmpm:agent_config()]}.
+
+
+%% *** handle_inform ***
+%% Handle a inform message received from an agent
+
+-callback handle_inform(TargetName :: snmpm:target_name(),
+ SnmpInform :: snmp_gen_info(),
+ UserData :: term()) ->
+ Reply :: ignore | no_reply |
+ unregister |
+ {register,
+ UserId :: term(),
+ RTargetName :: snmpm:target_name(),
+ AgentConfig :: [snmpm:agent_config()]}.
+
+
+%% *** handle_report ***
+%% Handle a report message received from an agent
+
+-callback handle_report(TargetName :: snmpm:target_name(),
+ SnmpReport :: snmp_gen_info(),
+ UserData :: term()) ->
+ Reply :: ignore |
+ unregister |
+ {register,
+ UserId :: term(),
+ RTargetName :: snmpm:target_name(),
+ AgentConfig :: [snmpm:agent_config()]}.
+
+
diff --git a/lib/snmp/src/manager/snmpm_usm.erl b/lib/snmp/src/manager/snmpm_usm.erl
index 497d6d6102..0a8a6436a3 100644
--- a/lib/snmp/src/manager/snmpm_usm.erl
+++ b/lib/snmp/src/manager/snmpm_usm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,6 +19,9 @@
%%-----------------------------------------------------------------
%% This module implements the User Based Security Model for SNMP,
%% as defined in rfc2274.
+%%
+%% AES: RFC 3826
+%%
%%-----------------------------------------------------------------
-module(snmpm_usm).
@@ -416,11 +419,14 @@ get_des_salt() ->
[?i32(EngineBoots), ?i32(SaltInt)].
aes_encrypt(PrivKey, Data) ->
- snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0).
+ EngineBoots = get_engine_boots(),
+ EngineTime = get_engine_time(),
+ snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0,
+ EngineBoots, EngineTime).
aes_decrypt(PrivKey, UsmSecParams, EncData) ->
- #usmSecurityParameters{msgPrivacyParameters = MsgPrivParams,
- msgAuthoritativeEngineTime = EngineTime,
+ #usmSecurityParameters{msgPrivacyParameters = MsgPrivParams,
+ msgAuthoritativeEngineTime = EngineTime,
msgAuthoritativeEngineBoots = EngineBoots} =
UsmSecParams,
snmp_usm:aes_decrypt(PrivKey, MsgPrivParams, EncData,
diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl
index 945b8719fc..a222f842e5 100644
--- a/lib/snmp/src/misc/snmp_config.erl
+++ b/lib/snmp/src/misc/snmp_config.erl
@@ -233,16 +233,18 @@ config_agent_sys() ->
fun verify_verbosity/1),
DbDir = ask("5. Database directory (absolute path)?", DefDir,
fun verify_dir/1),
- MibStorageType = ask("6. Mib storage type (ets/dets/mnesia)?", "ets",
+ DbInitError = ask("6. How to handle DB init error?",
+ "terminate", fun verify_db_init_error/1),
+ MibStorageType = ask("7. Mib storage type (ets/dets/mnesia)?", "ets",
fun verify_mib_storage_type/1),
MibStorage =
case MibStorageType of
ets ->
[{module, snmpa_mib_storage_ets}];
dets ->
- DetsDir = ask("6b. Mib storage directory (absolute path)?",
+ DetsDir = ask("7b. Mib storage directory (absolute path)?",
DbDir, fun verify_dir/1),
- DetsAction = ask("6c. Mib storage [dets] database start "
+ DetsAction = ask("7c. Mib storage [dets] database start "
"action "
"(default/clear/keep)?",
"default", fun verify_mib_storage_action/1),
@@ -257,7 +259,7 @@ config_agent_sys() ->
end;
mnesia ->
Nodes = [],
- MnesiaAction = ask("6b. Mib storage [mnesia] database start "
+ MnesiaAction = ask("7b. Mib storage [mnesia] database start "
"action "
"(default/clear/keep)?",
"default", fun verify_mib_storage_action/1),
@@ -275,80 +277,80 @@ config_agent_sys() ->
%% Here we should ask about mib-server data module,
%% but as we only have one at the moment...
- TargetCacheVerb = ask("7. Target cache verbosity "
+ TargetCacheVerb = ask("8. Target cache verbosity "
"(silence/info/log/debug/trace)?", "silence",
fun verify_verbosity/1),
- SymStoreVerb = ask("8. Symbolic store verbosity "
+ SymStoreVerb = ask("9. Symbolic store verbosity "
"(silence/info/log/debug/trace)?", "silence",
fun verify_verbosity/1),
- LocalDbVerb = ask("9. Local DB verbosity "
+ LocalDbVerb = ask("10. Local DB verbosity "
"(silence/info/log/debug/trace)?", "silence",
fun verify_verbosity/1),
- LocalDbRepair = ask("10. Local DB repair (true/false/force)?", "true",
+ LocalDbRepair = ask("11. Local DB repair (true/false/force)?", "true",
fun verify_dets_repair/1),
- LocalDbAutoSave = ask("11. Local DB auto save (infinity/milli seconds)?",
+ LocalDbAutoSave = ask("12. Local DB auto save (infinity/milli seconds)?",
"5000", fun verify_dets_auto_save/1),
- ErrorMod = ask("12. Error report module?", "snmpa_error_logger", fun verify_module/1),
- Type = ask("13. Agent type (master/sub)?", "master",
+ ErrorMod = ask("13. Error report module?", "snmpa_error_logger", fun verify_module/1),
+ Type = ask("14. Agent type (master/sub)?", "master",
fun verify_agent_type/1),
AgentConfig =
case Type of
master ->
- MasterAgentVerb = ask("14. Master-agent verbosity "
+ MasterAgentVerb = ask("15. Master-agent verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
- ForceLoad = ask("15. Shall the agent re-read the "
+ ForceLoad = ask("16. Shall the agent re-read the "
"configuration files during startup ~n"
" (and ignore the configuration "
"database) (true/false)?", "true",
fun verify_bool/1),
- MultiThreaded = ask("16. Multi threaded agent (true/false)?",
+ MultiThreaded = ask("17. Multi threaded agent (true/false)?",
"false",
fun verify_bool/1),
- MeOverride = ask("17. Check for duplicate mib entries when "
+ MeOverride = ask("18. Check for duplicate mib entries when "
"installing a mib (true/false)?", "false",
fun verify_bool/1),
- TrapOverride = ask("18. Check for duplicate trap names when "
+ TrapOverride = ask("19. Check for duplicate trap names when "
"installing a mib (true/false)?", "false",
fun verify_bool/1),
- MibServerVerb = ask("19. Mib server verbosity "
+ MibServerVerb = ask("20. Mib server verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
- MibServerCache = ask("20. Mib server cache "
+ MibServerCache = ask("21. Mib server cache "
"(true/false)?",
"true",
fun verify_bool/1),
- NoteStoreVerb = ask("21. Note store verbosity "
+ NoteStoreVerb = ask("22. Note store verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
- NoteStoreTimeout = ask("22. Note store GC timeout?", "30000",
+ NoteStoreTimeout = ask("23. Note store GC timeout?", "30000",
fun verify_timeout/1),
ATL =
- case ask("23. Shall the agent use an audit trail log "
+ case ask("24. Shall the agent use an audit trail log "
"(y/n)?",
"n", fun verify_yes_or_no/1) of
yes ->
- ATLType = ask("23b. Audit trail log type "
+ ATLType = ask("24b. Audit trail log type "
"(write/read_write)?",
"read_write", fun verify_atl_type/1),
- ATLDir = ask("23c. Where to store the "
+ ATLDir = ask("24c. Where to store the "
"audit trail log?",
DefDir, fun verify_dir/1),
- ATLMaxFiles = ask("23d. Max number of files?",
+ ATLMaxFiles = ask("24d. Max number of files?",
"10",
fun verify_pos_integer/1),
- ATLMaxBytes = ask("23e. Max size (in bytes) "
+ ATLMaxBytes = ask("24e. Max size (in bytes) "
"of each file?",
"10240",
fun verify_pos_integer/1),
ATLSize = {ATLMaxBytes, ATLMaxFiles},
- ATLRepair = ask("23f. Audit trail log repair "
+ ATLRepair = ask("24f. Audit trail log repair "
"(true/false/truncate/snmp_repair)?", "true",
fun verify_atl_repair/1),
- ATLSeqNo = ask("23g. Audit trail log "
+ ATLSeqNo = ask("24g. Audit trail log "
"sequence-numbering (true/false)?",
"false",
fun verify_atl_seqno/1),
@@ -360,33 +362,33 @@ config_agent_sys() ->
no ->
[]
end,
- NetIfVerb = ask("24. Network interface verbosity "
+ NetIfVerb = ask("25. Network interface verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
- NetIfMod = ask("25. Which network interface module shall be used?",
+ NetIfMod = ask("26. Which network interface module shall be used?",
"snmpa_net_if", fun verify_module/1),
NetIfOpts =
case NetIfMod of
snmpa_net_if ->
NetIfBindTo =
- ask("25a. Bind the agent IP address "
+ ask("26a. Bind the agent IP address "
"(true/false)?",
"false", fun verify_bool/1),
NetIfNoReuse =
- ask("25b. Shall the agents "
+ ask("26b. Shall the agents "
"IP address "
"and port be not reusable "
"(true/false)?",
"false", fun verify_bool/1),
NetIfReqLimit =
- ask("25c. Agent request limit "
+ ask("26c. Agent request limit "
"(used for flow control) "
"(infinity/pos integer)?",
"infinity",
fun verify_netif_req_limit/1),
NetIfRecbuf =
- case ask("25d. Receive buffer size of the "
+ case ask("26d. Receive buffer size of the "
"agent (in bytes) "
"(default/pos integer)?",
"default",
@@ -397,7 +399,7 @@ config_agent_sys() ->
[{recbuf, RecBufSz}]
end,
NetIfSndbuf =
- case ask("25e. Send buffer size of the agent "
+ case ask("26e. Send buffer size of the agent "
"(in bytes) (default/pos integer)?",
"default",
fun verify_netif_sndbuf/1) of
@@ -407,7 +409,7 @@ config_agent_sys() ->
[{sndbuf, SndBufSz}]
end,
NetIfFilter =
- case ask("25f. Do you wish to specify a "
+ case ask("26f. Do you wish to specify a "
"network interface filter module "
"(or use default)",
"default", fun verify_module/1) of
@@ -426,18 +428,18 @@ config_agent_sys() ->
NetIf = [{module, NetIfMod},
{verbosity, NetIfVerb},
{options, NetIfOpts}],
- TermDiscoEnable = ask("26a. Allow terminating discovery "
+ TermDiscoEnable = ask("27. Allow terminating discovery "
"(true/false)?", "true",
fun verify_bool/1),
TermDiscoConf =
case TermDiscoEnable of
true ->
TermDiscoStage2 =
- ask("26b. Second stage behaviour "
+ ask("27a. Second stage behaviour "
"(discovery/plain)?", "discovery",
fun verify_term_disco_behaviour/1),
TermDiscoTrigger =
- ask("26c. Trigger username "
+ ask("27b. Trigger username "
"(default/a string)?", "default",
fun verify_term_disco_trigger_username/1),
[{enable, TermDiscoEnable},
@@ -448,7 +450,7 @@ config_agent_sys() ->
{stage2, discovery},
{trigger_username, ""}]
end,
- OrigDiscoEnable = ask("27a. Allow originating discovery "
+ OrigDiscoEnable = ask("28. Allow originating discovery "
"(true/false)?", "true",
fun verify_bool/1),
OrigDiscoConf =
@@ -471,7 +473,7 @@ config_agent_sys() ->
{verbosity, NoteStoreVerb}]},
{net_if, NetIf}] ++ ATL;
sub ->
- SubAgentVerb = ask("14. Sub-agent verbosity "
+ SubAgentVerb = ask("15. Sub-agent verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
@@ -480,11 +482,12 @@ config_agent_sys() ->
{config, [{dir, ConfigDir}]}]
end,
SysConfig =
- [{priority, Prio},
- {versions, Vsns},
- {db_dir, DbDir},
- {mib_storage, MibStorage},
- {target_cache, [{verbosity, TargetCacheVerb}]},
+ [{priority, Prio},
+ {versions, Vsns},
+ {db_dir, DbDir},
+ {db_init_error, DbInitError},
+ {mib_storage, MibStorage},
+ {target_cache, [{verbosity, TargetCacheVerb}]},
{symbolic_store, [{verbosity, SymStoreVerb}]},
{local_db, [{repair, LocalDbRepair},
{auto_save, LocalDbAutoSave},
@@ -630,19 +633,21 @@ config_manager_sys() ->
fun verify_verbosity/1),
ConfigDbDir = ask("5. Database directory (absolute path)?",
DefDir, fun verify_dir/1),
- ConfigDbRepair = ask("6. Database repair "
+ ConfigDbInitError = ask("6. How to handle DB init error?",
+ "terminate", fun verify_db_init_error/1),
+ ConfigDbRepair = ask("7. Database repair "
"(true/false/force)?", "true",
fun verify_dets_repair/1),
- ConfigDbAutoSave = ask("7. Database auto save "
+ ConfigDbAutoSave = ask("8. Database auto save "
"(infinity/milli seconds)?",
"5000", fun verify_dets_auto_save/1),
IRB =
- case ask("8. Inform request behaviour (auto/user)?",
+ case ask("9. Inform request behaviour (auto/user)?",
"auto", fun verify_irb/1) of
auto ->
auto;
user ->
- case ask("8b. Use default GC timeout"
+ case ask("9b. Use default GC timeout"
"(default/seconds)?",
"default", fun verify_irb_user/1) of
default ->
@@ -651,31 +656,31 @@ config_manager_sys() ->
{user, IrbGcTo}
end
end,
- ServerVerb = ask("9. Server verbosity "
+ ServerVerb = ask("10. Server verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
- ServerTimeout = ask("10. Server GC timeout?", "30000",
+ ServerTimeout = ask("11. Server GC timeout?", "30000",
fun verify_timeout/1),
- NoteStoreVerb = ask("11. Note store verbosity "
+ NoteStoreVerb = ask("12. Note store verbosity "
"(silence/info/log/debug/trace)?",
"silence",
fun verify_verbosity/1),
- NoteStoreTimeout = ask("12. Note store GC timeout?", "30000",
+ NoteStoreTimeout = ask("13. Note store GC timeout?", "30000",
fun verify_timeout/1),
- NetIfMod = ask("13. Which network interface module shall be used?",
+ NetIfMod = ask("14. Which network interface module shall be used?",
"snmpm_net_if", fun verify_module/1),
- NetIfVerb = ask("14. Network interface verbosity "
+ NetIfVerb = ask("15. Network interface verbosity "
"(silence/info/log/debug/trace)?", "silence",
fun verify_verbosity/1),
- NetIfBindTo = ask("15. Bind the manager IP address "
+ NetIfBindTo = ask("16. Bind the manager IP address "
"(true/false)?",
"false", fun verify_bool/1),
- NetIfNoReuse = ask("16. Shall the manager IP address and port "
+ NetIfNoReuse = ask("17. Shall the manager IP address and port "
"be not reusable (true/false)?",
"false", fun verify_bool/1),
NetIfRecbuf =
- case ask("17. Receive buffer size of the manager (in bytes) "
+ case ask("18. Receive buffer size of the manager (in bytes) "
"(default/pos integer)?", "default",
fun verify_netif_recbuf/1) of
default ->
@@ -684,7 +689,7 @@ config_manager_sys() ->
[{recbuf, RecBufSz}]
end,
NetIfSndbuf =
- case ask("18. Send buffer size of the manager (in bytes) "
+ case ask("19. Send buffer size of the manager (in bytes) "
"(default/pos integer)?", "default",
fun verify_netif_sndbuf/1) of
default ->
@@ -700,28 +705,28 @@ config_manager_sys() ->
{verbosity, NetIfVerb},
{options, NetIfOpts}],
ATL =
- case ask("19. Shall the manager use an audit trail log "
+ case ask("20. Shall the manager use an audit trail log "
"(y/n)?",
"n", fun verify_yes_or_no/1) of
yes ->
- ATLType = ask("19b. Audit trail log type "
+ ATLType = ask("20b. Audit trail log type "
"(write/read_write)?",
"read_write", fun verify_atl_type/1),
- ATLDir = ask("19c. Where to store the "
+ ATLDir = ask("20c. Where to store the "
"audit trail log?",
DefDir, fun verify_dir/1),
- ATLMaxFiles = ask("19d. Max number of files?",
+ ATLMaxFiles = ask("20d. Max number of files?",
"10",
fun verify_pos_integer/1),
- ATLMaxBytes = ask("19e. Max size (in bytes) "
+ ATLMaxBytes = ask("20e. Max size (in bytes) "
"of each file?",
"10240",
fun verify_pos_integer/1),
ATLSize = {ATLMaxBytes, ATLMaxFiles},
- ATLRepair = ask("19f. Audit trail log repair "
+ ATLRepair = ask("20f. Audit trail log repair "
"(true/false/truncate/snmp_repair)?", "true",
fun verify_atl_repair/1),
- ATLSeqNo = ask("19g. Audit trail log sequence-numbering "
+ ATLSeqNo = ask("20g. Audit trail log sequence-numbering "
"(true/false)?", "false",
fun verify_atl_seqno/1),
[{audit_trail_log, [{type, ATLType},
@@ -733,14 +738,14 @@ config_manager_sys() ->
[]
end,
DefUser =
- case ask("20. Do you wish to assign a default user [yes] or use~n"
+ case ask("21. Do you wish to assign a default user [yes] or use~n"
" the default settings [no] (y/n)?", "n",
fun verify_yes_or_no/1) of
yes ->
- DefUserMod = ask("20b. Default user module?",
+ DefUserMod = ask("21b. Default user module?",
"snmpm_user_default",
fun verify_module/1),
- DefUserData = ask("20c. Default user data?", "undefined",
+ DefUserData = ask("21c. Default user data?", "undefined",
fun verify_user_data/1),
[{def_user_mod, DefUserMod},
{def_user_data, DefUserData}];
@@ -750,11 +755,12 @@ config_manager_sys() ->
SysConfig =
[{priority, Prio},
{versions, Vsns},
- {config, [{dir, ConfigDir},
- {verbosity, ConfigVerb},
- {db_dir, ConfigDbDir},
- {repair, ConfigDbRepair},
- {auto_save, ConfigDbAutoSave}]},
+ {config, [{dir, ConfigDir},
+ {db_dir, ConfigDbDir},
+ {db_init_error, ConfigDbInitError},
+ {repair, ConfigDbRepair},
+ {auto_save, ConfigDbAutoSave},
+ {verbosity, ConfigVerb}]},
{inform_request_behaviour, IRB},
{mibs, []},
{server, [{timeout, ServerTimeout},
@@ -1069,6 +1075,16 @@ verify_dir(Dir) ->
_E ->
{error, "invalid directory (not absolute): " ++ Dir}
end.
+
+
+verify_db_init_error("terminate") ->
+ {ok, true};
+verify_db_init_error("create") ->
+ {ok, create};
+verify_db_init_error("create_db_and_dir") ->
+ {ok, create_db_and_dir};
+verify_db_init_error(R) ->
+ {error, "invalid DB init error: " ++ R}.
verify_notif_type("trap") -> {ok, trap};
@@ -1164,13 +1180,20 @@ verify_dets_auto_save(I0) ->
%% I know that this is a little of the edge, but...
+verify_module(M) when is_atom(M) ->
+ {ok, M};
+verify_module(M0) when is_list(M0) ->
+ {ok, list_to_atom(M0)};
verify_module(M0) ->
- case (catch list_to_atom(M0)) of
- M when is_atom(M) ->
- {ok, M};
- _ ->
- {error, "invalid module: " ++ M0}
- end.
+ {error, lists:flatten(io_lib:format("invalid module: ~p", [M0]))}.
+
+%% verify_module(M0) ->
+%% case (catch list_to_atom(M0)) of
+%% M when is_atom(M) ->
+%% {ok, M};
+%% _ ->
+%% {error, "invalid module: " ++ M0}
+%% end.
verify_agent_type("master") ->
@@ -2168,6 +2191,8 @@ write_sys_config_file_agent_opt(Fid, {config, Opts}) ->
ok = io:format(Fid, "}", []);
write_sys_config_file_agent_opt(Fid, {db_dir, Dir}) ->
ok = io:format(Fid, " {db_dir, \"~s\"}", [Dir]);
+write_sys_config_file_agent_opt(Fid, {db_init_error, Action}) ->
+ ok = io:format(Fid, " {db_init_error, ~w}", [Action]);
write_sys_config_file_agent_opt(Fid, {mib_storage, ets}) ->
ok = io:format(Fid, " {mib_storage, ets}", []);
write_sys_config_file_agent_opt(Fid, {mib_storage, {dets, Dir}}) ->
@@ -2344,6 +2369,8 @@ write_sys_config_file_manager_config_opt(Fid, {dir, Dir}) ->
ok = io:format(Fid, "{dir, \"~s\"}", [Dir]);
write_sys_config_file_manager_config_opt(Fid, {db_dir, Dir}) ->
ok = io:format(Fid, "{db_dir, \"~s\"}", [Dir]);
+write_sys_config_file_manager_config_opt(Fid, {db_init_error, Action}) ->
+ ok = io:format(Fid, "{db_init_error, ~w}", [Action]);
write_sys_config_file_manager_config_opt(Fid, {repair, Rep}) ->
ok = io:format(Fid, "{repair, ~w}", [Rep]);
write_sys_config_file_manager_config_opt(Fid, {auto_save, As}) ->
diff --git a/lib/snmp/src/misc/snmp_log.erl b/lib/snmp/src/misc/snmp_log.erl
index a8c5df0b64..ae28df37fa 100644
--- a/lib/snmp/src/misc/snmp_log.erl
+++ b/lib/snmp/src/misc/snmp_log.erl
@@ -24,8 +24,8 @@
create/4, create/5, create/6, open/1, open/2,
change_size/2, close/1, sync/1, info/1,
log/4,
- log_to_txt/5, log_to_txt/6, log_to_txt/7,
- log_to_io/4, log_to_io/5, log_to_io/6
+ log_to_txt/6, log_to_txt/7, log_to_txt/8,
+ log_to_io/5, log_to_io/6, log_to_io/7
]).
-export([
upgrade/1, upgrade/2,
@@ -34,7 +34,17 @@
-export([
validate/1, validate/2
]).
+%% <BACKWARD-COMPAT>
+-export([
+ log_to_txt/5,
+ log_to_io/4
+ ]).
+%% </BACKWARD-COMPAT>
+-export_type([
+ log/0,
+ log_time/0
+ ]).
-define(SNMP_USE_V3, true).
-include("snmp_types.hrl").
@@ -42,12 +52,24 @@
-define(VMODULE,"LOG").
-include("snmp_verbosity.hrl").
--define(LOG_FORMAT, internal).
--define(LOG_TYPE, wrap).
+-define(LOG_FORMAT, internal).
+-define(LOG_TYPE, wrap).
+-define(BLOCK_DEFAULT, true).
-record(snmp_log, {id, seqno}).
+%%-----------------------------------------------------------------
+%% Types
+%%-----------------------------------------------------------------
+
+-opaque log() :: #snmp_log{}.
+-type log_time() :: null |
+ calendar:datetime() |
+ {local_time, calendar:datetime()} |
+ {universal_time, calendar:datetime()}.
+
+
%% --------------------------------------------------------------------
%% Exported functions
%% --------------------------------------------------------------------
@@ -322,7 +344,6 @@ validate_loop(Error, _Log, _Write, _PrevTS, _PrevSN) ->
%% log(Log, Packet, Addr, Port)
%%-----------------------------------------------------------------
-
log(#snmp_log{id = Log, seqno = SeqNo}, Packet, Addr, Port) ->
?vtrace("log -> entry with"
"~n Log: ~p"
@@ -378,53 +399,86 @@ do_change_size(Log, NewSize) ->
%% -- log_to_txt ---
+%% <BACKWARD-COMPAT>
log_to_txt(Log, FileName, Dir, Mibs, TextFile) ->
- log_to_txt(Log, FileName, Dir, Mibs, TextFile, null, null).
+ log_to_txt(Log, ?BLOCK_DEFAULT, FileName, Dir, Mibs, TextFile).
+%% </BACKWARD-COMPAT>
+log_to_txt(Log, Block, FileName, Dir, Mibs, TextFile)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ log_to_txt(Log, Block, FileName, Dir, Mibs, TextFile, null, null);
+%% <BACKWARD-COMPAT>
log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start) ->
- log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start, null).
-
-log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start, Stop)
- when is_list(Mibs) andalso is_list(TextFile) ->
+ log_to_txt(Log, ?BLOCK_DEFAULT, FileName, Dir, Mibs, TextFile, Start, null).
+%% </BACKWARD-COMPAT>
+
+log_to_txt(Log, Block, FileName, Dir, Mibs, TextFile, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ log_to_txt(Log, Block, FileName, Dir, Mibs, TextFile, Start, null);
+%% <BACKWARD-COMPAT>
+log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start, Stop) ->
+ log_to_txt(Log, ?BLOCK_DEFAULT, FileName, Dir, Mibs, TextFile, Start, Stop).
+%% </BACKWARD-COMPAT>
+
+log_to_txt(Log, Block, FileName, Dir, Mibs, TextFile, Start, Stop)
+ when (((Block =:= true) orelse (Block =:= false)) andalso
+ is_list(Mibs) andalso is_list(TextFile)) ->
?vtrace("log_to_txt -> entry with"
"~n Log: ~p"
+ "~n Block: ~p"
"~n FileName: ~p"
"~n Dir: ~p"
"~n Mibs: ~p"
"~n TextFile: ~p"
"~n Start: ~p"
"~n Stop: ~p",
- [Log, FileName, Dir, Mibs, TextFile, Start, Stop]),
+ [Log, Block, FileName, Dir, Mibs, TextFile, Start, Stop]),
File = filename:join(Dir, FileName),
Converter = fun(L) ->
do_log_to_file(L, TextFile, Mibs, Start, Stop)
end,
- log_convert(Log, File, Converter).
+ log_convert(Log, Block, File, Converter).
%% -- log_to_io ---
+%% <BACKWARD-COMPAT>
log_to_io(Log, FileName, Dir, Mibs) ->
- log_to_io(Log, FileName, Dir, Mibs, null, null).
+ log_to_io(Log, ?BLOCK_DEFAULT, FileName, Dir, Mibs, null, null).
+%% </BACKWARD-COMPAT>
+log_to_io(Log, Block, FileName, Dir, Mibs)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ log_to_io(Log, Block, FileName, Dir, Mibs, null, null);
+%% <BACKWARD-COMPAT>
log_to_io(Log, FileName, Dir, Mibs, Start) ->
- log_to_io(Log, FileName, Dir, Mibs, Start, null).
-
-log_to_io(Log, FileName, Dir, Mibs, Start, Stop)
+ log_to_io(Log, ?BLOCK_DEFAULT, FileName, Dir, Mibs, Start, null).
+%% </BACKWARD-COMPAT>
+
+log_to_io(Log, Block, FileName, Dir, Mibs, Start)
+ when ((Block =:= true) orelse (Block =:= false)) ->
+ log_to_io(Log, Block, FileName, Dir, Mibs, Start, null);
+%% <BACKWARD-COMPAT>
+log_to_io(Log, FileName, Dir, Mibs, Start, Stop) ->
+ log_to_io(Log, ?BLOCK_DEFAULT, FileName, Dir, Mibs, Start, Stop).
+%% </BACKWARD-COMPAT>
+
+log_to_io(Log, Block, FileName, Dir, Mibs, Start, Stop)
when is_list(Mibs) ->
?vtrace("log_to_io -> entry with"
"~n Log: ~p"
+ "~n Block: ~p"
"~n FileName: ~p"
"~n Dir: ~p"
"~n Mibs: ~p"
"~n Start: ~p"
"~n Stop: ~p",
- [Log, FileName, Dir, Mibs, Start, Stop]),
+ [Log, Block, FileName, Dir, Mibs, Start, Stop]),
File = filename:join(Dir, FileName),
Converter = fun(L) ->
do_log_to_io(L, Mibs, Start, Stop)
end,
- log_convert(Log, File, Converter).
+ log_convert(Log, Block, File, Converter).
%% --------------------------------------------------------------------
@@ -433,53 +487,121 @@ log_to_io(Log, FileName, Dir, Mibs, Start, Stop)
%% -- log_convert ---
-log_convert(#snmp_log{id = Log}, File, Converter) ->
- do_log_convert(Log, File, Converter);
-log_convert(Log, File, Converter) ->
- do_log_convert(Log, File, Converter).
+log_convert(#snmp_log{id = Log}, Block, File, Converter) ->
+ do_log_convert(Log, Block, File, Converter);
+log_convert(Log, Block, File, Converter) ->
+ do_log_convert(Log, Block, File, Converter).
-do_log_convert(Log, File, Converter) ->
+do_log_convert(Log, Block, File, Converter) ->
%% ?vtrace("do_log_converter -> entry with"
- %% "~n Log: ~p"
- %% "~n File: ~p"
- %% "~n disk_log:info(Log): ~p", [Log, File, disk_log:info(Log)]),
+ %% "~n Log: ~p"
+ %% "~n Block: ~p"
+ %% "~n File: ~p"
+ %% [Log, Block, File]),
+ Verbosity = get(verbosity),
{Pid, Ref} =
erlang:spawn_monitor(
fun() ->
- Result = do_log_convert2(Log, File, Converter),
+ put(sname, lc),
+ put(verbosity, Verbosity),
+ ?vlog("begin converting", []),
+ Result = do_log_convert2(Log, Block, File, Converter),
+ ?vlog("convert result: ~p", [Result]),
exit(Result)
end),
receive
{'DOWN', Ref, process, Pid, Result} ->
%% ?vtrace("do_log_converter -> received result"
- %% "~n Result: ~p"
- %% "~n disk_log:info(Log): ~p",
- %% [Result, disk_log:info(Log)]),
+ %% "~n Result: ~p", [Result]),
Result
end.
-do_log_convert2(Log, File, Converter) ->
+do_log_convert2(Log, Block, File, Converter) ->
+
+ %% ?vtrace("do_log_converter2 -> entry with"
+ %% "~n Log: ~p"
+ %% "~n Block: ~p"
+ %% "~n File: ~p"
+ %% "~n disk_log:info(Log): ~p",
+ %% [Log, Block, File, disk_log:info(Log)]),
+
%% First check if the caller process has already opened the
%% log, because if we close an already open log we will cause
%% a runtime error.
+
+ ?vtrace("do_log_convert2 -> entry - check if owner", []),
case is_owner(Log) of
true ->
- Converter(Log);
+ ?vtrace("do_log_converter2 -> convert an already owned log", []),
+ maybe_block(Log, Block),
+ Res = Converter(Log),
+ maybe_unblock(Log, Block),
+ Res;
false ->
%% Not yet member of the ruling party, apply for membership...
+ ?vtrace("do_log_converter2 -> convert log", []),
case log_open(Log, File) of
{ok, _} ->
+ ?vdebug("do_log_convert2 -> opened - now convert", []),
+ maybe_block(Log, Block),
Res = Converter(Log),
+ maybe_unblock(Log, Block),
disk_log:close(Log),
+ ?vdebug("do_log_convert2 -> converted - done: "
+ "~n Result: ~p", [Res]),
Res;
{error, {name_already_open, _}} ->
- Converter(Log);
+ ?vdebug("do_log_convert2 -> "
+ "already opened - now convert", []),
+ maybe_block(Log, Block),
+ Res = Converter(Log),
+ maybe_unblock(Log, Block),
+ ?vdebug("do_log_convert2 -> converted - done: "
+ "~n Result: ~p", [Res]),
+ Res;
{error, Reason} ->
+ ?vinfo("do_log_converter2 -> "
+ "failed converting log - open failed: "
+ "~n Reason: ~p", [Reason]),
{error, {Log, Reason}}
end
end.
+maybe_block(_Log, false = _Block) ->
+ %% ?vtrace("maybe_block(false) -> entry", []),
+ ok;
+maybe_block(Log, true = _Block) ->
+ %% ?vtrace("maybe_block(true) -> entry when"
+ %% "~n Log Status: ~p", [log_status(Log)]),
+ Res = disk_log:block(Log, true),
+ %% ?vtrace("maybe_block(true) -> "
+ %% "~n Log Status: ~p"
+ %% "~n Res: ~p", [log_status(Log), Res]),
+ Res.
+
+maybe_unblock(_Log, false = _Block) ->
+ %% ?vtrace("maybe_unblock(false) -> entry", []),
+ ok;
+maybe_unblock(Log, true = _Block) ->
+ %% ?vtrace("maybe_unblock(true) -> entry when"
+ %% "~n Log Status: ~p", [log_status(Log)]),
+ Res = disk_log:unblock(Log),
+ %% ?vtrace("maybe_unblock(true) -> "
+ %% "~n Log Status: ~p"
+ %% "~n Res: ~p", [log_status(Log), Res]),
+ Res.
+
+%% log_status(Log) ->
+%% Info = disk_log:info(Log),
+%% case lists:keysearch(status, 1, Info) of
+%% {value, {status, Status}} ->
+%% Status;
+%% false ->
+%% undefined
+%% end.
+
+
%% -- do_log_to_text ---
do_log_to_file(Log, TextFile, Mibs, Start, Stop) ->
diff --git a/lib/snmp/src/misc/snmp_usm.erl b/lib/snmp/src/misc/snmp_usm.erl
index 67e3476816..32198deb8b 100644
--- a/lib/snmp/src/misc/snmp_usm.erl
+++ b/lib/snmp/src/misc/snmp_usm.erl
@@ -16,6 +16,8 @@
%%
%% %CopyrightEnd%
%%
+%% AES: RFC 3826
+%%
-module(snmp_usm).
@@ -24,7 +26,7 @@
-export([passwd2localized_key/3, localize_key/3]).
-export([auth_in/4, auth_out/4, set_msg_auth_params/3]).
-export([des_encrypt/3, des_decrypt/3]).
--export([aes_encrypt/3, aes_decrypt/5]).
+-export([aes_encrypt/5, aes_decrypt/5]).
-define(SNMP_USE_V3, true).
@@ -42,6 +44,9 @@
-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).
+-define(BLOCK_CIPHER_AES, aes_cfb128).
+-define(BLOCK_CIPHER_DES, des_cbc).
+
%%-----------------------------------------------------------------
%% Func: passwd2localized_key/3
@@ -210,7 +215,8 @@ des_encrypt(PrivKey, Data, SaltFun) ->
IV = list_to_binary(snmp_misc:str_xor(PreIV, Salt)),
TailLen = (8 - (length(Data) rem 8)) rem 8,
Tail = mk_tail(TailLen),
- EncData = crypto:block_encrypt(des_cbc, DesKey, IV, [Data,Tail]),
+ EncData = crypto:block_encrypt(?BLOCK_CIPHER_DES,
+ DesKey, IV, [Data,Tail]),
{ok, binary_to_list(EncData), Salt}.
des_decrypt(PrivKey, MsgPrivParams, EncData)
@@ -224,7 +230,8 @@ des_decrypt(PrivKey, MsgPrivParams, EncData)
Salt = MsgPrivParams,
IV = list_to_binary(snmp_misc:str_xor(PreIV, Salt)),
%% Whatabout errors here??? E.g. not a mulitple of 8!
- Data = binary_to_list(crypto:block_decrypt(des_cbc, DesKey, IV, EncData)),
+ Data = binary_to_list(crypto:block_decrypt(?BLOCK_CIPHER_DES,
+ DesKey, IV, EncData)),
Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data),
{ok, Data2};
des_decrypt(PrivKey, BadMsgPrivParams, EncData) ->
@@ -236,13 +243,12 @@ des_decrypt(PrivKey, BadMsgPrivParams, EncData) ->
throw({error, {bad_msgPrivParams, PrivKey, BadMsgPrivParams, EncData}}).
-aes_encrypt(PrivKey, Data, SaltFun) ->
+aes_encrypt(PrivKey, Data, SaltFun, EngineBoots, EngineTime) ->
AesKey = PrivKey,
Salt = SaltFun(),
- EngineBoots = snmp_framework_mib:get_engine_boots(),
- EngineTime = snmp_framework_mib:get_engine_time(),
IV = list_to_binary([?i32(EngineBoots), ?i32(EngineTime) | Salt]),
- EncData = crypto:block_encrypt(aes_cbf128, AesKey, IV, Data),
+ EncData = crypto:block_encrypt(?BLOCK_CIPHER_AES,
+ AesKey, IV, Data),
{ok, binary_to_list(EncData), Salt}.
aes_decrypt(PrivKey, MsgPrivParams, EncData, EngineBoots, EngineTime)
@@ -251,7 +257,8 @@ aes_decrypt(PrivKey, MsgPrivParams, EncData, EngineBoots, EngineTime)
Salt = MsgPrivParams,
IV = list_to_binary([?i32(EngineBoots), ?i32(EngineTime) | Salt]),
%% Whatabout errors here??? E.g. not a mulitple of 8!
- Data = binary_to_list(crypto:block_decrypt(aes_cbf128, AesKey, IV, EncData)),
+ Data = binary_to_list(crypto:block_decrypt(?BLOCK_CIPHER_AES,
+ AesKey, IV, EncData)),
Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data),
{ok, Data2}.
diff --git a/lib/snmp/src/misc/snmp_verbosity.erl b/lib/snmp/src/misc/snmp_verbosity.erl
index df5986b7bc..f27c31db03 100644
--- a/lib/snmp/src/misc/snmp_verbosity.erl
+++ b/lib/snmp/src/misc/snmp_verbosity.erl
@@ -148,6 +148,8 @@ image_of_sname(mnifl) -> "M-NET-IF-LOGGER";
image_of_sname(mnifw) -> io_lib:format("M-NET-IF-worker(~p)", [self()]);
image_of_sname(mconf) -> "M-CONF";
+image_of_sname(lc) -> io_lib:format("LOG-CONVERTER(~p)", [self()]);
+
image_of_sname(mgr) -> "MGR";
image_of_sname(mgr_misc) -> "MGR_MISC";
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 7e683e315a..b7d34eb198 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -34,6 +34,7 @@
%% all_tcs - misc
app_info/1,
info_test/1,
+ create_local_db_dir/1,
%% all_tcs - test_v1
simple/1,
@@ -1506,7 +1507,8 @@ finish_misc(Config) ->
misc_cases() ->
[
app_info,
- info_test
+ info_test,
+ create_local_db_dir
].
app_info(suite) -> [];
@@ -1539,7 +1541,75 @@ app_dir(App) ->
"undefined"
end.
+create_local_db_dir(Config) when is_list(Config) ->
+ ?P(create_local_db_dir),
+ DataDir = snmp_test_lib:lookup(data_dir, Config),
+ T = erlang:now(),
+ [As,Bs,Cs] = [integer_to_list(I) || I <- tuple_to_list(T)],
+ DbDir = filename:join([DataDir, As, Bs, Cs]),
+ ok = del_dir(DbDir, 3),
+ Name = list_to_atom(atom_to_list(create_local_db_dir)
+ ++"-"++As++"-"++Bs++"-"++Cs),
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok,Node} = ?t:start_node(Name, slave, [{args, "-pa "++Pa}]),
+
+ %% first start with a nonexisting DbDir
+ Fun1 = fun() ->
+ false = filelib:is_dir(DbDir),
+ process_flag(trap_exit,true),
+ {error, {error, {failed_open_dets, {file_error, _, _}}}} =
+ snmpa_local_db:start_link(normal, DbDir, [{verbosity,trace}]),
+ false = filelib:is_dir(DbDir),
+ {ok, not_found}
+ end,
+ {ok, not_found} = nodecall(Node, Fun1),
+ %% now start with a nonexisting DbDir but pass the
+ %% create_local_db_dir option as well
+ Fun2 = fun() ->
+ false = filelib:is_dir(DbDir),
+ process_flag(trap_exit,true),
+ {ok, _Pid} =
+ snmpa_local_db:start_link(normal, DbDir,
+ create_db_and_dir, [{verbosity,trace}]),
+ snmpa_local_db:stop(),
+ true = filelib:is_dir(DbDir),
+ {ok, found}
+ end,
+ {ok, found} = nodecall(Node, Fun2),
+ %% cleanup
+ ?t:stop_node(Node),
+ ok = del_dir(DbDir, 3),
+ ok.
+
+nodecall(Node, Fun) ->
+ Parent = self(),
+ Ref = make_ref(),
+ spawn_link(Node,
+ fun() ->
+ Res = Fun(),
+ unlink(Parent),
+ Parent ! {Ref, Res}
+ end),
+ receive
+ {Ref, Res} ->
+ Res
+ end.
+del_dir(_Dir, 0) ->
+ ok;
+del_dir(Dir, Depth) ->
+ case filelib:is_dir(Dir) of
+ true ->
+ {ok, Files} = file:list_dir(Dir),
+ lists:map(fun(F) ->
+ Nm = filename:join(Dir,F),
+ ok = file:delete(Nm)
+ end, Files),
+ ok = file:del_dir(Dir),
+ del_dir(filename:dirname(Dir), Depth-1);
+ false ->
+ ok
+ end.
%v1_cases() -> [loop_mib];
v1_cases() ->
diff --git a/lib/snmp/test/snmp_log_test.erl b/lib/snmp/test/snmp_log_test.erl
index e9345b44cc..fb7285110f 100644
--- a/lib/snmp/test/snmp_log_test.erl
+++ b/lib/snmp/test/snmp_log_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -331,7 +331,7 @@ log_to_io1(doc) -> "Log to io from the same process that opened "
log_to_io1(Config) when is_list(Config) ->
p(log_to_io1),
put(sname,l2i1),
- put(verbosity,trace),
+ put(verbosity,debug),
?DBG("log_to_io1 -> start", []),
Dir = ?config(log_dir, Config),
Name = "snmp_test_l2i1",
@@ -365,7 +365,7 @@ log_to_io1(Config) when is_list(Config) ->
display_info(Info),
?DBG("log_to_io1 -> do the convert to io (stdout)", []),
- ? line ok = snmp_log:log_to_io(Log, File, Dir, []),
+ ? line ok = snmp:log_to_io(Dir, [], Name, File, false),
?DBG("log_to_io1 -> close log", []),
?line ok = snmp_log:close(Log),
@@ -377,7 +377,7 @@ log_to_io1(Config) when is_list(Config) ->
%%======================================================================
%% Start a logger-process that logs messages with a certain interval.
%% Start a reader-process that reads messages from the log at a certain
-%% point of time.
+%% point in time.
log_to_io2(suite) -> [];
log_to_io2(doc) -> "Log to io from a different process than which "
@@ -386,7 +386,7 @@ log_to_io2(Config) when is_list(Config) ->
process_flag(trap_exit, true),
p(log_to_io2),
put(sname, l2i2),
- put(verbosity,trace),
+ put(verbosity,debug),
?DBG("log_to_io2 -> start", []),
Dir = ?config(log_dir, Config),
Name = "snmp_test_l2i2",
@@ -414,13 +414,13 @@ log_to_io2(Config) when is_list(Config) ->
log_reader_log_to(Reader,
fun() ->
I = disk_log:info(Log),
- R = snmp_log:log_to_io(Log, File, Dir, []),
+ R = snmp:log_to_io(Dir, [], Name, File, true),
{R, I}
end),
case Res of
- {ok, Info} ->
- ?DBG("log_to_io2 -> ~n Info: ~p", [Info]),
+ {ok, _Info} ->
+ ?DBG("log_to_io2 -> ~n Info: ~p", [_Info]),
ok;
{Error, Info} ->
?DBG("log_to_io2 -> log to io failed: "
@@ -445,7 +445,7 @@ log_to_txt1(suite) -> [];
log_to_txt1(Config) when is_list(Config) ->
p(log_to_txt1),
put(sname,l2t1),
- put(verbosity,trace),
+ put(verbosity,debug),
?DBG("log_to_txt1 -> start", []),
Name = "snmp_test_l2t1",
@@ -463,7 +463,7 @@ log_to_txt2(suite) -> [];
log_to_txt2(Config) when is_list(Config) ->
p(log_to_txt2),
put(sname,l2t2),
- put(verbosity,trace),
+ put(verbosity,debug),
?DBG("log_to_txt2 -> start", []),
Name = "snmp_test_l2t2",
@@ -520,14 +520,21 @@ log_to_txt(Name, SeqNoGen, Config) when is_list(Config) ->
?line {ok, Info} = snmp_log:info(Log),
display_info(Info),
- Out1 = join(Dir, "snmp_text-1.txt"),
- ?DBG("log_to_txt -> do the convert to a text file when"
- "~n Out1: ~p", [Out1]),
- ?line ok = snmp:log_to_txt(Dir, [], Out1, Log, File),
+ Out1a = join(Dir, "snmp_text-1-unblocked.txt"),
+ ?DBG("log_to_txt -> do the convert to a text file (~s) unblocked", [Out1a]),
+ ?line ok = snmp:log_to_txt(Dir, [], Out1a, Log, File, false),
+
+ ?line {ok, #file_info{size = Size1a}} = file:read_file_info(Out1a),
+ ?DBG("log_to_txt -> text file size: ~p", [Size1a]),
+ validate_size(Size1a),
+
+ Out1b = join(Dir, "snmp_text-1-blocked.txt"),
+ ?DBG("log_to_txt -> do the convert to a text file (~s) blocked", [Out1b]),
+ ?line ok = snmp:log_to_txt(Dir, [], Out1b, Log, File, true),
- ?line {ok, #file_info{size = Size1}} = file:read_file_info(Out1),
- ?DBG("log_to_txt -> text file size: ~p", [Size1]),
- validate_size(Size1),
+ ?line {ok, #file_info{size = Size1b}} = file:read_file_info(Out1b),
+ ?DBG("log_to_txt -> text file size: ~p", [Size1b]),
+ validate_size(Size1b, {eq, Size1a}),
Out2 = join(Dir, "snmp_text-2.txt"),
?DBG("log_to_txt -> do the convert to a text file when"
@@ -538,7 +545,7 @@ log_to_txt(Name, SeqNoGen, Config) when is_list(Config) ->
?line {ok, #file_info{size = Size2}} = file:read_file_info(Out2),
?DBG("log_to_txt -> text file size: ~p", [Size2]),
- validate_size(Size2, {le, Size1}),
+ validate_size(Size2, {le, Size1a}),
%% Calculate new start / stop times...
GStart = calendar:datetime_to_gregorian_seconds(Start),
@@ -568,7 +575,7 @@ log_to_txt(Name, SeqNoGen, Config) when is_list(Config) ->
?line {ok, #file_info{size = Size3}} = file:read_file_info(Out3),
?DBG("log_to_txt -> text file size: ~p", [Size3]),
- validate_size(Size3, {l, Size1}),
+ validate_size(Size3, {l, Size1a}),
?DBG("log_to_txt -> close log", []),
?line ok = snmp_log:close(Log),
@@ -593,7 +600,7 @@ log_to_txt3(Config) when is_list(Config) ->
process_flag(trap_exit, true),
p(log_to_txt3),
put(sname,l2t3),
- put(verbosity,trace),
+ put(verbosity,debug),
?DBG("log_to_txt3 -> start", []),
Dir = ?config(log_dir, Config),
Name = "snmp_test_l2t3",
@@ -637,8 +644,8 @@ log_to_txt3(Config) when is_list(Config) ->
end),
case Res of
- {ok, Info} ->
- ?DBG("log_to_txt3 -> ~n Info: ~p", [Info]),
+ {ok, _Info} ->
+ ?DBG("log_to_txt3 -> ~n Info: ~p", [_Info]),
?line {ok, #file_info{size = FileSize}} =
file:read_file_info(TxtFile),
?DBG("log_to_txt3 -> text file size: ~p", [FileSize]),
@@ -667,6 +674,8 @@ validate_size(_) ->
validate_size(0, _) ->
?FAIL(invalid_size);
+validate_size(A, {eq, A}) ->
+ ok;
validate_size(A, {le, B}) when A =< B ->
ok;
validate_size(A, {l, B}) when A < B ->
@@ -695,11 +704,11 @@ log_writer_start(Name, File, Size, Repair) ->
log_writer_stop(Pid) ->
Pid ! {stop, self()},
- T1 = t(),
+ _T1 = t(),
receive
{'EXIT', Pid, normal} ->
- T2 = t(),
- ?DBG("it took ~w ms to stop the writer", [T2 - T1]),
+ _T2 = t(),
+ ?DBG("it took ~w ms to stop the writer", [_T2 - _T1]),
ok
after 60000 ->
Msg = receive Any -> Any after 0 -> nothing end,
@@ -712,11 +721,11 @@ log_writer_info(Pid) ->
log_writer_sleep(Pid, Time) ->
Pid ! {sleep, Time, self()},
- T1 = t(),
+ _T1 = t(),
receive
{sleeping, Pid} ->
- T2 = t(),
- ?DBG("it took ~w ms to put the writer to sleep", [T2 - T1]),
+ _T2 = t(),
+ ?DBG("it took ~w ms to put the writer to sleep", [_T2 - _T1]),
ok;
{'EXIT', Pid, Reason} ->
{error, Reason}
@@ -784,11 +793,11 @@ lp(F, A) ->
log_reader_start() ->
Pid = spawn_link(?MODULE, log_reader_main, [self()]),
- T1 = t(),
+ _T1 = t(),
receive
{started, Pid} ->
- T2 = t(),
- ?DBG("it took ~w ms to start the reader", [T2 - T1]),
+ _T2 = t(),
+ ?DBG("it took ~w ms to start the reader", [_T2 - _T1]),
{ok, Pid};
{'EXIT', Pid, Reason} ->
{error, Reason}
@@ -798,11 +807,11 @@ log_reader_start() ->
log_reader_stop(Pid) ->
Pid ! {stop, self()},
- T1 = t(),
+ _T1 = t(),
receive
{'EXIT', Pid, normal} ->
- T2 = t(),
- ?DBG("it took ~w ms to put the reader to eleep", [T2 - T1]),
+ _T2 = t(),
+ ?DBG("it took ~w ms to put the reader to eleep", [_T2 - _T1]),
ok
after 1000 ->
Msg = receive Any -> Any after 0 -> nothing end,
diff --git a/lib/snmp/test/snmp_manager_config_test.erl b/lib/snmp/test/snmp_manager_config_test.erl
index 3192fe1b40..7b9924b83c 100644
--- a/lib/snmp/test/snmp_manager_config_test.erl
+++ b/lib/snmp/test/snmp_manager_config_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -57,6 +57,7 @@
start_with_invalid_users_conf_file1/1,
start_with_invalid_agents_conf_file1/1,
start_with_invalid_usm_conf_file1/1,
+ start_with_create_db_and_dir_opt/1,
@@ -139,8 +140,13 @@ init_per_testcase(Case, Config) when is_list(Config) ->
file:make_dir(MgrTopDir = filename:join(CaseTopDir, "manager/")),
?line ok =
file:make_dir(MgrConfDir = filename:join(MgrTopDir, "conf/")),
- ?line ok =
- file:make_dir(MgrDbDir = filename:join(MgrTopDir, "db/")),
+ MgrDbDir = filename:join(MgrTopDir, "db/"),
+ case Case of
+ start_with_create_db_and_dir_opt ->
+ ok;
+ _ ->
+ ?line ok = file:make_dir(MgrDbDir)
+ end,
?line ok =
file:make_dir(MgrLogDir = filename:join(MgrTopDir, "log/")),
[{case_top_dir, CaseTopDir},
@@ -174,6 +180,7 @@ groups() ->
start_without_mandatory_opts2,
start_with_all_valid_opts, start_with_unknown_opts,
start_with_incorrect_opts,
+ start_with_create_db_and_dir_opt,
start_with_invalid_manager_conf_file1,
start_with_invalid_users_conf_file1,
start_with_invalid_agents_conf_file1,
@@ -332,7 +339,8 @@ start_with_all_valid_opts(Conf) when is_list(Conf) ->
{no_reuse, false}]}],
ServerOpts = [{timeout, 10000}, {verbosity, trace}],
NoteStoreOpts = [{timeout, 20000}, {verbosity, trace}],
- ConfigOpts = [{dir, ConfDir}, {verbosity, trace}, {db_dir, DbDir}],
+ ConfigOpts = [{dir, ConfDir}, {verbosity, trace},
+ {db_dir, DbDir}, {db_init_error, create}],
Mibs = [join(StdMibDir, "SNMP-NOTIFICATION-MIB"),
join(StdMibDir, "SNMP-USER-BASED-SM-MIB")],
Prio = normal,
@@ -1674,7 +1682,34 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) ->
%% ---
%%
+start_with_create_db_and_dir_opt(suite) -> [];
+start_with_create_db_and_dir_opt(doc) ->
+ "Start the snmp manager config process with the\n"
+ "create_db_and_dir option.";
+start_with_create_db_and_dir_opt(Conf) when is_list(Conf) ->
+ put(tname, swcdado),
+ p("start"),
+ process_flag(trap_exit, true),
+ ConfDir = ?config(manager_conf_dir, Conf),
+ DbDir = ?config(manager_db_dir, Conf),
+ true = not filelib:is_dir(DbDir) and not filelib:is_file(DbDir),
+ write_manager_conf(ConfDir),
+
+ p("verify nonexistent db_dir"),
+ ConfigOpts01 = [{verbosity,trace}, {dir, ConfDir}, {db_dir, DbDir}],
+ {error, Reason01} = config_start([{config, ConfigOpts01}]),
+ p("nonexistent db_dir res: ~p", [Reason01]),
+ {invalid_conf_db_dir, _, not_found} = Reason01,
+ p("verify nonexistent db_dir gets created"),
+ ConfigOpts02 = [{db_init_error, create_db_and_dir} | ConfigOpts01],
+ {ok, _Pid} = config_start([{config, ConfigOpts02}]),
+ true = filelib:is_dir(DbDir),
+ p("verified: nonexistent db_dir was correctly created"),
+ ok = config_stop(),
+
+ p("done"),
+ ok.
%%
%% ---
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index dedbae5ce4..5fe18980bc 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1615,10 +1615,10 @@ simple_sync_get1(Config) when is_list(Config) ->
ok.
do_simple_sync_get(Node, Addr, Port, Oids) ->
- ?line {ok, Reply, Rem} = mgr_user_sync_get(Node, Addr, Port, Oids),
+ ?line {ok, Reply, _Rem} = mgr_user_sync_get(Node, Addr, Port, Oids),
?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
%% verify that the operation actually worked:
%% The order should be the same, so no need to seach
@@ -1682,10 +1682,10 @@ do_simple_sync_get2(Config, Get, PostVerify) ->
do_simple_sync_get2(Node, TargetName, Oids, Get, PostVerify)
when is_function(Get, 3) andalso is_function(PostVerify, 0) ->
- ?line {ok, Reply, Rem} = Get(Node, TargetName, Oids),
+ ?line {ok, Reply, _Rem} = Get(Node, TargetName, Oids),
?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
%% verify that the operation actually worked:
%% The order should be the same, so no need to seach
@@ -2061,10 +2061,10 @@ simple_sync_get_next1(Config) when is_list(Config) ->
do_simple_get_next(N, Node, Addr, Port, Oids, Verify) ->
p("issue get-next command ~w", [N]),
case mgr_user_sync_get_next(Node, Addr, Port, Oids) of
- {ok, Reply, Rem} ->
+ {ok, Reply, _Rem} ->
?DBG("get-next ok:"
"~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
Verify(Reply);
Error ->
@@ -2217,10 +2217,10 @@ do_simple_sync_get_next2(Config, GetNext, PostVerify)
do_simple_get_next(N, Node, TargetName, Oids, Verify, GetNext, PostVerify) ->
p("issue get-next command ~w", [N]),
case GetNext(Node, TargetName, Oids) of
- {ok, Reply, Rem} ->
+ {ok, Reply, _Rem} ->
?DBG("get-next ok:"
"~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
PostVerify(Verify(Reply));
Error ->
@@ -2551,10 +2551,10 @@ simple_sync_set1(Config) when is_list(Config) ->
do_simple_set1(Node, Addr, Port, VAVs) ->
[SysName, SysLoc] = value_of_vavs(VAVs),
- ?line {ok, Reply, Rem} = mgr_user_sync_set(Node, Addr, Port, VAVs),
+ ?line {ok, Reply, _Rem} = mgr_user_sync_set(Node, Addr, Port, VAVs),
?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
%% verify that the operation actually worked:
%% The order should be the same, so no need to seach
@@ -2631,10 +2631,10 @@ do_simple_sync_set2(Config, Set, PostVerify)
do_simple_set2(Node, TargetName, VAVs, Set, PostVerify) ->
[SysName, SysLoc] = value_of_vavs(VAVs),
- ?line {ok, Reply, Rem} = Set(Node, TargetName, VAVs),
+ ?line {ok, Reply, _Rem} = Set(Node, TargetName, VAVs),
?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
%% verify that the operation actually worked:
%% The order should be the same, so no need to seach
@@ -3026,10 +3026,10 @@ fl(L) ->
do_simple_get_bulk1(N, Node, Addr, Port, NonRep, MaxRep, Oids, Verify) ->
p("issue get-bulk command ~w", [N]),
case mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) of
- {ok, Reply, Rem} ->
+ {ok, Reply, _Rem} ->
?DBG("get-bulk ok:"
"~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
Verify(Reply);
Error ->
@@ -3213,10 +3213,10 @@ do_simple_get_bulk2(N,
is_function(PostVerify) ->
p("issue get-bulk command ~w", [N]),
case GetBulk(NonRep, MaxRep, Oids) of
- {ok, Reply, Rem} ->
+ {ok, Reply, _Rem} ->
?DBG("get-bulk ok:"
"~n Reply: ~p"
- "~n Rem: ~w", [Reply, Rem]),
+ "~n Rem: ~w", [Reply, _Rem]),
PostVerify(Verify(Reply));
Error ->
@@ -5609,11 +5609,11 @@ init_mgr_user_data1(Conf) ->
[{address, Addr},
{port, Port},
{engine_id, "agentEngine"}]),
- Agents = mgr_user_which_own_agents(Node),
- ?DBG("Own agents: ~p", [Agents]),
+ _Agents = mgr_user_which_own_agents(Node),
+ ?DBG("Own agents: ~p", [_Agents]),
- ?line {ok, DefAgentConf} = mgr_user_agent_info(Node, TargetName, all),
- ?DBG("Default agent config: ~n~p", [DefAgentConf]),
+ ?line {ok, _DefAgentConf} = mgr_user_agent_info(Node, TargetName, all),
+ ?DBG("Default agent config: ~n~p", [_DefAgentConf]),
?line ok = mgr_user_update_agent_info(Node, TargetName,
community, "all-rights"),
@@ -5624,8 +5624,8 @@ init_mgr_user_data1(Conf) ->
?line ok = mgr_user_update_agent_info(Node, TargetName,
max_message_size, 1024),
- ?line {ok, AgentConf} = mgr_user_agent_info(Node, TargetName, all),
- ?DBG("Updated agent config: ~n~p", [AgentConf]),
+ ?line {ok, _AgentConf} = mgr_user_agent_info(Node, TargetName, all),
+ ?DBG("Updated agent config: ~n~p", [_AgentConf]),
Conf.
init_mgr_user_data2(Conf) ->
@@ -5639,11 +5639,11 @@ init_mgr_user_data2(Conf) ->
[{address, Addr},
{port, Port},
{engine_id, "agentEngine"}]),
- Agents = mgr_user_which_own_agents(Node),
- ?DBG("Own agents: ~p", [Agents]),
+ _Agents = mgr_user_which_own_agents(Node),
+ ?DBG("Own agents: ~p", [_Agents]),
- ?line {ok, DefAgentConf} = mgr_user_agent_info(Node, TargetName, all),
- ?DBG("Default agent config: ~n~p", [DefAgentConf]),
+ ?line {ok, _DefAgentConf} = mgr_user_agent_info(Node, TargetName, all),
+ ?DBG("Default agent config: ~n~p", [_DefAgentConf]),
?line ok = mgr_user_update_agent_info(Node, TargetName,
community, "all-rights"),
@@ -5652,8 +5652,8 @@ init_mgr_user_data2(Conf) ->
?line ok = mgr_user_update_agent_info(Node, TargetName,
max_message_size, 1024),
- ?line {ok, AgentConf} = mgr_user_agent_info(Node, TargetName, all),
- ?DBG("Updated agent config: ~n~p", [AgentConf]),
+ ?line {ok, _AgentConf} = mgr_user_agent_info(Node, TargetName, all),
+ ?DBG("Updated agent config: ~n~p", [_AgentConf]),
Conf.
fin_mgr_user_data1(Conf) ->
@@ -5853,12 +5853,12 @@ mgr_user_name_to_oid(Node, Name) ->
start_manager(Node, Vsns, Config) ->
start_manager(Node, Vsns, Config, []).
-start_manager(Node, Vsns, Conf0, Opts) ->
+start_manager(Node, Vsns, Conf0, _Opts) ->
?DBG("start_manager -> entry with"
"~n Node: ~p"
"~n Vsns: ~p"
"~n Conf0: ~p"
- "~n Opts: ~p", [Node, Vsns, Conf0, Opts]),
+ "~n Opts: ~p", [Node, Vsns, Conf0, _Opts]),
AtlDir = ?config(manager_log_dir, Conf0),
ConfDir = ?config(manager_conf_dir, Conf0),
@@ -5908,12 +5908,12 @@ stop_manager(Node, Conf) ->
start_agent(Node, Vsns, Config) ->
start_agent(Node, Vsns, Config, []).
-start_agent(Node, Vsns, Conf0, Opts) ->
+start_agent(Node, Vsns, Conf0, _Opts) ->
?DBG("start_agent -> entry with"
"~n Node: ~p"
"~n Vsns: ~p"
"~n Conf0: ~p"
- "~n Opts: ~p", [Node, Vsns, Conf0, Opts]),
+ "~n Opts: ~p", [Node, Vsns, Conf0, _Opts]),
AtlDir = ?config(agent_log_dir, Conf0),
ConfDir = ?config(agent_conf_dir, Conf0),
diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl
index 4e789bbaec..ddbe156130 100644
--- a/lib/snmp/test/snmp_manager_user.erl
+++ b/lib/snmp/test/snmp_manager_user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -681,6 +681,15 @@ loop(#state{parent = Parent, id = Id} = S) ->
Parent ! {async_event, TargetName, {report, SnmpReport}},
loop(S);
+ {handle_invalid_result, _Pid, In, Out} ->
+ d("loop -> received invalid result callback from manager for "
+ "~n In: ~p",
+ "~n Out: ~p", [In, Out]),
+ info("received invalid result message: "
+ "~n In: ~p"
+ "~n Out: ~p", [In, Out]),
+ loop(S);
+
{'EXIT', Parent, Reason} ->
d("received exit signal from parent: ~n~p", [Reason]),
info("received exit signal from parent: ~n~p", [Reason]),
@@ -770,7 +779,7 @@ handle_pdu(TargetName, ReqId, SnmpResponse, UserPid) ->
handle_trap(TargetName, SnmpTrap, UserPid) ->
UserPid ! {handle_trap, self(), TargetName, SnmpTrap},
- ok.
+ ignore.
handle_inform(TargetName, SnmpInform, UserPid) ->
UserPid ! {handle_inform, self(), TargetName, SnmpInform},
@@ -778,12 +787,12 @@ handle_inform(TargetName, SnmpInform, UserPid) ->
{handle_inform_no_response, TargetName} ->
no_reply;
{handle_inform_response, TargetName} ->
- ok
+ ignore
end.
handle_report(TargetName, SnmpReport, UserPid) ->
UserPid ! {handle_report, self(), TargetName, SnmpReport},
- ok.
+ ignore.
%%----------------------------------------------------------------------
diff --git a/lib/snmp/test/snmp_test_manager.erl b/lib/snmp/test/snmp_test_manager.erl
index 1f3383a7a8..925ae77ab5 100644
--- a/lib/snmp/test/snmp_test_manager.erl
+++ b/lib/snmp/test/snmp_test_manager.erl
@@ -47,7 +47,8 @@
handle_pdu/4,
handle_trap/3,
handle_inform/3,
- handle_report/3
+ handle_report/3,
+ handle_invalid_result/3
]).
@@ -279,12 +280,18 @@ handle_info({snmp_inform, TargetName, Info, Pid},
handle_info({snmp_report, TargetName, Info, Pid},
#state{parent = P} = State) ->
info_msg("received snmp report: "
- "~n TargetName: ~p"
- "~n Info: ~p", [TargetName, Info]),
+ "~n TargetName: ~p"
+ "~n Info: ~p", [TargetName, Info]),
Pid ! {snmp_report_reply, ignore, self()},
P ! {snmp_report, TargetName, Info},
{noreply, State};
+handle_info({snmp_invalid_result, In, Out}, State) ->
+ error_msg("Callback failure: "
+ "~n In: ~p"
+ "~n Out: ~p", [In, Out]),
+ {noreply, State};
+
handle_info(Info, State) ->
error_msg("received unknown info: "
"~n Info: ~p", [Info]),
@@ -369,7 +376,12 @@ handle_report(TargetName, SnmpInfo, Pid) ->
after 10000 ->
ignore
end.
-
+
+
+handle_invalid_result(In, Out, Pid) ->
+ Pid ! {snmp_invalid_result, In, Out},
+ ignore.
+
%%----------------------------------------------------------------------
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 2164121e86..70f7c2b19a 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 4.24.2
+SNMP_VSN = 4.25
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 896b98edc2..c1a51d57fc 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -53,8 +53,7 @@
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() - {N1,N2,N3,N4} % IPv4 |
- {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6</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>
@@ -181,10 +180,6 @@
<item>
<p>Allow an existing file descriptor to be used
(simply passed on to the transport protocol).</p></item>
- <tag><c><![CDATA[{ipv6_disabled, boolean()}]]></c></tag>
- <item>
- <p>Determines if SSH shall use IPv6 or not.</p>
- </item>
<tag><c><![CDATA[{rekey_limit, integer()}]]></c></tag>
<item>
<p>Provide, in bytes, when rekeying should be initiated,
@@ -202,8 +197,11 @@
Value}] </name>
<fsummary> Retrieves information about a connection. </fsummary>
<type>
- <v>Option = client_version | server_version | peer</v>
- <v>Value = term() </v>
+ <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>
</type>
<desc>
<p> Retrieves information about a connection.
@@ -249,13 +247,14 @@
<c><![CDATA[{shell, start, []}]]></c>
</item>
<tag><c><![CDATA[{ssh_cli, {channel_callback(),
- channel_init_args()}}]]></c></tag>
+ channel_init_args()} | no_cli}]]></c></tag>
<item>
- Provides your own cli implementation, i.e. a channel callback
+ 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.
+ your own CLI channel. If set to <c>no_cli</c> you will disable
+ CLI channels and only subsystem channels will be allowed.
</item>
<tag><c><![CDATA[{user_dir, String}]]></c></tag>
<item>
@@ -296,7 +295,7 @@
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>
+ <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
@@ -313,39 +312,22 @@
<item>
<p>Allow an existing file-descriptor to be used
(simply passed on to the transport protocol).</p></item>
- <tag><c><![CDATA[{ip_v6_disabled, boolean()}]]></c></tag>
- <item>
- <p>Determines if SSH shall use IPv6 or not (only used when
- HostAddress is set to any).</p></item>
- <tag><c><![CDATA[{failfun, fun()}]]></c></tag>
+ <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>Provide a fun to implement your own logging when a user fails to authenticate.</p>
</item>
- <tag><c><![CDATA[{connectfun, fun()}]]></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>Provide a fun to implement your own logging when a user authenticates to the server.</p>
</item>
- <tag><c><![CDATA[{disconnectfun, fun()}]]></c></tag>
+ <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>Provide a fun to implement your own logging when a user disconnects from the server.</p>
</item>
</taglist>
</desc>
</func>
- <func>
- <name>peername(ConnectionRef) -> {ok, {Address,Port}} | {error,Error} </name>
- <fsummary> </fsummary>
- <type>
- <v> ConnectionRef = ssh_connection_ref()</v>
- <v> Address = ip_address()</v>
- <v> Port = integer()</v>
- </type>
- <desc>
- <p>Returns the address and port for the other end of a connection.
- </p>
- </desc>
- </func>
<func>
<name>shell(Host) -> </name>
@@ -367,20 +349,6 @@
</func>
<func>
- <name>sockname(ConnectionRef) -> {ok, {Address,Port}} | {error,Error} </name>
- <fsummary> </fsummary>
- <type>
- <v> ConnectionRef = ssh_connection_ref()</v>
- <v> Address = ip_address()</v>
- <v> Port = integer()</v>
- </type>
- <desc>
- <p>Returns the local address and port number for a connection.
- </p>
- </desc>
- </func>
-
- <func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
<fsummary>Starts the SSH application. </fsummary>
diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml
index b9b1ec4efa..2fa06f8bf1 100644
--- a/lib/ssh/doc/src/ssh_client_key_api.xml
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -41,12 +41,14 @@
<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>
+ 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>
<p> boolean() = true | false</p>
<p> string() = [byte()] </p>
<p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
- <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p>
+ <p> private_key() = #'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</p>
<p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p>
</section>
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
index 51e1fc1f2e..ee537f2f60 100644
--- a/lib/ssh/doc/src/ssh_server_key_api.xml
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -40,7 +40,9 @@
<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>
+ 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>
<p> boolean() = true | false</p>
<p> string() = [byte()]</p>
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 93d0b54f57..2ef2859fd7 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -53,7 +53,6 @@ MODULES= \
ssh_connection_sup \
ssh_connection \
ssh_connection_handler \
- ssh_connection_manager \
ssh_shell \
ssh_system_sup \
ssh_subsystem_sup \
@@ -67,12 +66,12 @@ MODULES= \
ssh_file \
ssh_io \
ssh_math \
+ ssh_message \
ssh_no_io \
ssh_sftp \
ssh_sftpd \
ssh_sftpd_file\
ssh_transport \
- ssh_userreg \
ssh_xfer
PUBLIC_HRL_FILES= ssh.hrl ssh_userauth.hrl ssh_xfer.hrl
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 49707f3378..74d7293be0 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -8,6 +8,7 @@
ssh_acceptor,
ssh_acceptor_sup,
ssh_auth,
+ ssh_message,
ssh_bits,
ssh_cli,
ssh_client_key_api,
@@ -15,7 +16,6 @@
ssh_channel_sup,
ssh_connection,
ssh_connection_handler,
- ssh_connection_manager,
ssh_connection_sup,
ssh_daemon_channel,
ssh_shell,
@@ -34,7 +34,6 @@
ssh_sup,
ssh_system_sup,
ssh_transport,
- ssh_userreg,
ssh_xfer]},
{registered, []},
{applications, [kernel, stdlib, crypto, public_key]},
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 80d20abbbd..2685b1553b 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -28,15 +28,12 @@
-export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2,
channel_info/3,
daemon/1, daemon/2, daemon/3,
- peername/1,
- sockname/1,
stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
shell/1, shell/2, shell/3]).
%%--------------------------------------------------------------------
-%% Function: start([, Type]) -> ok
-%%
-%% Type = permanent | transient | temporary
+-spec start() -> ok.
+-spec start(permanent | transient | temporary) -> ok.
%%
%% Description: Starts the ssh application. Default type
%% is temporary. see application(3)
@@ -54,7 +51,7 @@ start(Type) ->
application:start(ssh, Type).
%%--------------------------------------------------------------------
-%% Function: stop() -> ok
+-spec stop() -> ok.
%%
%% Description: Stops the ssh application.
%%--------------------------------------------------------------------
@@ -62,13 +59,8 @@ stop() ->
application:stop(ssh).
%%--------------------------------------------------------------------
-%% Function: connect(Host, Port, Options) ->
-%% connect(Host, Port, Options, Timeout -> ConnectionRef | {error, Reason}
-%%
-%% Host - string()
-%% Port - integer()
-%% Options - [{Option, Value}]
-%% Timeout - infinity | integer().
+-spec connect(string(), integer(), proplists:proplists()) -> {ok, pid()} | {error, term()}.
+-spec connect(string(), integer(), proplists:proplists(), timeout()) -> {ok, pid()} | {error, term()}.
%%
%% Description: Starts an ssh connection.
%%--------------------------------------------------------------------
@@ -79,83 +71,52 @@ connect(Host, Port, Options, Timeout) ->
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions} ->
- DisableIpv6 = proplists:get_value(ipv6_disabled, SshOptions, false),
- Inet = inetopt(DisableIpv6),
- do_connect(Host, Port, [Inet | SocketOptions],
- [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)], Timeout, DisableIpv6)
+ {_, Transport, _} = TransportOpts =
+ proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ Inet = proplists:get_value(inet, SshOptions, inet),
+ try Transport:connect(Host, Port, [ {active, false}, Inet | SocketOptions], Timeout) of
+ {ok, Socket} ->
+ Opts = [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)],
+ ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
+ {error, Reason} ->
+ {error, Reason}
+ catch
+ exit:{function_clause, _} ->
+ {error, {options, {transport, TransportOpts}}};
+ exit:badarg ->
+ {error, {options, {socket_options, SocketOptions}}}
+ end
end.
-do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
- try sshc_sup:start_child([[{address, Host}, {port, Port},
- {role, client},
- {channel_pid, self()},
- {socket_opts, SocketOptions},
- {ssh_opts, SshOptions}]]) of
- {ok, ConnectionSup} ->
- {ok, Manager} =
- ssh_connection_sup:connection_manager(ConnectionSup),
- msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout)
- catch
- exit:{noproc, _} ->
- {error, ssh_not_started}
- end.
-msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout) ->
- receive
- {Manager, is_connected} ->
- {ok, Manager};
- %% When the connection fails
- %% ssh_connection_sup:connection_manager
- %% might return undefined as the connection manager
- %% could allready have terminated, so we will not
- %% match the Manager in this case
- {_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
- do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
- SshOptions, Timeout, true);
- {_, not_connected, {error, Reason}} ->
- {error, Reason};
- {_, not_connected, Other} ->
- {error, Other};
- {From, user_password} ->
- Pass = io:get_password(),
- From ! Pass,
- msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout);
- {From, question} ->
- Answer = io:get_line(""),
- From ! Answer,
- msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout)
- after Timeout ->
- ssh_connection_manager:stop(Manager),
- {error, timeout}
- end.
%%--------------------------------------------------------------------
-%% Function: close(ConnectionRef) -> ok
+-spec close(pid()) -> ok.
%%
%% Description: Closes an ssh connection.
%%--------------------------------------------------------------------
close(ConnectionRef) ->
- ssh_connection_manager:stop(ConnectionRef).
+ ssh_connection_handler:stop(ConnectionRef).
%%--------------------------------------------------------------------
-%% Function: connection_info(ConnectionRef) -> [{Option, Value}]
+-spec connection_info(pid(), [atom()]) -> [{atom(), term()}].
%%
%% Description: Retrieves information about a connection.
%%--------------------------------------------------------------------
connection_info(ConnectionRef, Options) ->
- ssh_connection_manager:connection_info(ConnectionRef, Options).
+ ssh_connection_handler:connection_info(ConnectionRef, Options).
%%--------------------------------------------------------------------
-%% Function: channel_info(ConnectionRef) -> [{Option, Value}]
+-spec channel_info(pid(), channel_id(), [atom()]) -> [{atom(), term()}].
%%
%% Description: Retrieves information about a connection.
%%--------------------------------------------------------------------
channel_info(ConnectionRef, ChannelId, Options) ->
- ssh_connection_manager:channel_info(ConnectionRef, ChannelId, Options).
+ ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options).
%%--------------------------------------------------------------------
-%% Function: daemon(Port) ->
-%% daemon(Port, Options) ->
-%% daemon(Address, Port, Options) -> SshSystemRef
-%%
+-spec daemon(integer()) -> {ok, pid()}.
+-spec daemon(integer(), proplists:proplist()) -> {ok, pid()}.
+-spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()}.
+
%% Description: Starts a server listening for SSH connections
%% on the given port.
%%--------------------------------------------------------------------
@@ -172,11 +133,11 @@ daemon(HostAddr, Port, Options0) ->
_ ->
Options0
end,
- DisableIpv6 = proplists:get_value(ipv6_disabled, Options0, false),
+
{Host, Inet, Options} = case HostAddr of
any ->
{ok, Host0} = inet:gethostname(),
- {Host0, inetopt(DisableIpv6), Options1};
+ {Host0, proplists:get_value(inet, Options1, inet), Options1};
{_,_,_,_} ->
{HostAddr, inet,
[{ip, HostAddr} | Options1]};
@@ -187,9 +148,8 @@ daemon(HostAddr, Port, Options0) ->
start_daemon(Host, Port, Options, Inet).
%%--------------------------------------------------------------------
-%% Function: stop_listener(SysRef) -> ok
-%% stop_listener(Address, Port) -> ok
-%%
+-spec stop_listener(pid()) -> ok.
+-spec stop_listener(inet:ip_address(), integer()) -> ok.
%%
%% Description: Stops the listener, but leaves
%% existing connections started by the listener up and running.
@@ -200,9 +160,8 @@ stop_listener(Address, Port) ->
ssh_system_sup:stop_listener(Address, Port).
%%--------------------------------------------------------------------
-%% Function: stop_daemon(SysRef) -> ok
-%%% stop_daemon(Address, Port) -> ok
-%%
+-spec stop_daemon(pid()) -> ok.
+-spec stop_daemon(inet:ip_address(), integer()) -> ok.
%%
%% Description: Stops the listener and all connections started by
%% the listener.
@@ -213,9 +172,10 @@ stop_daemon(Address, Port) ->
ssh_system_sup:stop_system(Address, Port).
%%--------------------------------------------------------------------
-%% Function: shell(Host [,Port,Options]) -> {ok, ConnectionRef} |
-%% {error, Reason}
-%%
+-spec shell(string()) -> _.
+-spec shell(string(), proplists:proplist()) -> _.
+-spec shell(string(), integer(), proplists:proplist()) -> _.
+
%% Host = string()
%% Port = integer()
%% Options = [{Option, Value}]
@@ -247,28 +207,6 @@ shell(Host, Port, Options) ->
end.
%%--------------------------------------------------------------------
-%% Function: peername(ConnectionRef) -> {ok, {Host,Port}}
-%% | {error,Error}
-%%
-%% Description: Returns the peer address of the connection
-%%--------------------------------------------------------------------
-peername(ConnectionRef) ->
- [{peer, {_Name,{IP,Port}}}] =
- ssh_connection_manager:connection_info(ConnectionRef, [peer]),
- {ok, {IP,Port}}.
-
-%%--------------------------------------------------------------------
-%% Function: sockname(ConnectionRef) -> {ok, {Host,Port}}
-%% | {error,Error}
-%%
-%% Description: Returns the local address of the connection
-%%--------------------------------------------------------------------
-sockname(ConnectionRef) ->
- [{sockname, Result}] =
- ssh_connection_manager:connection_info(ConnectionRef, [sockname]),
- Result.
-
-%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
fix_idle_time(SshOptions) ->
@@ -403,9 +341,9 @@ handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({user_dir_fun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Value == false ->
+handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false ->
+handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
handle_ssh_option({public_key_alg, ssh_dsa}) ->
{public_key_alg, 'ssh-dss'};
@@ -453,9 +391,8 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({ipv6_disabled, Value} = Opt) when Value == true;
- Value == false ->
- Opt;
+handle_ssh_option({ipv6_disabled, Value} = Opt) when is_boolean(Value) ->
+ throw({error, {{ipv6_disabled, Opt}, option_no_longer_valid_use_inet_option_instead}});
handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol),
is_atom(Cb),
is_atom(ClosTag) ->
@@ -464,13 +401,14 @@ handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({ssh_cli, {Cb, _}}= Opt) when is_atom(Cb) ->
Opt;
+handle_ssh_option({ssh_cli, no_cli} = Opt) ->
+ Opt;
handle_ssh_option({shell, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
handle_ssh_option({shell, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({quiet_mode, Value} = Opt) when Value == true;
- Value == false ->
+handle_ssh_option({quiet_mode, Value} = Opt) when is_boolean(Value) ->
Opt;
handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 ->
Opt;
@@ -483,10 +421,8 @@ handle_inet_option({active, _} = Opt) ->
throw({error, {{eoptions, Opt}, "Ssh has built in flow control, "
"and activ is handled internaly user is not allowd"
"to specify this option"}});
-handle_inet_option({inet, _} = Opt) ->
- throw({error, {{eoptions, Opt},"Is set internaly use ipv6_disabled to"
- " enforce iv4 in the server, client will fallback to ipv4 if"
- " it can not use ipv6"}});
+handle_inet_option({inet, Value} = Opt) when (Value == inet) or (Value == inet6) ->
+ Opt;
handle_inet_option({reuseaddr, _} = Opt) ->
throw({error, {{eoptions, Opt},"Is set internaly user is not allowd"
"to specify this option"}});
@@ -509,18 +445,3 @@ handle_pref_algs([H|T], Acc) ->
_ ->
false
end.
-%% Has IPv6 been disabled?
-inetopt(true) ->
- inet;
-inetopt(false) ->
- case gen_tcp:listen(0, [inet6]) of
- {ok, Dummyport} ->
- gen_tcp:close(Dummyport),
- inet6;
- _ ->
- inet
- end.
-
-%%%
-%% Deprecated
-%%%
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 4fd347ba8f..94ced9da6f 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,8 @@
-define(SSH_DEFAULT_PORT, 22).
-define(SSH_MAX_PACKET_SIZE, (256*1024)).
-define(SSH_LENGHT_INDICATOR_SIZE, 4).
+-define(REKEY_TIMOUT, 3600000).
+-define(REKEY_DATA_TIMOUT, 60000).
-define(FALSE, 0).
-define(TRUE, 1).
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index d023656c32..91905b2eaf 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,6 @@
-export([start_link/5]).
%% spawn export
-%% TODO: system messages
-export([acceptor_init/6, acceptor_loop/6]).
-define(SLEEP_TIME, 200).
@@ -81,17 +80,15 @@ acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
ListenSocket, AcceptTimeout)
end.
-handle_connection(Callback, Address, Port, Options, Socket) ->
+handle_connection(_Callback, Address, Port, Options, Socket) ->
SystemSup = ssh_system_sup:system_supervisor(Address, Port),
{ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
- ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
- {ok, Pid} =
- ssh_connection_sup:start_manager_child(ConnectionSup,
- [server, Socket, Options]),
- Callback:controlling_process(Socket, Pid),
- SshOpts = proplists:get_value(ssh_opts, Options),
- Pid ! {start_connection, server, [Address, Port, Socket, SshOpts, SubSysSup]}.
-
+ ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
+ ssh_connection_handler:start_connection(server, Socket,
+ [{supervisors, [{system_sup, SystemSup},
+ {subsystem_sup, SubSysSup},
+ {connection_sup, ConnectionSup}]}
+ | Options], infinity).
handle_error(timeout) ->
ok;
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index f37e1fe4ff..2be729d305 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -84,8 +84,8 @@ child_spec(ServerOpts) ->
[{active, false},
{reuseaddr, true}] ++ SocketOpts,
ServerOpts, Timeout]},
- Restart = permanent,
- Shutdown = 3600,
+ Restart = transient,
+ Shutdown = brutal_kill,
Modules = [ssh_acceptor],
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index cb0c7751f0..1fa3df847f 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,8 +30,7 @@
-export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1,
service_request_msg/1, init_userauth_request_msg/1,
userauth_request_msg/1, handle_userauth_request/3,
- handle_userauth_info_request/3, handle_userauth_info_response/2,
- userauth_messages/0
+ handle_userauth_info_request/3, handle_userauth_info_response/2
]).
%%--------------------------------------------------------------------
@@ -43,7 +42,6 @@ publickey_msg([Alg, #ssh{user = User,
opts = Opts} = Ssh]) ->
Hash = sha, %% Maybe option?!
- ssh_bits:install_messages(userauth_pk_messages()),
KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
case KeyCb:user_key(Alg, Opts) of
@@ -69,7 +67,6 @@ publickey_msg([Alg, #ssh{user = User,
password_msg([#ssh{opts = Opts, io_cb = IoCb,
user = User, service = Service} = Ssh]) ->
- ssh_bits:install_messages(userauth_passwd_messages()),
Password = case proplists:get_value(password, Opts) of
undefined ->
user_interaction(IoCb, Ssh);
@@ -99,7 +96,6 @@ user_interaction(IoCb, Ssh) ->
%% See RFC 4256 for info on keyboard-interactive
keyboard_interactive_msg([#ssh{user = User,
service = Service} = Ssh]) ->
- ssh_bits:install_messages(userauth_keyboard_interactive_messages()),
ssh_transport:ssh_packet(
#ssh_msg_userauth_request{user = User,
service = Service,
@@ -239,7 +235,6 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
partial_success = false}, Ssh)}
end;
?FALSE ->
- ssh_bits:install_messages(userauth_pk_messages()),
{not_authorized, {User, undefined},
ssh_transport:ssh_packet(
#ssh_msg_userauth_pk_ok{algorithm_name = Alg,
@@ -275,26 +270,10 @@ handle_userauth_info_request(
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = "Server does not support"
- "keyboard-interactive",
+ description = "Server does not support"
+ "keyboard-interactive",
language = "en"}).
-userauth_messages() ->
- [ {ssh_msg_userauth_request, ?SSH_MSG_USERAUTH_REQUEST,
- [string,
- string,
- string,
- '...']},
-
- {ssh_msg_userauth_failure, ?SSH_MSG_USERAUTH_FAILURE,
- [string,
- boolean]},
-
- {ssh_msg_userauth_success, ?SSH_MSG_USERAUTH_SUCCESS,
- []},
-
- {ssh_msg_userauth_banner, ?SSH_MSG_USERAUTH_BANNER,
- [string,
- string]}].
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -386,13 +365,8 @@ algorithm_string('ssh-rsa') ->
algorithm_string('ssh-dss') ->
"ssh-dss".
-decode_keyboard_interactive_prompts(NumPrompts, Data) ->
- Types = lists:append(lists:duplicate(NumPrompts, [string, boolean])),
- pairwise_tuplify(ssh_bits:decode(Data, Types)).
-
-pairwise_tuplify([E1, E2 | Rest]) -> [{E1, E2} | pairwise_tuplify(Rest)];
-pairwise_tuplify([]) -> [].
-
+decode_keyboard_interactive_prompts(_NumPrompts, Data) ->
+ ssh_message:decode_keyboard_interactive_prompts(Data, []).
keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
NumPrompts = length(PromptInfos),
@@ -431,50 +405,29 @@ keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
end,
Prompts).
-userauth_passwd_messages() ->
- [
- {ssh_msg_userauth_passwd_changereq, ?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
- [string,
- string]}
- ].
-
-userauth_keyboard_interactive_messages() ->
- [ {ssh_msg_userauth_info_request, ?SSH_MSG_USERAUTH_INFO_REQUEST,
- [string,
- string,
- string,
- uint32,
- '...']},
-
- {ssh_msg_userauth_info_response, ?SSH_MSG_USERAUTH_INFO_RESPONSE,
- [uint32,
- '...']}
- ].
-
-userauth_pk_messages() ->
- [ {ssh_msg_userauth_pk_ok, ?SSH_MSG_USERAUTH_PK_OK,
- [string, % algorithm name
- binary]} % key blob
- ].
-
other_alg('ssh-rsa') ->
'ssh-dss';
other_alg('ssh-dss') ->
'ssh-rsa'.
-decode_public_key_v2(K_S, "ssh-rsa") ->
- case ssh_bits:decode(K_S,[string,mpint,mpint]) of
- ["ssh-rsa", E, N] ->
- {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}};
- _ ->
- {error, bad_format}
- end;
-decode_public_key_v2(K_S, "ssh-dss") ->
- case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of
- ["ssh-dss",P,Q,G,Y] ->
- {ok, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}};
- _ ->
- {error, bad_format}
- end;
+decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
+ ?UINT32(Len1), BinE:Len1/binary,
+ ?UINT32(Len2), BinN:Len2/binary>>
+ ,"ssh-rsa") ->
+ E = ssh_bits:erlint(Len1, BinE),
+ N = ssh_bits:erlint(Len2, BinN),
+ {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}};
+decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
+ ?UINT32(Len1), BinP:Len1/binary,
+ ?UINT32(Len2), BinQ:Len2/binary,
+ ?UINT32(Len3), BinG:Len3/binary,
+ ?UINT32(Len4), BinY:Len4/binary>>
+ , "ssh-dss") ->
+ P = ssh_bits:erlint(Len1, BinP),
+ Q = ssh_bits:erlint(Len2, BinQ),
+ G = ssh_bits:erlint(Len3, BinG),
+ Y = ssh_bits:erlint(Len4, BinY),
+ {ok, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}};
+
decode_public_key_v2(_, _) ->
{error, bad_format}.
diff --git a/lib/ssh/src/ssh_bits.erl b/lib/ssh/src/ssh_bits.erl
index fc6efc817f..2b0241cb83 100644
--- a/lib/ssh/src/ssh_bits.erl
+++ b/lib/ssh/src/ssh_bits.erl
@@ -25,19 +25,9 @@
-include("ssh.hrl").
--export([encode/1, encode/2]).
--export([decode/1, decode/2, decode/3]).
--export([mpint/1, bignum/1, string/1, name_list/1]).
--export([b64_encode/1, b64_decode/1]).
--export([install_messages/1, uninstall_messages/1]).
-
-%% integer utils
--export([isize/1]).
+-export([encode/2]).
+-export([mpint/1, erlint/2, string/1, name_list/1]).
-export([random/1]).
--export([xor_bits/2, fill_bits/2]).
--export([i2bin/2, bin2i/1]).
-
--import(lists, [foreach/2, reverse/1]).
-define(name_list(X),
(fun(B) -> ?binary(B) end)(list_to_binary(name_concat(X)))).
@@ -95,38 +85,6 @@ mpint_pos(X,I,Ds) ->
mpint_pos(X bsr 8,I+1,[(X band 255)|Ds]).
-%% BIGNUM representation SSH1
-bignum(X) ->
- XSz = isize(X),
- Pad = (8 - (XSz rem 8)) rem 8,
- <<?UINT16(XSz),0:Pad/unsigned-integer,X:XSz/big-unsigned-integer>>.
-
-
-install_messages(Codes) ->
- foreach(fun({Name, Code, Ts}) ->
- put({msg_name,Code}, {Name,Ts}),
- put({msg_code,Name}, {Code,Ts})
- end, Codes).
-
-uninstall_messages(Codes) ->
- foreach(fun({Name, Code, _Ts}) ->
- erase({msg_name,Code}),
- erase({msg_code,Name})
- end, Codes).
-
-%%
-%% Encode a record, the type spec is expected to be
-%% in process dictionary under the key {msg_code, RecodeName}
-%%
-encode(Record) ->
- case get({msg_code, element(1, Record)}) of
- undefined ->
- {error, unimplemented};
- {Code, Ts} ->
- Data = enc(tl(tuple_to_list(Record)), Ts),
- list_to_binary([Code, Data])
- end.
-
encode(List, Types) ->
list_to_binary(enc(List, Types)).
@@ -136,230 +94,58 @@ encode(List, Types) ->
enc(Xs, Ts) ->
enc(Xs, Ts, 0).
-enc(Xs, [Type|Ts], Offset) ->
- case Type of
- boolean ->
- X=hd(Xs),
- [?boolean(X) | enc(tl(Xs), Ts, Offset+1)];
- byte ->
- X=hd(Xs),
- [?byte(X) | enc(tl(Xs), Ts,Offset+1)];
- uint16 ->
- X=hd(Xs),
- [?uint16(X) | enc(tl(Xs), Ts,Offset+2)];
- uint32 ->
- X=hd(Xs),
- [?uint32(X) | enc(tl(Xs), Ts,Offset+4)];
- uint64 ->
- X=hd(Xs),
- [?uint64(X) | enc(tl(Xs), Ts,Offset+8)];
- mpint ->
- Y=mpint(hd(Xs)),
- [Y | enc(tl(Xs), Ts,Offset+size(Y))];
- bignum ->
- Y=bignum(hd(Xs)),
- [Y | enc(tl(Xs),Ts,Offset+size(Y))];
- string ->
- X0=hd(Xs),
- Y=?string(X0),
- [Y | enc(tl(Xs),Ts,Offset+size(Y))];
- binary ->
- X0=hd(Xs),
- Y=?binary(X0),
- [Y | enc(tl(Xs), Ts,Offset+size(Y))];
- name_list ->
- X0=hd(Xs),
- Y=?name_list(X0),
- [Y | enc(tl(Xs), Ts, Offset+size(Y))];
- cookie ->
- [random(16) | enc(tl(Xs), Ts, Offset+16)];
- {pad,N} ->
- K = (N - (Offset rem N)) rem N,
- [fill_bits(K,0) | enc(Xs, Ts, Offset+K)];
- '...' when Ts==[] ->
- X=hd(Xs),
- if is_binary(X) ->
- [X];
- is_list(X) ->
- [list_to_binary(X)];
- X==undefined ->
- []
- end
+enc(Xs, [boolean|Ts], Offset) ->
+ X = hd(Xs),
+ [?boolean(X) | enc(tl(Xs), Ts, Offset+1)];
+enc(Xs, [byte|Ts], Offset) ->
+ X = hd(Xs),
+ [?byte(X) | enc(tl(Xs), Ts,Offset+1)];
+enc(Xs, [uint16|Ts], Offset) ->
+ X = hd(Xs),
+ [?uint16(X) | enc(tl(Xs), Ts,Offset+2)];
+enc(Xs, [uint32 |Ts], Offset) ->
+ X = hd(Xs),
+ [?uint32(X) | enc(tl(Xs), Ts,Offset+4)];
+enc(Xs, [uint64|Ts], Offset) ->
+ X = hd(Xs),
+ [?uint64(X) | enc(tl(Xs), Ts,Offset+8)];
+enc(Xs, [mpint|Ts], Offset) ->
+ Y = mpint(hd(Xs)),
+ [Y | enc(tl(Xs), Ts,Offset+size(Y))];
+enc(Xs, [string|Ts], Offset) ->
+ X0 = hd(Xs),
+ Y = ?string(X0),
+ [Y | enc(tl(Xs),Ts,Offset+size(Y))];
+enc(Xs, [binary|Ts], Offset) ->
+ X0 = hd(Xs),
+ Y = ?binary(X0),
+ [Y | enc(tl(Xs), Ts,Offset+size(Y))];
+enc(Xs, [name_list|Ts], Offset) ->
+ X0 = hd(Xs),
+ Y = ?name_list(X0),
+ [Y | enc(tl(Xs), Ts, Offset+size(Y))];
+enc(Xs, [cookie|Ts], Offset) ->
+ [random(16) | enc(tl(Xs), Ts, Offset+16)];
+enc(Xs, [{pad,N}|Ts], Offset) ->
+ K = (N - (Offset rem N)) rem N,
+ [fill_bits(K,0) | enc(Xs, Ts, Offset+K)];
+enc(Xs, ['...'| []], _Offset) ->
+ X = hd(Xs),
+ if is_binary(X) ->
+ [X];
+ is_list(X) ->
+ [list_to_binary(X)];
+ X==undefined ->
+ []
end;
enc([], [],_) ->
[].
-
-
-%%
-%% Decode a SSH record the type is encoded as the first byte
-%% and the type spec MUST be installed in {msg_name, ID}
-%%
-
-decode(Binary = <<?BYTE(ID), _/binary>>) ->
- case get({msg_name, ID}) of
- undefined ->
- {unknown, Binary};
- {Name, Ts} ->
- {_, Elems} = decode(Binary,1,Ts),
- list_to_tuple([Name | Elems])
- end.
-
-%%
-%% Decode a binary form offset 0
-%%
-
-decode(Binary, Types) when is_binary(Binary) andalso is_list(Types) ->
- {_,Elems} = decode(Binary, 0, Types),
- Elems.
-
-
-%%
-%% Decode a binary from byte offset Offset
-%% return {UpdatedOffset, DecodedElements}
-%%
-decode(Binary, Offset, Types) ->
- decode(Binary, Offset, Types, []).
-
-decode(Binary, Offset, [Type|Ts], Acc) ->
- case Type of
- boolean ->
- <<_:Offset/binary, ?BOOLEAN(X0), _/binary>> = Binary,
- X = if X0 == 0 -> false; true -> true end,
- decode(Binary, Offset+1, Ts, [X | Acc]);
-
- byte ->
- <<_:Offset/binary, ?BYTE(X), _/binary>> = Binary,
- decode(Binary, Offset+1, Ts, [X | Acc]);
-
- uint16 ->
- <<_:Offset/binary, ?UINT16(X), _/binary>> = Binary,
- decode(Binary, Offset+2, Ts, [X | Acc]);
-
- uint32 ->
- <<_:Offset/binary, ?UINT32(X), _/binary>> = Binary,
- decode(Binary, Offset+4, Ts, [X | Acc]);
-
- uint64 ->
- <<_:Offset/binary, ?UINT64(X), _/binary>> = Binary,
- decode(Binary, Offset+8, Ts, [X | Acc]);
-
- mpint ->
- <<_:Offset/binary, ?UINT32(L), X0:L/binary,_/binary>> = Binary,
- Sz = L*8,
- <<X:Sz/big-signed-integer>> = X0,
- decode(Binary, Offset+4+L, Ts, [X | Acc]);
-
- bignum ->
- <<_:Offset/binary, ?UINT16(Bits),_/binary>> = Binary,
- L = (Bits+7) div 8,
- Pad = (8 - (Bits rem 8)) rem 8,
- <<_:Offset/binary, _:16, _:Pad, X:Bits/big-unsigned-integer,
- _/binary>> = Binary,
- decode(Binary, Offset+2+L, Ts, [X | Acc]);
-
- string ->
- Size = size(Binary),
- if Size < Offset + 4 ->
- %% empty string at end
- {Size, reverse(["" | Acc])};
- true ->
- <<_:Offset/binary,?UINT32(L), X:L/binary,_/binary>> =
- Binary,
- decode(Binary, Offset+4+L, Ts, [binary_to_list(X) |
- Acc])
- end;
-
- binary ->
- <<_:Offset/binary,?UINT32(L), X:L/binary,_/binary>> = Binary,
- decode(Binary, Offset+4+L, Ts, [X | Acc]);
-
- name_list ->
- <<_:Offset/binary,?UINT32(L), X:L/binary,_/binary>> = Binary,
- List = string:tokens(binary_to_list(X), ","),
- decode(Binary, Offset+4+L, Ts, [List | Acc]);
-
- cookie ->
- <<_:Offset/binary, X:16/binary, _/binary>> = Binary,
- decode(Binary, Offset+16, Ts, [X | Acc]);
-
- {pad,N} -> %% pad offset to a multiple of N
- K = (N - (Offset rem N)) rem N,
- decode(Binary, Offset+K, Ts, Acc);
-
+erlint(Len, BinInt) ->
+ Sz = Len*8,
+ <<Int:Sz/big-signed-integer>> = BinInt,
+ Int.
- '...' when Ts==[] ->
- <<_:Offset/binary, X/binary>> = Binary,
- {Offset+size(X), reverse([X | Acc])}
- end;
-decode(_Binary, Offset, [], Acc) ->
- {Offset, reverse(Acc)}.
-
-
-
-%% HACK WARNING :-)
--define(VERSION_MAGIC, 131).
--define(SMALL_INTEGER_EXT, $a).
--define(INTEGER_EXT, $b).
--define(SMALL_BIG_EXT, $n).
--define(LARGE_BIG_EXT, $o).
-
-isize(N) when N > 0 ->
- case term_to_binary(N) of
- <<?VERSION_MAGIC, ?SMALL_INTEGER_EXT, X>> ->
- isize_byte(X);
- <<?VERSION_MAGIC, ?INTEGER_EXT, X3,X2,X1,X0>> ->
- isize_bytes([X3,X2,X1,X0]);
- <<?VERSION_MAGIC, ?SMALL_BIG_EXT, S:8/big-unsigned-integer, 0,
- Ds:S/binary>> ->
- K = S - 1,
- <<_:K/binary, Top>> = Ds,
- isize_byte(Top)+K*8;
- <<?VERSION_MAGIC, ?LARGE_BIG_EXT, S:32/big-unsigned-integer, 0,
- Ds:S/binary>> ->
- K = S - 1,
- <<_:K/binary, Top>> = Ds,
- isize_byte(Top)+K*8
- end;
-isize(0) -> 0.
-
-%% big endian byte list
-isize_bytes([0|L]) ->
- isize_bytes(L);
-isize_bytes([Top|L]) ->
- isize_byte(Top) + length(L)*8.
-
-%% Well could be improved
-isize_byte(X) ->
- if X >= 2#10000000 -> 8;
- X >= 2#1000000 -> 7;
- X >= 2#100000 -> 6;
- X >= 2#10000 -> 5;
- X >= 2#1000 -> 4;
- X >= 2#100 -> 3;
- X >= 2#10 -> 2;
- X >= 2#1 -> 1;
- true -> 0
- end.
-
-%% Convert integer into binary
-%% When XLen is the wanted size in octets of the output
-i2bin(X, XLen) ->
- XSz = isize(X),
- Sz = XLen*8,
- if Sz < XSz ->
- exit(integer_to_large);
- true ->
- (<<X:Sz/big-unsigned-integer>>)
- end.
-
-%% Convert a binary into an integer
-%%
-bin2i(X) ->
- Sz = size(X)*8,
- <<Y:Sz/big-unsigned-integer>> = X,
- Y.
-
%%
%% Create a binary with constant bytes
%%
@@ -377,15 +163,6 @@ fill(N,C) ->
[C,Cs,Cs]
end.
-%% xor 2 binaries
-xor_bits(XBits, YBits) ->
- XSz = size(XBits)*8,
- YSz = size(YBits)*8,
- Sz = if XSz < YSz -> XSz; true -> YSz end, %% min
- <<X:Sz, _/binary>> = XBits,
- <<Y:Sz, _/binary>> = YBits,
- <<(X bxor Y):Sz>>.
-
%% random/1
%% Generate N random bytes
@@ -393,18 +170,5 @@ xor_bits(XBits, YBits) ->
random(N) ->
crypto:strong_rand_bytes(N).
-%%
-%% Base 64 encode/decode
-%%
-
-b64_encode(Bs) when is_list(Bs) ->
- base64:encode(Bs);
-b64_encode(Bin) when is_binary(Bin) ->
- base64:encode(Bin).
-
-b64_decode(Bin) when is_binary(Bin) ->
- base64:mime_decode(Bin);
-b64_decode(Cs) when is_list(Cs) ->
- base64:mime_decode(Cs).
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 062ed764ca..508ae637cf 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -284,7 +284,7 @@ handle_info(Msg, #state{cm = ConnectionManager, channel_cb = Module,
terminate(Reason, #state{cm = ConnectionManager,
channel_id = ChannelId,
close_sent = false} = State) ->
- ssh_connection:close(ConnectionManager, ChannelId),
+ catch ssh_connection:close(ConnectionManager, ChannelId),
terminate(Reason, State#state{close_sent = true});
terminate(_, #state{channel_cb = Cb, channel_state = ChannelState}) ->
catch Cb:terminate(Cb, ChannelState),
diff --git a/lib/ssh/src/ssh_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl
index 0093bce9c2..ee37ed35f8 100644
--- a/lib/ssh/src/ssh_channel_sup.erl
+++ b/lib/ssh/src/ssh_channel_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@
-export([init/1]).
%%%=========================================================================
-%%% API
+%%% Internal API
%%%=========================================================================
start_link(Args) ->
supervisor:start_link(?MODULE, [Args]).
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 54911e757c..5cb1e133d3 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -32,9 +32,6 @@
%% ssh_channel callbacks
-export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2]).
-%% backwards compatibility
--export([listen/1, listen/2, listen/3, listen/4, stop/1]).
-
%% state
-record(state, {
cm,
@@ -65,14 +62,14 @@ init([Shell]) ->
%%
%% Description: Handles channel messages received on the ssh-connection.
%%--------------------------------------------------------------------
-handle_ssh_msg({ssh_cm, _ConnectionManager,
+handle_ssh_msg({ssh_cm, _ConnectionHandler,
{data, _ChannelId, _Type, Data}},
#state{group = Group} = State) ->
List = binary_to_list(Data),
to_group(List, Group),
{ok, State};
-handle_ssh_msg({ssh_cm, ConnectionManager,
+handle_ssh_msg({ssh_cm, ConnectionHandler,
{pty, ChannelId, WantReply,
{TermName, Width, Height, PixWidth, PixHeight, Modes}}},
State0) ->
@@ -85,53 +82,53 @@ handle_ssh_msg({ssh_cm, ConnectionManager,
modes = Modes},
buf = empty_buf()},
set_echo(State),
- ssh_connection:reply_request(ConnectionManager, WantReply,
+ ssh_connection:reply_request(ConnectionHandler, WantReply,
success, ChannelId),
{ok, State};
-handle_ssh_msg({ssh_cm, ConnectionManager,
+handle_ssh_msg({ssh_cm, ConnectionHandler,
{env, ChannelId, WantReply, _Var, _Value}}, State) ->
- ssh_connection:reply_request(ConnectionManager,
+ ssh_connection:reply_request(ConnectionHandler,
WantReply, failure, ChannelId),
{ok, State};
-handle_ssh_msg({ssh_cm, ConnectionManager,
+handle_ssh_msg({ssh_cm, ConnectionHandler,
{window_change, ChannelId, Width, Height, PixWidth, PixHeight}},
#state{buf = Buf, pty = Pty0} = State) ->
Pty = Pty0#ssh_pty{width = Width, height = Height,
pixel_width = PixWidth,
pixel_height = PixHeight},
{Chars, NewBuf} = io_request({window_change, Pty0}, Buf, Pty),
- write_chars(ConnectionManager, ChannelId, Chars),
+ write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{pty = Pty, buf = NewBuf}};
-handle_ssh_msg({ssh_cm, ConnectionManager,
+handle_ssh_msg({ssh_cm, ConnectionHandler,
{shell, ChannelId, WantReply}}, State) ->
- NewState = start_shell(ConnectionManager, State),
- ssh_connection:reply_request(ConnectionManager, WantReply,
+ NewState = start_shell(ConnectionHandler, State),
+ ssh_connection:reply_request(ConnectionHandler, WantReply,
success, ChannelId),
{ok, NewState#state{channel = ChannelId,
- cm = ConnectionManager}};
+ cm = ConnectionHandler}};
-handle_ssh_msg({ssh_cm, ConnectionManager,
+handle_ssh_msg({ssh_cm, ConnectionHandler,
{exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined} = State) ->
{Reply, Status} = exec(Cmd),
- write_chars(ConnectionManager,
+ write_chars(ConnectionHandler,
ChannelId, io_lib:format("~p\n", [Reply])),
- ssh_connection:reply_request(ConnectionManager, WantReply,
+ ssh_connection:reply_request(ConnectionHandler, WantReply,
success, ChannelId),
- ssh_connection:exit_status(ConnectionManager, ChannelId, Status),
- ssh_connection:send_eof(ConnectionManager, ChannelId),
- {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionManager}};
-handle_ssh_msg({ssh_cm, ConnectionManager,
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
+ {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+handle_ssh_msg({ssh_cm, ConnectionHandler,
{exec, ChannelId, WantReply, Cmd}}, State) ->
- NewState = start_shell(ConnectionManager, Cmd, State),
- ssh_connection:reply_request(ConnectionManager, WantReply,
+ NewState = start_shell(ConnectionHandler, Cmd, State),
+ ssh_connection:reply_request(ConnectionHandler, WantReply,
success, ChannelId),
{ok, NewState#state{channel = ChannelId,
- cm = ConnectionManager}};
+ cm = ConnectionHandler}};
-handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) ->
+handle_ssh_msg({ssh_cm, _ConnectionHandler, {eof, _ChannelId}}, State) ->
{ok, State};
handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) ->
@@ -159,16 +156,16 @@ handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State) ->
%%
%% Description: Handles other channel messages.
%%--------------------------------------------------------------------
-handle_msg({ssh_channel_up, ChannelId, ConnectionManager},
+handle_msg({ssh_channel_up, ChannelId, ConnectionHandler},
#state{channel = ChannelId,
- cm = ConnectionManager} = State) ->
+ cm = ConnectionHandler} = State) ->
{ok, State};
handle_msg({Group, Req}, #state{group = Group, buf = Buf, pty = Pty,
- cm = ConnectionManager,
+ cm = ConnectionHandler,
channel = ChannelId} = State) ->
{Chars, NewBuf} = io_request(Req, Buf, Pty),
- write_chars(ConnectionManager, ChannelId, Chars),
+ write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{buf = NewBuf}};
handle_msg({'EXIT', Group, _Reason}, #state{group = Group,
@@ -399,12 +396,12 @@ move_cursor(From, To, #ssh_pty{width=Width, term=Type}) ->
%% %%% write out characters
%% %%% make sure that there is data to send
%% %%% before calling ssh_connection:send
-write_chars(ConnectionManager, ChannelId, Chars) ->
+write_chars(ConnectionHandler, ChannelId, Chars) ->
case erlang:iolist_size(Chars) of
0 ->
ok;
_ ->
- ssh_connection:send(ConnectionManager, ChannelId,
+ ssh_connection:send(ConnectionHandler, ChannelId,
?SSH_EXTENDED_DATA_DEFAULT, Chars)
end.
@@ -434,18 +431,20 @@ bin_to_list(L) when is_list(L) ->
bin_to_list(I) when is_integer(I) ->
I.
-start_shell(ConnectionManager, State) ->
+start_shell(ConnectionHandler, State) ->
Shell = State#state.shell,
+ ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ [peer, user]),
ShellFun = case is_function(Shell) of
true ->
{ok, User} =
- ssh_userreg:lookup_user(ConnectionManager),
+ proplists:get_value(user, ConnectionInfo),
case erlang:fun_info(Shell, arity) of
{arity, 1} ->
fun() -> Shell(User) end;
{arity, 2} ->
- {ok, PeerAddr} =
- ssh_connection_manager:peer_addr(ConnectionManager),
+ [{_, PeerAddr}] =
+ proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(User, PeerAddr) end;
_ ->
Shell
@@ -457,12 +456,15 @@ start_shell(ConnectionManager, State) ->
Group = group:start(self(), ShellFun, [{echo, Echo}]),
State#state{group = Group, buf = empty_buf()}.
-start_shell(_ConnectionManager, Cmd, #state{exec={M, F, A}} = State) ->
+start_shell(_ConnectionHandler, Cmd, #state{exec={M, F, A}} = State) ->
Group = group:start(self(), {M, F, A++[Cmd]}, [{echo, false}]),
State#state{group = Group, buf = empty_buf()};
-start_shell(ConnectionManager, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
+start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
+
+ ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ [peer, user]),
{ok, User} =
- ssh_userreg:lookup_user(ConnectionManager),
+ proplists:get_value(user, ConnectionInfo),
ShellFun =
case erlang:fun_info(Shell, arity) of
{arity, 1} ->
@@ -470,8 +472,8 @@ start_shell(ConnectionManager, Cmd, #state{exec=Shell} = State) when is_function
{arity, 2} ->
fun() -> Shell(Cmd, User) end;
{arity, 3} ->
- {ok, PeerAddr} =
- ssh_connection_manager:peer_addr(ConnectionManager),
+ [{_, PeerAddr}] =
+ proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(Cmd, User, PeerAddr) end;
_ ->
Shell
@@ -505,31 +507,3 @@ not_zero(0, B) ->
not_zero(A, _) ->
A.
-%%% Backwards compatibility
-
-%%--------------------------------------------------------------------
-%% Function: listen(...) -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts a listening server
-%% Note that the pid returned is NOT the pid of this gen_server;
-%% this server is started when an SSH connection is made on the
-%% listening port
-%%--------------------------------------------------------------------
-listen(Shell) ->
- listen(Shell, 22).
-
-listen(Shell, Port) ->
- listen(Shell, Port, []).
-
-listen(Shell, Port, Opts) ->
- listen(Shell, any, Port, Opts).
-
-listen(Shell, HostAddr, Port, Opts) ->
- ssh:daemon(HostAddr, Port, [{shell, Shell} | Opts]).
-
-
-%%--------------------------------------------------------------------
-%% Function: stop(Pid) -> ok
-%% Description: Stops the listener
-%%--------------------------------------------------------------------
-stop(Pid) ->
- ssh:stop_listener(Pid).
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 932b0642f1..8421b07167 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,8 @@
%%% Description : SSH connection protocol
+-type channel_id() :: integer().
+
-define(DEFAULT_PACKET_SIZE, 32768).
-define(DEFAULT_WINDOW_SIZE, 2*?DEFAULT_PACKET_SIZE).
-define(DEFAULT_TIMEOUT, 5000).
@@ -260,6 +262,7 @@
port,
options,
exec,
+ system_supervisor,
sub_system_supervisor,
connection_supervisor
}).
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 9424cdd423..03dddae3c8 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,232 +29,205 @@
-include("ssh_connect.hrl").
-include("ssh_transport.hrl").
+%% API
-export([session_channel/2, session_channel/4,
exec/4, shell/2, subsystem/4, send/3, send/4, send/5,
- send_eof/2, adjust_window/3, open_pty/3, open_pty/7,
- open_pty/9, setenv/5, window_change/4, window_change/6,
+ send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4]).
+
+%% Potential API currently unsupported and not tested
+-export([open_pty/3, open_pty/7,
+ open_pty/9, window_change/4, window_change/6,
direct_tcpip/6, direct_tcpip/8, tcpip_forward/3,
- cancel_tcpip_forward/3, signal/3, exit_status/3, encode_ip/1, close/2,
- reply_request/4]).
+ cancel_tcpip_forward/3, signal/3, exit_status/3]).
--export([channel_data/6, handle_msg/4, channel_eof_msg/1,
+%% Internal application API
+-export([channel_data/5, handle_msg/3, channel_eof_msg/1,
channel_close_msg/1, channel_success_msg/1, channel_failure_msg/1,
+ channel_status_msg/1,
channel_adjust_window_msg/2, channel_data_msg/3,
channel_open_msg/5, channel_open_confirmation_msg/4,
channel_open_failure_msg/4, channel_request_msg/4,
global_request_msg/3, request_failure_msg/0,
request_success_msg/1, bind/4, unbind/3, unbind_channel/2,
- bound_channel/3, messages/0]).
+ bound_channel/3, encode_ip/1]).
%%--------------------------------------------------------------------
-%%% Internal application API
+%%% API
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
-%% Function: session_channel(ConnectionManager
-%% [, InitialWindowSize, MaxPacketSize],
-%% Timeout) -> {ok, }
-%% ConnectionManager = pid()
-%% InitialWindowSize = integer()
-%% MaxPacketSize = integer()
-%%
+-spec session_channel(pid(), timeout()) -> {ok, channel_id()} | {error, term()}.
+-spec session_channel(pid(), integer(), integer(), timeout()) -> {ok, channel_id()} | {error, term()}.
+
%% Description: Opens a channel for a ssh session. A session is a
%% remote execution of a program. The program may be a shell, an
%% application, a system command, or some built-in subsystem.
%% --------------------------------------------------------------------
-session_channel(ConnectionManager, Timeout) ->
- session_channel(ConnectionManager,
+
+session_channel(ConnectionHandler, Timeout) ->
+ session_channel(ConnectionHandler,
?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE,
Timeout).
-session_channel(ConnectionManager, InitialWindowSize,
+
+session_channel(ConnectionHandler, InitialWindowSize,
MaxPacketSize, Timeout) ->
- ssh_connection_manager:open_channel(ConnectionManager, "session", <<>>,
+ case ssh_connection_handler:open_channel(ConnectionHandler, "session", <<>>,
InitialWindowSize,
- MaxPacketSize, Timeout).
+ MaxPacketSize, Timeout) of
+ {open, Channel} ->
+ {ok, Channel};
+ Error ->
+ Error
+ end.
+
%%--------------------------------------------------------------------
-%% Function: exec(ConnectionManager, ChannelId, Command, Timeout) ->
-%%
-%% ConnectionManager = pid()
-%% ChannelId = integer()
-%% Cmd = string()
-%% Timeout = integer()
-%%
+-spec exec(pid(), channel_id(), string(), timeout()) -> success | failure.
+
%% Description: Will request that the server start the
%% execution of the given command.
%%--------------------------------------------------------------------
-exec(ConnectionManager, ChannelId, Command, TimeOut) ->
- ssh_connection_manager:request(ConnectionManager, self(), ChannelId, "exec",
- true, [?string(Command)], TimeOut).
+exec(ConnectionHandler, ChannelId, Command, TimeOut) ->
+ ssh_connection_handler:request(ConnectionHandler, self(), ChannelId, "exec",
+ true, [?string(Command)], TimeOut).
+
%%--------------------------------------------------------------------
-%% Function: shell(ConnectionManager, ChannelId) ->
-%%
-%% ConnectionManager = pid()
-%% ChannelId = integer()
-%%
+-spec shell(pid(), channel_id()) -> _.
+
%% Description: Will request that the user's default shell (typically
%% defined in /etc/passwd in UNIX systems) be started at the other
%% end.
%%--------------------------------------------------------------------
-shell(ConnectionManager, ChannelId) ->
- ssh_connection_manager:request(ConnectionManager, self(), ChannelId,
+shell(ConnectionHandler, ChannelId) ->
+ ssh_connection_handler:request(ConnectionHandler, self(), ChannelId,
"shell", false, <<>>, 0).
%%--------------------------------------------------------------------
-%% Function: subsystem(ConnectionManager, ChannelId, SubSystem, TimeOut) ->
-%%
-%% ConnectionManager = pid()
-%% ChannelId = integer()
-%% SubSystem = string()
-%% TimeOut = integer()
-%%
+-spec subsystem(pid(), channel_id(), string(), timeout()) ->
+ success | failure | {error, timeout}.
%%
%% Description: Executes a predefined subsystem.
%%--------------------------------------------------------------------
-subsystem(ConnectionManager, ChannelId, SubSystem, TimeOut) ->
- ssh_connection_manager:request(ConnectionManager, self(),
+subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
+ ssh_connection_handler:request(ConnectionHandler, self(),
ChannelId, "subsystem",
true, [?string(SubSystem)], TimeOut).
%%--------------------------------------------------------------------
-%% Function: send(ConnectionManager, ChannelId, Type, Data, [TimeOut]) ->
+-spec send(pid(), channel_id(), iodata()) ->
+ ok | {error, closed}.
+-spec send(pid(), channel_id(), integer()| iodata(), timeout() | iodata()) ->
+ ok | {error, timeout} | {error, closed}.
+-spec send(pid(), channel_id(), integer(), iodata(), timeout()) ->
+ ok | {error, timeout} | {error, closed}.
%%
%%
%% Description: Sends channel data.
%%--------------------------------------------------------------------
-send(ConnectionManager, ChannelId, Data) ->
- send(ConnectionManager, ChannelId, 0, Data, infinity).
-send(ConnectionManager, ChannelId, Data, TimeOut) when is_integer(TimeOut) ->
- send(ConnectionManager, ChannelId, 0, Data, TimeOut);
-send(ConnectionManager, ChannelId, Data, infinity) ->
- send(ConnectionManager, ChannelId, 0, Data, infinity);
-send(ConnectionManager, ChannelId, Type, Data) ->
- send(ConnectionManager, ChannelId, Type, Data, infinity).
-send(ConnectionManager, ChannelId, Type, Data, TimeOut) ->
- ssh_connection_manager:send(ConnectionManager, ChannelId,
+send(ConnectionHandler, ChannelId, Data) ->
+ send(ConnectionHandler, ChannelId, 0, Data, infinity).
+send(ConnectionHandler, ChannelId, Data, TimeOut) when is_integer(TimeOut) ->
+ send(ConnectionHandler, ChannelId, 0, Data, TimeOut);
+send(ConnectionHandler, ChannelId, Data, infinity) ->
+ send(ConnectionHandler, ChannelId, 0, Data, infinity);
+send(ConnectionHandler, ChannelId, Type, Data) ->
+ send(ConnectionHandler, ChannelId, Type, Data, infinity).
+send(ConnectionHandler, ChannelId, Type, Data, TimeOut) ->
+ ssh_connection_handler:send(ConnectionHandler, ChannelId,
Type, Data, TimeOut).
%%--------------------------------------------------------------------
-%% Function: send_eof(ConnectionManager, ChannelId) ->
+-spec send_eof(pid(), channel_id()) -> ok | {error, closed}.
%%
%%
%% Description: Sends eof on the channel <ChannelId>.
%%--------------------------------------------------------------------
-send_eof(ConnectionManager, Channel) ->
- ssh_connection_manager:send_eof(ConnectionManager, Channel).
+send_eof(ConnectionHandler, Channel) ->
+ ssh_connection_handler:send_eof(ConnectionHandler, Channel).
%%--------------------------------------------------------------------
-%% Function: adjust_window(ConnectionManager, Channel, Bytes) ->
+-spec adjust_window(pid(), channel_id(), integer()) -> ok.
%%
%%
%% Description: Adjusts the ssh flowcontrol window.
%%--------------------------------------------------------------------
-adjust_window(ConnectionManager, Channel, Bytes) ->
- ssh_connection_manager:adjust_window(ConnectionManager, Channel, Bytes).
+adjust_window(ConnectionHandler, Channel, Bytes) ->
+ ssh_connection_handler:adjust_window(ConnectionHandler, Channel, Bytes).
%%--------------------------------------------------------------------
-%% Function: setenv(ConnectionManager, ChannelId, Var, Value, TimeOut) ->
+-spec setenv(pid(), channel_id(), string(), string(), timeout()) -> success | failure.
%%
%%
%% Description: Environment variables may be passed to the shell/command to be
%% started later.
%%--------------------------------------------------------------------
-setenv(ConnectionManager, ChannelId, Var, Value, TimeOut) ->
- ssh_connection_manager:request(ConnectionManager, ChannelId,
+setenv(ConnectionHandler, ChannelId, Var, Value, TimeOut) ->
+ ssh_connection_handler:request(ConnectionHandler, ChannelId,
"env", true, [?string(Var), ?string(Value)], TimeOut).
%%--------------------------------------------------------------------
-%% Function: close(ConnectionManager, ChannelId) ->
+-spec close(pid(), channel_id()) -> ok.
%%
%%
%% Description: Sends a close message on the channel <ChannelId>.
%%--------------------------------------------------------------------
-close(ConnectionManager, ChannelId) ->
- ssh_connection_manager:close(ConnectionManager, ChannelId).
-
+close(ConnectionHandler, ChannelId) ->
+ ssh_connection_handler:close(ConnectionHandler, ChannelId).
%%--------------------------------------------------------------------
-%% Function: reply_request(ConnectionManager, WantReply, Status, CannelId) ->_
+-spec reply_request(pid(), boolean(), success | failure, channel_id()) -> ok.
%%
%%
%% Description: Send status replies to requests that want such replies.
%%--------------------------------------------------------------------
-reply_request(ConnectionManager, true, Status, ChannelId) ->
- ssh_connection_manager:reply_request(ConnectionManager, Status, ChannelId),
- ok;
+reply_request(ConnectionHandler, true, Status, ChannelId) ->
+ ssh_connection_handler:reply_request(ConnectionHandler, Status, ChannelId);
reply_request(_,false, _, _) ->
ok.
-
%%--------------------------------------------------------------------
-%% Function: window_change(ConnectionManager, Channel, Width, Height) ->
-%%
-%%
-%% Description: Not yet officialy supported.
+%% Not yet officialy supported! The following functions are part of the
+%% initial contributed ssh application. They are untested. Do we want them?
+%% Should they be documented and tested?
%%--------------------------------------------------------------------
-window_change(ConnectionManager, Channel, Width, Height) ->
- window_change(ConnectionManager, Channel, Width, Height, 0, 0).
-window_change(ConnectionManager, Channel, Width, Height,
+window_change(ConnectionHandler, Channel, Width, Height) ->
+ window_change(ConnectionHandler, Channel, Width, Height, 0, 0).
+window_change(ConnectionHandler, Channel, Width, Height,
PixWidth, PixHeight) ->
- ssh_connection_manager:request(ConnectionManager, Channel,
+ ssh_connection_handler:request(ConnectionHandler, Channel,
"window-change", false,
[?uint32(Width), ?uint32(Height),
?uint32(PixWidth), ?uint32(PixHeight)], 0).
-%%--------------------------------------------------------------------
-%% Function: signal(ConnectionManager, Channel, Sig) ->
-%%
-%%
-%% Description: Not yet officialy supported.
-%%--------------------------------------------------------------------
-signal(ConnectionManager, Channel, Sig) ->
- ssh_connection_manager:request(ConnectionManager, Channel,
+
+signal(ConnectionHandler, Channel, Sig) ->
+ ssh_connection_handler:request(ConnectionHandler, Channel,
"signal", false, [?string(Sig)], 0).
-%%--------------------------------------------------------------------
-%% Function: signal(ConnectionManager, Channel, Status) ->
-%%
-%%
-%% Description: Not yet officialy supported.
-%%--------------------------------------------------------------------
-exit_status(ConnectionManager, Channel, Status) ->
- ssh_connection_manager:request(ConnectionManager, Channel,
- "exit-status", false, [?uint32(Status)], 0).
+exit_status(ConnectionHandler, Channel, Status) ->
+ ssh_connection_handler:request(ConnectionHandler, Channel,
+ "exit-status", false, [?uint32(Status)], 0).
-%%--------------------------------------------------------------------
-%% Function: open_pty(ConnectionManager, Channel, TimeOut) ->
-%%
-%%
-%% Description: Not yet officialy supported.
-%%--------------------------------------------------------------------
-open_pty(ConnectionManager, Channel, TimeOut) ->
- open_pty(ConnectionManager, Channel,
+open_pty(ConnectionHandler, Channel, TimeOut) ->
+ open_pty(ConnectionHandler, Channel,
os:getenv("TERM"), 80, 24, [], TimeOut).
-open_pty(ConnectionManager, Channel, Term, Width, Height, PtyOpts, TimeOut) ->
- open_pty(ConnectionManager, Channel, Term, Width,
+open_pty(ConnectionHandler, Channel, Term, Width, Height, PtyOpts, TimeOut) ->
+ open_pty(ConnectionHandler, Channel, Term, Width,
Height, 0, 0, PtyOpts, TimeOut).
-open_pty(ConnectionManager, Channel, Term, Width, Height,
+open_pty(ConnectionHandler, Channel, Term, Width, Height,
PixWidth, PixHeight, PtyOpts, TimeOut) ->
- ssh_connection_manager:request(ConnectionManager,
+ ssh_connection_handler:request(ConnectionHandler,
Channel, "pty-req", true,
[?string(Term),
?uint32(Width), ?uint32(Height),
?uint32(PixWidth),?uint32(PixHeight),
encode_pty_opts(PtyOpts)], TimeOut).
-
-%%--------------------------------------------------------------------
-%% Function: direct_tcpip(ConnectionManager, RemoteHost,
-%% RemotePort, OrigIP, OrigPort, Timeout) ->
-%%
-%%
-%% Description: Not yet officialy supported.
-%%--------------------------------------------------------------------
-direct_tcpip(ConnectionManager, RemoteHost,
+direct_tcpip(ConnectionHandler, RemoteHost,
RemotePort, OrigIP, OrigPort, Timeout) ->
- direct_tcpip(ConnectionManager, RemoteHost, RemotePort, OrigIP, OrigPort,
+ direct_tcpip(ConnectionHandler, RemoteHost, RemotePort, OrigIP, OrigPort,
?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
-direct_tcpip(ConnectionManager, RemoteIP, RemotePort, OrigIP, OrigPort,
+direct_tcpip(ConnectionHandler, RemoteIP, RemotePort, OrigIP, OrigPort,
InitialWindowSize, MaxPacketSize, Timeout) ->
case {encode_ip(RemoteIP), encode_ip(OrigIP)} of
{false, _} ->
@@ -262,7 +235,7 @@ direct_tcpip(ConnectionManager, RemoteIP, RemotePort, OrigIP, OrigPort,
{_, false} ->
{error, einval};
{RIP, OIP} ->
- ssh_connection_manager:open_channel(ConnectionManager,
+ ssh_connection_handler:open_channel(ConnectionHandler,
"direct-tcpip",
[?string(RIP),
?uint32(RemotePort),
@@ -272,34 +245,24 @@ direct_tcpip(ConnectionManager, RemoteIP, RemotePort, OrigIP, OrigPort,
MaxPacketSize,
Timeout)
end.
-%%--------------------------------------------------------------------
-%% Function: tcpip_forward(ConnectionManager, BindIP, BindPort) ->
-%%
-%%
-%% Description: Not yet officialy supported.
-%%--------------------------------------------------------------------
-tcpip_forward(ConnectionManager, BindIP, BindPort) ->
+
+tcpip_forward(ConnectionHandler, BindIP, BindPort) ->
case encode_ip(BindIP) of
false ->
{error, einval};
IPStr ->
- ssh_connection_manager:global_request(ConnectionManager,
+ ssh_connection_handler:global_request(ConnectionHandler,
"tcpip-forward", true,
[?string(IPStr),
?uint32(BindPort)])
end.
-%%--------------------------------------------------------------------
-%% Function: cancel_tcpip_forward(ConnectionManager, BindIP, Port) ->
-%%
-%%
-%% Description: Not yet officialy supported.
-%%--------------------------------------------------------------------
-cancel_tcpip_forward(ConnectionManager, BindIP, Port) ->
+
+cancel_tcpip_forward(ConnectionHandler, BindIP, Port) ->
case encode_ip(BindIP) of
false ->
{error, einval};
IPStr ->
- ssh_connection_manager:global_request(ConnectionManager,
+ ssh_connection_handler:global_request(ConnectionHandler,
"cancel-tcpip-forward", true,
[?string(IPStr),
?uint32(Port)])
@@ -308,22 +271,23 @@ cancel_tcpip_forward(ConnectionManager, BindIP, Port) ->
%%--------------------------------------------------------------------
%%% Internal API
%%--------------------------------------------------------------------
-channel_data(ChannelId, DataType, Data, Connection, ConnectionPid, From)
+channel_data(ChannelId, DataType, Data, Connection, From)
when is_list(Data)->
channel_data(ChannelId, DataType,
- list_to_binary(Data), Connection, ConnectionPid, From);
+ list_to_binary(Data), Connection, From);
channel_data(ChannelId, DataType, Data,
- #connection{channel_cache = Cache} = Connection, ConnectionPid,
+ #connection{channel_cache = Cache} = Connection,
From) ->
case ssh_channel:cache_lookup(Cache, ChannelId) of
#channel{remote_id = Id, sent_close = false} = Channel0 ->
- {SendList, Channel} = update_send_window(Channel0#channel{flow_control = From}, DataType,
- Data, Connection),
+ {SendList, Channel} =
+ update_send_window(Channel0#channel{flow_control = From}, DataType,
+ Data, Connection),
Replies =
lists:map(fun({SendDataType, SendData}) ->
- {connection_reply, ConnectionPid,
+ {connection_reply,
channel_data_msg(Id,
SendDataType,
SendData)}
@@ -333,7 +297,7 @@ channel_data(ChannelId, DataType, Data,
Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
_ ->
- gen_server:reply(From, {error, closed}),
+ gen_fsm:reply(From, {error, closed}),
{noreply, Connection}
end.
@@ -341,7 +305,7 @@ handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId,
sender_channel = RemoteId,
initial_window_size = WindowSz,
maximum_packet_size = PacketSz},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
#channel{remote_id = undefined} = Channel =
ssh_channel:cache_lookup(Cache, ChannelId),
@@ -357,7 +321,7 @@ handle_msg(#ssh_msg_channel_open_failure{recipient_channel = ChannelId,
reason = Reason,
description = Descr,
lang = Lang},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
ssh_channel:cache_delete(Cache, ChannelId),
{Reply, Connection} =
@@ -365,51 +329,59 @@ handle_msg(#ssh_msg_channel_open_failure{recipient_channel = ChannelId,
{{replies, [Reply]}, Connection};
handle_msg(#ssh_msg_channel_success{recipient_channel = ChannelId},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
- {Reply, Connection} = reply_msg(Channel, Connection0, success),
- {{replies, [Reply]}, Connection};
+ case reply_msg(Channel, Connection0, success) of
+ {[], Connection} ->
+ {noreply, Connection};
+ {Reply, Connection} ->
+ {{replies, [Reply]}, Connection}
+ end;
handle_msg(#ssh_msg_channel_failure{recipient_channel = ChannelId},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
- {Reply, Connection} = reply_msg(Channel, Connection0, failure),
- {{replies, [Reply]}, Connection};
+ case reply_msg(Channel, Connection0, failure) of
+ {[], Connection} ->
+ {noreply, Connection};
+ {Reply, Connection} ->
+ {{replies, [Reply]}, Connection}
+ end;
+
handle_msg(#ssh_msg_channel_eof{recipient_channel = ChannelId},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
{Reply, Connection} = reply_msg(Channel, Connection0, {eof, ChannelId}),
{{replies, [Reply]}, Connection};
handle_msg(#ssh_msg_channel_close{recipient_channel = ChannelId},
- #connection{channel_cache = Cache} = Connection0,
- ConnectionPid, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{sent_close = Closed, remote_id = RemoteId, flow_control = FlowControl} = Channel ->
+ #channel{sent_close = Closed, remote_id = RemoteId,
+ flow_control = FlowControl} = Channel ->
ssh_channel:cache_delete(Cache, ChannelId),
{CloseMsg, Connection} =
reply_msg(Channel, Connection0, {closed, ChannelId}),
-
- ConnReplyMsgs =
- case Closed of
- true -> [];
- false ->
- RemoteCloseMsg = channel_close_msg(RemoteId),
- [{connection_reply, ConnectionPid, RemoteCloseMsg}]
- end,
-
- %% if there was a send() in progress, make it fail
- SendReplyMsgs =
- case FlowControl of
- undefined -> [];
- From ->
- [{flow_control, From, {error, closed}}]
- end,
-
- Replies = ConnReplyMsgs ++ [CloseMsg] ++ SendReplyMsgs,
- {{replies, Replies}, Connection};
+ ConnReplyMsgs =
+ case Closed of
+ true -> [];
+ false ->
+ RemoteCloseMsg = channel_close_msg(RemoteId),
+ [{connection_reply, RemoteCloseMsg}]
+ end,
+
+ %% if there was a send() in progress, make it fail
+ SendReplyMsgs =
+ case FlowControl of
+ undefined -> [];
+ From ->
+ [{flow_control, From, {error, closed}}]
+ end,
+
+ Replies = ConnReplyMsgs ++ [CloseMsg] ++ SendReplyMsgs,
+ {{replies, Replies}, Connection};
undefined ->
{{replies, []}, Connection0}
@@ -417,21 +389,24 @@ handle_msg(#ssh_msg_channel_close{recipient_channel = ChannelId},
handle_msg(#ssh_msg_channel_data{recipient_channel = ChannelId,
data = Data},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
- #channel{recv_window_size = Size} = Channel =
- ssh_channel:cache_lookup(Cache, ChannelId),
- WantedSize = Size - size(Data),
- ssh_channel:cache_update(Cache, Channel#channel{
- recv_window_size = WantedSize}),
- {Replies, Connection} =
- channel_data_reply(Cache, Channel, Connection0, 0, Data),
- {{replies, Replies}, Connection};
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{recv_window_size = Size} = Channel ->
+ WantedSize = Size - size(Data),
+ ssh_channel:cache_update(Cache, Channel#channel{
+ recv_window_size = WantedSize}),
+ {Replies, Connection} =
+ channel_data_reply(Cache, Channel, Connection0, 0, Data),
+ {{replies, Replies}, Connection};
+ undefined ->
+ {noreply, Connection0}
+ end;
handle_msg(#ssh_msg_channel_extended_data{recipient_channel = ChannelId,
data_type_code = DataType,
data = Data},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
#channel{recv_window_size = Size} = Channel =
ssh_channel:cache_lookup(Cache, ChannelId),
@@ -444,9 +419,7 @@ handle_msg(#ssh_msg_channel_extended_data{recipient_channel = ChannelId,
handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
bytes_to_add = Add},
- #connection{channel_cache = Cache} = Connection,
- ConnectionPid, _) ->
-
+ #connection{channel_cache = Cache} = Connection, _) ->
#channel{send_window_size = Size, remote_id = RemoteId} =
Channel0 = ssh_channel:cache_lookup(Cache, ChannelId),
@@ -455,8 +428,7 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
0, undefined, Connection),
Replies = lists:map(fun({Type, Data}) ->
- {connection_reply, ConnectionPid,
- channel_data_msg(RemoteId, Type, Data)}
+ {connection_reply, channel_data_msg(RemoteId, Type, Data)}
end, SendList),
FlowCtrlMsgs = flow_control(Channel, Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
@@ -464,10 +436,9 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
sender_channel = RemoteId,
initial_window_size = WindowSz,
- maximum_packet_size = PacketSz}, Connection0,
- ConnectionPid, server) ->
+ maximum_packet_size = PacketSz}, Connection0, server) ->
- try setup_session(Connection0, ConnectionPid, RemoteId,
+ try setup_session(Connection0, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
@@ -475,20 +446,20 @@ handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]},
+ {{replies, [{connection_reply, FailMsg}]},
Connection0}
end;
handle_msg(#ssh_msg_channel_open{channel_type = "session",
sender_channel = RemoteId},
- Connection, ConnectionPid, client) ->
+ Connection, client) ->
%% Client implementations SHOULD reject any session channel open
%% requests to make it more difficult for a corrupt server to attack the
%% client. See See RFC 4254 6.1.
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]},
+ {{replies, [{connection_reply, FailMsg}]},
Connection};
handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
@@ -496,8 +467,7 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
initial_window_size = RWindowSz,
maximum_packet_size = RPacketSz,
data = Data},
- #connection{channel_cache = Cache} = Connection0,
- ConnectionPid, server) ->
+ #connection{channel_cache = Cache} = Connection0, server) ->
<<?UINT32(ALen), Address:ALen/binary, ?UINT32(Port),
?UINT32(OLen), Orig:OLen/binary, ?UINT32(OrigPort)>> = Data,
@@ -507,7 +477,7 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies,
- [{connection_reply, ConnectionPid, FailMsg}]}, Connection0};
+ [{connection_reply, FailMsg}]}, Connection0};
ChannelPid ->
{ChannelId, Connection1} = new_channel_id(Connection0),
LWindowSz = ?DEFAULT_WINDOW_SIZE,
@@ -528,32 +498,31 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
{open, Channel, {forwarded_tcpip,
decode_ip(Address), Port,
decode_ip(Orig), OrigPort}}),
- {{replies, [{connection_reply, ConnectionPid, OpenConfMsg},
+ {{replies, [{connection_reply, OpenConfMsg},
OpenMsg]}, Connection}
end;
handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
sender_channel = RemoteId},
- Connection, ConnectionPid, client) ->
+ Connection, client) ->
%% Client implementations SHOULD reject direct TCP/IP open requests for
%% security reasons. See RFC 4254 7.2.
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
+ {{replies, [{connection_reply, FailMsg}]}, Connection};
-handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection,
- ConnectionPid, _) ->
+handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection, _) ->
FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
"Not allowed", "en"),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
+ {{replies, [{connection_reply, FailMsg}]}, Connection};
handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "exit-status",
data = Data},
- #connection{channel_cache = Cache} = Connection, _, _) ->
+ #connection{channel_cache = Cache} = Connection, _) ->
<<?UINT32(Status)>> = Data,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
{Reply, Connection} =
@@ -564,8 +533,7 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "exit-signal",
want_reply = false,
data = Data},
- #connection{channel_cache = Cache} = Connection0,
- ConnectionPid, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
<<?UINT32(SigLen), SigName:SigLen/binary,
?BOOLEAN(_Core),
?UINT32(ErrLen), Err:ErrLen/binary,
@@ -578,14 +546,14 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
binary_to_list(Err),
binary_to_list(Lang)}),
CloseMsg = channel_close_msg(RemoteId),
- {{replies, [{connection_reply, ConnectionPid, CloseMsg}, Reply]},
+ {{replies, [{connection_reply, CloseMsg}, Reply]},
Connection};
handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "xon-xoff",
want_reply = false,
data = Data},
- #connection{channel_cache = Cache} = Connection, _, _) ->
+ #connection{channel_cache = Cache} = Connection, _) ->
<<?BOOLEAN(CDo)>> = Data,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
{Reply, Connection} =
@@ -596,7 +564,7 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "window-change",
want_reply = false,
data = Data},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
<<?UINT32(Width),?UINT32(Height),
?UINT32(PixWidth), ?UINT32(PixHeight)>> = Data,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
@@ -609,7 +577,7 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "signal",
data = Data},
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
<<?UINT32(SigLen), SigName:SigLen/binary>> = Data,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
@@ -622,8 +590,7 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "subsystem",
want_reply = WantReply,
data = Data},
- #connection{channel_cache = Cache} = Connection,
- ConnectionPid, server) ->
+ #connection{channel_cache = Cache} = Connection, server) ->
<<?UINT32(SsLen), SsName:SsLen/binary>> = Data,
#channel{remote_id = RemoteId} = Channel0 =
@@ -631,22 +598,23 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
ReplyMsg = {subsystem, ChannelId, WantReply, binary_to_list(SsName)},
- try start_subsytem(SsName, Connection, Channel0, ReplyMsg) of
- {ok, Pid} ->
- erlang:monitor(process, Pid),
- Channel = Channel0#channel{user = Pid},
- ssh_channel:cache_update(Cache, Channel),
- Reply = {connection_reply, ConnectionPid,
- channel_success_msg(RemoteId)},
- {{replies, [Reply]}, Connection}
- catch _:_ ->
- Reply = {connection_reply, ConnectionPid,
- channel_failure_msg(RemoteId)},
- {{replies, [Reply]}, Connection}
+ try
+ {ok, Pid} = start_subsytem(SsName, Connection, Channel0, ReplyMsg),
+ erlang:monitor(process, Pid),
+ Channel = Channel0#channel{user = Pid},
+ ssh_channel:cache_update(Cache, Channel),
+ Reply = {connection_reply,
+ channel_success_msg(RemoteId)},
+ {{replies, [Reply]}, Connection}
+ catch
+ _:_ ->
+ ErrorReply = {connection_reply,
+ channel_failure_msg(RemoteId)},
+ {{replies, [ErrorReply]}, Connection}
end;
handle_msg(#ssh_msg_channel_request{request_type = "subsystem"},
- Connection, _, client) ->
+ Connection, client) ->
%% The client SHOULD ignore subsystem requests. See RFC 4254 6.5.
{{replies, []}, Connection};
@@ -654,8 +622,7 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "pty-req",
want_reply = WantReply,
data = Data},
- #connection{channel_cache = Cache} = Connection,
- ConnectionPid, server) ->
+ #connection{channel_cache = Cache} = Connection, server) ->
<<?UINT32(TermLen), BTermName:TermLen/binary,
?UINT32(Width),?UINT32(Height),
?UINT32(PixWidth), ?UINT32(PixHeight),
@@ -667,27 +634,26 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
- handle_cli_msg(Connection, ConnectionPid, Channel,
+ handle_cli_msg(Connection, Channel,
{pty, ChannelId, WantReply, PtyRequest});
handle_msg(#ssh_msg_channel_request{request_type = "pty-req"},
- Connection, _, client) ->
+ Connection, client) ->
%% The client SHOULD ignore pty requests. See RFC 4254 6.2.
{{replies, []}, Connection};
handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "shell",
want_reply = WantReply},
- #connection{channel_cache = Cache} = Connection,
- ConnectionPid, server) ->
+ #connection{channel_cache = Cache} = Connection, server) ->
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
- handle_cli_msg(Connection, ConnectionPid, Channel,
+ handle_cli_msg(Connection, Channel,
{shell, ChannelId, WantReply});
handle_msg(#ssh_msg_channel_request{request_type = "shell"},
- Connection, _, client) ->
+ Connection, client) ->
%% The client SHOULD ignore shell requests. See RFC 4254 6.5.
{{replies, []}, Connection};
@@ -695,17 +661,16 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "exec",
want_reply = WantReply,
data = Data},
- #connection{channel_cache = Cache} = Connection,
- ConnectionPid, server) ->
+ #connection{channel_cache = Cache} = Connection, server) ->
<<?UINT32(Len), Command:Len/binary>> = Data,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
- handle_cli_msg(Connection, ConnectionPid, Channel,
+ handle_cli_msg(Connection, Channel,
{exec, ChannelId, WantReply, binary_to_list(Command)});
handle_msg(#ssh_msg_channel_request{request_type = "exec"},
- Connection, _, client) ->
+ Connection, client) ->
%% The client SHOULD ignore exec requests. See RFC 4254 6.5.
{{replies, []}, Connection};
@@ -713,31 +678,30 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = "env",
want_reply = WantReply,
data = Data},
- #connection{channel_cache = Cache} = Connection,
- ConnectionPid, server) ->
+ #connection{channel_cache = Cache} = Connection, server) ->
<<?UINT32(VarLen),
Var:VarLen/binary, ?UINT32(ValueLen), Value:ValueLen/binary>> = Data,
Channel = ssh_channel:cache_lookup(Cache, ChannelId),
- handle_cli_msg(Connection, ConnectionPid, Channel,
+ handle_cli_msg(Connection, Channel,
{env, ChannelId, WantReply, Var, Value});
handle_msg(#ssh_msg_channel_request{request_type = "env"},
- Connection, _, client) ->
+ Connection, client) ->
%% The client SHOULD ignore env requests.
{{replies, []}, Connection};
handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = _Other,
- want_reply = WantReply}, #connection{channel_cache = Cache} = Connection,
- ConnectionPid, _) ->
+ want_reply = WantReply},
+ #connection{channel_cache = Cache} = Connection, _) ->
if WantReply == true ->
case ssh_channel:cache_lookup(Cache, ChannelId) of
#channel{remote_id = RemoteId} ->
FailMsg = channel_failure_msg(RemoteId),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]},
+ {{replies, [{connection_reply, FailMsg}]},
Connection};
undefined -> %% Chanel has been closed
{noreply, Connection}
@@ -748,61 +712,74 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
handle_msg(#ssh_msg_global_request{name = _Type,
want_reply = WantReply,
- data = _Data}, Connection,
- ConnectionPid, _) ->
+ data = _Data}, Connection, _) ->
if WantReply == true ->
FailMsg = request_failure_msg(),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]},
+ {{replies, [{connection_reply, FailMsg}]},
Connection};
true ->
{noreply, Connection}
end;
-%%% This transport message will also be handled at the connection level
+handle_msg(#ssh_msg_request_failure{},
+ #connection{requests = [{_, From} | Rest]} = Connection, _) ->
+ {{replies, [{channel_requst_reply, From, {failure, <<>>}}]},
+ Connection#connection{requests = Rest}};
+handle_msg(#ssh_msg_request_success{data = Data},
+ #connection{requests = [{_, From} | Rest]} = Connection, _) ->
+ {{replies, [{channel_requst_reply, From, {success, Data}}]},
+ Connection#connection{requests = Rest}};
+
handle_msg(#ssh_msg_disconnect{code = Code,
description = Description,
language = _Lang },
- #connection{channel_cache = Cache} = Connection0, _, _) ->
+ #connection{channel_cache = Cache} = Connection0, _) ->
{Connection, Replies} =
ssh_channel:cache_foldl(fun(Channel, {Connection1, Acc}) ->
{Reply, Connection2} =
reply_msg(Channel,
- Connection1, {closed, Channel#channel.local_id}),
+ Connection1,
+ {closed, Channel#channel.local_id}),
{Connection2, [Reply | Acc]}
end, {Connection0, []}, Cache),
ssh_channel:cache_delete(Cache),
{disconnect, {Code, Description}, {{replies, Replies}, Connection}}.
-handle_cli_msg(#connection{channel_cache = Cache} = Connection0,
- ConnectionPid,
+handle_cli_msg(#connection{channel_cache = Cache} = Connection,
#channel{user = undefined,
+ remote_id = RemoteId,
local_id = ChannelId} = Channel0, Reply0) ->
- case (catch start_cli(Connection0, ChannelId)) of
+ case (catch start_cli(Connection, ChannelId)) of
{ok, Pid} ->
erlang:monitor(process, Pid),
Channel = Channel0#channel{user = Pid},
ssh_channel:cache_update(Cache, Channel),
- {Reply, Connection} = reply_msg(Channel, Connection0, Reply0),
- {{replies, [Reply]}, Connection};
- _ ->
- Reply = {connection_reply, ConnectionPid,
- request_failure_msg()},
- {{replies, [Reply]}, Connection0}
+ Reply = {connection_reply,
+ channel_success_msg(RemoteId)},
+ {{replies, [{channel_data, Pid, Reply0}, Reply]}, Connection};
+ _Other ->
+ Reply = {connection_reply,
+ channel_failure_msg(RemoteId)},
+ {{replies, [Reply]}, Connection}
end;
-handle_cli_msg(Connection0, _, Channel, Reply0) ->
+handle_cli_msg(Connection0, Channel, Reply0) ->
{Reply, Connection} = reply_msg(Channel, Connection0, Reply0),
{{replies, [Reply]}, Connection}.
-
channel_eof_msg(ChannelId) ->
#ssh_msg_channel_eof{recipient_channel = ChannelId}.
channel_close_msg(ChannelId) ->
#ssh_msg_channel_close {recipient_channel = ChannelId}.
+channel_status_msg({success, ChannelId}) ->
+ channel_success_msg(ChannelId);
+channel_status_msg({failure, ChannelId}) ->
+ channel_failure_msg(ChannelId).
+
channel_success_msg(ChannelId) ->
#ssh_msg_channel_success{recipient_channel = ChannelId}.
@@ -880,70 +857,6 @@ bound_channel(IP, Port, Connection) ->
_ -> undefined
end.
-messages() ->
- [ {ssh_msg_global_request, ?SSH_MSG_GLOBAL_REQUEST,
- [string,
- boolean,
- '...']},
-
- {ssh_msg_request_success, ?SSH_MSG_REQUEST_SUCCESS,
- ['...']},
-
- {ssh_msg_request_failure, ?SSH_MSG_REQUEST_FAILURE,
- []},
-
- {ssh_msg_channel_open, ?SSH_MSG_CHANNEL_OPEN,
- [string,
- uint32,
- uint32,
- uint32,
- '...']},
-
- {ssh_msg_channel_open_confirmation, ?SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
- [uint32,
- uint32,
- uint32,
- uint32,
- '...']},
-
- {ssh_msg_channel_open_failure, ?SSH_MSG_CHANNEL_OPEN_FAILURE,
- [uint32,
- uint32,
- string,
- string]},
-
- {ssh_msg_channel_window_adjust, ?SSH_MSG_CHANNEL_WINDOW_ADJUST,
- [uint32,
- uint32]},
-
- {ssh_msg_channel_data, ?SSH_MSG_CHANNEL_DATA,
- [uint32,
- binary]},
-
- {ssh_msg_channel_extended_data, ?SSH_MSG_CHANNEL_EXTENDED_DATA,
- [uint32,
- uint32,
- binary]},
-
- {ssh_msg_channel_eof, ?SSH_MSG_CHANNEL_EOF,
- [uint32]},
-
- {ssh_msg_channel_close, ?SSH_MSG_CHANNEL_CLOSE,
- [uint32]},
-
- {ssh_msg_channel_request, ?SSH_MSG_CHANNEL_REQUEST,
- [uint32,
- string,
- boolean,
- '...']},
-
- {ssh_msg_channel_success, ?SSH_MSG_CHANNEL_SUCCESS,
- [uint32]},
-
- {ssh_msg_channel_failure, ?SSH_MSG_CHANNEL_FAILURE,
- [uint32]}
- ].
-
encode_ip(Addr) when is_tuple(Addr) ->
case catch inet_parse:ntoa(Addr) of
{'EXIT',_} -> false;
@@ -965,14 +878,14 @@ start_channel(Cb, Id, Args, SubSysSup) ->
start_channel(Cb, Id, Args, SubSysSup, Exec) ->
ChildSpec = child_spec(Cb, Id, Args, Exec),
- ChannelSup =ssh_subsystem_sup:channel_supervisor(SubSysSup),
+ ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
ssh_channel_sup:start_child(ChannelSup, ChildSpec).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
setup_session(#connection{channel_cache = Cache} = Connection0,
- ConnectionPid, RemoteId,
+ RemoteId,
Type, WindowSize, PacketSize) ->
{ChannelId, Connection} = new_channel_id(Connection0),
@@ -990,7 +903,7 @@ setup_session(#connection{channel_cache = Cache} = Connection0,
?DEFAULT_WINDOW_SIZE,
?DEFAULT_PACKET_SIZE),
- {{replies, [{connection_reply, ConnectionPid, OpenConfMsg}]}, Connection}.
+ {{replies, [{connection_reply, OpenConfMsg}]}, Connection}.
check_subsystem("sftp"= SsName, Options) ->
@@ -1019,35 +932,21 @@ child_spec(Callback, Id, Args, Exec) ->
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, [ssh_channel]}.
-%% Backwards compatibility
-start_cli(#connection{address = Address, port = Port, cli_spec = {Fun, [Shell]},
- options = Options},
- _ChannelId) when is_function(Fun) ->
- case Fun(Shell, Address, Port, Options) of
- NewFun when is_function(NewFun) ->
- {ok, NewFun()};
- Pid when is_pid(Pid) ->
- {ok, Pid}
- end;
-
+start_cli(#connection{cli_spec = no_cli}, _) ->
+ {error, cli_disabled};
start_cli(#connection{cli_spec = {CbModule, Args}, exec = Exec,
sub_system_supervisor = SubSysSup}, ChannelId) ->
start_channel(CbModule, ChannelId, Args, SubSysSup, Exec).
-start_subsytem(BinName, #connection{address = Address, port = Port,
- options = Options,
+start_subsytem(BinName, #connection{options = Options,
sub_system_supervisor = SubSysSup},
- #channel{local_id = ChannelId, remote_id = RemoteChannelId},
- ReplyMsg) ->
+ #channel{local_id = ChannelId}, _ReplyMsg) ->
Name = binary_to_list(BinName),
case check_subsystem(Name, Options) of
{Callback, Opts} when is_atom(Callback), Callback =/= none ->
start_channel(Callback, ChannelId, Opts, SubSysSup);
{Other, _} when Other =/= none ->
- handle_backwards_compatibility(Other, self(),
- ChannelId, RemoteChannelId,
- Options, Address, Port,
- {ssh_cm, self(), ReplyMsg})
+ {error, legacy_option_not_supported}
end.
channel_data_reply(_, #channel{local_id = ChannelId} = Channel,
@@ -1070,9 +969,12 @@ reply_msg(Channel, Connection, failure = Reply) ->
request_reply_or_data(Channel, Connection, Reply);
reply_msg(Channel, Connection, {closed, _} = Reply) ->
request_reply_or_data(Channel, Connection, Reply);
+reply_msg(undefined, Connection, _Reply) ->
+ {noreply, Connection};
reply_msg(#channel{user = ChannelPid}, Connection, Reply) ->
{{channel_data, ChannelPid, Reply}, Connection}.
+
request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid},
#connection{requests = Requests} =
Connection, Reply) ->
@@ -1080,10 +982,13 @@ request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid},
{value, {ChannelId, From}} ->
{{channel_requst_reply, From, Reply},
Connection#connection{requests =
- lists:keydelete(ChannelId, 1, Requests)}};
+ lists:keydelete(ChannelId, 1, Requests)}};
+ false when (Reply == success) or (Reply == failure) ->
+ {[], Connection};
false ->
{{channel_data, ChannelPid, Reply}, Connection}
end.
+
update_send_window(Channel, _, undefined,
#connection{channel_cache = Cache}) ->
do_update_send_window(Channel, Channel#channel.send_buf, Cache);
@@ -1139,7 +1044,7 @@ flow_control([], Channel, Cache) ->
[];
flow_control([_|_], #channel{flow_control = From,
- send_buf = []} = Channel, Cache) when From =/= undefined ->
+ send_buf = []} = Channel, Cache) when From =/= undefined ->
[{flow_control, Cache, Channel, From, ok}];
flow_control(_,_,_) ->
[].
@@ -1341,43 +1246,3 @@ decode_ip(Addr) when is_binary(Addr) ->
{ok,A} -> A
end.
-%% This is really awful and that is why it is beeing phased out.
-handle_backwards_compatibility({_,_,_,_,_,_} = ChildSpec, _, _, _, _,
- Address, Port, _) ->
- SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- ChannelSup = ssh_system_sup:channel_supervisor(SystemSup),
- ssh_channel_sup:start_child(ChannelSup, ChildSpec);
-
-handle_backwards_compatibility(Module, ConnectionManager, ChannelId,
- RemoteChannelId, Opts,
- _, _, Msg) when is_atom(Module) ->
- {ok, SubSystemPid} = gen_server:start_link(Module, [Opts], []),
- SubSystemPid !
- {ssh_cm, ConnectionManager,
- {open, ChannelId, RemoteChannelId, {session}}},
- SubSystemPid ! Msg,
- {ok, SubSystemPid};
-
-handle_backwards_compatibility(Fun, ConnectionManager, ChannelId,
- RemoteChannelId,
- _, _, _, Msg) when is_function(Fun) ->
- SubSystemPid = Fun(),
- SubSystemPid !
- {ssh_cm, ConnectionManager,
- {open, ChannelId, RemoteChannelId, {session}}},
- SubSystemPid ! Msg,
- {ok, SubSystemPid};
-
-handle_backwards_compatibility(ChildSpec,
- ConnectionManager,
- ChannelId, RemoteChannelId, _,
- Address, Port, Msg) ->
- SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- ChannelSup = ssh_system_sup:channel_supervisor(SystemSup),
- {ok, SubSystemPid}
- = ssh_channel_sup:start_child(ChannelSup, ChildSpec),
- SubSystemPid !
- {ssh_cm, ConnectionManager,
- {open, ChannelId, RemoteChannelId, {session}}},
- SubSystemPid ! Msg,
- {ok, SubSystemPid}.
diff --git a/lib/ssh/src/ssh_connection_controler.erl b/lib/ssh/src/ssh_connection_controler.erl
deleted file mode 100644
index ca3e62dc83..0000000000
--- a/lib/ssh/src/ssh_connection_controler.erl
+++ /dev/null
@@ -1,137 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2010. 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%
-%%
-%%--------------------------------------------------------------------
-%% File : ssh_connection_controler.erl
-%% Description :
-%%
-%%--------------------------------------------------------------------
-
--module(ssh_connection_controler).
-
--behaviour(gen_server).
-
-%%-----------------------------------------------------------------
-%% External exports
-%%-----------------------------------------------------------------
--export([start_link/1, start_handler_child/2, start_manager_child/2,
- connection_manager/1]).
-
-%%-----------------------------------------------------------------
-%% Internal exports
-%%-----------------------------------------------------------------
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- code_change/3, terminate/2, stop/1]).
-
--record(state, {role, manager, handler, timeout}).
-
-%%-----------------------------------------------------------------
-%% External interface functions
-%%-----------------------------------------------------------------
-%%-----------------------------------------------------------------
-%% Func: start/0
-%%-----------------------------------------------------------------
-start_link(Args) ->
- gen_server:start_link(?MODULE, [Args], []).
-
-%% Will be called from the manager child process
-start_handler_child(ServerRef, Args) ->
- gen_server:call(ServerRef, {handler, self(), Args}, infinity).
-
-%% Will be called from the acceptor process
-start_manager_child(ServerRef, Args) ->
- gen_server:call(ServerRef, {manager, Args}, infinity).
-
-connection_manager(ServerRef) ->
- {ok, gen_server:call(ServerRef, manager, infinity)}.
-
-%%-----------------------------------------------------------------
-%% Internal interface functions
-%%-----------------------------------------------------------------
-%%-----------------------------------------------------------------
-%% Func: stop/1
-%%-----------------------------------------------------------------
-stop(Pid) ->
- gen_server:cast(Pid, stop).
-
-%%-----------------------------------------------------------------
-%% Server functions
-%%-----------------------------------------------------------------
-%%-----------------------------------------------------------------
-%% Func: init/1
-%%-----------------------------------------------------------------
-init([Opts]) ->
- process_flag(trap_exit, true),
- case proplists:get_value(role, Opts) of
- client ->
- {ok, Manager} = ssh_connection_manager:start_link([client, Opts]),
- {ok, #state{role = client, manager = Manager}};
- _server ->
- %% Children started by acceptor process
- {ok, #state{role = server}}
- end.
-
-
-%%-----------------------------------------------------------------
-%% Func: terminate/2
-%%-----------------------------------------------------------------
-terminate(_Reason, #state{}) ->
- ok.
-
-%%-----------------------------------------------------------------
-%% Func: handle_call/3
-%%-----------------------------------------------------------------
-handle_call({handler, Pid, [Role, Socket, Opts]}, _From, State) ->
- {ok, Handler} = ssh_connection_handler:start_link(Role, Pid, Socket, Opts),
- {reply, {ok, Handler}, State#state{handler = Handler}};
-handle_call({manager, [server = Role, Socket, Opts, SubSysSup]}, _From, State) ->
- {ok, Manager} = ssh_connection_manager:start_link([Role, Socket, Opts, SubSysSup]),
- {reply, {ok, Manager}, State#state{manager = Manager}};
-handle_call({manager, [client = Role | Opts]}, _From, State) ->
- {ok, Manager} = ssh_connection_manager:start_link([Role, Opts]),
- {reply, {ok, Manager}, State#state{manager = Manager}};
-handle_call(manager, _From, State) ->
- {reply, State#state.manager, State};
-handle_call(stop, _From, State) ->
- {stop, normal, ok, State};
-handle_call(_, _, State) ->
- {noreply, State, State#state.timeout}.
-
-%%-----------------------------------------------------------------
-%% Func: handle_cast/2
-%%-----------------------------------------------------------------
-handle_cast(stop, State) ->
- {stop, normal, State};
-handle_cast(_, State) ->
- {noreply, State, State#state.timeout}.
-
-%%-----------------------------------------------------------------
-%% Func: handle_info/2
-%%-----------------------------------------------------------------
-%% handle_info(ssh_connected, State) ->
-%% {stop, normal, State};
-%% Servant termination.
-handle_info({'EXIT', _Pid, Reason}, State) ->
- {stop, Reason, State}.
-
-%%-----------------------------------------------------------------
-%% Func: code_change/3
-%%-----------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index c3e8a3c742..3462b98172 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -18,10 +18,11 @@
%%
%%
%%----------------------------------------------------------------------
-%% Purpose: Handles the setup of an ssh connection, e.i. both the
-%% setup SSH Transport Layer Protocol (RFC 4253) and Authentication
-%% Protocol (RFC 4252). Details of the different protocols are
-%% implemented in ssh_transport.erl, ssh_auth.erl
+%% Purpose: Handles an ssh connection, e.i. both the
+%% setup SSH Transport Layer Protocol (RFC 4253), Authentication
+%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255)
+%% Details of the different protocols are
+%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl
%% ----------------------------------------------------------------------
-module(ssh_connection_handler).
@@ -33,10 +34,14 @@
-include("ssh_auth.hrl").
-include("ssh_connect.hrl").
--export([start_link/4, send/2, renegotiate/1, send_event/2,
- connection_info/3,
- peer_address/1,
- renegotiate_data/1]).
+-export([start_link/3]).
+
+%% Internal application API
+-export([open_channel/6, reply_request/3, request/6, request/7,
+ global_request/4, send/5, send_eof/2, info/1, info/2,
+ connection_info/2, channel_info/3,
+ adjust_window/3, close/2, stop/1, renegotiate/1, renegotiate_data/1,
+ start_connection/4]).
%% gen_fsm callbacks
-export([hello/2, kexinit/2, key_exchange/2, new_keys/2,
@@ -45,10 +50,14 @@
-export([init/1, handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-%% spawn export
--export([ssh_info_handler/4]).
-
-record(state, {
+ role,
+ client,
+ starter,
+ auth_user,
+ connection_state,
+ latest_channel_id = 0,
+ idle_timer_ref,
transport_protocol, % ex: tcp
transport_cb,
transport_close_tag,
@@ -59,105 +68,234 @@
undecoded_packet_length, % integer()
key_exchange_init_msg, % #ssh_msg_kexinit{}
renegotiate = false, % boolean()
- manager, % pid()
connection_queue,
address,
port,
opts
}).
--define(DBG_MESSAGE, true).
+-type state_name() :: hello | kexinit | key_exchange | new_keys | userauth | connection.
+-type gen_fsm_state_return() :: {next_state, state_name(), term()} |
+ {next_state, state_name(), term(), timeout()} |
+ {stop, term(), term()}.
%%====================================================================
%% Internal application API
%%====================================================================
+
%%--------------------------------------------------------------------
-%% Function: start_link() -> ok,Pid} | ignore | {error,Error}
-%% Description:Creates a gen_fsm process which calls Module:init/1 to
-%% initialize. To ensure a synchronized start-up procedure, this function
-%% does not return until Module:init/1 has returned.
+-spec start_connection(client| server, port(), proplists:proplist(),
+ timeout()) -> {ok, pid()} | {error, term()}.
%%--------------------------------------------------------------------
-start_link(Role, Manager, Socket, Options) ->
- gen_fsm:start_link(?MODULE, [Role, Manager, Socket, Options], []).
-
-send(ConnectionHandler, Data) ->
- send_all_state_event(ConnectionHandler, {send, Data}).
+start_connection(client = Role, Socket, Options, Timeout) ->
+ try
+ {ok, Pid} = sshc_sup:start_child([Role, Socket, Options]),
+ {_, Callback, _} =
+ proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ ok = socket_control(Socket, Pid, Callback),
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout)
+ catch
+ exit:{noproc, _} ->
+ {error, ssh_not_started};
+ _:Error ->
+ {error, Error}
+ end;
-renegotiate(ConnectionHandler) ->
- send_all_state_event(ConnectionHandler, renegotiate).
-
-renegotiate_data(ConnectionHandler) ->
- send_all_state_event(ConnectionHandler, data_size).
-connection_info(ConnectionHandler, From, Options) ->
- send_all_state_event(ConnectionHandler, {info, From, Options}).
+start_connection(server = Role, Socket, Options, Timeout) ->
+ try
+ Sups = proplists:get_value(supervisors, Options),
+ ConnectionSup = proplists:get_value(connection_sup, Sups),
+ Opts = [{supervisors, Sups}, {user_pid, self()} | proplists:get_value(ssh_opts, Options, [])],
+ {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
+ {_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ socket_control(Socket, Pid, Callback),
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout)
+ catch
+ exit:{noproc, _} ->
+ {error, ssh_not_started};
+ _:Error ->
+ {error, Error}
+ end.
-%% Replaced with option to connection_info/3. For now keep
-%% for backwards compatibility
-peer_address(ConnectionHandler) ->
- sync_send_all_state_event(ConnectionHandler, peer_address).
+start_link(Role, Socket, Options) ->
+ {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Socket, Options]])}.
-%%====================================================================
-%% gen_fsm callbacks
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, StateName, State} |
-%% {ok, StateName, State, Timeout} |
-%% ignore |
-%% {stop, StopReason}
-%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
-%% gen_fsm:start_link/3,4, this function is called by the new process to
-%% initialize.
-%%--------------------------------------------------------------------
-init([Role, Manager, Socket, SshOpts]) ->
+init([Role, Socket, SshOpts]) ->
process_flag(trap_exit, true),
{NumVsn, StrVsn} = ssh_transport:versions(Role, SshOpts),
- ssh_bits:install_messages(ssh_transport:transport_messages(NumVsn)),
{Protocol, Callback, CloseTag} =
proplists:get_value(transport, SshOpts, {tcp, gen_tcp, tcp_closed}),
+ Cache = ssh_channel:cache_create(),
+ State0 = #state{
+ role = Role,
+ connection_state = #connection{channel_cache = Cache,
+ channel_id_seed = 0,
+ port_bindings = [],
+ requests = [],
+ options = SshOpts},
+ socket = Socket,
+ decoded_data_buffer = <<>>,
+ encoded_data_buffer = <<>>,
+ transport_protocol = Protocol,
+ transport_cb = Callback,
+ transport_close_tag = CloseTag,
+ opts = SshOpts
+ },
+
+ State = init_role(State0),
+
try init_ssh(Role, NumVsn, StrVsn, SshOpts, Socket) of
Ssh ->
- {ok, hello, #state{ssh_params =
- Ssh#ssh{send_sequence = 0, recv_sequence = 0},
- socket = Socket,
- decoded_data_buffer = <<>>,
- encoded_data_buffer = <<>>,
- transport_protocol = Protocol,
- transport_cb = Callback,
- transport_close_tag = CloseTag,
- manager = Manager,
- opts = SshOpts
- }}
+ gen_fsm:enter_loop(?MODULE, [], hello,
+ State#state{ssh_params = Ssh})
catch
- exit:Reason ->
- {stop, {shutdown, Reason}}
+ _:Error ->
+ gen_fsm:enter_loop(?MODULE, [], error, {Error, State0})
end.
+
+%%--------------------------------------------------------------------
+-spec open_channel(pid(), string(), iodata(), integer(), integer(),
+ timeout()) -> {open, channel_id()} | {open_error, term(), string(), string()}.
+%%--------------------------------------------------------------------
+open_channel(ConnectionHandler, ChannelType, ChannelSpecificData,
+ InitialWindowSize,
+ MaxPacketSize, Timeout) ->
+ sync_send_all_state_event(ConnectionHandler, {open, self(), ChannelType,
+ InitialWindowSize, MaxPacketSize,
+ ChannelSpecificData,
+ Timeout}).
+%%--------------------------------------------------------------------
+-spec request(pid(), pid(), channel_id(), string(), boolean(), iodata(),
+ timeout()) -> success | failure | ok | {error, term()}.
+%%--------------------------------------------------------------------
+request(ConnectionHandler, ChannelPid, ChannelId, Type, true, Data, Timeout) ->
+ sync_send_all_state_event(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data,
+ Timeout});
+request(ConnectionHandler, ChannelPid, ChannelId, Type, false, Data, _) ->
+ send_all_state_event(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data}).
+
+%%--------------------------------------------------------------------
+-spec request(pid(), channel_id(), string(), boolean(), iodata(),
+ timeout()) -> success | failure | {error, timeout}.
+%%--------------------------------------------------------------------
+request(ConnectionHandler, ChannelId, Type, true, Data, Timeout) ->
+ sync_send_all_state_event(ConnectionHandler, {request, ChannelId, Type, Data, Timeout});
+request(ConnectionHandler, ChannelId, Type, false, Data, _) ->
+ send_all_state_event(ConnectionHandler, {request, ChannelId, Type, Data}).
+
+%%--------------------------------------------------------------------
+-spec reply_request(pid(), success | failure, channel_id()) -> ok.
+%%--------------------------------------------------------------------
+reply_request(ConnectionHandler, Status, ChannelId) ->
+ send_all_state_event(ConnectionHandler, {reply_request, Status, ChannelId}).
+
+%%--------------------------------------------------------------------
+-spec global_request(pid(), string(), boolean(), iolist()) -> ok | error.
+%%--------------------------------------------------------------------
+global_request(ConnectionHandler, Type, true = Reply, Data) ->
+ case sync_send_all_state_event(ConnectionHandler,
+ {global_request, self(), Type, Reply, Data}) of
+ {ssh_cm, ConnectionHandler, {success, _}} ->
+ ok;
+ {ssh_cm, ConnectionHandler, {failure, _}} ->
+ error
+ end;
+global_request(ConnectionHandler, Type, false = Reply, Data) ->
+ send_all_state_event(ConnectionHandler, {global_request, self(), Type, Reply, Data}).
+
+%%--------------------------------------------------------------------
+-spec send(pid(), channel_id(), integer(), iolist(), timeout()) ->
+ ok | {error, timeout} | {error, closed}.
+%%--------------------------------------------------------------------
+send(ConnectionHandler, ChannelId, Type, Data, Timeout) ->
+ sync_send_all_state_event(ConnectionHandler, {data, ChannelId, Type, Data, Timeout}).
+
+%%--------------------------------------------------------------------
+-spec send_eof(pid(), channel_id()) -> ok | {error, closed}.
+%%--------------------------------------------------------------------
+send_eof(ConnectionHandler, ChannelId) ->
+ sync_send_all_state_event(ConnectionHandler, {eof, ChannelId}).
+
+%%--------------------------------------------------------------------
+-spec connection_info(pid(), [atom()]) -> proplists:proplist().
+%%--------------------------------------------------------------------
+connection_info(ConnectionHandler, Options) ->
+ sync_send_all_state_event(ConnectionHandler, {connection_info, Options}).
+
+%%--------------------------------------------------------------------
+-spec channel_info(pid(), channel_id(), [atom()]) -> proplists:proplist().
+%%--------------------------------------------------------------------
+channel_info(ConnectionHandler, ChannelId, Options) ->
+ sync_send_all_state_event(ConnectionHandler, {channel_info, ChannelId, Options}).
+
+%%--------------------------------------------------------------------
+-spec adjust_window(pid(), channel_id(), integer()) -> ok.
+%%--------------------------------------------------------------------
+adjust_window(ConnectionHandler, Channel, Bytes) ->
+ send_all_state_event(ConnectionHandler, {adjust_window, Channel, Bytes}).
+%%--------------------------------------------------------------------
+-spec renegotiate(pid()) -> ok.
+%%--------------------------------------------------------------------
+renegotiate(ConnectionHandler) ->
+ send_all_state_event(ConnectionHandler, renegotiate).
+
+%%--------------------------------------------------------------------
+-spec renegotiate_data(pid()) -> ok.
+%%--------------------------------------------------------------------
+renegotiate_data(ConnectionHandler) ->
+ send_all_state_event(ConnectionHandler, data_size).
+
+%%--------------------------------------------------------------------
+-spec close(pid(), channel_id()) -> ok.
+%%--------------------------------------------------------------------
+close(ConnectionHandler, ChannelId) ->
+ sync_send_all_state_event(ConnectionHandler, {close, ChannelId}).
+
%%--------------------------------------------------------------------
-%% Function:
-%% state_name(Event, State) -> {next_state, NextStateName, NextState}|
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}
-%% Description:There should be one instance of this function for each possible
-%% state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:send_event/2, the instance of this function with the same name as
-%% the current state name StateName is called to handle the event. It is also
-%% called if a timeout occurs.
+-spec stop(pid()) -> ok | {error, term()}.
%%--------------------------------------------------------------------
+stop(ConnectionHandler)->
+ case sync_send_all_state_event(ConnectionHandler, stop) of
+ {error, closed} ->
+ ok;
+ Other ->
+ Other
+ end.
+
+info(ConnectionHandler) ->
+ info(ConnectionHandler, {info, all}).
+
+info(ConnectionHandler, ChannelProcess) ->
+ sync_send_all_state_event(ConnectionHandler, {info, ChannelProcess}).
+
+
+%%====================================================================
+%% gen_fsm callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec hello(socket_control | {info_line, list()} | {version_exchange, list()},
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
+
hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
send_msg(VsnMsg, State),
- inet:setopts(Socket, [{packet, line}]),
- {next_state, hello, next_packet(State)};
+ inet:setopts(Socket, [{packet, line}, {active, once}]),
+ {next_state, hello, State};
-hello({info_line, _Line}, State) ->
- {next_state, hello, next_packet(State)};
+hello({info_line, _Line},#state{socket = Socket} = State) ->
+ inet:setopts(Socket, [{active, once}]),
+ {next_state, hello, State};
hello({version_exchange, Version}, #state{ssh_params = Ssh0,
socket = Socket} = State) ->
{NumVsn, StrVsn} = ssh_transport:handle_hello_version(Version),
case handle_version(NumVsn, StrVsn, Ssh0) of
{ok, Ssh1} ->
- inet:setopts(Socket, [{packet,0}, {mode,binary}]),
+ inet:setopts(Socket, [{packet,0}, {mode,binary}, {active, once}]),
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
send_msg(SshPacket, State),
{next_state, kexinit, next_packet(State#state{ssh_params = Ssh,
@@ -173,12 +311,15 @@ hello({version_exchange, Version}, #state{ssh_params = Ssh0,
handle_disconnect(DisconnectMsg, State)
end.
+%%--------------------------------------------------------------------
+-spec kexinit({#ssh_msg_kexinit{}, binary()}, #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
kexinit({#ssh_msg_kexinit{} = Kex, Payload},
#state{ssh_params = #ssh{role = Role} = Ssh0,
- key_exchange_init_msg = OwnKex} =
- State) ->
+ key_exchange_init_msg = OwnKex} =
+ State) ->
Ssh1 = ssh_transport:key_init(opposite_role(Role), Ssh0, Payload),
- try ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
+ case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
{ok, NextKexMsg, Ssh} when Role == client ->
send_msg(NextKexMsg, State),
{next_state, key_exchange,
@@ -186,157 +327,75 @@ kexinit({#ssh_msg_kexinit{} = Kex, Payload},
{ok, Ssh} when Role == server ->
{next_state, key_exchange,
next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
end.
-
+
+%%--------------------------------------------------------------------
+-spec key_exchange(#ssh_msg_kexdh_init{} | #ssh_msg_kexdh_reply{} |
+ #ssh_msg_kex_dh_gex_group{} | #ssh_msg_kex_dh_gex_request{} |
+ #ssh_msg_kex_dh_gex_request{} | #ssh_msg_kex_dh_gex_reply{}, #state{})
+ -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
+
key_exchange(#ssh_msg_kexdh_init{} = Msg,
- #state{ssh_params = #ssh{role = server} =Ssh0} = State) ->
- try ssh_transport:handle_kexdh_init(Msg, Ssh0) of
+ #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
+ case ssh_transport:handle_kexdh_init(Msg, Ssh0) of
{ok, KexdhReply, Ssh1} ->
send_msg(KexdhReply, State),
{ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
send_msg(NewKeys, State),
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
end;
-key_exchange({#ssh_msg_kexinit{} = Kex, Payload},
- #state{ssh_params = #ssh{role = Role} = Ssh0,
- key_exchange_init_msg = OwnKex} =
- State) ->
- Ssh1 = ssh_transport:key_init(opposite_role(Role), Ssh0, Payload),
- try ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
- {ok, NextKexMsg, Ssh} when Role == client ->
- send_msg(NextKexMsg, State),
- {next_state, key_exchange,
- next_packet(State#state{ssh_params = Ssh})};
- {ok, Ssh} when Role == server ->
- {next_state, key_exchange,
- next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
- end;
-
key_exchange(#ssh_msg_kexdh_reply{} = Msg,
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- try ssh_transport:handle_kexdh_reply(Msg, Ssh0) of
- {ok, NewKeys, Ssh} ->
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- {ErrorToDisplay, #ssh_msg_disconnect{} = DisconnectMsg} ->
- handle_disconnect(DisconnectMsg, State, ErrorToDisplay);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
- end;
+ {ok, NewKeys, Ssh} = ssh_transport:handle_kexdh_reply(Msg, Ssh0),
+ send_msg(NewKeys, State),
+ {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
#state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- try ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0) of
- {ok, NextKexMsg, Ssh1} ->
- send_msg(NextKexMsg, State),
- {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
- end;
+ {ok, NextKexMsg, Ssh1} = ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0),
+ send_msg(NextKexMsg, State),
+ {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
+ send_msg(NewKeys, State),
+ {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg,
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- try ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0) of
- {ok, NextKexMsg, Ssh} ->
- send_msg(NextKexMsg, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
- end;
+ {ok, NextKexMsg, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0),
+ send_msg(NextKexMsg, State),
+ {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
+
key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg,
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- try ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0) of
- {ok, NewKeys, Ssh} ->
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
- end.
+ {ok, NewKeys, Ssh} = ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0),
+ send_msg(NewKeys, State),
+ {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}.
+
+%%--------------------------------------------------------------------
+-spec new_keys(#ssh_msg_newkeys{}, #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) ->
- try ssh_transport:handle_new_keys(Msg, Ssh0) of
- {ok, Ssh} ->
- {NextStateName, State} =
- after_new_keys(State0#state{ssh_params = Ssh}),
- {next_state, NextStateName, next_packet(State)}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State0);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State0)
- end.
+ {ok, Ssh} = ssh_transport:handle_new_keys(Msg, Ssh0),
+ {NextStateName, State} =
+ after_new_keys(State0#state{ssh_params = Ssh}),
+ {next_state, NextStateName, next_packet(State)}.
+
+%%--------------------------------------------------------------------
+-spec userauth(#ssh_msg_service_request{} | #ssh_msg_service_accept{} |
+ #ssh_msg_userauth_request{} | #ssh_msg_userauth_info_request{} |
+ #ssh_msg_userauth_info_response{} | #ssh_msg_userauth_success{} |
+ #ssh_msg_userauth_failure{} | #ssh_msg_userauth_banner{},
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
userauth(#ssh_msg_service_request{name = "ssh-userauth"} = Msg,
#state{ssh_params = #ssh{role = server,
session_id = SessionId} = Ssh0} = State) ->
- ssh_bits:install_messages(ssh_auth:userauth_messages()),
- try ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of
- {ok, {Reply, Ssh}} ->
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = Desc,
- language = "en"}, State)
- end;
+ {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_service_accept{name = "ssh-userauth"},
#state{ssh_params = #ssh{role = client,
@@ -344,93 +403,55 @@ userauth(#ssh_msg_service_accept{name = "ssh-userauth"},
State) ->
{Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0),
send_msg(Msg, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
+ {next_state, userauth, next_packet(State#state{auth_user = Ssh#ssh.user, ssh_params = Ssh})};
userauth(#ssh_msg_userauth_request{service = "ssh-connection",
method = "none"} = Msg,
#state{ssh_params = #ssh{session_id = SessionId, role = server,
service = "ssh-connection"} = Ssh0
} = State) ->
- try ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of
- {not_authorized, {_User, _Reason}, {Reply, Ssh}} ->
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = Desc,
- language = "en"}, State)
- end;
+ {not_authorized, {_User, _Reason}, {Reply, Ssh}} =
+ ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_userauth_request{service = "ssh-connection",
method = Method} = Msg,
#state{ssh_params = #ssh{session_id = SessionId, role = server,
service = "ssh-connection",
peer = {_, Address}} = Ssh0,
- opts = Opts, manager = Pid} = State) ->
- try ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of
+ opts = Opts, starter = Pid} = State) ->
+ case ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of
{authorized, User, {Reply, Ssh}} ->
send_msg(Reply, State),
- ssh_userreg:register_user(User, Pid),
Pid ! ssh_connected,
connected_fun(User, Address, Method, Opts),
{next_state, connected,
- next_packet(State#state{ssh_params = Ssh})};
+ next_packet(State#state{auth_user = User, ssh_params = Ssh})};
{not_authorized, {User, Reason}, {Reply, Ssh}} ->
- retry_fun(User, Reason, Opts),
+ retry_fun(User, Address, Reason, Opts),
send_msg(Reply, State),
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = Desc,
- language = "en"}, State)
end;
userauth(#ssh_msg_userauth_info_request{} = Msg,
#state{ssh_params = #ssh{role = client,
io_cb = IoCb} = Ssh0} = State) ->
- try ssh_auth:handle_userauth_info_request(Msg, IoCb, Ssh0) of
- {ok, {Reply, Ssh}} ->
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = Desc,
- language = "en"}, State)
- end;
+ {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_request(Msg, IoCb, Ssh0),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_userauth_info_response{} = Msg,
#state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- try ssh_auth:handle_userauth_info_response(Msg, Ssh0) of
- {ok, {Reply, Ssh}} ->
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
- catch
- #ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State);
- _:Error ->
- Desc = log_error(Error),
- handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
- description = Desc,
- language = "en"}, State)
- end;
+ {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response(Msg, Ssh0),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh,
- manager = Pid} = State) ->
+ starter = Pid} = State) ->
Pid ! ssh_connected,
- {next_state, connected, next_packet(State#state{ssh_params = Ssh#ssh{authenticated = true}})};
-
+ {next_state, connected, next_packet(State#state{ssh_params =
+ Ssh#ssh{authenticated = true}})};
userauth(#ssh_msg_userauth_failure{},
#state{ssh_params = #ssh{role = client,
userauth_methods = []}}
@@ -479,31 +500,28 @@ userauth(#ssh_msg_userauth_banner{message = Msg},
io:format("~s", [Msg]),
{next_state, userauth, next_packet(State)}.
+%%--------------------------------------------------------------------
+-spec connected({#ssh_msg_kexinit{}, binary()}, %%| %% #ssh_msg_kexdh_init{},
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
connected({#ssh_msg_kexinit{}, _Payload} = Event, State) ->
- kexinit(Event, State#state{renegotiate = true});
-connected({#ssh_msg_kexdh_init{}, _Payload} = Event, State) ->
- key_exchange(Event, State#state{renegotiate = true}).
+ kexinit(Event, State#state{renegotiate = true}).
+%% ;
+%% connected(#ssh_msg_kexdh_init{} = Event, State) ->
+%% key_exchange(Event, State#state{renegotiate = true}).
%%--------------------------------------------------------------------
-%% Function:
-%% handle_event(Event, StateName, State) -> {next_state, NextStateName,
-%% NextState} |
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}
-%% Description: Whenever a gen_fsm receives an event sent using
-%% gen_fsm:send_all_state_event/2, this function is called to handle
-%% the event.
-%%--------------------------------------------------------------------
-handle_event({send, Data}, StateName, #state{ssh_params = Ssh0} = State) ->
- {Packet, Ssh} = ssh_transport:pack(Data, Ssh0),
- send_msg(Packet, State),
- {next_state, StateName, next_packet(State#state{ssh_params = Ssh})};
+-spec handle_event(#ssh_msg_disconnect{} | #ssh_msg_ignore{} | #ssh_msg_debug{} |
+ #ssh_msg_unimplemented{} | {adjust_window, integer(), integer()} |
+ {reply_request, success | failure, integer()} | renegotiate |
+ data_size | {request, pid(), integer(), integer(), iolist()} |
+ {request, integer(), integer(), iolist()}, state_name(),
+ #state{}) -> gen_fsm_state_return().
-handle_event(#ssh_msg_disconnect{} = Msg, _StateName,
- #state{manager = Pid} = State) ->
- (catch ssh_connection_manager:event(Pid, Msg)),
- {stop, normal, State};
+%%--------------------------------------------------------------------
+handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName, #state{} = State) ->
+ handle_disconnect(DisconnectMsg, State),
+ {stop, {shutdown, Desc}, State};
handle_event(#ssh_msg_ignore{}, StateName, State) ->
{next_state, StateName, next_packet(State)};
@@ -519,30 +537,58 @@ handle_event(#ssh_msg_debug{}, StateName, State) ->
handle_event(#ssh_msg_unimplemented{}, StateName, State) ->
{next_state, StateName, next_packet(State)};
+handle_event({adjust_window, ChannelId, Bytes}, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ State =
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{recv_window_size = WinSize, remote_id = Id} = Channel ->
+ ssh_channel:cache_update(Cache, Channel#channel{recv_window_size =
+ WinSize + Bytes}),
+ Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes),
+ send_replies([{connection_reply, Msg}], State0);
+ undefined ->
+ State0
+ end,
+ {next_state, StateName, next_packet(State)};
+
+handle_event({reply_request, success, ChannelId}, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ State = case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = RemoteId} ->
+ Msg = ssh_connection:channel_success_msg(RemoteId),
+ send_replies([{connection_reply, Msg}], State0);
+ undefined ->
+ State0
+ end,
+ {next_state, StateName, State};
+
handle_event(renegotiate, connected, #state{ssh_params = Ssh0}
= State) ->
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0),
send_msg(SshPacket, State),
- {next_state, connected,
+ timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]),
+ {next_state, kexinit,
next_packet(State#state{ssh_params = Ssh,
key_exchange_init_msg = KeyInitMsg,
renegotiate = true})};
handle_event(renegotiate, StateName, State) ->
+ timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiatie]),
%% Allready in keyexcahange so ignore
{next_state, StateName, State};
-handle_event({info, From, Options}, StateName, #state{ssh_params = Ssh} = State) ->
- spawn(?MODULE, ssh_info_handler, [Options, Ssh, State, From]),
- {next_state, StateName, State};
+%% Rekey due to sent data limit reached?
handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) ->
{ok, [{send_oct,Sent}]} = inet:getstat(State#state.socket, [send_oct]),
MaxSent = proplists:get_value(rekey_limit, State#state.opts, 1024000000),
+ timer:apply_after(?REKEY_DATA_TIMOUT, gen_fsm, send_all_state_event, [self(), data_size]),
case Sent >= MaxSent of
true ->
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0),
send_msg(SshPacket, State),
- {next_state, connected,
+ {next_state, kexinit,
next_packet(State#state{ssh_params = Ssh,
key_exchange_init_msg = KeyInitMsg,
renegotiate = true})};
@@ -551,42 +597,196 @@ handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) ->
end;
handle_event(data_size, StateName, State) ->
{next_state, StateName, State};
+
+handle_event({request, ChannelPid, ChannelId, Type, Data}, StateName, State0) ->
+ {{replies, Replies}, State1} = handle_request(ChannelPid, ChannelId,
+ Type, Data,
+ false, none, State0),
+ State = send_replies(Replies, State1),
+ {next_state, StateName, next_packet(State)};
+
+handle_event({request, ChannelId, Type, Data}, StateName, State0) ->
+ {{replies, Replies}, State1} = handle_request(ChannelId, Type, Data,
+ false, none, State0),
+ State = send_replies(Replies, State1),
+ {next_state, StateName, next_packet(State)};
+
handle_event({unknown, Data}, StateName, State) ->
Msg = #ssh_msg_unimplemented{sequence = Data},
send_msg(Msg, State),
{next_state, StateName, next_packet(State)}.
+
%%--------------------------------------------------------------------
-%% Function:
-%% handle_sync_event(Event, From, StateName,
-%% State) -> {next_state, NextStateName, NextState} |
-%% {next_state, NextStateName, NextState,
-%% Timeout} |
-%% {reply, Reply, NextStateName, NextState}|
-%% {reply, Reply, NextStateName, NextState,
-%% Timeout} |
-%% {stop, Reason, NewState} |
-%% {stop, Reason, Reply, NewState}
-%% Description: Whenever a gen_fsm receives an event sent using
-%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
-%% the event.
+-spec handle_sync_event({request, pid(), channel_id(), integer(), binary(), timeout()} |
+ {request, channel_id(), integer(), binary(), timeout()} |
+ {global_request, pid(), integer(), boolean(), binary()} | {eof, integer()} |
+ {open, pid(), integer(), channel_id(), integer(), binary(), _} |
+ {send_window, channel_id()} | {recv_window, channel_id()} |
+ {connection_info, [client_version | server_version | peer |
+ sockname]} | {channel_info, channel_id(), [recv_window |
+ send_window]} |
+ {close, channel_id()} | stop, term(), state_name(), #state{})
+ -> gen_fsm_state_return().
%%--------------------------------------------------------------------
+handle_sync_event({request, ChannelPid, ChannelId, Type, Data, Timeout}, From, StateName, State0) ->
+ {{replies, Replies}, State1} = handle_request(ChannelPid,
+ ChannelId, Type, Data,
+ true, From, State0),
+ %% Note reply to channel will happen later when
+ %% reply is recived from peer on the socket
+ State = send_replies(Replies, State1),
+ start_timeout(ChannelId, From, Timeout),
+ handle_idle_timeout(State),
+ {next_state, StateName, next_packet(State)};
+
+handle_sync_event({request, ChannelId, Type, Data, Timeout}, From, StateName, State0) ->
+ {{replies, Replies}, State1} = handle_request(ChannelId, Type, Data,
+ true, From, State0),
+ %% Note reply to channel will happen later when
+ %% reply is recived from peer on the socket
+ State = send_replies(Replies, State1),
+ start_timeout(ChannelId, From, Timeout),
+ handle_idle_timeout(State),
+ {next_state, StateName, next_packet(State)};
+
+handle_sync_event({global_request, Pid, _, _, _} = Request, From, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ State1 = handle_global_request(Request, State0),
+ Channel = ssh_channel:cache_find(Pid, Cache),
+ State = add_request(true, Channel#channel.local_id, From, State1),
+ {next_state, StateName, next_packet(State)};
+
+handle_sync_event({data, ChannelId, Type, Data, Timeout}, From, StateName,
+ #state{connection_state = #connection{channel_cache = _Cache}
+ = Connection0} = State0) ->
+
+ case ssh_connection:channel_data(ChannelId, Type, Data, Connection0, From) of
+ {{replies, Replies}, Connection} ->
+ State = send_replies(Replies, State0#state{connection_state = Connection}),
+ start_timeout(ChannelId, From, Timeout),
+ {next_state, StateName, next_packet(State)};
+ {noreply, Connection} ->
+ start_timeout(ChannelId, From, Timeout),
+ {next_state, StateName, next_packet(State0#state{connection_state = Connection})}
+ end;
+
+handle_sync_event({eof, ChannelId}, _From, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = Id, sent_close = false} ->
+ State = send_replies([{connection_reply,
+ ssh_connection:channel_eof_msg(Id)}], State0),
+ {reply, ok, StateName, next_packet(State)};
+ _ ->
+ {reply, {error,closed}, StateName, State0}
+ end;
+
+handle_sync_event({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
+ From, StateName, #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ erlang:monitor(process, ChannelPid),
+ {ChannelId, State1} = new_channel_id(State0),
+ Msg = ssh_connection:channel_open_msg(Type, ChannelId,
+ InitialWindowSize,
+ MaxPacketSize, Data),
+ State2 = send_replies([{connection_reply, Msg}], State1),
+ Channel = #channel{type = Type,
+ sys = "none",
+ user = ChannelPid,
+ local_id = ChannelId,
+ recv_window_size = InitialWindowSize,
+ recv_packet_size = MaxPacketSize},
+ ssh_channel:cache_update(Cache, Channel),
+ State = add_request(true, ChannelId, From, State2),
+ start_timeout(ChannelId, From, Timeout),
+ {next_state, StateName, next_packet(remove_timer_ref(State))};
+
+handle_sync_event({send_window, ChannelId}, _From, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State) ->
+ Reply = case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{send_window_size = WinSize,
+ send_packet_size = Packsize} ->
+ {ok, {WinSize, Packsize}};
+ undefined ->
+ {error, einval}
+ end,
+ {reply, Reply, StateName, next_packet(State)};
+
+handle_sync_event({recv_window, ChannelId}, _From, StateName,
+ #state{connection_state = #connection{channel_cache = Cache}}
+ = State) ->
-%% Replaced with option to connection_info/3. For now keep
-%% for backwards compatibility
-handle_sync_event(peer_address, _From, StateName,
- #state{ssh_params = #ssh{peer = {_, Address}}} = State) ->
- {reply, {ok, Address}, StateName, State}.
+ Reply = case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{recv_window_size = WinSize,
+ recv_packet_size = Packsize} ->
+ {ok, {WinSize, Packsize}};
+ undefined ->
+ {error, einval}
+ end,
+ {reply, Reply, StateName, next_packet(State)};
+
+handle_sync_event({connection_info, Options}, _From, StateName, State) ->
+ Info = ssh_info(Options, State, []),
+ {reply, Info, StateName, State};
+
+handle_sync_event({channel_info, ChannelId, Options}, _From, StateName,
+ #state{connection_state = #connection{channel_cache = Cache}} = State) ->
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{} = Channel ->
+ Info = ssh_channel_info(Options, Channel, []),
+ {reply, Info, StateName, State};
+ undefined ->
+ {reply, [], StateName, State}
+ end;
+
+handle_sync_event({info, ChannelPid}, _From, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State) ->
+ Result = ssh_channel:cache_foldl(
+ fun(Channel, Acc) when ChannelPid == all;
+ Channel#channel.user == ChannelPid ->
+ [Channel | Acc];
+ (_, Acc) ->
+ Acc
+ end, [], Cache),
+ {reply, {ok, Result}, StateName, State};
+
+handle_sync_event({close, ChannelId}, _, StateName,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ State =
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = Id} = Channel ->
+ State1 = send_replies([{connection_reply,
+ ssh_connection:channel_close_msg(Id)}], State0),
+ ssh_channel:cache_update(Cache, Channel#channel{sent_close = true}),
+ handle_idle_timeout(State1),
+ State1;
+ undefined ->
+ State0
+ end,
+ {reply, ok, StateName, next_packet(State)};
+
+handle_sync_event(stop, _, _StateName, #state{connection_state = Connection0,
+ role = Role,
+ opts = Opts} = State0) ->
+ {disconnect, Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "User closed down connection",
+ language = "en"}, Connection0, Role),
+ State = send_replies(Replies, State0),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, normal, ok, State#state{connection_state = Connection}}.
%%--------------------------------------------------------------------
-%% Function:
-%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
-%% {next_state, NextStateName, NextState,
-%% Timeout} |
-%% {stop, Reason, NewState}
-%% Description: This function is called by a gen_fsm when it receives any
-%% other message than a synchronous or asynchronous event
-%% (or a system message).
+-spec handle_info({atom(), port(), binary()} | {atom(), port()} |
+ term (), state_name(), #state{}) -> gen_fsm_state_return().
%%--------------------------------------------------------------------
+
handle_info({Protocol, Socket, "SSH-" ++ _ = Version}, hello,
#state{socket = Socket,
transport_protocol = Protocol} = State ) ->
@@ -651,15 +851,39 @@ handle_info({Protocol, Socket, Data}, Statename,
handle_info({CloseTag, _Socket}, _StateName,
#state{transport_close_tag = CloseTag,
ssh_params = #ssh{role = _Role, opts = _Opts}} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_CONNECTION_LOST,
- description = "Connection Lost",
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Connection closed",
language = "en"},
- {stop, {shutdown, DisconnectMsg}, State};
+ handle_disconnect(DisconnectMsg, State);
+
+handle_info({timeout, {_, From} = Request}, Statename,
+ #state{connection_state = #connection{requests = Requests} = Connection} = State) ->
+ case lists:member(Request, Requests) of
+ true ->
+ gen_fsm:reply(From, {error, timeout}),
+ {next_state, Statename,
+ State#state{connection_state =
+ Connection#connection{requests =
+ lists:delete(Request, Requests)}}};
+ false ->
+ {next_state, Statename, State}
+ end;
+
+%%% Handle that ssh channels user process goes down
+handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, Statename, State0) ->
+ {{replies, Replies}, State1} = handle_channel_down(ChannelPid, State0),
+ State = send_replies(Replies, State1),
+ {next_state, Statename, next_packet(State)};
%%% So that terminate will be run when supervisor is shutdown
handle_info({'EXIT', _Sup, Reason}, _StateName, State) ->
- {stop, Reason, State};
+ {stop, {shutdown, Reason}, State};
+
+handle_info({check_cache, _ , _},
+ StateName, #state{connection_state =
+ #connection{channel_cache = Cache}} = State) ->
+ {next_state, StateName, check_cache(State, Cache)};
handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State) ->
Msg = lists:flatten(io_lib:format(
@@ -673,20 +897,16 @@ handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State
{next_state, StateName, State}.
%%--------------------------------------------------------------------
-%% Function: terminate(Reason, StateName, State) -> void()
-%% Description:This function is called by a gen_fsm when it is about
-%% to terminate. It should be the opposite of Module:init/1 and do any
-%% necessary cleaning up. When it returns, the gen_fsm terminates with
-%% Reason. The return value is ignored.
+-spec terminate(Reason::term(), state_name(), #state{}) -> _.
%%--------------------------------------------------------------------
terminate(normal, _, #state{transport_cb = Transport,
- socket = Socket,
- manager = Pid}) ->
- (catch ssh_userreg:delete_user(Pid)),
+ connection_state = Connection,
+ socket = Socket}) ->
+ terminate_subsytem(Connection),
(catch Transport:close(Socket)),
ok;
-%% Terminated as manager terminated
+%% Terminated by supervisor
terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) ->
DisconnectMsg =
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
@@ -696,31 +916,34 @@ terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) ->
send_msg(SshPacket, State),
terminate(normal, StateName, State#state{ssh_params = Ssh});
-terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
- {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
- send_msg(SshPacket, State),
- ssh_connection_manager:event(Pid, Msg),
- terminate(normal, StateName, State#state{ssh_params = Ssh});
-terminate({shutdown, {#ssh_msg_disconnect{} = Msg, ErrorMsg}}, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
- {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
+terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName,
+ #state{ssh_params = Ssh0} = State) ->
+ {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
send_msg(SshPacket, State),
- ssh_connection_manager:event(Pid, Msg, ErrorMsg),
- terminate(normal, StateName, State#state{ssh_params = Ssh});
-terminate(Reason, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
+ terminate(normal, StateName, State#state{ssh_params = Ssh});
+terminate({shutdown, _}, StateName, State) ->
+ terminate(normal, StateName, State);
+terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid,
+ connection_state = Connection} = State) ->
+ terminate_subsytem(Connection),
log_error(Reason),
DisconnectMsg =
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
description = "Internal error",
language = "en"},
{SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0),
- ssh_connection_manager:event(Pid, DisconnectMsg),
send_msg(SshPacket, State),
terminate(normal, StateName, State#state{ssh_params = Ssh}).
+terminate_subsytem(#connection{system_supervisor = SysSup,
+ sub_system_supervisor = SubSysSup}) when is_pid(SubSysSup) ->
+ ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
+terminate_subsytem(_) ->
+ ok.
+
%%--------------------------------------------------------------------
-%% Function:
-%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
-%% Description: Convert process state when code is changed
+-spec code_change(OldVsn::term(), state_name(), Oldstate::term(), Extra::term()) ->
+ {ok, state_name(), #state{}}.
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
{ok, StateName, State}.
@@ -728,6 +951,39 @@ code_change(_OldVsn, StateName, State, _Extra) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+init_role(#state{role = client, opts = Opts} = State0) ->
+ Pid = proplists:get_value(user_pid, Opts),
+ TimerRef = get_idle_time(Opts),
+ timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]),
+ timer:apply_after(?REKEY_DATA_TIMOUT, gen_fsm, send_all_state_event,
+ [self(), data_size]),
+ State0#state{starter = Pid,
+ idle_timer_ref = TimerRef};
+init_role(#state{role = server, opts = Opts, connection_state = Connection} = State) ->
+ Sups = proplists:get_value(supervisors, Opts),
+ Pid = proplists:get_value(user_pid, Opts),
+ SystemSup = proplists:get_value(system_sup, Sups),
+ SubSystemSup = proplists:get_value(subsystem_sup, Sups),
+ ConnectionSup = proplists:get_value(connection_sup, Sups),
+ Shell = proplists:get_value(shell, Opts),
+ Exec = proplists:get_value(exec, Opts),
+ CliSpec = proplists:get_value(ssh_cli, Opts, {ssh_cli, [Shell]}),
+ State#state{starter = Pid, connection_state = Connection#connection{
+ cli_spec = CliSpec,
+ exec = Exec,
+ system_supervisor = SystemSup,
+ sub_system_supervisor = SubSystemSup,
+ connection_supervisor = ConnectionSup
+ }}.
+
+get_idle_time(SshOptions) ->
+ case proplists:get_value(idle_time, SshOptions) of
+ infinity ->
+ infinity;
+ _IdleTime -> %% We dont want to set the timeout on first connect
+ undefined
+ end.
+
init_ssh(client = Role, Vsn, Version, Options, Socket) ->
IOCb = case proplists:get_value(user_interaction, Options, true) of
true ->
@@ -845,7 +1101,15 @@ send_all_state_event(FsmPid, Event) ->
gen_fsm:send_all_state_event(FsmPid, Event).
sync_send_all_state_event(FsmPid, Event) ->
- gen_fsm:sync_send_all_state_event(FsmPid, Event).
+ try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity)
+ catch
+ exit:{noproc, _} ->
+ {error, closed};
+ exit:{normal, _} ->
+ {error, closed};
+ exit:{{shutdown, _},_} ->
+ {error, closed}
+ end.
%% simulate send_all_state_event(self(), Event)
event(#ssh_msg_disconnect{} = Event, StateName, State) ->
@@ -858,10 +1122,33 @@ event(#ssh_msg_unimplemented{} = Event, StateName, State) ->
handle_event(Event, StateName, State);
%% simulate send_event(self(), Event)
event(Event, StateName, State) ->
- ?MODULE:StateName(Event, State).
+ try
+ ?MODULE:StateName(Event, State)
+ catch
+ throw:#ssh_msg_disconnect{} = DisconnectMsg ->
+ handle_disconnect(DisconnectMsg, State);
+ throw:{ErrorToDisplay, #ssh_msg_disconnect{} = DisconnectMsg} ->
+ handle_disconnect(DisconnectMsg, State, ErrorToDisplay);
+ _:Error ->
+ log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = error_code(StateName),
+ description = "Internal error",
+ language = "en"}, State)
+ end.
+error_code(key_exchange) ->
+ ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED;
+error_code(new_keys) ->
+ ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED;
+error_code(_) ->
+ ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE.
generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
- #state{manager = Pid} = State0, EncData)
+ #state{
+ role = Role,
+ starter = User,
+ opts = Opts,
+ renegotiate = Renegotiation,
+ connection_state = Connection0} = State0, EncData)
when Byte == ?SSH_MSG_GLOBAL_REQUEST;
Byte == ?SSH_MSG_REQUEST_SUCCESS;
Byte == ?SSH_MSG_REQUEST_FAILURE;
@@ -876,18 +1163,40 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
Byte == ?SSH_MSG_CHANNEL_REQUEST;
Byte == ?SSH_MSG_CHANNEL_SUCCESS;
Byte == ?SSH_MSG_CHANNEL_FAILURE ->
-
- try
- ssh_connection_manager:event(Pid, Msg),
- State = generate_event_new_state(State0, EncData),
- next_packet(State),
- {next_state, StateName, State}
+ ConnectionMsg = ssh_message:decode(Msg),
+ State1 = generate_event_new_state(State0, EncData),
+ try ssh_connection:handle_msg(ConnectionMsg, Connection0, Role) of
+ {{replies, Replies}, Connection} ->
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ {next_state, StateName, next_packet(State)};
+ {noreply, Connection} ->
+ {next_state, StateName, next_packet(State1#state{connection_state = Connection})};
+ {disconnect, {_, Reason}, {{replies, Replies}, Connection}} when
+ Role == client andalso ((StateName =/= connected) and (not Renegotiation)) ->
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ User ! {self(), not_connected, Reason},
+ {stop, {shutdown, normal},
+ next_packet(State#state{connection_state = Connection})};
+ {disconnect, Reason, {{replies, Replies}, Connection}} ->
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, {shutdown, normal}, State#state{connection_state = Connection}}
catch
- exit:{noproc, Reason} ->
- {stop, {shutdown, Reason}, State0}
+ _:Error ->
+ {disconnect, Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error",
+ language = "en"}, Connection0, Role),
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, {shutdown, Error}, State#state{connection_state = Connection}}
end;
+
generate_event(Msg, StateName, State0, EncData) ->
- Event = ssh_bits:decode(Msg),
+ Event = ssh_message:decode(Msg),
State = generate_event_new_state(State0, EncData),
case Event of
#ssh_msg_kexinit{} ->
@@ -897,6 +1206,100 @@ generate_event(Msg, StateName, State0, EncData) ->
event(Event, StateName, State)
end.
+
+handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = Id} = Channel ->
+ update_sys(Cache, Channel, Type, ChannelPid),
+ Msg = ssh_connection:channel_request_msg(Id, Type,
+ WantReply, Data),
+ Replies = [{connection_reply, Msg}],
+ State = add_request(WantReply, ChannelId, From, State0),
+ {{replies, Replies}, State};
+ undefined ->
+ {{replies, []}, State0}
+ end.
+
+handle_request(ChannelId, Type, Data, WantReply, From,
+ #state{connection_state =
+ #connection{channel_cache = Cache}} = State0) ->
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = Id} ->
+ Msg = ssh_connection:channel_request_msg(Id, Type,
+ WantReply, Data),
+ Replies = [{connection_reply, Msg}],
+ State = add_request(WantReply, ChannelId, From, State0),
+ {{replies, Replies}, State};
+ undefined ->
+ {{replies, []}, State0}
+ end.
+
+handle_global_request({global_request, ChannelPid,
+ "tcpip-forward" = Type, WantReply,
+ <<?UINT32(IPLen),
+ IP:IPLen/binary, ?UINT32(Port)>> = Data},
+ #state{connection_state =
+ #connection{channel_cache = Cache}
+ = Connection0} = State) ->
+ ssh_channel:cache_update(Cache, #channel{user = ChannelPid,
+ type = "forwarded-tcpip",
+ sys = none}),
+ Connection = ssh_connection:bind(IP, Port, ChannelPid, Connection0),
+ Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
+ send_replies([{connection_reply, Msg}], State#state{connection_state = Connection});
+
+handle_global_request({global_request, _Pid, "cancel-tcpip-forward" = Type,
+ WantReply, <<?UINT32(IPLen),
+ IP:IPLen/binary, ?UINT32(Port)>> = Data},
+ #state{connection_state = Connection0} = State) ->
+ Connection = ssh_connection:unbind(IP, Port, Connection0),
+ Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
+ send_replies([{connection_reply, Msg}], State#state{connection_state = Connection});
+
+handle_global_request({global_request, _, "cancel-tcpip-forward" = Type,
+ WantReply, Data}, State) ->
+ Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
+ send_replies([{connection_reply, Msg}], State).
+
+handle_idle_timeout(#state{opts = Opts}) ->
+ case proplists:get_value(idle_time, Opts, infinity) of
+ infinity ->
+ ok;
+ IdleTime ->
+ erlang:send_after(IdleTime, self(), {check_cache, [], []})
+ end.
+
+handle_channel_down(ChannelPid, #state{connection_state =
+ #connection{channel_cache = Cache}} =
+ State) ->
+ ssh_channel:cache_foldl(
+ fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
+ ssh_channel:cache_delete(Cache,
+ Channel#channel.local_id),
+ Acc;
+ (_,Acc) ->
+ Acc
+ end, [], Cache),
+ {{replies, []}, check_cache(State, Cache)}.
+
+update_sys(Cache, Channel, Type, ChannelPid) ->
+ ssh_channel:cache_update(Cache,
+ Channel#channel{sys = Type, user = ChannelPid}).
+add_request(false, _ChannelId, _From, State) ->
+ State;
+add_request(true, ChannelId, From, #state{connection_state =
+ #connection{requests = Requests0} =
+ Connection} = State) ->
+ Requests = [{ChannelId, From} | Requests0],
+ State#state{connection_state = Connection#connection{requests = Requests}}.
+
+new_channel_id(#state{connection_state = #connection{channel_id_seed = Id} =
+ Connection}
+ = State) ->
+ {Id, State#state{connection_state =
+ Connection#connection{channel_id_seed = Id + 1}}}.
generate_event_new_state(#state{ssh_params =
#ssh{recv_sequence = SeqNum0}
= Ssh} = State, EncData) ->
@@ -906,7 +1309,6 @@ generate_event_new_state(#state{ssh_params =
encoded_data_buffer = EncData,
undecoded_packet_length = undefined}.
-
next_packet(#state{decoded_data_buffer = <<>>,
encoded_data_buffer = Buff,
ssh_params = #ssh{decrypt_block_size = BlockSize},
@@ -931,7 +1333,6 @@ after_new_keys(#state{renegotiate = true} = State) ->
{connected, State#state{renegotiate = false}};
after_new_keys(#state{renegotiate = false,
ssh_params = #ssh{role = client} = Ssh0} = State) ->
- ssh_bits:install_messages(ssh_auth:userauth_messages()),
{Msg, Ssh} = ssh_auth:service_request_msg(Ssh0),
send_msg(Msg, State),
{userauth, State#state{ssh_params = Ssh}};
@@ -981,10 +1382,16 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
handle_disconnect(DisconnectMsg, State0)
end.
-handle_disconnect(#ssh_msg_disconnect{} = Msg, State) ->
- {stop, {shutdown, Msg}, State}.
-handle_disconnect(#ssh_msg_disconnect{} = Msg, State, ErrorMsg) ->
- {stop, {shutdown, {Msg, ErrorMsg}}, State}.
+handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
+ role = Role} = State0) ->
+ {disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
+ State = send_replies(Replies, State0),
+ {stop, {shutdown, Desc}, State#state{connection_state = Connection}}.
+handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
+ role = Role} = State0, ErrorMsg) ->
+ {disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
+ State = send_replies(Replies, State0),
+ {stop, {shutdown, {Desc, ErrorMsg}}, State#state{connection_state = Connection}}.
counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
@@ -1003,48 +1410,67 @@ connected_fun(User, PeerAddr, Method, Opts) ->
catch Fun(User, PeerAddr, Method)
end.
-retry_fun(_, undefined, _) ->
+retry_fun(_, _, undefined, _) ->
ok;
-retry_fun(User, {error, Reason}, Opts) ->
+retry_fun(User, PeerAddr, {error, Reason}, Opts) ->
case proplists:get_value(failfun, Opts) of
undefined ->
ok;
Fun ->
- catch Fun(User, Reason)
+ do_retry_fun(Fun, User, PeerAddr, Reason)
end;
-retry_fun(User, Reason, Opts) ->
+retry_fun(User, PeerAddr, Reason, Opts) ->
case proplists:get_value(infofun, Opts) of
undefined ->
ok;
- Fun ->
- catch Fun(User, Reason)
+ Fun ->
+ do_retry_fun(Fun, User, PeerAddr, Reason)
end.
-ssh_info_handler(Options, Ssh, State, From) ->
- Info = ssh_info(Options, Ssh, State, []),
- ssh_connection_manager:send_msg({channel_requst_reply, From, Info}).
+do_retry_fun(Fun, User, PeerAddr, Reason) ->
+ case erlang:fun_info(Fun, arity) of
+ {arity, 2} -> %% Backwards compatible
+ catch Fun(User, Reason);
+ {arity, 3} ->
+ catch Fun(User, PeerAddr, Reason)
+ end.
-ssh_info([], _, _, Acc) ->
+ssh_info([], _State, Acc) ->
+ Acc;
+ssh_info([client_version | Rest], #state{ssh_params = #ssh{c_vsn = IntVsn,
+ c_version = StringVsn}} = State, Acc) ->
+ ssh_info(Rest, State, [{client_version, {IntVsn, StringVsn}} | Acc]);
+
+ssh_info([server_version | Rest], #state{ssh_params =#ssh{s_vsn = IntVsn,
+ s_version = StringVsn}} = State, Acc) ->
+ ssh_info(Rest, State, [{server_version, {IntVsn, StringVsn}} | Acc]);
+ssh_info([peer | Rest], #state{ssh_params = #ssh{peer = Peer}} = State, Acc) ->
+ ssh_info(Rest, State, [{peer, Peer} | Acc]);
+ssh_info([sockname | Rest], #state{socket = Socket} = State, Acc) ->
+ {ok, SockName} = inet:sockname(Socket),
+ ssh_info(Rest, State, [{sockname, SockName}|Acc]);
+ssh_info([user | Rest], #state{auth_user = User} = State, Acc) ->
+ ssh_info(Rest, State, [{user, User}|Acc]);
+ssh_info([ _ | Rest], State, Acc) ->
+ ssh_info(Rest, State, Acc).
+
+ssh_channel_info([], _, Acc) ->
Acc;
-ssh_info([client_version | Rest], #ssh{c_vsn = IntVsn,
- c_version = StringVsn} = SshParams, State, Acc) ->
- ssh_info(Rest, SshParams, State, [{client_version, {IntVsn, StringVsn}} | Acc]);
-
-ssh_info([server_version | Rest], #ssh{s_vsn = IntVsn,
- s_version = StringVsn} = SshParams, State, Acc) ->
- ssh_info(Rest, SshParams, State, [{server_version, {IntVsn, StringVsn}} | Acc]);
-
-ssh_info([peer | Rest], #ssh{peer = Peer} = SshParams, State, Acc) ->
- ssh_info(Rest, SshParams, State, [{peer, Peer} | Acc]);
-
-ssh_info([sockname | Rest], SshParams, #state{socket=Socket}=State, Acc) ->
- ssh_info(Rest, SshParams, State, [{sockname,inet:sockname(Socket)}|Acc]);
-
-ssh_info([ _ | Rest], SshParams, State, Acc) ->
- ssh_info(Rest, SshParams, State, Acc).
+ssh_channel_info([recv_window | Rest], #channel{recv_window_size = WinSize,
+ recv_packet_size = Packsize
+ } = Channel, Acc) ->
+ ssh_channel_info(Rest, Channel, [{recv_window, {{win_size, WinSize},
+ {packet_size, Packsize}}} | Acc]);
+ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize,
+ send_packet_size = Packsize
+ } = Channel, Acc) ->
+ ssh_channel_info(Rest, Channel, [{send_window, {{win_size, WinSize},
+ {packet_size, Packsize}}} | Acc]);
+ssh_channel_info([ _ | Rest], Channel, Acc) ->
+ ssh_channel_info(Rest, Channel, Acc).
log_error(Reason) ->
Report = io_lib:format("Erlang ssh connection handler failed with reason: "
@@ -1053,3 +1479,101 @@ log_error(Reason) ->
[Reason, erlang:get_stacktrace()]),
error_logger:error_report(Report),
"Internal error".
+
+send_replies([], State) ->
+ State;
+send_replies([{connection_reply, Data} | Rest], #state{ssh_params = Ssh0} = State) ->
+ {Packet, Ssh} = ssh_transport:ssh_packet(Data, Ssh0),
+ send_msg(Packet, State),
+ send_replies(Rest, State#state{ssh_params = Ssh});
+send_replies([Msg | Rest], State) ->
+ catch send_reply(Msg),
+ send_replies(Rest, State).
+
+send_reply({channel_data, Pid, Data}) ->
+ Pid ! {ssh_cm, self(), Data};
+send_reply({channel_requst_reply, From, Data}) ->
+ gen_fsm:reply(From, Data);
+send_reply({flow_control, Cache, Channel, From, Msg}) ->
+ ssh_channel:cache_update(Cache, Channel#channel{flow_control = undefined}),
+ gen_fsm:reply(From, Msg);
+send_reply({flow_control, From, Msg}) ->
+ gen_fsm:reply(From, Msg).
+
+disconnect_fun(_, undefined) ->
+ ok;
+disconnect_fun(Reason, Opts) ->
+ case proplists:get_value(disconnectfun, Opts) of
+ undefined ->
+ ok;
+ Fun ->
+ catch Fun(Reason)
+ end.
+
+check_cache(#state{opts = Opts} = State, Cache) ->
+ %% Check the number of entries in Cache
+ case proplists:get_value(size, ets:info(Cache)) of
+ 0 ->
+ case proplists:get_value(idle_time, Opts, infinity) of
+ infinity ->
+ State;
+ Time ->
+ handle_idle_timer(Time, State)
+ end;
+ _ ->
+ State
+ end.
+
+handle_idle_timer(Time, #state{idle_timer_ref = undefined} = State) ->
+ TimerRef = erlang:send_after(Time, self(), {'EXIT', [], "Timeout"}),
+ State#state{idle_timer_ref=TimerRef};
+handle_idle_timer(_, State) ->
+ State.
+
+remove_timer_ref(State) ->
+ case State#state.idle_timer_ref of
+ infinity -> %% If the timer is not activated
+ State;
+ undefined -> %% If we already has cancelled the timer
+ State;
+ TimerRef -> %% Timer is active
+ erlang:cancel_timer(TimerRef),
+ State#state{idle_timer_ref = undefined}
+ end.
+
+socket_control(Socket, Pid, Transport) ->
+ case Transport:controlling_process(Socket, Pid) of
+ ok ->
+ send_event(Pid, socket_control);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+handshake(Pid, Ref, Timeout) ->
+ receive
+ ssh_connected ->
+ erlang:demonitor(Ref),
+ {ok, Pid};
+ {Pid, not_connected, Reason} ->
+ {error, Reason};
+ {Pid, user_password} ->
+ Pass = io:get_password(),
+ Pid ! Pass,
+ handshake(Pid, Ref, Timeout);
+ {Pid, question} ->
+ Answer = io:get_line(""),
+ Pid ! Answer,
+ handshake(Pid, Ref, Timeout);
+ {'DOWN', _, process, Pid, {shutdown, Reason}} ->
+ {error, Reason};
+ {'DOWN', _, process, Pid, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ stop(Pid),
+ {error, Timeout}
+ end.
+
+start_timeout(_,_, infinity) ->
+ ok;
+start_timeout(Channel, From, Time) ->
+ erlang:send_after(Time, self(), {timeout, {Channel, From}}).
diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl
deleted file mode 100644
index 99a0b6a7c8..0000000000
--- a/lib/ssh/src/ssh_connection_manager.erl
+++ /dev/null
@@ -1,916 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% 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%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: Handles multiplexing to ssh channels and global connection
-%% requests e.i. the SSH Connection Protocol (RFC 4254), that provides
-%% interactive login sessions, remote execution of commands, forwarded
-%% TCP/IP connections, and forwarded X11 connections. Details of the
-%% protocol is implemented in ssh_connection.erl
-%% ----------------------------------------------------------------------
--module(ssh_connection_manager).
-
--behaviour(gen_server).
-
--include("ssh.hrl").
--include("ssh_connect.hrl").
--include("ssh_transport.hrl").
-
--export([start_link/1]).
-
--export([info/1, info/2,
- renegotiate/1, connection_info/2, channel_info/3,
- peer_addr/1, send_window/3, recv_window/3, adjust_window/3,
- close/2, stop/1, send/5,
- send_eof/2]).
-
--export([open_channel/6, reply_request/3, request/6, request/7, global_request/4, event/2, event/3, cast/2]).
-
-%% Internal application API and spawn
--export([send_msg/1, ssh_channel_info_handler/3]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--define(DBG_MESSAGE, true).
-
--record(state,
- {
- role,
- client,
- starter,
- connection, % pid()
- connection_state, % #connection{}
- latest_channel_id = 0,
- opts,
- channel_args,
- idle_timer_ref, % timerref
- connected
- }).
-
-%%====================================================================
-%% Internal application API
-%%====================================================================
-
-start_link(Opts) ->
- gen_server:start_link(?MODULE, Opts, []).
-
-open_channel(ConnectionManager, ChannelType, ChannelSpecificData,
- InitialWindowSize, MaxPacketSize, Timeout) ->
- case (catch call(ConnectionManager, {open, self(), ChannelType,
- InitialWindowSize,
- MaxPacketSize, ChannelSpecificData},
- Timeout)) of
- {open, Channel} ->
- {ok, Channel};
- Error ->
- %% TODO: Best way?
- Error
- end.
-
-request(ConnectionManager, ChannelPid, ChannelId, Type, true, Data, Timeout) ->
- call(ConnectionManager, {request, ChannelPid, ChannelId, Type, Data}, Timeout);
-request(ConnectionManager, ChannelPid, ChannelId, Type, false, Data, _) ->
- cast(ConnectionManager, {request, ChannelPid, ChannelId, Type, Data}).
-
-request(ConnectionManager, ChannelId, Type, true, Data, Timeout) ->
- call(ConnectionManager, {request, ChannelId, Type, Data}, Timeout);
-request(ConnectionManager, ChannelId, Type, false, Data, _) ->
- cast(ConnectionManager, {request, ChannelId, Type, Data}).
-
-reply_request(ConnectionManager, Status, ChannelId) ->
- cast(ConnectionManager, {reply_request, Status, ChannelId}).
-
-global_request(ConnectionManager, Type, true = Reply, Data) ->
- case call(ConnectionManager,
- {global_request, self(), Type, Reply, Data}) of
- {ssh_cm, ConnectionManager, {success, _}} ->
- ok;
- {ssh_cm, ConnectionManager, {failure, _}} ->
- error
- end;
-
-global_request(ConnectionManager, Type, false = Reply, Data) ->
- cast(ConnectionManager, {global_request, self(), Type, Reply, Data}).
-
-event(ConnectionManager, BinMsg, ErrorMsg) ->
- call(ConnectionManager, {ssh_msg, self(), BinMsg, ErrorMsg}).
-event(ConnectionManager, BinMsg) ->
- call(ConnectionManager, {ssh_msg, self(), BinMsg}).
-info(ConnectionManager) ->
- info(ConnectionManager, {info, all}).
-
-info(ConnectionManager, ChannelProcess) ->
- call(ConnectionManager, {info, ChannelProcess}).
-
-%% TODO: Do we really want this function? Should not
-%% renegotiation be triggered by configurable timer
-%% or amount of data sent counter!
-renegotiate(ConnectionManager) ->
- cast(ConnectionManager, renegotiate).
-renegotiate_data(ConnectionManager) ->
- cast(ConnectionManager, renegotiate_data).
-connection_info(ConnectionManager, Options) ->
- call(ConnectionManager, {connection_info, Options}).
-
-channel_info(ConnectionManager, ChannelId, Options) ->
- call(ConnectionManager, {channel_info, ChannelId, Options}).
-
-%% Replaced by option peer to connection_info/2 keep for now
-%% for Backwards compatibility!
-peer_addr(ConnectionManager) ->
- call(ConnectionManager, {peer_addr, self()}).
-
-%% Backwards compatibility!
-send_window(ConnectionManager, Channel, TimeOut) ->
- call(ConnectionManager, {send_window, Channel}, TimeOut).
-%% Backwards compatibility!
-recv_window(ConnectionManager, Channel, TimeOut) ->
- call(ConnectionManager, {recv_window, Channel}, TimeOut).
-
-adjust_window(ConnectionManager, Channel, Bytes) ->
- cast(ConnectionManager, {adjust_window, Channel, Bytes}).
-
-close(ConnectionManager, ChannelId) ->
- case call(ConnectionManager, {close, ChannelId}) of
- ok ->
- ok;
- {error, channel_closed} ->
- ok
- end.
-
-stop(ConnectionManager) ->
- case call(ConnectionManager, stop) of
- ok ->
- ok;
- {error, channel_closed} ->
- ok
- end.
-
-send(ConnectionManager, ChannelId, Type, Data, Timeout) ->
- call(ConnectionManager, {data, ChannelId, Type, Data}, Timeout).
-
-send_eof(ConnectionManager, ChannelId) ->
- call(ConnectionManager, {eof, ChannelId}).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([server, _Socket, Opts]) ->
- process_flag(trap_exit, true),
- ssh_bits:install_messages(ssh_connection:messages()),
- Cache = ssh_channel:cache_create(),
- {ok, #state{role = server,
- connection_state = #connection{channel_cache = Cache,
- channel_id_seed = 0,
- port_bindings = [],
- requests = []},
- opts = Opts,
- connected = false}};
-
-init([client, Opts]) ->
- process_flag(trap_exit, true),
- {links, [Parent]} = process_info(self(), links),
- ssh_bits:install_messages(ssh_connection:messages()),
- Cache = ssh_channel:cache_create(),
- Address = proplists:get_value(address, Opts),
- Port = proplists:get_value(port, Opts),
- SocketOpts = proplists:get_value(socket_opts, Opts),
- Options = proplists:get_value(ssh_opts, Opts),
- ChannelPid = proplists:get_value(channel_pid, Opts),
- self() !
- {start_connection, client, [Parent, Address, Port, SocketOpts, Options]},
- TimerRef = get_idle_time(Options),
-
- {ok, #state{role = client,
- client = ChannelPid,
- connection_state = #connection{channel_cache = Cache,
- channel_id_seed = 0,
- port_bindings = [],
- connection_supervisor = Parent,
- requests = []},
- opts = Opts,
- idle_timer_ref = TimerRef,
- connected = false}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call({request, ChannelPid, ChannelId, Type, Data}, From, State0) ->
- {{replies, Replies}, State} = handle_request(ChannelPid,
- ChannelId, Type, Data,
- true, From, State0),
- %% Sends message to the connection handler process, reply to
- %% channel is sent later when reply arrives from the connection
- %% handler.
- lists:foreach(fun send_msg/1, Replies),
- SshOpts = proplists:get_value(ssh_opts, State0#state.opts),
- case proplists:get_value(idle_time, SshOpts) of
- infinity ->
- ok;
- _IdleTime ->
- erlang:send_after(5000, self(), {check_cache, [], []})
- end,
- {noreply, State};
-
-handle_call({request, ChannelId, Type, Data}, From, State0) ->
- {{replies, Replies}, State} = handle_request(ChannelId, Type, Data,
- true, From, State0),
- %% Sends message to the connection handler process, reply to
- %% channel is sent later when reply arrives from the connection
- %% handler.
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State};
-
-%% Message from ssh_connection_handler
-handle_call({ssh_msg, Pid, Msg}, From,
- #state{connection_state = Connection0,
- role = Role, opts = Opts, connected = IsConnected,
- client = ClientPid}
- = State) ->
-
- %% To avoid that not all data sent by the other side is processes before
- %% possible crash in ssh_connection_handler takes down the connection.
- gen_server:reply(From, ok),
- ConnectionMsg = decode_ssh_msg(Msg),
- try ssh_connection:handle_msg(ConnectionMsg, Connection0, Pid, Role) of
- {{replies, Replies}, Connection} ->
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State#state{connection_state = Connection}};
- {noreply, Connection} ->
- {noreply, State#state{connection_state = Connection}};
- {disconnect, {_, Reason}, {{replies, Replies}, Connection}}
- when Role == client andalso (not IsConnected) ->
- lists:foreach(fun send_msg/1, Replies),
- ClientPid ! {self(), not_connected, Reason},
- {stop, {shutdown, normal}, State#state{connection = Connection}};
- {disconnect, Reason, {{replies, Replies}, Connection}} ->
- lists:foreach(fun send_msg/1, Replies),
- SSHOpts = proplists:get_value(ssh_opts, Opts),
- disconnect_fun(Reason, SSHOpts),
- {stop, {shutdown, normal}, State#state{connection_state = Connection}}
- catch
- _:Error ->
- {disconnect, Reason, {{replies, Replies}, Connection}} =
- ssh_connection:handle_msg(
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Internal error",
- language = "en"}, Connection0, undefined,
- Role),
- lists:foreach(fun send_msg/1, Replies),
- SSHOpts = proplists:get_value(ssh_opts, Opts),
- disconnect_fun(Reason, SSHOpts),
- {stop, {shutdown, Error}, State#state{connection_state = Connection}}
- end;
-handle_call({ssh_msg, Pid, Msg, ErrorMsg}, From,
- #state{connection_state = Connection0,
- role = Role, opts = Opts, connected = IsConnected,
- client = ClientPid}
- = State) ->
-
- %% To avoid that not all data sent by the other side is processes before
- %% possible crash in ssh_connection_handler takes down the connection.
- gen_server:reply(From, ok),
- ConnectionMsg = decode_ssh_msg(Msg),
- try ssh_connection:handle_msg(ConnectionMsg, Connection0, Pid, Role) of
- {{replies, Replies}, Connection} ->
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State#state{connection_state = Connection}};
- {noreply, Connection} ->
- {noreply, State#state{connection_state = Connection}};
- {disconnect, {_, Reason}, {{replies, Replies}, Connection}}
- when Role == client andalso (not IsConnected) ->
- lists:foreach(fun send_msg/1, Replies),
- ClientPid ! {self(), not_connected, {Reason, ErrorMsg}},
- {stop, {shutdown, normal}, State#state{connection = Connection}};
- {disconnect, Reason, {{replies, Replies}, Connection}} ->
- lists:foreach(fun send_msg/1, Replies),
- SSHOpts = proplists:get_value(ssh_opts, Opts),
- disconnect_fun(Reason, SSHOpts),
- {stop, {shutdown, normal}, State#state{connection_state = Connection}}
- catch
- _:Error ->
- {disconnect, Reason, {{replies, Replies}, Connection}} =
- ssh_connection:handle_msg(
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Internal error",
- language = "en"}, Connection0, undefined,
- Role),
- lists:foreach(fun send_msg/1, Replies),
- SSHOpts = proplists:get_value(ssh_opts, Opts),
- disconnect_fun(Reason, SSHOpts),
- {stop, {shutdown, Error}, State#state{connection_state = Connection}}
- end;
-handle_call({global_request, Pid, _, _, _} = Request, From,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- State1 = handle_global_request(Request, State0),
- Channel = ssh_channel:cache_find(Pid, Cache),
- State = add_request(true, Channel#channel.local_id, From, State1),
- {noreply, State};
-
-handle_call({data, ChannelId, Type, Data}, From,
- #state{connection_state = #connection{channel_cache = _Cache}
- = Connection0,
- connection = ConnectionPid} = State) ->
- channel_data(ChannelId, Type, Data, Connection0, ConnectionPid, From,
- State);
-
-handle_call({eof, ChannelId}, _From,
- #state{connection = Pid, connection_state =
- #connection{channel_cache = Cache}} = State) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id, sent_close = false} ->
- send_msg({connection_reply, Pid,
- ssh_connection:channel_eof_msg(Id)}),
- {reply, ok, State};
- _ ->
- {reply, {error,closed}, State}
- end;
-
-handle_call({connection_info, Options}, From,
- #state{connection = Connection} = State) ->
- ssh_connection_handler:connection_info(Connection, From, Options),
- %% Reply will be sent by the connection handler by calling
- %% ssh_connection_handler:send_msg/1.
- {noreply, State};
-
-handle_call({channel_info, ChannelId, Options}, From,
- #state{connection_state = #connection{channel_cache = Cache}} = State) ->
-
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{} = Channel ->
- spawn(?MODULE, ssh_channel_info_handler, [Options, Channel, From]),
- {noreply, State};
- undefined ->
- {reply, []}
- end;
-
-handle_call({info, ChannelPid}, _From,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State) ->
- Result = ssh_channel:cache_foldl(
- fun(Channel, Acc) when ChannelPid == all;
- Channel#channel.user == ChannelPid ->
- [Channel | Acc];
- (_, Acc) ->
- Acc
- end, [], Cache),
- {reply, {ok, Result}, State};
-
-handle_call({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data},
- From, #state{connection = Pid,
- connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- erlang:monitor(process, ChannelPid),
- {ChannelId, State1} = new_channel_id(State0),
- Msg = ssh_connection:channel_open_msg(Type, ChannelId,
- InitialWindowSize,
- MaxPacketSize, Data),
- send_msg({connection_reply, Pid, Msg}),
- Channel = #channel{type = Type,
- sys = "none",
- user = ChannelPid,
- local_id = ChannelId,
- recv_window_size = InitialWindowSize,
- recv_packet_size = MaxPacketSize},
- ssh_channel:cache_update(Cache, Channel),
- State = add_request(true, ChannelId, From, State1),
- {noreply, remove_timer_ref(State)};
-
-handle_call({send_window, ChannelId}, _From,
- #state{connection_state =
- #connection{channel_cache = Cache}} = State) ->
- Reply = case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{send_window_size = WinSize,
- send_packet_size = Packsize} ->
- {ok, {WinSize, Packsize}};
- undefined ->
- {error, einval}
- end,
- {reply, Reply, State};
-
-handle_call({recv_window, ChannelId}, _From,
- #state{connection_state = #connection{channel_cache = Cache}}
- = State) ->
-
- Reply = case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{recv_window_size = WinSize,
- recv_packet_size = Packsize} ->
- {ok, {WinSize, Packsize}};
- undefined ->
- {error, einval}
- end,
- {reply, Reply, State};
-
-%% Replaced by option peer to connection_info/2 keep for now
-%% for Backwards compatibility!
-handle_call({peer_addr, _ChannelId}, _From,
- #state{connection = Pid} = State) ->
- Reply = ssh_connection_handler:peer_address(Pid),
- {reply, Reply, State};
-
-handle_call(opts, _, #state{opts = Opts} = State) ->
- {reply, Opts, State};
-
-handle_call({close, ChannelId}, _,
- #state{connection = Pid, connection_state =
- #connection{channel_cache = Cache}} = State) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} = Channel ->
- send_msg({connection_reply, Pid,
- ssh_connection:channel_close_msg(Id)}),
- ssh_channel:cache_update(Cache, Channel#channel{sent_close = true}),
- SshOpts = proplists:get_value(ssh_opts, State#state.opts),
- case proplists:get_value(idle_time, SshOpts) of
- infinity ->
- ok;
- _IdleTime ->
- erlang:send_after(5000, self(), {check_cache, [], []})
- end,
- {reply, ok, State};
- undefined ->
- {reply, ok, State}
- end;
-
-handle_call(stop, _, #state{connection_state = Connection0,
- role = Role,
- opts = Opts} = State) ->
- {disconnect, Reason, {{replies, Replies}, Connection}} =
- ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "User closed down connection",
- language = "en"}, Connection0, undefined,
- Role),
- lists:foreach(fun send_msg/1, Replies),
- SSHOpts = proplists:get_value(ssh_opts, Opts),
- disconnect_fun(Reason, SSHOpts),
- {stop, normal, ok, State#state{connection_state = Connection}};
-
-%% API violation make it the violaters problem
-%% by ignoring it. The violating process will get
-%% a timeout or hang.
-handle_call(_, _, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast({request, ChannelPid, ChannelId, Type, Data}, State0) ->
- {{replies, Replies}, State} = handle_request(ChannelPid, ChannelId,
- Type, Data,
- false, none, State0),
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State};
-
-handle_cast({request, ChannelId, Type, Data}, State0) ->
- {{replies, Replies}, State} = handle_request(ChannelId, Type, Data,
- false, none, State0),
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State};
-
-handle_cast({reply_request, Status, ChannelId}, #state{connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- State = case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = RemoteId} ->
- cm_message({Status, RemoteId}, State0);
- undefined ->
- State0
- end,
- {noreply, State};
-
-handle_cast({global_request, _, _, _, _} = Request, State0) ->
- State = handle_global_request(Request, State0),
- {noreply, State};
-
-handle_cast(renegotiate, #state{connection = Pid} = State) ->
- ssh_connection_handler:renegotiate(Pid),
- {noreply, State};
-handle_cast(renegotiate_data, #state{connection = Pid} = State) ->
- ssh_connection_handler:renegotiate_data(Pid),
- {noreply, State};
-handle_cast({adjust_window, ChannelId, Bytes},
- #state{connection = Pid, connection_state =
- #connection{channel_cache = Cache}} = State) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{recv_window_size = WinSize, remote_id = Id} = Channel ->
- ssh_channel:cache_update(Cache, Channel#channel{recv_window_size =
- WinSize + Bytes}),
- Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes),
- send_msg({connection_reply, Pid, Msg});
- undefined ->
- ignore
- end,
- {noreply, State};
-
-handle_cast({success, ChannelId}, #state{connection = Pid} = State) ->
- Msg = ssh_connection:channel_success_msg(ChannelId),
- send_msg({connection_reply, Pid, Msg}),
- {noreply, State};
-
-handle_cast({failure, ChannelId}, #state{connection = Pid} = State) ->
- Msg = ssh_connection:channel_failure_msg(ChannelId),
- send_msg({connection_reply, Pid, Msg}),
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info({start_connection, server,
- [Address, Port, Socket, Options, SubSysSup]},
- #state{connection_state = CState} = State) ->
- {ok, Connection} = ssh_transport:accept(Address, Port, Socket, Options),
- Shell = proplists:get_value(shell, Options),
- Exec = proplists:get_value(exec, Options),
- CliSpec = proplists:get_value(ssh_cli, Options, {ssh_cli, [Shell]}),
- ssh_connection_handler:send_event(Connection, socket_control),
- erlang:send_after(60000, self(), rekey_data),
- {noreply, State#state{connection = Connection,
- connection_state =
- CState#connection{address = Address,
- port = Port,
- cli_spec = CliSpec,
- options = Options,
- exec = Exec,
- sub_system_supervisor = SubSysSup
- }}};
-
-handle_info({start_connection, client,
- [Parent, Address, Port, SocketOpts, Options]},
- #state{client = Pid} = State) ->
- case (catch ssh_transport:connect(Parent, Address,
- Port, SocketOpts, Options)) of
- {ok, Connection} ->
- erlang:send_after(60000, self(), rekey_data),
- erlang:send_after(3600000, self(), rekey),
- {noreply, State#state{connection = Connection}};
- Reason ->
- Pid ! {self(), not_connected, Reason},
- {stop, {shutdown, normal}, State}
- end;
-handle_info({check_cache, _ , _},
- #state{connection_state =
- #connection{channel_cache = Cache}} = State) ->
- {noreply, check_cache(State, Cache)};
-handle_info({ssh_cm, _Sender, Msg}, State0) ->
- %% Backwards compatibility!
- State = cm_message(Msg, State0),
- {noreply, State};
-
-%% Nop backwards compatibility
-handle_info({same_user, _}, State) ->
- {noreply, State};
-
-handle_info(ssh_connected, #state{role = client, client = Pid}
- = State) ->
- Pid ! {self(), is_connected},
- {noreply, State#state{connected = true, opts = handle_password(State#state.opts)}};
-
-handle_info(ssh_connected, #state{role = server} = State) ->
- {noreply, State#state{connected = true}};
-
-%%% Handle that ssh channels user process goes down
-handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) ->
- handle_down(handle_channel_down(ChannelPid, State));
-
-%%% So that terminate will be run when supervisor is shutdown
-handle_info({'EXIT', _Sup, Reason}, State) ->
- {stop, Reason, State};
-handle_info(rekey, State) ->
- renegotiate(self()),
- erlang:send_after(3600000, self(), rekey),
- {noreply, State};
-handle_info(rekey_data, State) ->
- renegotiate_data(self()),
- erlang:send_after(60000, self(), rekey_data),
- {noreply, State}.
-handle_password(Opts) ->
- handle_rsa_password(handle_dsa_password(handle_normal_password(Opts))).
-handle_normal_password(Opts) ->
- case proplists:get_value(ssh_opts, Opts, false) of
- false ->
- Opts;
- SshOpts ->
- case proplists:get_value(password, SshOpts, false) of
- false ->
- Opts;
- _Password ->
- NewOpts = [{password, undefined}|lists:keydelete(password, 1, SshOpts)],
- [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)]
- end
- end.
-handle_dsa_password(Opts) ->
- case proplists:get_value(ssh_opts, Opts, false) of
- false ->
- Opts;
- SshOpts ->
- case proplists:get_value(dsa_pass_phrase, SshOpts, false) of
- false ->
- Opts;
- _Password ->
- NewOpts = [{dsa_pass_phrase, undefined}|lists:keydelete(dsa_pass_phrase, 1, SshOpts)],
- [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)]
- end
- end.
-handle_rsa_password(Opts) ->
- case proplists:get_value(ssh_opts, Opts, false) of
- false ->
- Opts;
- SshOpts ->
- case proplists:get_value(rsa_pass_phrase, SshOpts, false) of
- false ->
- Opts;
- _Password ->
- NewOpts = [{rsa_pass_phrase, undefined}|lists:keydelete(rsa_pass_phrase, 1, SshOpts)],
- [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)]
- end
- end.
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, #state{role = client,
- connection_state =
- #connection{connection_supervisor = Supervisor}}) ->
- sshc_sup:stop_child(Supervisor);
-
-terminate(_Reason, #state{role = server,
- connection_state =
- #connection{sub_system_supervisor = SubSysSup},
- opts = Opts}) ->
- Address = proplists:get_value(address, Opts),
- Port = proplists:get_value(port, Opts),
- SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- ssh_system_sup:stop_subsystem(SystemSup, SubSysSup).
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-get_idle_time(SshOptions) ->
- case proplists:get_value(idle_time, SshOptions) of
- infinity ->
- infinity;
- _IdleTime -> %% We dont want to set the timeout on first connect
- undefined
- end.
-check_cache(State, Cache) ->
- %% Check the number of entries in Cache
- case proplists:get_value(size, ets:info(Cache)) of
- 0 ->
- Opts = proplists:get_value(ssh_opts, State#state.opts),
- case proplists:get_value(idle_time, Opts) of
- infinity ->
- State;
- undefined ->
- State;
- Time ->
- case State#state.idle_timer_ref of
- undefined ->
- TimerRef = erlang:send_after(Time, self(), {'EXIT', [], "Timeout"}),
- State#state{idle_timer_ref=TimerRef};
- _ ->
- State
- end
- end;
- _ ->
- State
- end.
-remove_timer_ref(State) ->
- case State#state.idle_timer_ref of
- infinity -> %% If the timer is not activated
- State;
- undefined -> %% If we already has cancelled the timer
- State;
- TimerRef -> %% Timer is active
- erlang:cancel_timer(TimerRef),
- State#state{idle_timer_ref = undefined}
- end.
-channel_data(Id, Type, Data, Connection0, ConnectionPid, From, State) ->
- case ssh_connection:channel_data(Id, Type, Data, Connection0,
- ConnectionPid, From) of
- {{replies, Replies}, Connection} ->
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State#state{connection_state = Connection}};
- {noreply, Connection} ->
- {noreply, State#state{connection_state = Connection}}
- end.
-
-call(Pid, Msg) ->
- call(Pid, Msg, infinity).
-call(Pid, Msg, Timeout) ->
- try gen_server:call(Pid, Msg, Timeout) of
- Result ->
- Result
- catch
- exit:{timeout, _} ->
- {error, timeout};
- exit:{normal, _} ->
- {error, channel_closed};
- exit:{{shutdown, _}, _} ->
- {error, channel_closed};
- exit:{noproc,_} ->
- {error, channel_closed}
- end.
-
-cast(Pid, Msg) ->
- gen_server:cast(Pid, Msg).
-
-decode_ssh_msg(BinMsg) when is_binary(BinMsg)->
- ssh_bits:decode(BinMsg);
-decode_ssh_msg(Msg) ->
- Msg.
-
-
-send_msg(Msg) ->
- catch do_send_msg(Msg).
-do_send_msg({channel_data, Pid, Data}) ->
- Pid ! {ssh_cm, self(), Data};
-do_send_msg({channel_requst_reply, From, Data}) ->
- gen_server:reply(From, Data);
-do_send_msg({connection_reply, Pid, Data}) ->
- Msg = ssh_bits:encode(Data),
- ssh_connection_handler:send(Pid, Msg);
-do_send_msg({flow_control, Cache, Channel, From, Msg}) ->
- ssh_channel:cache_update(Cache, Channel#channel{flow_control = undefined}),
- gen_server:reply(From, Msg);
-do_send_msg({flow_control, From, Msg}) ->
- gen_server:reply(From, Msg).
-
-handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From,
- #state{connection = Pid,
- connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} = Channel ->
- update_sys(Cache, Channel, Type, ChannelPid),
- Msg = ssh_connection:channel_request_msg(Id, Type,
- WantReply, Data),
- Replies = [{connection_reply, Pid, Msg}],
- State = add_request(WantReply, ChannelId, From, State0),
- {{replies, Replies}, State};
- undefined ->
- {{replies, []}, State0}
- end.
-
-handle_request(ChannelId, Type, Data, WantReply, From,
- #state{connection = Pid,
- connection_state =
- #connection{channel_cache = Cache}} = State0) ->
- case ssh_channel:cache_lookup(Cache, ChannelId) of
- #channel{remote_id = Id} ->
- Msg = ssh_connection:channel_request_msg(Id, Type,
- WantReply, Data),
- Replies = [{connection_reply, Pid, Msg}],
- State = add_request(WantReply, ChannelId, From, State0),
- {{replies, Replies}, State};
- undefined ->
- {{replies, []}, State0}
- end.
-
-handle_down({{replies, Replies}, State}) ->
- lists:foreach(fun send_msg/1, Replies),
- {noreply, State}.
-
-handle_channel_down(ChannelPid, #state{connection_state =
- #connection{channel_cache = Cache}} =
- State) ->
- ssh_channel:cache_foldl(
- fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
- ssh_channel:cache_delete(Cache,
- Channel#channel.local_id),
- Acc;
- (_,Acc) ->
- Acc
- end, [], Cache),
- {{replies, []}, check_cache(State, Cache)}.
-
-update_sys(Cache, Channel, Type, ChannelPid) ->
- ssh_channel:cache_update(Cache,
- Channel#channel{sys = Type, user = ChannelPid}).
-
-add_request(false, _ChannelId, _From, State) ->
- State;
-add_request(true, ChannelId, From, #state{connection_state =
- #connection{requests = Requests0} =
- Connection} = State) ->
- Requests = [{ChannelId, From} | Requests0],
- State#state{connection_state = Connection#connection{requests = Requests}}.
-
-new_channel_id(#state{connection_state = #connection{channel_id_seed = Id} =
- Connection}
- = State) ->
- {Id, State#state{connection_state =
- Connection#connection{channel_id_seed = Id + 1}}}.
-
-handle_global_request({global_request, ChannelPid,
- "tcpip-forward" = Type, WantReply,
- <<?UINT32(IPLen),
- IP:IPLen/binary, ?UINT32(Port)>> = Data},
- #state{connection = ConnectionPid,
- connection_state =
- #connection{channel_cache = Cache}
- = Connection0} = State) ->
- ssh_channel:cache_update(Cache, #channel{user = ChannelPid,
- type = "forwarded-tcpip",
- sys = none}),
- Connection = ssh_connection:bind(IP, Port, ChannelPid, Connection0),
- Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
- send_msg({connection_reply, ConnectionPid, Msg}),
- State#state{connection_state = Connection};
-
-handle_global_request({global_request, _Pid, "cancel-tcpip-forward" = Type,
- WantReply, <<?UINT32(IPLen),
- IP:IPLen/binary, ?UINT32(Port)>> = Data},
- #state{connection = Pid,
- connection_state = Connection0} = State) ->
- Connection = ssh_connection:unbind(IP, Port, Connection0),
- Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
- send_msg({connection_reply, Pid, Msg}),
- State#state{connection_state = Connection};
-
-handle_global_request({global_request, _Pid, "cancel-tcpip-forward" = Type,
- WantReply, Data}, #state{connection = Pid} = State) ->
- Msg = ssh_connection:global_request_msg(Type, WantReply, Data),
- send_msg({connection_reply, Pid, Msg}),
- State.
-
-cm_message(Msg, State) ->
- {noreply, NewState} = handle_cast(Msg, State),
- NewState.
-
-disconnect_fun(Reason, Opts) ->
- case proplists:get_value(disconnectfun, Opts) of
- undefined ->
- ok;
- Fun ->
- catch Fun(Reason)
- end.
-
-ssh_channel_info_handler(Options, Channel, From) ->
- Info = ssh_channel_info(Options, Channel, []),
- send_msg({channel_requst_reply, From, Info}).
-
-ssh_channel_info([], _, Acc) ->
- Acc;
-
-ssh_channel_info([recv_window | Rest], #channel{recv_window_size = WinSize,
- recv_packet_size = Packsize
- } = Channel, Acc) ->
- ssh_channel_info(Rest, Channel, [{recv_window, {{win_size, WinSize},
- {packet_size, Packsize}}} | Acc]);
-ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize,
- send_packet_size = Packsize
- } = Channel, Acc) ->
- ssh_channel_info(Rest, Channel, [{send_window, {{win_size, WinSize},
- {packet_size, Packsize}}} | Acc]);
-ssh_channel_info([ _ | Rest], Channel, Acc) ->
- ssh_channel_info(Rest, Channel, Acc).
-
-
-
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index b620056310..c5abc8f23b 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,8 +25,9 @@
-behaviour(supervisor).
--export([start_link/1, start_handler_child/2, start_manager_child/2,
- connection_manager/1]).
+%% API
+-export([start_link/1]).
+-export([start_child/2]).
%% Supervisor callback
-export([init/1]).
@@ -37,83 +38,23 @@
start_link(Args) ->
supervisor:start_link(?MODULE, [Args]).
-%% Will be called from the manager child process
-start_handler_child(Sup, Args) ->
- [Spec] = child_specs(handler, Args),
- supervisor:start_child(Sup, Spec).
-
-%% Will be called from the acceptor process
-start_manager_child(Sup, Args) ->
- [Spec] = child_specs(manager, Args),
- supervisor:start_child(Sup, Spec).
-
-connection_manager(SupPid) ->
- try supervisor:which_children(SupPid) of
- Children ->
- {ok, ssh_connection_manager(Children)}
- catch exit:{noproc,_} ->
- {ok, undefined}
- end.
+start_child(Sup, Args) ->
+ supervisor:start_child(Sup, Args).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-init([Args]) ->
- RestartStrategy = one_for_all,
+init(_) ->
+ RestartStrategy = simple_one_for_one,
MaxR = 0,
MaxT = 3600,
- Children = child_specs(Args),
- {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
-
-%%%=========================================================================
-%%% Internal functions
-%%%=========================================================================
-child_specs(Opts) ->
- case proplists:get_value(role, Opts) of
- client ->
- child_specs(manager, [client | Opts]);
- server ->
- %% Children started by acceptor process
- []
- end.
-
-% The manager process starts the handler process
-child_specs(manager, Opts) ->
- [manager_spec(Opts)];
-child_specs(handler, Opts) ->
- [handler_spec(Opts)].
-
-manager_spec([server = Role, Socket, Opts]) ->
- Name = make_ref(),
- StartFunc = {ssh_connection_manager, start_link, [[Role, Socket, Opts]]},
- Restart = temporary,
- Shutdown = 3600,
- Modules = [ssh_connection_manager],
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, Modules};
-
-manager_spec([client = Role | Opts]) ->
- Name = make_ref(),
- StartFunc = {ssh_connection_manager, start_link, [[Role, Opts]]},
- Restart = temporary,
- Shutdown = 3600,
- Modules = [ssh_connection_manager],
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-handler_spec([Role, Socket, Opts]) ->
- Name = make_ref(),
- StartFunc = {ssh_connection_handler,
- start_link, [Role, self(), Socket, Opts]},
- Restart = temporary,
- Shutdown = 3600,
+ Name = undefined, % As simple_one_for_one is used.
+ StartFunc = {ssh_connection_handler, start_link, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 4000,
Modules = [ssh_connection_handler],
Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-ssh_connection_manager([]) ->
- undefined;
-ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) ->
- Child;
-ssh_connection_manager([_ | Rest]) ->
- ssh_connection_manager(Rest).
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 01fc713569..832b144db9 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,6 @@
-module(ssh_io).
-export([yes_no/2, read_password/2, read_line/2, format/2]).
--import(lists, [reverse/1]).
-include("ssh.hrl").
read_line(Prompt, Ssh) ->
@@ -81,7 +80,7 @@ format(Fmt, Args) ->
trim(Line) when is_list(Line) ->
- reverse(trim1(reverse(trim1(Line))));
+ lists:reverse(trim1(lists:reverse(trim1(Line))));
trim(Other) -> Other.
trim1([$\s|Cs]) -> trim(Cs);
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
new file mode 100644
index 0000000000..7bd0375521
--- /dev/null
+++ b/lib/ssh/src/ssh_message.erl
@@ -0,0 +1,529 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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(ssh_message).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-include("ssh.hrl").
+-include("ssh_connect.hrl").
+-include("ssh_auth.hrl").
+-include("ssh_transport.hrl").
+
+-export([encode/1, decode/1, encode_host_key/1, decode_keyboard_interactive_prompts/2]).
+
+encode(#ssh_msg_global_request{
+ name = Name,
+ want_reply = Bool,
+ data = Data}) ->
+ ssh_bits:encode([?SSH_MSG_GLOBAL_REQUEST,
+ Name, Bool, Data], [byte, string, boolean, '...']);
+encode(#ssh_msg_request_success{data = Data}) ->
+ <<?BYTE(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>;
+encode(#ssh_msg_request_failure{}) ->
+ <<?BYTE(?SSH_MSG_REQUEST_FAILURE)>>;
+encode(#ssh_msg_channel_open{
+ channel_type = Type,
+ sender_channel = Sender,
+ initial_window_size = Window,
+ maximum_packet_size = Max,
+ data = Data
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_OPEN,
+ Type, Sender, Window, Max, Data], [byte, string, uint32,
+ uint32, uint32, '...']);
+encode(#ssh_msg_channel_open_confirmation{
+ recipient_channel = Recipient,
+ sender_channel = Sender,
+ initial_window_size = InitWindowSize,
+ maximum_packet_size = MaxPacketSize,
+ data = Data
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_OPEN_CONFIRMATION, Recipient,
+ Sender, InitWindowSize, MaxPacketSize, Data],
+ [byte, uint32, uint32, uint32, uint32, '...']);
+encode(#ssh_msg_channel_open_failure{
+ recipient_channel = Recipient,
+ reason = Reason,
+ description = Desc,
+ lang = Lang
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_OPEN_FAILURE, Recipient,
+ Reason, Desc, Lang], [byte, uint32, uint32, string, string]);
+encode(#ssh_msg_channel_window_adjust{
+ recipient_channel = Recipient,
+ bytes_to_add = Bytes
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_WINDOW_ADJUST, Recipient, Bytes],
+ [byte, uint32, uint32]);
+encode(#ssh_msg_channel_data{
+ recipient_channel = Recipient,
+ data = Data
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_DATA, Recipient, Data], [byte, uint32, binary]);
+
+encode(#ssh_msg_channel_extended_data{
+ recipient_channel = Recipient,
+ data_type_code = DataType,
+ data = Data
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_EXTENDED_DATA, Recipient,
+ DataType, Data], [byte, uint32, uint32, binary]);
+
+encode(#ssh_msg_channel_eof{recipient_channel = Recipient
+ }) ->
+ <<?BYTE(?SSH_MSG_CHANNEL_EOF), ?UINT32(Recipient)>>;
+encode(#ssh_msg_channel_close{
+ recipient_channel = Recipient
+ }) ->
+ <<?BYTE(?SSH_MSG_CHANNEL_CLOSE), ?UINT32(Recipient)>>;
+encode(#ssh_msg_channel_request{
+ recipient_channel = Recipient,
+ request_type = Type,
+ want_reply = Bool,
+ data = Data
+ }) ->
+ ssh_bits:encode([?SSH_MSG_CHANNEL_REQUEST, Recipient, Type, Bool, Data],
+ [byte, uint32, string, boolean, '...']);
+encode(#ssh_msg_channel_success{
+ recipient_channel = Recipient
+ }) ->
+ <<?BYTE(?SSH_MSG_CHANNEL_SUCCESS), ?UINT32(Recipient)>>;
+encode(#ssh_msg_channel_failure{
+ recipient_channel = Recipient
+ }) ->
+ <<?BYTE(?SSH_MSG_CHANNEL_FAILURE), ?UINT32(Recipient)>>;
+
+encode(#ssh_msg_userauth_request{
+ user = User,
+ service = Service,
+ method = Method,
+ data = Data
+ }) ->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_REQUEST, User, Service, Method, Data],
+ [byte, string, string, string, '...']);
+encode(#ssh_msg_userauth_failure{
+ authentications = Auths,
+ partial_success = Bool
+ }) ->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_FAILURE, Auths, Bool],
+ [byte, string, boolean]);
+encode(#ssh_msg_userauth_success{}) ->
+ <<?BYTE(?SSH_MSG_USERAUTH_SUCCESS)>>;
+
+encode(#ssh_msg_userauth_banner{
+ message = Banner,
+ language = Lang
+ }) ->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_BANNER, Banner, Lang],
+ [byte, string, string]);
+
+encode(#ssh_msg_userauth_pk_ok{
+ algorithm_name = Alg,
+ key_blob = KeyBlob
+ }) ->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_PK_OK, Alg, KeyBlob],
+ [byte, string, binary]);
+
+encode(#ssh_msg_userauth_passwd_changereq{prompt = Prompt,
+ languge = Lang
+ })->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, Prompt, Lang],
+ [byte, string, string]);
+
+encode(#ssh_msg_userauth_info_request{
+ name = Name,
+ instruction = Inst,
+ language_tag = Lang,
+ num_prompts = NumPromtps,
+ data = Data}) ->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_INFO_REQUEST, Name, Inst, Lang, NumPromtps, Data],
+ [byte, string, string, string, uint32, '...']);
+
+encode(#ssh_msg_userauth_info_response{
+ num_responses = Num,
+ data = Data}) ->
+ ssh_bits:encode([?SSH_MSG_USERAUTH_INFO_RESPONSE, Num, Data],
+ [byte, uint32, '...']);
+encode(#ssh_msg_disconnect{
+ code = Code,
+ description = Desc,
+ language = Lang
+ }) ->
+ ssh_bits:encode([?SSH_MSG_DISCONNECT, Code, Desc, Lang],
+ [byte, uint32, string, string]);
+
+encode(#ssh_msg_service_request{
+ name = Service
+ }) ->
+ ssh_bits:encode([?SSH_MSG_SERVICE_REQUEST, Service], [byte, string]);
+
+encode(#ssh_msg_service_accept{
+ name = Service
+ }) ->
+ ssh_bits:encode([?SSH_MSG_SERVICE_ACCEPT, Service], [byte, string]);
+
+encode(#ssh_msg_newkeys{}) ->
+ <<?BYTE(?SSH_MSG_NEWKEYS)>>;
+
+encode(#ssh_msg_kexinit{
+ cookie = Cookie,
+ kex_algorithms = KeyAlgs,
+ server_host_key_algorithms = HostKeyAlgs,
+ encryption_algorithms_client_to_server = EncAlgC2S,
+ encryption_algorithms_server_to_client = EncAlgS2C,
+ mac_algorithms_client_to_server = MacAlgC2S,
+ mac_algorithms_server_to_client = MacAlgS2C,
+ compression_algorithms_client_to_server = CompAlgS2C,
+ compression_algorithms_server_to_client = CompAlgC2S,
+ languages_client_to_server = LangC2S,
+ languages_server_to_client = LangS2C,
+ first_kex_packet_follows = Bool,
+ reserved = Reserved
+ }) ->
+ ssh_bits:encode([?SSH_MSG_KEXINIT, Cookie, KeyAlgs, HostKeyAlgs, EncAlgC2S, EncAlgS2C,
+ MacAlgC2S, MacAlgS2C, CompAlgS2C, CompAlgC2S, LangC2S, LangS2C, Bool,
+ Reserved],
+ [byte, cookie,
+ name_list, name_list,
+ name_list, name_list,
+ name_list, name_list,
+ name_list, name_list,
+ name_list, name_list,
+ boolean, uint32]);
+
+encode(#ssh_msg_kexdh_init{e = E}) ->
+ ssh_bits:encode([?SSH_MSG_KEXDH_INIT, E], [byte, mpint]);
+
+encode(#ssh_msg_kexdh_reply{
+ public_host_key = Key,
+ f = F,
+ h_sig = Signature
+ }) ->
+ EncKey = encode_host_key(Key),
+ EncSign = encode_sign(Key, Signature),
+ ssh_bits:encode([?SSH_MSG_KEXDH_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]);
+
+encode(#ssh_msg_kex_dh_gex_request{
+ min = Min,
+ n = N,
+ max = Max
+ }) ->
+ ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REQUEST, Min, N, Max],
+ [byte, uint32, uint32, uint32, uint32]);
+encode(#ssh_msg_kex_dh_gex_request_old{n = N}) ->
+ ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REQUEST_OLD, N],
+ [byte, uint32]);
+
+encode(#ssh_msg_kex_dh_gex_group{p = Prime, g = Generator}) ->
+ ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_GROUP, Prime, Generator],
+ [byte, mpint, mpint]);
+
+encode(#ssh_msg_kex_dh_gex_init{e = Public}) ->
+ ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_INIT, Public], [byte, mpint]);
+
+encode(#ssh_msg_kex_dh_gex_reply{
+ %% Will be private key encode_host_key extracts only the public part!
+ public_host_key = Key,
+ f = F,
+ h_sig = Signature
+ }) ->
+ EncKey = encode_host_key(Key),
+ EncSign = encode_sign(Key, Signature),
+ ssh_bits:encode([?SSH_MSG_KEXDH_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]);
+
+encode(#ssh_msg_ignore{data = Data}) ->
+ ssh_bits:encode([?SSH_MSG_IGNORE, Data], [byte, string]);
+
+encode(#ssh_msg_unimplemented{sequence = Seq}) ->
+ ssh_bits:encode([?SSH_MSG_IGNORE, Seq], [byte, uint32]);
+
+encode(#ssh_msg_debug{always_display = Bool,
+ message = Msg,
+ language = Lang}) ->
+ ssh_bits:encode([?SSH_MSG_DEBUG, Bool, Msg, Lang], [byte, boolean, string, string]).
+
+
+%% Connection Messages
+decode(<<?BYTE(?SSH_MSG_GLOBAL_REQUEST), ?UINT32(Len), Name:Len/binary,
+ ?BYTE(Bool), Data/binary>>) ->
+ #ssh_msg_global_request{
+ name = Name,
+ want_reply = erl_boolean(Bool),
+ data = Data
+ };
+decode(<<?BYTE(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>) ->
+ #ssh_msg_request_success{data = Data};
+decode(<<?BYTE(?SSH_MSG_REQUEST_FAILURE)>>) ->
+ #ssh_msg_request_failure{};
+decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN),
+ ?UINT32(Len), Type:Len/binary,
+ ?UINT32(Sender), ?UINT32(Window), ?UINT32(Max),
+ Data/binary>>) ->
+ #ssh_msg_channel_open{
+ channel_type = binary_to_list(Type),
+ sender_channel = Sender,
+ initial_window_size = Window,
+ maximum_packet_size = Max,
+ data = Data
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_CONFIRMATION), ?UINT32(Recipient), ?UINT32(Sender),
+ ?UINT32(InitWindowSize), ?UINT32(MaxPacketSize),
+ Data/binary>>) ->
+ #ssh_msg_channel_open_confirmation{
+ recipient_channel = Recipient,
+ sender_channel = Sender,
+ initial_window_size = InitWindowSize,
+ maximum_packet_size = MaxPacketSize,
+ data = Data
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_FAILURE), ?UINT32(Recipient), ?UINT32(Reason),
+ ?UINT32(Len0), Desc:Len0/binary, ?UINT32(Len1), Lang:Len1/binary >>) ->
+ #ssh_msg_channel_open_failure{
+ recipient_channel = Recipient,
+ reason = Reason,
+ description = unicode:characters_to_list(Desc),
+ lang = Lang
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?UINT32(Recipient), ?UINT32(Bytes)>>) ->
+ #ssh_msg_channel_window_adjust{
+ recipient_channel = Recipient,
+ bytes_to_add = Bytes
+ };
+
+decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?UINT32(Len), Data:Len/binary>>) ->
+ #ssh_msg_channel_data{
+ recipient_channel = Recipient,
+ data = Data
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
+ ?UINT32(DataType), Data/binary>>) ->
+ #ssh_msg_channel_extended_data{
+ recipient_channel = Recipient,
+ data_type_code = DataType,
+ data = Data
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_EOF), ?UINT32(Recipient)>>) ->
+ #ssh_msg_channel_eof{
+ recipient_channel = Recipient
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_CLOSE), ?UINT32(Recipient)>>) ->
+ #ssh_msg_channel_close{
+ recipient_channel = Recipient
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_REQUEST), ?UINT32(Recipient),
+ ?UINT32(Len), RequestType:Len/binary,
+ ?BYTE(Bool), Data/binary>>) ->
+ #ssh_msg_channel_request{
+ recipient_channel = Recipient,
+ request_type = unicode:characters_to_list(RequestType),
+ want_reply = erl_boolean(Bool),
+ data = Data
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_SUCCESS), ?UINT32(Recipient)>>) ->
+ #ssh_msg_channel_success{
+ recipient_channel = Recipient
+ };
+decode(<<?BYTE(?SSH_MSG_CHANNEL_FAILURE), ?UINT32(Recipient)>>) ->
+ #ssh_msg_channel_failure{
+ recipient_channel = Recipient
+ };
+
+%%% Auth Messages
+decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST),
+ ?UINT32(Len0), User:Len0/binary,
+ ?UINT32(Len1), Service:Len1/binary,
+ ?UINT32(Len2), Method:Len2/binary,
+ Data/binary>>) ->
+ #ssh_msg_userauth_request{
+ user = unicode:characters_to_list(User),
+ service = unicode:characters_to_list(Service),
+ method = unicode:characters_to_list(Method),
+ data = Data
+ };
+
+decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE),
+ ?UINT32(Len0), Auths:Len0/binary,
+ ?BYTE(Bool)>>) ->
+ #ssh_msg_userauth_failure {
+ authentications = unicode:characters_to_list(Auths),
+ partial_success = erl_boolean(Bool)
+ };
+
+decode(<<?BYTE(?SSH_MSG_USERAUTH_SUCCESS)>>) ->
+ #ssh_msg_userauth_success{};
+
+decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER),
+ ?UINT32(Len0), Banner:Len0/binary,
+ ?UINT32(Len1), Lang:Len1/binary>>) ->
+ #ssh_msg_userauth_banner{
+ message = Banner,
+ language = Lang
+ };
+
+decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
+ #ssh_msg_userauth_pk_ok{
+ algorithm_name = Alg,
+ key_blob = KeyBlob
+ };
+
+decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/binary,
+ ?UINT32(Len1), Lang:Len1/binary>>) ->
+ #ssh_msg_userauth_passwd_changereq{
+ prompt = Prompt,
+ languge = Lang
+ };
+decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
+ ?UINT32(Len1), Inst:Len1/binary, ?UINT32(Len2), Lang:Len2/binary,
+ ?UINT32(NumPromtps), Data/binary>>) ->
+ #ssh_msg_userauth_info_request{
+ name = Name,
+ instruction = Inst,
+ language_tag = Lang,
+ num_prompts = NumPromtps,
+ data = Data};
+
+decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
+ #ssh_msg_userauth_info_response{
+ num_responses = Num,
+ data = Data};
+
+%%% Keyexchange messages
+decode(<<?BYTE(?SSH_MSG_KEXINIT), Cookie:128, Data/binary>>) ->
+ decode_kex_init(Data, [Cookie, ssh_msg_kexinit], 10);
+
+decode(<<?BYTE(?SSH_MSG_KEXDH_INIT), ?UINT32(Len), E:Len/binary>>) ->
+ #ssh_msg_kexdh_init{e = erlint(Len, E)
+ };
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST), ?UINT32(Min), ?UINT32(N), ?UINT32(Max)>>) ->
+ #ssh_msg_kex_dh_gex_request{
+ min = Min,
+ n = N,
+ max = Max
+ };
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?UINT32(N)>>) ->
+ #ssh_msg_kex_dh_gex_request_old{
+ n = N
+ };
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP), ?UINT32(Len0), Prime:Len0/big-signed-integer,
+ ?UINT32(Len1), Generator:Len1/big-signed-integer>>) ->
+ #ssh_msg_kex_dh_gex_group{
+ p = Prime,
+ g = Generator
+ };
+decode(<<?BYTE(?SSH_MSG_KEXDH_REPLY), ?UINT32(Len0), Key:Len0/binary,
+ ?UINT32(Len1), F:Len1/binary,
+ ?UINT32(Len2), Hashsign:Len2/binary>>) ->
+ #ssh_msg_kexdh_reply{
+ public_host_key = decode_host_key(Key),
+ f = erlint(Len1, F),
+ h_sig = decode_sign(Hashsign)
+ };
+
+decode(<<?SSH_MSG_SERVICE_REQUEST, ?UINT32(Len0), Service:Len0/binary>>) ->
+ #ssh_msg_service_request{
+ name = unicode:characters_to_list(Service)
+ };
+
+decode(<<?SSH_MSG_SERVICE_ACCEPT, ?UINT32(Len0), Service:Len0/binary>>) ->
+ #ssh_msg_service_accept{
+ name = unicode:characters_to_list(Service)
+ };
+
+decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
+ ?UINT32(Len0), Desc:Len0/binary, ?UINT32(Len1), Lang:Len1/binary>>) ->
+ #ssh_msg_disconnect{
+ code = Code,
+ description = unicode:characters_to_list(Desc),
+ language = Lang
+ };
+
+decode(<<?SSH_MSG_NEWKEYS>>) ->
+ #ssh_msg_newkeys{};
+
+decode(<<?BYTE(?SSH_MSG_IGNORE), Data/binary>>) ->
+ #ssh_msg_ignore{data = Data};
+
+decode(<<?BYTE(?SSH_MSG_UNIMPLEMENTED), ?UINT32(Seq)>>) ->
+ #ssh_msg_unimplemented{sequence = Seq};
+
+decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bool), ?UINT32(Len0), Msg:Len0/binary,
+ ?UINT32(Len1), Lang:Len1/binary>>) ->
+ #ssh_msg_debug{always_display = erl_boolean(Bool),
+ message = Msg,
+ language = Lang}.
+
+decode_keyboard_interactive_prompts(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_keyboard_interactive_prompts(<<?UINT32(Len), Prompt:Len/binary, ?BYTE(Bool), Bin/binary>>,
+ Acc) ->
+ decode_keyboard_interactive_prompts(Bin, [{Prompt, erl_boolean(Bool)} | Acc]).
+
+erl_boolean(0) ->
+ false;
+erl_boolean(1) ->
+ true.
+
+decode_kex_init(<<?BYTE(Bool), ?UINT32(X)>>, Acc, 0) ->
+ list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
+decode_kex_init(<<?UINT32(Len), Data:Len/binary, Rest/binary>>, Acc, N) ->
+ Names = string:tokens(unicode:characters_to_list(Data), ","),
+ decode_kex_init(Rest, [Names | Acc], N -1).
+
+erlint(MPIntSize, MPIntValue) ->
+ Bits = MPIntSize * 8,
+ <<Integer:Bits/integer>> = MPIntValue,
+ Integer.
+
+decode_sign(<<?UINT32(Len), _Alg:Len/binary, ?UINT32(_), Signature/binary>>) ->
+ Signature.
+
+decode_host_key(<<?UINT32(Len), Alg:Len/binary, Rest/binary>>) ->
+ decode_host_key(Alg, Rest).
+
+decode_host_key(<<"ssh-rsa">>, <<?UINT32(Len0), E:Len0/binary,
+ ?UINT32(Len1), N:Len1/binary>>) ->
+ #'RSAPublicKey'{publicExponent = erlint(Len0, E),
+ modulus = erlint(Len1, N)};
+
+decode_host_key(<<"ssh-dss">>,
+ <<?UINT32(Len0), P:Len0/binary,
+ ?UINT32(Len1), Q:Len1/binary,
+ ?UINT32(Len2), G:Len2/binary,
+ ?UINT32(Len3), Y:Len3/binary>>) ->
+ {erlint(Len3, Y), #'Dss-Parms'{p = erlint(Len0, P), q = erlint(Len1, Q),
+ g = erlint(Len2, G)}}.
+
+encode_host_key(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
+ ssh_bits:encode(["ssh-rsa", E, N], [string, mpint, mpint]);
+encode_host_key({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
+ ssh_bits:encode(["ssh-dss", P, Q, G, Y],
+ [string, mpint, mpint, mpint, mpint]);
+encode_host_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
+ ssh_bits:encode(["ssh-rsa", E, N], [string, mpint, mpint]);
+encode_host_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
+ ssh_bits:encode(["ssh-dss", P, Q, G, Y],
+ [string, mpint, mpint, mpint, mpint]).
+encode_sign(#'RSAPrivateKey'{}, Signature) ->
+ ssh_bits:encode(["ssh-rsa", Signature],[string, binary]);
+encode_sign(#'DSAPrivateKey'{}, Signature) ->
+ ssh_bits:encode(["ssh-dss", Signature],[string, binary]).
diff --git a/lib/ssh/src/ssh_no_io.erl b/lib/ssh/src/ssh_no_io.erl
index 2c8dd92ee2..825a0d4af5 100644
--- a/lib/ssh/src/ssh_no_io.erl
+++ b/lib/ssh/src/ssh_no_io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,18 +22,31 @@
%%% Description: ssh_io replacement that throws on everything
-module(ssh_no_io).
-
--export([yes_no/1, read_password/1, read_line/1, format/2]).
-
-yes_no(_Prompt) ->
- throw({no_io_allowed, yes_no}).
-
-read_password(_Prompt) ->
- throw({no_io_allowed, read_password}).
-
-read_line(_Prompt) ->
- throw({no_io_allowed, read_line}).
-
-format(_Fmt, _Args) ->
- throw({no_io_allowed, format}).
+-include("ssh_transport.hrl").
+
+-export([yes_no/2, read_password/2, read_line/2, format/2]).
+
+yes_no(_, _) ->
+ throw({{no_io_allowed, yes_no},
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed",
+ language = "en"}}).
+
+read_password(_, _) ->
+ throw({{no_io_allowed, read_password},
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed",
+ language = "en"}}).
+
+read_line(_, _) ->
+ throw({{no_io_allowed, read_line},
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed",
+ language = "en"}} ).
+
+format(_, _) ->
+ throw({{no_io_allowed, format},
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction is not allowed",
+ language = "en"}}).
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index 3d469d3c6e..174ca0126b 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -76,7 +76,7 @@ listen(Addr, Port, Options) ->
%% Description: Stops the listener
%%--------------------------------------------------------------------
stop(Pid) ->
- ssh_cli:stop(Pid).
+ ssh:stop_listener(Pid).
%%% DEPRECATED END %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index cd6defd535..e8855b09ac 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,9 @@
-behaviour(supervisor).
--export([start_link/1, connection_supervisor/1, channel_supervisor/1
+-export([start_link/1,
+ connection_supervisor/1,
+ channel_supervisor/1
]).
%% Supervisor callback
@@ -61,9 +63,9 @@ init([Opts]) ->
child_specs(Opts) ->
case proplists:get_value(role, Opts) of
client ->
- [ssh_connectinon_child_spec(Opts)];
+ [];
server ->
- [ssh_connectinon_child_spec(Opts), ssh_channel_child_spec(Opts)]
+ [ssh_channel_child_spec(Opts), ssh_connectinon_child_spec(Opts)]
end.
ssh_connectinon_child_spec(Opts) ->
@@ -72,9 +74,9 @@ ssh_connectinon_child_spec(Opts) ->
Role = proplists:get_value(role, Opts),
Name = id(Role, ssh_connection_sup, Address, Port),
StartFunc = {ssh_connection_sup, start_link, [Opts]},
- Restart = transient,
+ Restart = temporary,
Shutdown = 5000,
- Modules = [ssh_connection_sup],
+ Modules = [ssh_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
@@ -84,7 +86,7 @@ ssh_channel_child_spec(Opts) ->
Role = proplists:get_value(role, Opts),
Name = id(Role, ssh_channel_sup, Address, Port),
StartFunc = {ssh_channel_sup, start_link, [Opts]},
- Restart = transient,
+ Restart = temporary,
Shutdown = infinity,
Modules = [ssh_channel_sup],
Type = supervisor,
diff --git a/lib/ssh/src/ssh_sup.erl b/lib/ssh/src/ssh_sup.erl
index f307d1f833..6d2b9c107d 100644
--- a/lib/ssh/src/ssh_sup.erl
+++ b/lib/ssh/src/ssh_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -51,8 +51,7 @@ children() ->
Clients = [Service || Service <- Services, is_client(Service)],
Servers = [Service || Service <- Services, is_server(Service)],
- [server_child_spec(Servers), client_child_spec(Clients),
- ssh_userauth_reg_spec()].
+ [server_child_spec(Servers), client_child_spec(Clients)].
server_child_spec(Servers) ->
Name = sshd_sup,
@@ -72,16 +71,6 @@ client_child_spec(Clients) ->
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-ssh_userauth_reg_spec() ->
- Name = ssh_userreg,
- StartFunc = {ssh_userreg, start_link, []},
- Restart = transient,
- Shutdown = 5000,
- Modules = [ssh_userreg],
- Type = worker,
- {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
-
is_server({sftpd, _}) ->
true;
is_server({shelld, _}) ->
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 36daf3b1ac..848133f838 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,7 @@
-export([init/1]).
%%%=========================================================================
-%%% API
+%%% Internal API
%%%=========================================================================
start_link(ServerOpts) ->
Address = proplists:get_value(address, ServerOpts),
@@ -54,13 +54,15 @@ stop_listener(SysSup) ->
stop_listener(Address, Port) ->
Name = make_name(Address, Port),
stop_acceptor(whereis(Name)).
-
+
stop_system(SysSup) ->
Name = sshd_sup:system_name(SysSup),
- sshd_sup:stop_child(Name).
-
+ spawn(fun() -> sshd_sup:stop_child(Name) end),
+ ok.
+
stop_system(Address, Port) ->
- sshd_sup:stop_child(Address, Port).
+ spawn(fun() -> sshd_sup:stop_child(Address, Port) end),
+ ok.
system_supervisor(Address, Port) ->
Name = make_name(Address, Port),
@@ -121,7 +123,7 @@ restart_acceptor(Address, Port) ->
%%%=========================================================================
init([ServerOpts]) ->
RestartStrategy = one_for_one,
- MaxR = 10,
+ MaxR = 0,
MaxT = 3600,
Children = child_specs(ServerOpts),
{ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
@@ -137,7 +139,7 @@ ssh_acceptor_child_spec(ServerOpts) ->
Port = proplists:get_value(port, ServerOpts),
Name = id(ssh_acceptor_sup, Address, Port),
StartFunc = {ssh_acceptor_sup, start_link, [ServerOpts]},
- Restart = permanent,
+ Restart = transient,
Shutdown = infinity,
Modules = [ssh_acceptor_sup],
Type = supervisor,
@@ -146,7 +148,7 @@ ssh_acceptor_child_spec(ServerOpts) ->
ssh_subsystem_child_spec(ServerOpts) ->
Name = make_ref(),
StartFunc = {ssh_subsystem_sup, start_link, [ServerOpts]},
- Restart = transient,
+ Restart = temporary,
Shutdown = infinity,
Modules = [ssh_subsystem_sup],
Type = supervisor,
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 682d766d99..27723dc870 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -29,12 +29,12 @@
-include("ssh_transport.hrl").
-include("ssh.hrl").
--export([connect/5, accept/4]).
-export([versions/2, hello_version_msg/1]).
-export([next_seqnum/1, decrypt_first_block/2, decrypt_blocks/3,
- is_valid_mac/3, transport_messages/1, kexdh_messages/0,
- kex_dh_gex_messages/0, handle_hello_version/1,
- key_exchange_init_msg/1, key_init/3, new_keys_message/1,
+ is_valid_mac/3,
+ handle_hello_version/1,
+ key_exchange_init_msg/1,
+ key_init/3, new_keys_message/1,
handle_kexinit_msg/3, handle_kexdh_init/2,
handle_kex_dh_gex_group/2, handle_kex_dh_gex_reply/2,
handle_new_keys/2, handle_kex_dh_gex_request/2,
@@ -74,113 +74,9 @@ is_valid_mac(Mac, Data, #ssh{recv_mac = Algorithm,
recv_mac_key = Key, recv_sequence = SeqNum}) ->
Mac == mac(Algorithm, Key, SeqNum, Data).
-transport_messages(_) ->
- [{ssh_msg_disconnect, ?SSH_MSG_DISCONNECT,
- [uint32, string, string]},
-
- {ssh_msg_ignore, ?SSH_MSG_IGNORE,
- [string]},
-
- {ssh_msg_unimplemented, ?SSH_MSG_UNIMPLEMENTED,
- [uint32]},
-
- {ssh_msg_debug, ?SSH_MSG_DEBUG,
- [boolean, string, string]},
-
- {ssh_msg_service_request, ?SSH_MSG_SERVICE_REQUEST,
- [string]},
-
- {ssh_msg_service_accept, ?SSH_MSG_SERVICE_ACCEPT,
- [string]},
-
- {ssh_msg_kexinit, ?SSH_MSG_KEXINIT,
- [cookie,
- name_list, name_list,
- name_list, name_list,
- name_list, name_list,
- name_list, name_list,
- name_list, name_list,
- boolean,
- uint32]},
-
- {ssh_msg_newkeys, ?SSH_MSG_NEWKEYS,
- []}
- ].
-
-kexdh_messages() ->
- [{ssh_msg_kexdh_init, ?SSH_MSG_KEXDH_INIT,
- [mpint]},
-
- {ssh_msg_kexdh_reply, ?SSH_MSG_KEXDH_REPLY,
- [binary, mpint, binary]}
- ].
-
-kex_dh_gex_messages() ->
- [{ssh_msg_kex_dh_gex_request, ?SSH_MSG_KEX_DH_GEX_REQUEST,
- [uint32, uint32, uint32]},
-
- {ssh_msg_kex_dh_gex_request_old, ?SSH_MSG_KEX_DH_GEX_REQUEST_OLD,
- [uint32]},
-
- {ssh_msg_kex_dh_gex_group, ?SSH_MSG_KEX_DH_GEX_GROUP,
- [mpint, mpint]},
-
- {ssh_msg_kex_dh_gex_init, ?SSH_MSG_KEX_DH_GEX_INIT,
- [mpint]},
-
- {ssh_msg_kex_dh_gex_reply, ?SSH_MSG_KEX_DH_GEX_REPLY,
- [binary, mpint, binary]}
- ].
-
yes_no(Ssh, Prompt) ->
(Ssh#ssh.io_cb):yes_no(Prompt, Ssh).
-connect(ConnectionSup, Address, Port, SocketOpts, Opts) ->
- Timeout = proplists:get_value(connect_timeout, Opts, infinity),
- {_, Callback, _} =
- proplists:get_value(transport, Opts, {tcp, gen_tcp, tcp_closed}),
- case do_connect(Callback, Address, Port, SocketOpts, Timeout) of
- {ok, Socket} ->
- {ok, Pid} =
- ssh_connection_sup:start_handler_child(ConnectionSup,
- [client, Socket,
- [{address, Address},
- {port, Port} |
- Opts]]),
- Callback:controlling_process(Socket, Pid),
- ssh_connection_handler:send_event(Pid, socket_control),
- {ok, Pid};
- {error, Reason} ->
- {error, Reason}
- end.
-
-do_connect(Callback, Address, Port, SocketOpts, Timeout) ->
- Opts = [{active, false} | SocketOpts],
- case Callback:connect(Address, Port, Opts, Timeout) of
- {error, nxdomain} ->
- Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout);
- {error, eafnosupport} ->
- Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout);
- {error, enetunreach} ->
- Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout);
- Other ->
- Other
- end.
-
-accept(Address, Port, Socket, Options) ->
- {_, Callback, _} =
- proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
- ConnectionSup =
- ssh_system_sup:connection_supervisor(
- ssh_system_sup:system_supervisor(Address, Port)),
- {ok, Pid} =
- ssh_connection_sup:start_handler_child(ConnectionSup,
- [server, Socket,
- [{address, Address},
- {port, Port} | Options]]),
- Callback:controlling_process(Socket, Pid),
- {ok, Pid}.
-
format_version({Major,Minor}) ->
"SSH-" ++ integer_to_list(Major) ++ "." ++
integer_to_list(Minor) ++ "-Erlang".
@@ -257,7 +153,6 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
{ok, Algoritms} = select_algorithm(client, Own, CounterPart),
case verify_algorithm(Algoritms) of
true ->
- install_messages(Algoritms#alg.kex),
key_exchange_first_msg(Algoritms#alg.kex,
Ssh0#ssh{algorithms = Algoritms});
_ ->
@@ -271,7 +166,6 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
#ssh{role = server} = Ssh) ->
{ok, Algoritms} = select_algorithm(server, CounterPart, Own),
- install_messages(Algoritms#alg.kex),
{ok, Ssh#ssh{algorithms = Algoritms}}.
@@ -284,11 +178,6 @@ verify_algorithm(#alg{kex = 'diffie-hellman-group-exchange-sha1'}) ->
verify_algorithm(_) ->
false.
-install_messages('diffie-hellman-group1-sha1') ->
- ssh_bits:install_messages(kexdh_messages());
-install_messages('diffie-hellman-group-exchange-sha1') ->
- ssh_bits:install_messages(kex_dh_gex_messages()).
-
key_exchange_first_msg('diffie-hellman-group1-sha1', Ssh0) ->
{G, P} = dh_group1(),
{Private, Public} = dh_gen_key(G, P, 1024),
@@ -312,10 +201,10 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) ->
{G, P} = dh_group1(),
{Private, Public} = dh_gen_key(G, P, 1024),
K = ssh_math:ipow(E, Private, P),
- {Key, K_S} = get_host_key(Ssh0),
- H = kex_h(Ssh0, K_S, E, Public, K),
+ Key = get_host_key(Ssh0),
+ H = kex_h(Ssh0, Key, E, Public, K),
H_SIG = sign_host_key(Ssh0, Key, H),
- {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_reply{public_host_key = K_S,
+ {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_reply{public_host_key = Key,
f = Public,
h_sig = H_SIG
}, Ssh0),
@@ -411,65 +300,33 @@ get_host_key(SSH) ->
#ssh{key_cb = Mod, opts = Opts, algorithms = ALG} = SSH,
case Mod:host_key(ALG#alg.hkey, Opts) of
- {ok, #'RSAPrivateKey'{modulus = N, publicExponent = E} = Key} ->
- {Key,
- ssh_bits:encode(["ssh-rsa",E,N],[string,mpint,mpint])};
- {ok, #'DSAPrivateKey'{y = Y, p = P, q = Q, g = G} = Key} ->
- {Key, ssh_bits:encode(["ssh-dss",P,Q,G,Y],
- [string,mpint,mpint,mpint,mpint])};
+ {ok, #'RSAPrivateKey'{} = Key} ->
+ Key;
+ {ok, #'DSAPrivateKey'{} = Key} ->
+ Key;
Result ->
exit({error, {Result, unsupported_key_type}})
end.
sign_host_key(_Ssh, #'RSAPrivateKey'{} = Private, H) ->
Hash = sha, %% Option ?!
- Signature = sign(H, Hash, Private),
- ssh_bits:encode(["ssh-rsa", Signature],[string, binary]);
+ _Signature = sign(H, Hash, Private);
sign_host_key(_Ssh, #'DSAPrivateKey'{} = Private, H) ->
Hash = sha, %% Option ?!
- RawSignature = sign(H, Hash, Private),
- ssh_bits:encode(["ssh-dss", RawSignature],[string, binary]).
+ _RawSignature = sign(H, Hash, Private).
-verify_host_key(SSH, K_S, H, H_SIG) ->
- ALG = SSH#ssh.algorithms,
- case ALG#alg.hkey of
- 'ssh-rsa' ->
- verify_host_key_rsa(SSH, K_S, H, H_SIG);
- 'ssh-dss' ->
- verify_host_key_dss(SSH, K_S, H, H_SIG);
- _ ->
- {error, bad_host_key_algorithm}
- end.
-
-verify_host_key_rsa(SSH, K_S, H, H_SIG) ->
- case ssh_bits:decode(K_S,[string,mpint,mpint]) of
- ["ssh-rsa", E, N] ->
- ["ssh-rsa",SIG] = ssh_bits:decode(H_SIG,[string,binary]),
- Public = #'RSAPublicKey'{publicExponent = E, modulus = N},
- case verify(H, sha, SIG, Public) of
- false ->
- {error, bad_signature};
- true ->
- known_host_key(SSH, Public, 'ssh-rsa')
- end;
- _ ->
- {error, bad_format}
+verify_host_key(SSH, PublicKey, Digest, Signature) ->
+ case verify(Digest, sha, Signature, PublicKey) of
+ false ->
+ {error, bad_signature};
+ true ->
+ known_host_key(SSH, PublicKey, public_algo(PublicKey))
end.
-verify_host_key_dss(SSH, K_S, H, H_SIG) ->
- case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of
- ["ssh-dss",P,Q,G,Y] ->
- ["ssh-dss",SIG] = ssh_bits:decode(H_SIG,[string,binary]),
- Public = {Y, #'Dss-Parms'{p = P, q = Q, g = G}},
- case verify(H, sha, SIG, Public) of
- false ->
- {error, bad_signature};
- true ->
- known_host_key(SSH, Public, 'ssh-dss')
- end;
- _ ->
- {error, bad_host_key_format}
- end.
+public_algo(#'RSAPublicKey'{}) ->
+ 'ssh-rsa';
+public_algo({_, #'Dss-Parms'{}}) ->
+ 'ssh-dss'.
accepted_host(Ssh, PeerName, Opts) ->
case proplists:get_value(silently_accept_hosts, Opts, false) of
@@ -636,12 +493,12 @@ select(CL, SL) ->
C.
ssh_packet(#ssh_msg_kexinit{} = Msg, Ssh0) ->
- BinMsg = ssh_bits:encode(Msg),
+ BinMsg = ssh_message:encode(Msg),
Ssh = key_init(Ssh0#ssh.role, Ssh0, BinMsg),
pack(BinMsg, Ssh);
ssh_packet(Msg, Ssh) ->
- BinMsg = ssh_bits:encode(Msg),
+ BinMsg = ssh_message:encode(Msg),
pack(BinMsg, Ssh).
pack(Data0, #ssh{encrypt_block_size = BlockSize,
@@ -1021,23 +878,23 @@ hash(K, H, Ki, N, HASH) ->
Kj = HASH([K, H, Ki]),
hash(K, H, <<Ki/binary, Kj/binary>>, N-128, HASH).
-kex_h(SSH, K_S, E, F, K) ->
+kex_h(SSH, Key, E, F, K) ->
L = ssh_bits:encode([SSH#ssh.c_version, SSH#ssh.s_version,
SSH#ssh.c_keyinit, SSH#ssh.s_keyinit,
- K_S, E,F,K],
+ ssh_message:encode_host_key(Key), E,F,K],
[string,string,binary,binary,binary,
mpint,mpint,mpint]),
crypto:hash(sha,L).
-kex_h(SSH, K_S, Min, NBits, Max, Prime, Gen, E, F, K) ->
+kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) ->
L = if Min==-1; Max==-1 ->
Ts = [string,string,binary,binary,binary,
uint32,
mpint,mpint,mpint,mpint,mpint],
ssh_bits:encode([SSH#ssh.c_version,SSH#ssh.s_version,
SSH#ssh.c_keyinit,SSH#ssh.s_keyinit,
- K_S, NBits, Prime, Gen, E,F,K],
+ ssh_message:encode_host_key(Key), NBits, Prime, Gen, E,F,K],
Ts);
true ->
Ts = [string,string,binary,binary,binary,
@@ -1045,7 +902,7 @@ kex_h(SSH, K_S, Min, NBits, Max, Prime, Gen, E, F, K) ->
mpint,mpint,mpint,mpint,mpint],
ssh_bits:encode([SSH#ssh.c_version,SSH#ssh.s_version,
SSH#ssh.c_keyinit,SSH#ssh.s_keyinit,
- K_S, Min, NBits, Max,
+ ssh_message:encode_host_key(Key), Min, NBits, Max,
Prime, Gen, E,F,K], Ts)
end,
crypto:hash(sha,L).
diff --git a/lib/ssh/src/ssh_userreg.erl b/lib/ssh/src/ssh_userreg.erl
deleted file mode 100644
index f901461aea..0000000000
--- a/lib/ssh/src/ssh_userreg.erl
+++ /dev/null
@@ -1,141 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%% Description: User register for ssh_cli
-
--module(ssh_userreg).
-
--behaviour(gen_server).
-
-%% API
--export([start_link/0,
- register_user/2,
- lookup_user/1,
- delete_user/1]).
-
-%% gen_server callbacks
--export([init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3]).
-
--record(state, {user_db = []}).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-register_user(User, Cm) ->
- gen_server:cast(?MODULE, {register, {User, Cm}}).
-
-delete_user(Cm) ->
- gen_server:cast(?MODULE, {delete, Cm}).
-
-lookup_user(Cm) ->
- gen_server:call(?MODULE, {get_user, Cm}, infinity).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([]) ->
- {ok, #state{}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call({get_user, Cm}, _From, #state{user_db = Db} = State) ->
- User = lookup(Cm, Db),
- {reply, {ok, User}, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast({register, UserCm}, State) ->
- {noreply, insert(UserCm, State)};
-handle_cast({delete, UserCm}, State) ->
- {noreply, delete(UserCm, State)}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, _State) ->
- ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-insert({User, Cm}, #state{user_db = Db} = State) ->
- State#state{user_db = [{User, Cm} | Db]}.
-
-delete(Cm, #state{user_db = Db} = State) ->
- State#state{user_db = lists:keydelete(Cm, 2, Db)}.
-
-lookup(_, []) ->
- undefined;
-lookup(Cm, [{User, Cm} | _Rest]) ->
- User;
-lookup(Cm, [_ | Rest]) ->
- lookup(Cm, Rest).
-
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index b299868d41..e18e18a9a9 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -267,7 +267,7 @@ xf_request(XF, Op, Arg) ->
list_to_binary(Arg)
end,
Size = 1+size(Data),
- ssh_connection:send(CM, Channel, <<?UINT32(Size), Op, Data/binary>>).
+ ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]).
xf_send_reply(#ssh_xfer{cm = CM, channel = Channel}, Op, Arg) ->
Data = if
@@ -277,7 +277,7 @@ xf_send_reply(#ssh_xfer{cm = CM, channel = Channel}, Op, Arg) ->
list_to_binary(Arg)
end,
Size = 1 + size(Data),
- ssh_connection:send(CM, Channel, <<?UINT32(Size), Op, Data/binary>>).
+ ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]).
xf_send_name(XF, ReqId, Name, Attr) ->
xf_send_names(XF, ReqId, [{Name, Attr}]).
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 1d2779de23..e6b4b681a4 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -61,9 +61,9 @@ init(Args) ->
%%%=========================================================================
child_spec(_) ->
Name = undefined, % As simple_one_for_one is used.
- StartFunc = {ssh_connection_sup, start_link, []},
+ StartFunc = {ssh_connection_handler, start_link, []},
Restart = temporary,
Shutdown = infinity,
- Modules = [ssh_connection_sup],
+ Modules = [ssh_connection_handler],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index 747906b2cf..60222f5172 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -58,12 +58,7 @@ start_child(ServerOpts) ->
end.
stop_child(Name) ->
- case supervisor:terminate_child(?MODULE, Name) of
- ok ->
- supervisor:delete_child(?MODULE, Name);
- Error ->
- Error
- end.
+ supervisor:terminate_child(?MODULE, Name).
stop_child(Address, Port) ->
Name = id(Address, Port),
@@ -94,7 +89,7 @@ init([Servers]) ->
child_spec(Address, Port, ServerOpts) ->
Name = id(Address, Port),
StartFunc = {ssh_system_sup, start_link, [ServerOpts]},
- Restart = transient,
+ Restart = temporary,
Shutdown = infinity,
Modules = [ssh_system_sup],
Type = supervisor,
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 13caafc055..740dbd0235 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -39,7 +39,8 @@ MODULES= \
ssh_sftpd_erlclient_SUITE \
ssh_connection_SUITE \
ssh_echo_server \
- ssh_peername_sockname_server
+ ssh_peername_sockname_server \
+ ssh_test_cli
HRL_FILES_NEEDED_IN_TEST= \
$(ERL_TOP)/lib/ssh/src/ssh.hrl \
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index e8f1d5213c..b4e3871efd 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -46,17 +46,21 @@ all() ->
daemon_already_started,
server_password_option,
server_userpassword_option,
- close].
+ double_close].
groups() ->
- [{dsa_key, [], [send,
- peername_sockname,
- exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
- {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
+ [{dsa_key, [], basic_tests()},
+ {rsa_key, [], basic_tests()},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{internal_error, [], [internal_error]}
].
+
+basic_tests() ->
+ [send, close, peername_sockname,
+ exec, exec_compressed, shell, cli, known_hosts,
+ idle_time, rekey, openssh_zlib_basic_test].
+
%%--------------------------------------------------------------------
init_per_suite(Config) ->
case catch crypto:start() of
@@ -255,7 +259,7 @@ idle_time(Config) ->
ssh_connection:close(ConnectionRef, Id),
receive
after 10000 ->
- {error,channel_closed} = ssh_connection:session_channel(ConnectionRef, 1000)
+ {error, closed} = ssh_connection:session_channel(ConnectionRef, 1000)
end,
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -303,6 +307,41 @@ shell(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
+cli() ->
+ [{doc, ""}].
+cli(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir},
+ {password, "morot"},
+ {ssh_cli, {ssh_test_cli, [cli]}},
+ {subsystems, []},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ct:sleep(500),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ssh_connection:shell(ConnectionRef, ChannelId),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ receive
+ {ssh_cm, ConnectionRef,
+ {data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ end,
+
+ receive
+ {ssh_cm, ConnectionRef,{closed, ChannelId}} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
daemon_already_started() ->
[{doc, "Test that get correct error message if you try to start a daemon",
"on an adress that already runs a daemon see also seq10667"}].
@@ -448,10 +487,11 @@ internal_error(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{failfun, fun ssh_test_lib:failfun/2}]),
- {error,"Internal error"} =
+ {error, Error} =
ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
{user_interaction, false}]),
+ check_error(Error),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -477,7 +517,7 @@ send(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
peername_sockname() ->
- [{doc, "Test ssh:peername/1 and ssh:sockname/1"}].
+ [{doc, "Test ssh:connection_info([peername, sockname])"}].
peername_sockname(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -495,13 +535,17 @@ peername_sockname(Config) when is_list(Config) ->
{user_interaction, false}]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:subsystem(ConnectionRef, ChannelId, "peername_sockname", infinity),
- {ok,{HostPeerClient,PortPeerClient}} = ssh:peername(ConnectionRef),
- {ok,{HostSockClient,PortSockClient}} = ssh:sockname(ConnectionRef),
+ [{peer, {_Name, {HostPeerClient,PortPeerClient} = ClientPeer}}] =
+ ssh:connection_info(ConnectionRef, [peer]),
+ [{sockname, {HostSockClient,PortSockClient} = ClientSock}] =
+ ssh:connection_info(ConnectionRef, [sockname]),
+ ct:pal("Client: ~p ~p", [ClientPeer, ClientSock]),
receive
{ssh_cm, ConnectionRef, {data, ChannelId, _, Response}} ->
{PeerNameSrv,SockNameSrv} = binary_to_term(Response),
- {ok,{HostPeerSrv,PortPeerSrv}} = PeerNameSrv,
- {ok,{HostSockSrv,PortSockSrv}} = SockNameSrv,
+ {HostPeerSrv,PortPeerSrv} = PeerNameSrv,
+ {HostSockSrv,PortSockSrv} = SockNameSrv,
+ ct:pal("Server: ~p ~p", [PeerNameSrv, SockNameSrv]),
host_equal(HostPeerSrv, HostSockClient),
PortPeerSrv = PortSockClient,
host_equal(HostSockSrv, HostPeerClient),
@@ -522,9 +566,35 @@ ips(Name) when is_list(Name) ->
ordsets:from_list(IPs4++IPs6).
%%--------------------------------------------------------------------
+
close() ->
- [{doc, "Simulate that we try to close an already closed connection"}].
+ [{doc, "Client receives close when server closes"}].
close(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ Client =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(Client, infinity),
+
+ ssh:stop_daemon(Server),
+ receive
+ {ssh_cm, Client,{closed, ChannelId}} ->
+ ok
+ after 5000 ->
+ ct:fail(timeout)
+ end.
+
+%%--------------------------------------------------------------------
+double_close() ->
+ [{doc, "Simulate that we try to close an already closed connection"}].
+double_close(Config) when is_list(Config) ->
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
@@ -543,6 +613,8 @@ close(Config) when is_list(Config) ->
exit(CM, {shutdown, normal}),
ok = ssh:close(CM).
+%%--------------------------------------------------------------------
+
openssh_zlib_basic_test() ->
[{doc, "Test basic connection with openssh_zlib"}].
openssh_zlib_basic_test(Config) ->
@@ -564,6 +636,15 @@ openssh_zlib_basic_test(Config) ->
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
+%% Due to timing the error message may or may not be delivered to
+%% the "tcp-application" before the socket closed message is recived
+check_error("Internal error") ->
+ ok;
+check_error("Connection closed") ->
+ ok;
+check_error(Error) ->
+ ct:fail(Error).
+
basic_test(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 6c781e0e91..f4f0682b40 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -73,6 +73,9 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
+ %% To make sure we start clean as it is not certain that
+ %% end_per_testcase will be run!
+ ssh:stop(),
ssh:start(),
Config.
@@ -91,7 +94,6 @@ simple_exec(Config) when is_list(Config) ->
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"echo testing", infinity),
-
%% receive response to input
receive
{ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} ->
@@ -146,7 +148,6 @@ small_cat(Config) when is_list(Config) ->
{ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
ok
end.
-
%%--------------------------------------------------------------------
big_cat() ->
[{doc,"Use 'cat' to echo large data block back to us."}].
@@ -204,37 +205,33 @@ send_after_exit(Config) when is_list(Config) ->
ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
{user_interaction, false}]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ Data = <<"I like spaghetti squash">>,
%% Shell command "false" will exit immediately
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"false", infinity),
-
- timer:sleep(2000), %% Allow incoming eof/close/exit_status ssh messages to be processed
-
- Data = <<"I like spaghetti squash">>,
- case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of
- {error, closed} -> ok;
- ok ->
- ct:fail({expected,{error,closed}});
- {error, timeout} ->
- ct:fail({expected,{error,closed}});
- Else ->
- ct:fail(Else)
- end,
-
- %% receive close messages
receive
{ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
ok
end,
receive
- {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _}} ->
+ {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _ExitStatus}} ->
ok
end,
receive
{ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
ok
+ end,
+ case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of
+ {error, closed} -> ok;
+ ok ->
+ ct:fail({expected,{error,closed}, {got, ok}});
+ {error, timeout} ->
+ ct:fail({expected,{error,closed}, {got, {error, timeout}}});
+ Else ->
+ ct:fail(Else)
end.
+
%%--------------------------------------------------------------------
interrupted_send() ->
[{doc, "Use a subsystem that echos n char and then sends eof to cause a channel exit partway through a large send."}].
diff --git a/lib/ssh/test/ssh_peername_sockname_server.erl b/lib/ssh/test/ssh_peername_sockname_server.erl
index 7664f3ee25..bc505695d3 100644
--- a/lib/ssh/test/ssh_peername_sockname_server.erl
+++ b/lib/ssh/test/ssh_peername_sockname_server.erl
@@ -34,12 +34,10 @@ init([]) ->
{ok, #state{}}.
handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ [{peer, {_Name, Peer}}] = ssh:connection_info(ConnectionManager, [peer]),
+ [{sockname, Sock}] = ssh:connection_info(ConnectionManager, [sockname]),
ssh_connection:send(ConnectionManager, ChannelId,
- term_to_binary(
- {catch ssh:peername(ConnectionManager),
- catch ssh:sockname(ConnectionManager)
- })
- ),
+ term_to_binary({Peer, Sock})),
{ok, State}.
handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}},
diff --git a/lib/ssh/test/ssh_test_cli.erl b/lib/ssh/test/ssh_test_cli.erl
new file mode 100644
index 0000000000..cd9ad5f2ff
--- /dev/null
+++ b/lib/ssh/test/ssh_test_cli.erl
@@ -0,0 +1,81 @@
+-module(ssh_test_cli).
+
+-export([init/1, terminate/2, handle_ssh_msg/2, handle_msg/2]).
+
+-record(state, {
+ type,
+ id,
+ ref,
+ port
+ }).
+
+init([Type]) ->
+ {ok, #state{type = Type}}.
+
+handle_msg({ssh_channel_up, Id, Ref}, S) ->
+ User = get_ssh_user(Ref),
+ ok = ssh_connection:send(Ref,
+ Id,
+ << "\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n" >>),
+ Port = run_portprog(User, S#state.type),
+ {ok, S#state{port = Port, id = Id, ref = Ref}};
+
+handle_msg({Port, {data, Data}}, S = #state{port = Port}) ->
+ ok = ssh_connection:send(S#state.ref, S#state.id, Data),
+ {ok, S};
+handle_msg({Port, {exit_status, Exit}}, S = #state{port = Port}) ->
+ if
+ S#state.type =:= cli ->
+ ok = ssh_connection:send(S#state.ref, S#state.id, << "\r\n" >>);
+ true ->
+ ok
+ end,
+ ok = ssh_connection:exit_status(S#state.ref, S#state.id, Exit),
+ {stop, S#state.id, S#state{port = undefined}};
+handle_msg({'EXIT', Port, _}, S = #state{port = Port}) ->
+ ok = ssh_connection:exit_status(S#state.ref, S#state.id, 0),
+ {stop, S#state.id, S#state{port = undefined}};
+handle_msg(_Msg, S) ->
+ {ok, S}.
+
+handle_ssh_msg({ssh_cm, Ref, {data, Id, _Type, <<"q">>}}, S) ->
+ ssh_connection:send_eof(Ref, Id),
+ {stop, Id, S};
+handle_ssh_msg({ssh_cm, _Ref, {data, _Id, _Type, Data}}, S) ->
+ true = port_command(S#state.port, Data),
+ {ok, S};
+handle_ssh_msg({ssh_cm, _, {eof, _}}, S) ->
+ {ok, S};
+handle_ssh_msg({ssh_cm, Ref, {env, Id, WantReply, _Var, _Value}}, S) ->
+ ok = ssh_connection:reply_request(Ref, WantReply, success, Id),
+ {ok, S};
+handle_ssh_msg({ssh_cm, Ref, {pty, Id, WantReply, _Terminal_jox}}, S) ->
+ ok = ssh_connection:reply_request(Ref, WantReply, success, Id),
+ {ok, S};
+handle_ssh_msg({ssh_cm, Ref, {shell, Id, WantReply}}, S) ->
+ ok = ssh_connection:reply_request(Ref, WantReply, success, Id),
+ {ok, S};
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, S) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, S};
+handle_ssh_msg({ssh_cm, _,
+ {window_change, _Id, _Width, _Height, _Pixw, _PixH}}, S) ->
+ {ok, S};
+handle_ssh_msg({ssh_cm, _, {exit_signal, Id, _, _, _}},
+ S) ->
+ {stop, Id, S}.
+
+terminate(_Why, _S) ->
+ nop.
+
+run_portprog(User, cli) ->
+ Pty_bin = os:find_executable("cat"),
+ open_port({spawn_executable, Pty_bin},
+ [stream, {cd, "/tmp"}, {env, [{"USER", User}]},
+ {args, []}, binary,
+ exit_status, use_stdio, stderr_to_stdout]).
+
+get_ssh_user(Ref) ->
+ [{user, User}] = ssh:connection_info(Ref, [user]),
+ User.
+
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 19c0c8c9ee..1d74faf1b3 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -76,7 +76,7 @@
<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>.
</p>
- <p> <c>ssloption() = {verify, verify_type()} |
+ <p><marker id="type-ssloption"></marker><c>ssloption() = {verify, verify_type()} |
{verify_fun, {fun(), term()}} |
{fail_if_no_peer_cert, boolean()}
{depth, integer()} |