aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/ssh/src/ssh.erl37
-rw-r--r--lib/ssh/src/ssh_acceptor.erl7
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl66
3 files changed, 107 insertions, 3 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 5bde184070..bb50e436a3 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -235,10 +235,27 @@ start_daemon(Host, Port, Options, Inet) ->
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions}->
- do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions])
+ try
+ do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions])
+ catch
+ throw:bad_fd -> {error,bad_fd};
+ _C:_E -> {error,{cannot_start_daemon,_C,_E}}
+ end
end.
-do_start_daemon(Host, Port, Options, SocketOptions) ->
+do_start_daemon(Host0, Port0, Options, SocketOptions) ->
+ {Host,Port} = try
+ case proplists:get_value(fd, SocketOptions) of
+ undefined ->
+ {Host0,Port0};
+ Fd when Port0==0 ->
+ find_hostport(Fd);
+ _ ->
+ {Host0,Port0}
+ end
+ catch
+ _:_ -> throw(bad_fd)
+ end,
Profile = proplists:get_value(profile, Options, ?DEFAULT_PROFILE),
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
@@ -272,6 +289,22 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
end
end.
+find_hostport(Fd) ->
+ %% Using internal functions inet:open/8 and inet:close/0.
+ %% Don't try this at home unless you know what you are doing!
+ {ok,S} = inet:open(Fd, {0,0,0,0}, 0, [], tcp, inet, stream, inet_tcp),
+ {ok, HostPort} = inet:sockname(S),
+ ok = inet:close(S),
+ HostPort.
+
+%% find_port(Fd) ->
+%% %% Hack....
+%% {ok,TmpSock} = gen_tcp:listen(0,[{fd,Fd}]),
+%% {ok, {_,ThePort}} = inet:sockname(TmpSock),
+%% gen_tcp:close(TmpSock),
+%% ThePort.
+
+
handle_options(Opts) ->
try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of
{Inet, Ssh} ->
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index c5ad1d7b6c..d94dedf1bf 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -56,7 +56,12 @@ acceptor_init(Parent, Port, Address, SockOpts, Opts, AcceptTimeout) ->
error
end.
-do_socket_listen(Callback, Port, Opts) ->
+do_socket_listen(Callback, Port0, Opts) ->
+ Port =
+ case proplists:get_value(fd, Opts) of
+ undefined -> Port0;
+ _ -> 0
+ end,
case Callback:listen(Port, Opts) of
{error, nxdomain} ->
Callback:listen(Port, lists:delete(inet6, Opts));
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 400edb4d2c..0a5964c560 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -36,6 +36,8 @@
cli/1,
close/1,
daemon_already_started/1,
+ daemon_opt_fd/1,
+ multi_daemon_opt_fd/1,
double_close/1,
exec/1,
exec_compressed/1,
@@ -85,6 +87,8 @@ all() ->
{group, internal_error},
daemon_already_started,
double_close,
+ daemon_opt_fd,
+ multi_daemon_opt_fd,
packet_size_zero,
ssh_info_print
].
@@ -705,6 +709,68 @@ double_close(Config) when is_list(Config) ->
ok = ssh:close(CM).
%%--------------------------------------------------------------------
+daemon_opt_fd(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
+ file:make_dir(UserDir),
+
+ {ok,S1} = gen_tcp:listen(0,[]),
+ {ok,Fd1} = prim_inet:getfd(S1),
+
+ {ok,Pid1} = ssh:daemon(0, [{system_dir, SystemDir},
+ {fd,Fd1},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+ {ok,{_Host1,Port1}} = inet:sockname(S1),
+ {ok, C1} = ssh:connect("localhost", Port1, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false}]),
+ exit(C1, {shutdown, normal}),
+ ssh:stop_daemon(Pid1),
+ gen_tcp:close(S1).
+
+
+%%--------------------------------------------------------------------
+multi_daemon_opt_fd(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
+ file:make_dir(UserDir),
+
+ Test =
+ fun() ->
+ {ok,S} = gen_tcp:listen(0,[]),
+ {ok,Fd} = prim_inet:getfd(S),
+
+ {ok,Pid} = ssh:daemon(0, [{system_dir, SystemDir},
+ {fd,Fd},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+ {ok,{_Host,Port}} = inet:sockname(S),
+ {ok, C} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false}]),
+ {S,Pid,C}
+ end,
+
+ Tests = [Test(),Test(),Test(),Test(),Test(),Test()],
+
+ [begin
+ gen_tcp:close(S),
+ ssh:stop_daemon(Pid),
+ exit(C, {shutdown, normal})
+ end || {S,Pid,C} <- Tests].
+
+%%--------------------------------------------------------------------
packet_size_zero(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),