aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/Makefile14
-rw-r--r--lib/ssh/doc/src/ssh.xml14
-rw-r--r--lib/ssh/examples/Makefile4
-rw-r--r--lib/ssh/src/Makefile54
-rw-r--r--lib/ssh/src/ssh.appup.src24
-rw-r--r--lib/ssh/src/ssh.erl55
-rw-r--r--lib/ssh/src/ssh_auth.erl16
-rw-r--r--lib/ssh/src/ssh_channel.erl4
-rw-r--r--lib/ssh/src/ssh_connection.erl29
-rw-r--r--lib/ssh/src/ssh_connection_manager.erl9
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl10
-rw-r--r--lib/ssh/src/ssh_file.erl11
-rw-r--r--lib/ssh/src/ssh_sftpd_file_api.erl62
-rw-r--r--lib/ssh/test/Makefile12
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl36
-rw-r--r--lib/ssh/vsn.mk2
16 files changed, 204 insertions, 152 deletions
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index 125dcf8775..38782a3b00 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -109,13 +109,13 @@ debug opt:
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
- $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
- $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
- $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(HTMLDIR)/* \
- $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
- $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+ "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+ $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
release_spec:
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e30c6f1ccc..b84b3a3dcb 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -259,11 +259,17 @@
that identifies the host for ssh. The default is
<c><![CDATA[/etc/ssh]]></c>, note that SSH normally
requires the host files there to be readable only by
- root.</p>
+ root.</p>
+ </item>
+ <tag><c><![CDATA[{auth_methods, string()}]]></c></tag>
+ <item>
+ <p>Comma separated string that determines which authentication methodes that the server
+ should support and in what order they will be tried. Defaults to
+ <c><![CDATA["publickey,keyboard_interactive,password"]]></c></p>
</item>
- <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
- <item>
- <p>Provide passwords for password authentication.They will
+ <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag>
+ <item>
+ <p>Provide passwords for password authentication.They will
be used when someone tries to connect to the server and
public key user authentication fails. The option provides
a list of valid user names and the corresponding password.
diff --git a/lib/ssh/examples/Makefile b/lib/ssh/examples/Makefile
index 5f17542fb8..84c7edac8c 100644
--- a/lib/ssh/examples/Makefile
+++ b/lib/ssh/examples/Makefile
@@ -69,8 +69,8 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/examples
- $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/examples
+ $(INSTALL_DIR) "$(RELSYSDIR)/examples"
+ $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/examples"
release_docs_spec:
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 1734ae4df4..b8eecd3fa2 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -38,15 +38,17 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN)
# Behaviour (api) modules are first so they are compiled when
# the compiler reaches a callback module using them.
-MODULES= \
+BEHAVIOUR_MODULES= \
ssh_sftpd_file_api \
- ssh_key_api \
+ ssh_channel \
+ ssh_key_api
+
+MODULES= \
ssh \
ssh_sup \
sshc_sup \
sshd_sup \
ssh_connection_sup \
- ssh_channel \
ssh_connection \
ssh_connection_handler \
ssh_connection_manager \
@@ -73,11 +75,14 @@ MODULES= \
PUBLIC_HRL_FILES= ssh.hrl ssh_userauth.hrl ssh_xfer.hrl
-ERL_FILES= $(MODULES:%=%.erl)
+ERL_FILES= \
+ $(MODULES:%=%.erl) \
+ $(BEHAVIOUR_MODULES:%=%.erl)
-ALL_MODULES= $(MODULES)
-TARGET_FILES= $(ALL_MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR))
APP_FILE= ssh.app
APPUP_FILE= ssh.appup
@@ -93,29 +98,29 @@ INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -pa$(EBIN)\
- -pz $(ERL_TOP)/lib/public_key/ebin
+EXTRA_ERLC_FLAGS = +warn_unused_vars
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
+ -pz $(EBIN) \
+ -pz $(ERL_TOP)/lib/public_key/ebin \
+ $(EXTRA_ERLC_FLAGS)
+
+
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-debug opt: $(TARGET_FILES)
+$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
-debug: ERLC_FLAGS += -Ddebug
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
rm -f errs core *~
-$(TARGET_FILES): ssh.hrl
-
-# $(EBIN)/ssh_sftpd_file.$(EMULATOR): ERLC_FLAGS += -pa$(EBIN)
-# $(EBIN)/ssh_sftpd_file.$(EMULATOR): $(EBIN)/ssh_sftpd_file_api.$(EMULATOR)
-
-$(APP_TARGET): $(APP_SRC) ../vsn.mk
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
-$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
@@ -127,12 +132,13 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
- $(INSTALL_DIR) $(RELSYSDIR)/include
- $(INSTALL_DATA) $(PUBLIC_HRL_FILES) $(RELSYSDIR)/include
+ $(INSTALL_DIR) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \
+ $(APPUP_TARGET) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DIR) "$(RELSYSDIR)/include"
+ $(INSTALL_DATA) $(PUBLIC_HRL_FILES) "$(RELSYSDIR)/include"
release_docs_spec:
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 0542054596..d08dbafc32 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -18,12 +18,26 @@
%%
{"%VSN%",
- [
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ [
+ {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []},
+ {load_module, ssh_connection, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []},
+ {load_module, ssh_auth, soft_purge, soft_purge, []},
+ {load_module, ssh_channel, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []}]},
+ {load_module, ssh, soft_purge, soft_purge, []}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
],
[
- {<<"2\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []},
+ {load_module, ssh_connection, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []},
+ {load_module, ssh_auth, soft_purge, soft_purge, []},
+ {load_module, ssh_channel, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []}]},
+ {load_module, ssh, soft_purge, soft_purge, []}]},
+ {<<"2.0\\.*">>, [{restart_application, ssh}]},
+ {<<"1\\.*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 39c7fe329e..3395f73884 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -91,10 +91,8 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{ok, ConnectionSup} ->
{ok, Manager} =
ssh_connection_sup:connection_manager(ConnectionSup),
- MRef = erlang:monitor(process, Manager),
receive
{Manager, is_connected} ->
- do_demonitor(MRef, Manager),
{ok, Manager};
%% When the connection fails
%% ssh_connection_sup:connection_manager
@@ -102,30 +100,13 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
%% could allready have terminated, so we will not
%% match the Manager in this case
{_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
- do_demonitor(MRef, Manager),
do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
SshOptions, Timeout, true);
{_, not_connected, {error, Reason}} ->
- do_demonitor(MRef, Manager),
{error, Reason};
{_, not_connected, Other} ->
- do_demonitor(MRef, Manager),
- {error, Other};
- {'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) ->
- error_logger:warning_report([{ssh, connect},
- {diagnose,
- "Connection was closed before properly set up."},
- {host, Host},
- {port, Port},
- {reason, Reason}]),
- receive %% Clear EXIT message from queue
- {'EXIT', Manager, _What} ->
- {error, channel_closed}
- after 0 ->
- {error, channel_closed}
- end
+ {error, Other}
after Timeout ->
- do_demonitor(MRef, Manager),
ssh_connection_manager:stop(Manager),
{error, timeout}
end
@@ -134,16 +115,6 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
{error, ssh_not_started}
end.
-do_demonitor(MRef, Manager) ->
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, Manager, _} ->
- ok
- after 0 ->
- ok
- end.
-
-
%%--------------------------------------------------------------------
%% Function: close(ConnectionRef) -> ok
%%
@@ -346,8 +317,9 @@ handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{allow_user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+%%Backwards compatibility
+handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({user_interaction, Value}) | SshOptions]);
handle_option([{infofun, _} = Opt | Rest],SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{connectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -366,6 +338,10 @@ handle_option([{ssh_cli, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -401,8 +377,11 @@ handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
-handle_ssh_option({allow_user_interaction, Value} = Opt) when Value == true;
- Value == false ->
+handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
+ is_atom(Function) ->
+
+ Opt;
+handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
@@ -412,11 +391,12 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_function(Value) ->
+handle_ssh_option({ip_v6_disabled, Value} = Opt) when Value == true;
+ Value == false ->
Opt;
handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol),
- is_atom(Cb),
- is_atom(ClosTag) ->
+ is_atom(Cb),
+ is_atom(ClosTag) ->
Opt;
handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) ->
Opt;
@@ -495,4 +475,3 @@ verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signatur
Error ->
Error
end.
-
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 1a4517c689..aa452a8e09 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -71,7 +71,7 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb,
ssh_bits:install_messages(userauth_passwd_messages()),
Password = case proplists:get_value(password, Opts) of
undefined ->
- user_interaction(Opts, IoCb);
+ user_interaction(IoCb);
PW ->
PW
end,
@@ -89,13 +89,10 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb,
Ssh)
end.
-user_interaction(Opts, IoCb) ->
- case proplists:get_value(allow_user_interaction, Opts, true) of
- true ->
- IoCb:read_password("ssh password: ");
- false ->
- not_ok
- end.
+user_interaction(ssh_no_io) ->
+ not_ok;
+user_interaction(IoCb) ->
+ IoCb:read_password("ssh password: ").
%% See RFC 4256 for info on keyboard-interactive
@@ -124,8 +121,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts,
?PREFERRED_PK_ALG)),
SecondAlg = other_alg(FirstAlg),
- AllowUserInt = proplists:get_value(allow_user_interaction, Opts,
- true),
+ AllowUserInt = proplists:get_value(user_interaction, Opts, true),
Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 7b600ed8b2..1938858420 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -215,7 +215,7 @@ handle_info({ssh_cm, ConnectionManager, {closed, ChannelId}},
close_sent = false} = State) ->
%% To be on the safe side, i.e. the manager has already been terminated.
(catch ssh_connection:close(ConnectionManager, ChannelId)),
- {stop, normal, State};
+ {stop, normal, State#state{close_sent = true}};
handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager,
channel_cb = Module,
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 46f0c7e688..c2a7c63cbe 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -436,32 +436,32 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
#connection{channel_cache = Cache} = Connection,
ConnectionPid, _) ->
- #channel{send_window_size = Size} =
+ #channel{send_window_size = Size, remote_id = RemoteId} =
Channel0 = ssh_channel:cache_lookup(Cache, ChannelId),
-
+
{SendList, Channel} = %% TODO: Datatype 0 ?
update_send_window(Channel0#channel{send_window_size = Size + Add},
0, <<>>, Connection),
Replies = lists:map(fun({Type, Data}) ->
{connection_reply, ConnectionPid,
- channel_data_msg(ChannelId, Type, Data)}
+ channel_data_msg(RemoteId, Type, Data)}
end, SendList),
FlowCtrlMsgs = flow_control(Channel, Cache),
{{replies, Replies ++ FlowCtrlMsgs}, Connection};
handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
- sender_channel = ChannelId,
+ sender_channel = RemoteId,
initial_window_size = WindowSz,
maximum_packet_size = PacketSz}, Connection0,
ConnectionPid, server) ->
- try setup_session(Connection0, ConnectionPid, ChannelId,
+ try setup_session(Connection0, ConnectionPid, RemoteId,
Type, WindowSz, PacketSz) of
Result ->
Result
catch _:_ ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_CONNECT_FAILED,
"Connection refused", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]},
@@ -532,9 +532,9 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
-handle_msg(#ssh_msg_channel_open{sender_channel = ChannelId}, Connection,
+handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection,
ConnectionPid, _) ->
- FailMsg = channel_open_failure_msg(ChannelId,
+ FailMsg = channel_open_failure_msg(RemoteId,
?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
"Not allowed", "en"),
{{replies, [{connection_reply, ConnectionPid, FailMsg}]}, Connection};
@@ -720,12 +720,17 @@ handle_msg(#ssh_msg_channel_request{request_type = "env"},
handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
request_type = _Other,
- want_reply = WantReply}, Connection,
+ want_reply = WantReply}, #connection{channel_cache = Cache} = Connection,
ConnectionPid, _) ->
if WantReply == true ->
- FailMsg = channel_failure_msg(ChannelId),
- {{replies, [{connection_reply, ConnectionPid, FailMsg}]},
- Connection};
+ case ssh_channel:cache_lookup(Cache, ChannelId) of
+ #channel{remote_id = RemoteId} ->
+ FailMsg = channel_failure_msg(RemoteId),
+ {{replies, [{connection_reply, ConnectionPid, FailMsg}]},
+ Connection};
+ undefined -> %% Chanel has been closed
+ {noreply, Connection}
+ end;
true ->
{noreply, Connection}
end;
diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl
index e993f597a5..e53cd4f4f7 100644
--- a/lib/ssh/src/ssh_connection_manager.erl
+++ b/lib/ssh/src/ssh_connection_manager.erl
@@ -267,7 +267,7 @@ handle_call({ssh_msg, Pid, Msg}, From,
disconnect_fun(Reason, SSHOpts),
{stop, {shutdown, normal}, State#state{connection_state = Connection}}
catch
- _:Reason ->
+ _:Error ->
{disconnect, Reason, {{replies, Replies}, Connection}} =
ssh_connection:handle_msg(
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
@@ -277,7 +277,7 @@ handle_call({ssh_msg, Pid, Msg}, From,
lists:foreach(fun send_msg/1, Replies),
SSHOpts = proplists:get_value(ssh_opts, Opts),
disconnect_fun(Reason, SSHOpts),
- {stop, {shutdown, Reason}, State#state{connection_state = Connection}}
+ {stop, {shutdown, Error}, State#state{connection_state = Connection}}
end;
handle_call({global_request, Pid, _, _, _} = Request, From,
@@ -384,9 +384,10 @@ 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{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}),
{reply, ok, State};
undefined ->
{reply, ok, State}
@@ -585,7 +586,7 @@ call(Pid, Msg, Timeout) ->
catch
exit:{timeout, _} ->
{error, timeout};
- exit:{normal} ->
+ exit:{normal, _} ->
{error, channel_closed};
exit:{{shutdown, _}, _} ->
{error, channel_closed};
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
index e3544af1c6..b620056310 100644
--- a/lib/ssh/src/ssh_connection_sup.erl
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -48,8 +48,12 @@ start_manager_child(Sup, Args) ->
supervisor:start_child(Sup, Spec).
connection_manager(SupPid) ->
- Children = supervisor:which_children(SupPid),
- {ok, ssh_connection_manager(Children)}.
+ try supervisor:which_children(SupPid) of
+ Children ->
+ {ok, ssh_connection_manager(Children)}
+ catch exit:{noproc,_} ->
+ {ok, undefined}
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -107,6 +111,8 @@ handler_spec([Role, Socket, Opts]) ->
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+ssh_connection_manager([]) ->
+ undefined;
ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) ->
Child;
ssh_connection_manager([_ | Rest]) ->
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index d05fa8e09a..a6b82a7a13 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -232,7 +232,7 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, known_hosts) of
+ case ssh_decode_line(Line, known_hosts) of
[{Key, Attributes}] ->
handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType);
[] ->
@@ -240,6 +240,13 @@ lookup_host_key_fd(Fd, Host, KeyType) ->
end
end.
+ssh_decode_line(Line, Type) ->
+ try
+ public_key:ssh_decode(Line, Type)
+ catch _:_ ->
+ []
+ end.
+
handle_host(Fd, Host, HostList, Key, KeyType) ->
Host1 = host_name(Host),
case lists:member(Host1, HostList) and key_match(Key, KeyType) of
@@ -285,7 +292,7 @@ lookup_user_key_fd(Fd, Key) ->
eof ->
{error, not_found};
Line ->
- case public_key:ssh_decode(Line, auth_keys) of
+ case ssh_decode_line(Line, auth_keys) of
[{AuthKey, _}] ->
case is_auth_key(Key, AuthKey) of
true ->
diff --git a/lib/ssh/src/ssh_sftpd_file_api.erl b/lib/ssh/src/ssh_sftpd_file_api.erl
index 38371f809d..83d90907f5 100644
--- a/lib/ssh/src/ssh_sftpd_file_api.erl
+++ b/lib/ssh/src/ssh_sftpd_file_api.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,40 +22,40 @@
-module(ssh_sftpd_file_api).
%% To be further specified later
--callback close(IoDevice::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback delete(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback del_dir(Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+-callback close(file:io_device(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback delete(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback del_dir(file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback get_cwd(State::term()) ->
- {ok, Dir::term()} | {error, Reason::term()}.
--callback is_dir(AbsPath::term(), State::term()) ->
- boolean().
--callback list_dir(AbsPath::term(), State::term()) ->
- {ok, Filenames::term()} | {error, Reason::term()}.
+ {{ok, Dir::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback is_dir(file:name(), State::term()) ->
+ {boolean(), State::term()}.
+-callback list_dir(file:name(), State::term()) ->
+ {{ok, Filenames::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_dir(Dir::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, State::term()},State::term()} | {{error, Reason::term()}, State::term()}.
-callback make_symlink(Path2::term(), Path::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
-callback open(Path::term(), Flags::term(), State::term()) ->
- {ok, IoDevice::term()} | {error, Reason::term()}.
--callback position(IoDevice::term(), Offs::term(), State::term()) ->
- {ok, NewPosition::term()} | {error, Reason::term()}.
--callback read(IoDevice::term(), Len::term(), State::term()) ->
- {ok, Data::term()} | eof | {error, Reason::term()}.
--callback read_link(Path::term(), State::term()) ->
- {ok, FileName::term()} | {error, Reason::term()}.
--callback read_link_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback read_file_info(Path::term(), State::term()) ->
- {ok, FileInfo::term()} | {error, Reason::term()}.
--callback rename(Path::term(), Path2::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write(IoDevice::term(), Data::term(), State::term()) ->
- ok | {error, Reason::term()}.
--callback write_file_info(Path::term(),Info::term(), State::term()) ->
- ok | {error, Reason::term()}.
+ {{ok, IoDevice::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback position(file:io_device(), Offs::term(), State::term()) ->
+ {{ok, NewPosition::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read(file:io_device(), Len::term(), State::term()) ->
+ {{ok, Data::term()},State::term()} | {eof, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link(file:name(), State::term()) ->
+ {{ok, FileName::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_link_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback read_file_info(file:name(), State::term()) ->
+ {{ok, FileInfo::term()}, State::term()} | {{error, Reason::term()},State::term()}.
+-callback rename(file:name(), file:name(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write(file:io_device(), Data::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
+-callback write_file_info(file:name(),Info::term(), State::term()) ->
+ {ok, State::term()} | {{error, Reason::term()}, State::term()}.
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 145d5d2ad6..25072688ad 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -110,11 +110,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
release_tests_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)
- $(INSTALL_DATA) ssh.spec ssh.cover $(RELSYSDIR)
- $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) $(RELSYSDIR)
- chmod -R u+w $(RELSYSDIR)
- @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+ $(INSTALL_DIR) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) ssh.spec ssh.cover "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) "$(RELSYSDIR)"
+ chmod -R u+w "$(RELSYSDIR)"
+ @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index d66214d415..2ceaa9daa5 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -183,7 +183,29 @@ app_test(doc) ->
app_test(Config) when is_list(Config) ->
?t:app_test(ssh),
ok.
+%%--------------------------------------------------------------------
+misc_ssh_options(doc) ->
+ ["Test that we can set some misc options not tested elsewhere, "
+ "some options not yet present are not decided if we should support or "
+ "if they need thier own test case."];
+misc_ssh_options(suite) ->
+ [];
+misc_ssh_options(Config) when is_list(Config) ->
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disabled, false}, {user_dir, UserDir}],
+ CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disabled, true}, {user_dir, UserDir}],
+ SMiscOpt0 = [{ip_v6_disabled, false}, {user_dir, UserDir}, {system_dir, SystemDir}],
+ SMiscOpt1 = [{ip_v6_disabled, true}, {user_dir, UserDir}, {system_dir, SystemDir}],
+
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ basic_test([{client_opts, CMiscOpt0 ++ ClientOpts}, {server_opts, SMiscOpt0 ++ ServerOpts}]),
+ basic_test([{client_opts, CMiscOpt1 ++ ClientOpts}, {server_opts, SMiscOpt1 ++ ServerOpts}]).
+%%--------------------------------------------------------------------
exec(doc) ->
["Test api function ssh_connection:exec"];
@@ -500,13 +522,14 @@ internal_error(Config) when is_list(Config) ->
SystemDir = filename:join(?config(priv_dir, Config), system),
UserDir = ?config(priv_dir, Config),
- {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{failfun, fun ssh_test_lib:failfun/2}]),
{error,"Internal error"} =
ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
- {user_interaction, false}]).
+ {user_interaction, false}]),
+ ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
close(doc) ->
@@ -539,3 +562,12 @@ close(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
+
+basic_test(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon(ServerOpts),
+ {ok, CM} = ssh:connect(Host, Port, ClientOpts),
+ ok = ssh:close(CM),
+ ssh:stop_daemon(Pid).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index bff73a1b40..defa47f824 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 2.1
+SSH_VSN = 2.1.1
APP_VSN = "ssh-$(SSH_VSN)"