aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src/ssh.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src/ssh.erl')
-rw-r--r--lib/ssh/src/ssh.erl339
1 files changed, 339 insertions, 0 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
new file mode 100644
index 0000000000..f9a986a8b6
--- /dev/null
+++ b/lib/ssh/src/ssh.erl
@@ -0,0 +1,339 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. 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).
+
+-include("ssh.hrl").
+-include("ssh_connect.hrl").
+
+-export([start/0, start/1, stop/0, connect/3, close/1, connection_info/2,
+ channel_info/3,
+ daemon/1, daemon/2, daemon/3,
+ 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
+%%
+%% Description: Starts the inets application. Default type
+%% is temporary. see application(3)
+%%--------------------------------------------------------------------
+start() ->
+ application:start(ssh).
+
+start(Type) ->
+ application:start(ssh, Type).
+
+%%--------------------------------------------------------------------
+%% Function: stop() -> ok
+%%
+%% Description: Stops the inets application.
+%%--------------------------------------------------------------------
+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().
+%%
+%% Description: Starts an ssh connection.
+%%--------------------------------------------------------------------
+connect(Host, Port, Options) ->
+ connect(Host, Port, Options, infinity).
+connect(Host, Port, Options, Timeout) ->
+ {SocketOpts, Opts} = handle_options(Options),
+ DisableIpv6 = proplists:get_value(ip_v6_disabled, Opts, false),
+ Inet = inetopt(DisableIpv6),
+ try sshc_sup:start_child([[{address, Host}, {port, Port},
+ {role, client},
+ {channel_pid, self()},
+ {socket_opts, [Inet | SocketOpts]},
+ {ssh_opts, [{host, Host}| Opts]}]]) of
+ {ok, ConnectionSup} ->
+ {ok, Manager} =
+ ssh_connection_controler: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
+ %% might return undefined as the connection manager
+ %% could allready have terminated, so we will not
+ %% match the Manager in this case
+ {_, 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) ->
+ receive %% Clear EXIT message from queue
+ {'EXIT', Manager, _What} ->
+ {error, Reason}
+ after 0 ->
+ {error, Reason}
+ end
+ after Timeout ->
+ do_demonitor(MRef, Manager),
+ ssh_connection_manager:stop(Manager),
+ {error, timeout}
+ end
+ catch
+ exit:{noproc, _} ->
+ {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
+%%
+%% Description: Closes an ssh connection.
+%%--------------------------------------------------------------------
+close(ConnectionRef) ->
+ ssh_connection_manager:stop(ConnectionRef).
+
+%%--------------------------------------------------------------------
+%% Function: connection_info(ConnectionRef) -> [{Option, Value}]
+%%
+%% Description: Retrieves information about a connection.
+%%--------------------------------------------------------------------
+connection_info(ConnectionRef, Options) ->
+ ssh_connection_manager:connection_info(ConnectionRef, Options).
+
+%%--------------------------------------------------------------------
+%% Function: channel_info(ConnectionRef) -> [{Option, Value}]
+%%
+%% Description: Retrieves information about a connection.
+%%--------------------------------------------------------------------
+channel_info(ConnectionRef, ChannelId, Options) ->
+ ssh_connection_manager:channel_info(ConnectionRef, ChannelId, Options).
+
+%%--------------------------------------------------------------------
+%% Function: daemon(Port) ->
+%% daemon(Port, Options) ->
+%% daemon(Address, Port, Options) -> SshSystemRef
+%%
+%% Description: Starts a server listening for SSH connections
+%% on the given port.
+%%--------------------------------------------------------------------
+daemon(Port) ->
+ daemon(Port, []).
+
+daemon(Port, Options) ->
+ daemon(any, Port, Options).
+
+daemon(HostAddr, Port, Options0) ->
+ Options1 = case proplists:get_value(shell, Options0) of
+ undefined ->
+ [{shell, {shell, start, []}} | Options0];
+ _ ->
+ Options0
+ end,
+ DisableIpv6 = proplists:get_value(ip_v6_disabled, Options0, false),
+ {Host, Inet, Options} = case HostAddr of
+ any ->
+ {ok, Host0} = inet:gethostname(),
+ {Host0, inetopt(DisableIpv6), Options1};
+ {_,_,_,_} ->
+ {HostAddr, inet,
+ [{ip, HostAddr} | Options1]};
+ {_,_,_,_,_,_,_,_} ->
+ {HostAddr, inet6,
+ [{ip, HostAddr} | Options1]}
+ end,
+ start_daemon(Host, Port, [{role, server} | Options], Inet).
+
+%%--------------------------------------------------------------------
+%% Function: stop_listener(SysRef) -> ok
+%% stop_listener(Address, Port) -> ok
+%%
+%%
+%% Description: Stops the listener, but leaves
+%% existing connections started by the listener up and running.
+%%--------------------------------------------------------------------
+stop_listener(SysSup) ->
+ ssh_system_sup:stop_listener(SysSup).
+stop_listener(Address, Port) ->
+ ssh_system_sup:stop_listener(Address, Port).
+
+%%--------------------------------------------------------------------
+%% Function: stop_daemon(SysRef) -> ok
+%%% stop_daemon(Address, Port) -> ok
+%%
+%%
+%% Description: Stops the listener and all connections started by
+%% the listener.
+%%--------------------------------------------------------------------
+stop_daemon(SysSup) ->
+ ssh_system_sup:stop_system(SysSup).
+stop_daemon(Address, Port) ->
+ ssh_system_sup:stop_system(Address, Port).
+
+%%--------------------------------------------------------------------
+%% Function: shell(Host [,Port,Options]) -> {ok, ConnectionRef} |
+%% {error, Reason}
+%%
+%% Host = string()
+%% Port = integer()
+%% Options = [{Option, Value}]
+%%
+%% Description: Starts an interactive shell to an SSH server on the
+%% given <Host>. The function waits for user input,
+%% and will not return until the remote shell is ended.(e.g. on
+%% exit from the shell)
+%%--------------------------------------------------------------------
+shell(Host) ->
+ shell(Host, ?SSH_DEFAULT_PORT, []).
+shell(Host, Options) ->
+ shell(Host, ?SSH_DEFAULT_PORT, Options).
+shell(Host, Port, Options) ->
+ case connect(Host, Port, Options) of
+ {ok, ConnectionRef} ->
+ case ssh_connection:session_channel(ConnectionRef, infinity) of
+ {ok,ChannelId} ->
+ Args = [{channel_cb, ssh_shell},
+ {init_args,[ConnectionRef, ChannelId]},
+ {cm, ConnectionRef}, {channel_id, ChannelId}],
+ {ok, State} = ssh_channel:init([Args]),
+ ssh_channel:enter_loop(State);
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+start_daemon(Host, Port, Options, Inet) ->
+ {SocketOpts, Opts} = handle_options(Options),
+ case ssh_system_sup:system_supervisor(Host, Port) of
+ undefined ->
+ %% TODO: It would proably make more sense to call the
+ %% address option host but that is a too big change at the
+ %% monent. The name is a legacy name!
+ try sshd_sup:start_child([{address, Host},
+ {port, Port}, {role, server},
+ {socket_opts, [Inet | SocketOpts]},
+ {ssh_opts, Opts}]) of
+ {ok, SysSup} ->
+ {ok, SysSup};
+ {error, {already_started, _}} ->
+ {error, eaddrinuse}
+ catch
+ exit:{noproc, _} ->
+ {error, ssh_not_started}
+ end;
+ Sup ->
+ case ssh_system_sup:restart_acceptor(Host, Port) of
+ {ok, _} ->
+ {ok, Sup};
+ _ ->
+ {error, eaddrinuse}
+ end
+ end.
+
+handle_options(Opts) ->
+ handle_options(proplists:unfold(Opts), [], []).
+handle_options([], SockOpts, Opts) ->
+ {SockOpts, Opts};
+%% TODO: Could do some type checks here on plain ssh-opts
+handle_options([{system_dir, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{user_dir, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{user_dir_fun, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{silently_accept_hosts, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{user_interaction, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{public_key_alg, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{connect_timeout, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{user, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{password, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{user_passwords, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{pwdfun, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{user_auth, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{key_cb, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{role, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{channel, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{compression, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{allow_user_interaction, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{infofun, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{connectfun, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{disconnectfun , _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{failfun, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{ip_v6_disabled, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]);
+handle_options([{ip, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, [Opt |SockOpts], Opts);
+handle_options([{ifaddr, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, [Opt |SockOpts], Opts);
+handle_options([{fd, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, [Opt | SockOpts], Opts);
+handle_options([{nodelay, _} = Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, [Opt | SockOpts], Opts);
+handle_options([Opt | Rest], SockOpts, Opts) ->
+ handle_options(Rest, SockOpts, [Opt | Opts]).
+
+inetopt(true) ->
+ inet6;
+
+inetopt(false) ->
+ inet.
+
+