aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2017-03-02 16:37:54 +0100
committerHans Nilsson <[email protected]>2017-04-07 10:19:56 +0200
commitda7902412f1e77b8241c0bacbeac2d6013e8f345 (patch)
tree9a7067bee8c9226c9e8c5211c2079809ddcb723f
parent6ff33b1548a24d9f195c27a1ee5bcfcdb1b892d8 (diff)
downloadotp-da7902412f1e77b8241c0bacbeac2d6013e8f345.tar.gz
otp-da7902412f1e77b8241c0bacbeac2d6013e8f345.tar.bz2
otp-da7902412f1e77b8241c0bacbeac2d6013e8f345.zip
ssh: Unified way of starting listening sockets
-rw-r--r--lib/ssh/src/ssh.erl215
-rw-r--r--lib/ssh/src/ssh_acceptor.erl106
2 files changed, 136 insertions, 185 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index c1be9f732d..c139556791 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -132,13 +132,12 @@ connect(Host, Port, UserOptions, Timeout) when is_integer(Port),
SocketOpts = [{active,false} | ?GET_OPT(socket_options,Options)],
try Transport:connect(Host, Port, SocketOpts, ConnectionTimeout) of
{ok, Socket} ->
- Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
+ Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,fmt_host(Host)}], Options),
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
{error, Reason} ->
{error, Reason}
catch
exit:{function_clause, _F} ->
- io:format('function_clause ~p~n',[_F]),
{error, {options, {transport, TransportOpts}}};
exit:badarg ->
{error, {options, {socket_options, SocketOpts}}}
@@ -311,16 +310,15 @@ handle_daemon_args(Host, UserOptions0) ->
%%%----------------------------------------------------------------
valid_socket_to_use(Socket, {tcp,_,_}) ->
%% Is this tcp-socket a valid socket?
- case {is_tcp_socket(Socket),
- {ok,[{active,false}]} == inet:getopts(Socket, [active])
- }
+ try {is_tcp_socket(Socket),
+ {ok,[{active,false}]} == inet:getopts(Socket, [active])
+ }
of
- {true, true} ->
- ok;
- {true, false} ->
- {error, not_passive_mode};
- _ ->
- {error, not_tcp_socket}
+ {true, true} -> ok;
+ {true, false} -> {error, not_passive_mode};
+ _ -> {error, not_tcp_socket}
+ catch
+ _:_ -> {error, bad_socket}
end;
valid_socket_to_use(_, {L4,_,_}) ->
@@ -340,13 +338,7 @@ start_daemon(_, _, {error,Error}) ->
start_daemon(socket, Socket, Options) ->
case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
ok ->
- try
- do_start_daemon(Socket, Options)
- catch
- throw:bad_fd -> {error,bad_fd};
- throw:bad_socket -> {error,bad_socket};
- _C:_E -> {error,{cannot_start_daemon,_C,_E}}
- end;
+ start_daemon(inet:sockname(Socket), Socket, Options);
{error,SockError} ->
{error,SockError}
end;
@@ -355,136 +347,107 @@ start_daemon(Host, Port, Options) ->
try
do_start_daemon(Host, Port, Options)
catch
- throw:bad_fd -> {error,bad_fd};
- throw:bad_socket -> {error,bad_socket};
- _C:_E -> {error,{cannot_start_daemon,_C,_E}}
+ throw:bad_fd ->
+ {error,bad_fd};
+ throw:bad_socket ->
+ {error,bad_socket};
+ error:{badmatch,{error,Error}} ->
+ {error,Error};
+ _C:_E ->
+ {error,{cannot_start_daemon,_C,_E}}
end.
+%%%----------------------------------------------------------------
+do_start_daemon({error,Error}, _, _) ->
+ {error,Error};
-do_start_daemon(Socket, Options) ->
- {ok, {IP,Port}} =
- try {ok,_} = inet:sockname(Socket)
- catch
- _:_ -> throw(bad_socket)
- end,
- Host = fmt_host(IP),
- Opts = ?PUT_INTERNAL_OPT([{connected_socket, Socket},
- {address, Host},
- {port, Port},
- {role, server}], Options),
-
- Profile = ?GET_OPT(profile, Options),
- case ssh_system_sup:system_supervisor(Host, Port, Profile) of
- undefined ->
- try sshd_sup:start_child(Opts) of
- {error, {already_started, _}} ->
- {error, eaddrinuse};
- Result = {ok,_} ->
- call_ssh_acceptor_handle_connection(Host, Port, Opts, Socket, Result);
- Result = {error, _} ->
- Result
- catch
- exit:{noproc, _} ->
- {error, ssh_not_started}
- end;
- Sup ->
- AccPid = ssh_system_sup:acceptor_supervisor(Sup),
- case ssh_acceptor_sup:start_child(AccPid, Opts) of
- {error, {already_started, _}} ->
- {error, eaddrinuse};
- {ok, _} ->
- call_ssh_acceptor_handle_connection(Host, Port, Opts, Socket, {ok,Sup});
- Other ->
- Other
- end
- end.
+do_start_daemon({ok, {IP,Port}}, Socket, Options0) ->
+ finalize_start(fmt_host(IP),
+ Port,
+ ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options0),
+ fun(Opts, DefaultResult) ->
+ try ssh_acceptor:handle_established_connection(
+ ?GET_INTERNAL_OPT(address, Opts),
+ ?GET_INTERNAL_OPT(port, Opts),
+ Opts,
+ Socket)
+ of
+ {error,Error} ->
+ {error,Error};
+ _ ->
+ DefaultResult
+ catch
+ C:R ->
+ {error,{could_not_start_connection,{C,R}}}
+ end
+ end);
do_start_daemon(Host0, Port0, Options0) ->
- {Host,Port1} =
- try
- case ?GET_SOCKET_OPT(fd, Options0) of
- undefined ->
- {Host0,Port0};
- Fd when Port0==0 ->
- find_hostport(Fd)
- end
- catch
- _:_ -> throw(bad_fd)
- end,
- {Port, WaitRequestControl, Options1} =
- case Port1 of
- 0 -> %% Allocate the socket here to get the port number...
- {ok,LSock} = ssh_acceptor:callback_listen(0, Options0),
- {ok,{_,LPort}} = inet:sockname(LSock),
- {LPort,
- LSock,
- ?PUT_INTERNAL_OPT({lsocket,{LSock,self()}}, Options0)
- };
- _ ->
- {Port1, false, Options0}
- end,
+ {{Host,Port}, ListenSocket} =
+ open_listen_socket(Host0, Port0, Options0),
+
+ %% Now Host,Port is what to use for the supervisor to register its name,
+ %% and ListenSocket is for listening on connections. But it is still owned
+ %% by self()...
+
+ finalize_start(Host, Port,
+ ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options0),
+ fun(Opts, Result) ->
+ {_, Callback, _} = ?GET_OPT(transport, Opts),
+ receive
+ {request_control, ListenSocket, ReqPid} ->
+ ok = Callback:controlling_process(ListenSocket, ReqPid),
+ ReqPid ! {its_yours,ListenSocket},
+ Result
+ end
+ end).
+
+
+open_listen_socket(Host0, Port0, Options0) ->
+ case ?GET_SOCKET_OPT(fd, Options0) of
+ undefined ->
+ {ok,LSock} = ssh_acceptor:listen(Port0, Options0),
+ {ok,{_,LPort}} = inet:sockname(LSock),
+ {{fmt_host(Host0),LPort}, LSock};
+
+ Fd when is_integer(Fd) ->
+ %% Do gen_tcp:listen with the option {fd,Fd}:
+ {ok,LSock} = ssh_acceptor:listen(0, Options0),
+ {ok,{LHost,LPort}} = inet:sockname(LSock),
+ {{fmt_host(LHost),LPort}, LSock}
+ end.
+
+%%%----------------------------------------------------------------
+finalize_start(Host, Port, Options0, F) ->
Options = ?PUT_INTERNAL_OPT([{address, Host},
{port, Port},
- {role, server}], Options1),
- Profile = ?GET_OPT(profile, Options0),
+ {role, server}], Options0),
+ Profile = ?GET_OPT(profile, Options),
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
try sshd_sup:start_child(Options) of
{error, {already_started, _}} ->
{error, eaddrinuse};
+ {error, Error} ->
+ {error, Error};
Result = {ok,_} ->
- sync_request_control(WaitRequestControl, Options),
- Result;
- Result = {error, _} ->
- Result
+ F(Options, Result)
catch
exit:{noproc, _} ->
{error, ssh_not_started}
end;
- Sup ->
+ Sup ->
AccPid = ssh_system_sup:acceptor_supervisor(Sup),
case ssh_acceptor_sup:start_child(AccPid, Options) of
{error, {already_started, _}} ->
{error, eaddrinuse};
+ {error, Error} ->
+ {error, Error};
{ok, _} ->
- sync_request_control(WaitRequestControl, Options),
- {ok, Sup};
- Other ->
- Other
+ F(Options, {ok,Sup})
end
end.
-call_ssh_acceptor_handle_connection(Host, Port, Options, Socket, DefaultResult) ->
- {_, Callback, _} = ?GET_OPT(transport, Options),
- try ssh_acceptor:handle_connection(Callback, Host, Port, Options, Socket)
- of
- {error,Error} -> {error,Error};
- _ -> DefaultResult
- catch
- C:R -> {error,{could_not_start_connection,{C,R}}}
- end.
-
-
-sync_request_control(false, _Options) ->
- ok;
-sync_request_control(LSock, Options) ->
- {_, Callback, _} = ?GET_OPT(transport, Options),
- receive
- {request_control,LSock,ReqPid} ->
- ok = Callback:controlling_process(LSock, ReqPid),
- ReqPid ! {its_yours,LSock},
- ok
- 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.
-
-fmt_host({A,B,C,D}) ->
- lists:concat([A,".",B,".",C,".",D]);
-fmt_host(T={_,_,_,_,_,_,_,_}) ->
- lists:flatten(string:join([io_lib:format("~.16B",[A]) || A <- tuple_to_list(T)], ":")).
+%%%----------------------------------------------------------------
+fmt_host(IP) when is_tuple(IP) -> inet:ntoa(IP);
+fmt_host(Str) when is_list(Str) -> Str.
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 4943f062b4..f9e2280212 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -27,8 +27,8 @@
%% Internal application API
-export([start_link/4,
number_of_connections/1,
- callback_listen/2,
- handle_connection/5]).
+ listen/2,
+ handle_established_connection/4]).
%% spawn export
-export([acceptor_init/5, acceptor_loop/6]).
@@ -42,40 +42,57 @@ start_link(Port, Address, Options, AcceptTimeout) ->
Args = [self(), Port, Address, Options, AcceptTimeout],
proc_lib:start_link(?MODULE, acceptor_init, Args).
+%%%----------------------------------------------------------------
+number_of_connections(SystemSup) ->
+ length([X ||
+ {R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup),
+ is_pid(X),
+ is_reference(R)
+ ]).
+
+%%%----------------------------------------------------------------
+listen(Port, Options) ->
+ {_, Callback, _} = ?GET_OPT(transport, Options),
+ SockOpts = [{active, false}, {reuseaddr,true} | ?GET_OPT(socket_options, Options)],
+ case Callback:listen(Port, SockOpts) of
+ {error, nxdomain} ->
+ Callback:listen(Port, lists:delete(inet6, SockOpts));
+ {error, enetunreach} ->
+ Callback:listen(Port, lists:delete(inet6, SockOpts));
+ {error, eafnosupport} ->
+ Callback:listen(Port, lists:delete(inet6, SockOpts));
+ Other ->
+ Other
+ end.
+
+%%%----------------------------------------------------------------
+handle_established_connection(Address, Port, Options, Socket) ->
+ {_, Callback, _} = ?GET_OPT(transport, Options),
+ handle_connection(Callback, Address, Port, Options, Socket).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
acceptor_init(Parent, Port, Address, Opts, AcceptTimeout) ->
- {_, Callback, _} = ?GET_OPT(transport, Opts),
try
- {LSock0,SockOwner0} = ?GET_INTERNAL_OPT(lsocket, Opts),
- true = is_pid(SockOwner0),
- {ok,{_,Port}} = inet:sockname(LSock0),
- {LSock0, SockOwner0}
+ ?GET_INTERNAL_OPT(lsocket, Opts)
of
{LSock, SockOwner} ->
- %% Use existing socket
- proc_lib:init_ack(Parent, {ok, self()}),
- request_ownership(LSock, SockOwner),
- acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout)
- catch
- error:{badkey,lsocket} ->
- %% Open new socket
- try
- socket_listen(Port, Opts)
- of
- {ok, ListenSocket} ->
+ case inet:sockname(LSock) of
+ {ok,{_,Port}} -> % A usable, open LSock
proc_lib:init_ack(Parent, {ok, self()}),
- acceptor_loop(Callback,
- Port, Address, Opts, ListenSocket, AcceptTimeout);
- {error,Error} ->
- proc_lib:init_ack(Parent, Error),
- {error,Error}
- catch
- _:_ ->
- {error,listen_socket_failed}
- end;
+ request_ownership(LSock, SockOwner),
+ {_, Callback, _} = ?GET_OPT(transport, Opts),
+ acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout);
+ {error,_} -> % Not open, a restart
+ {ok,NewLSock} = listen(Port, Opts),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ Opts1 = ?DELETE_INTERNAL_OPT(lsocket, Opts),
+ {_, Callback, _} = ?GET_OPT(transport, Opts1),
+ acceptor_loop(Callback, Port, Address, Opts1, NewLSock, AcceptTimeout)
+ end
+ catch
_:_ ->
{error,use_existing_socket_failed}
end.
@@ -87,30 +104,7 @@ request_ownership(LSock, SockOwner) ->
{its_yours,LSock} -> ok
end.
-
-socket_listen(Port0, Opts) ->
- Port = case ?GET_SOCKET_OPT(fd, Opts) of
- undefined -> Port0;
- _ -> 0
- end,
- callback_listen(Port, Opts).
-
-
-callback_listen(Port, Opts0) ->
- {_, Callback, _} = ?GET_OPT(transport, Opts0),
- Opts = ?PUT_SOCKET_OPT([{active, false}, {reuseaddr,true}], Opts0),
- SockOpts = ?GET_OPT(socket_options, Opts),
- case Callback:listen(Port, SockOpts) of
- {error, nxdomain} ->
- Callback:listen(Port, lists:delete(inet6, SockOpts));
- {error, enetunreach} ->
- Callback:listen(Port, lists:delete(inet6, SockOpts));
- {error, eafnosupport} ->
- Callback:listen(Port, lists:delete(inet6, SockOpts));
- Other ->
- Other
- end.
-
+%%%----------------------------------------------------------------
acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
case (catch Callback:accept(ListenSocket, AcceptTimeout)) of
{ok, Socket} ->
@@ -127,6 +121,7 @@ acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
ListenSocket, AcceptTimeout)
end.
+%%%----------------------------------------------------------------
handle_connection(Callback, Address, Port, Options, Socket) ->
Profile = ?GET_OPT(profile, Options),
SystemSup = ssh_system_sup:system_supervisor(Address, Port, Profile),
@@ -158,7 +153,7 @@ handle_connection(Callback, Address, Port, Options, Socket) ->
{error,max_sessions}
end.
-
+%%%----------------------------------------------------------------
handle_error(timeout) ->
ok;
@@ -185,10 +180,3 @@ handle_error(Reason) ->
error_logger:error_report(String),
exit({accept_failed, String}).
-
-number_of_connections(SystemSup) ->
- length([X ||
- {R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup),
- is_pid(X),
- is_reference(R)
- ]).