diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/common_test/src/ct_ssh.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/common_test/src/ct_ssh.erl')
-rw-r--r-- | lib/common_test/src/ct_ssh.erl | 1346 |
1 files changed, 1346 insertions, 0 deletions
diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl new file mode 100644 index 0000000000..f2b25b1fcd --- /dev/null +++ b/lib/common_test/src/ct_ssh.erl @@ -0,0 +1,1346 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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% +%% + +%%% @doc SSH/SFTP client module. +%%% +%%% <p>ct_ssh uses the OTP ssh application and more detailed information +%%% about e.g. functions, types and options can be found in the +%%% documentation for this application.</p> +%%% +%%% <p>The <code>Server</code> argument in the SFTP functions should +%%% only be used for SFTP sessions that have been started on existing +%%% SSH connections (i.e. when the original connection type is +%%% <code>ssh</code>). Whenever the connection type is +%%% <code>sftp</code>, use the SSH connection reference only.</p> +%%% +%%% <p>The following options are valid for specifying an SSH/SFTP +%%% connection (i.e. may be used as config elements):</p> +%%% +%%% <pre> +%%% +%%% [{ConnType, Addr}, +%%% {port, Port}, +%%% {user, UserName} +%%% {password, Pwd} +%%% {user_dir, String} +%%% {public_key_alg, PubKeyAlg} +%%% {connect_timeout, Timeout} +%%% {key_cb, KeyCallbackMod}] +%%% </pre> +%%% +%%% <p><code>ConnType = ssh | sftp</code>.</p> +%%% <p>Please see ssh(3) for other types.</p> +%%% +%%% <p>All timeout parameters in ct_ssh functions are values in +%%% milliseconds.</p> +%%% +%%% @type connection() = handle() | ct:target_name() +%%% @type handle() = ct_gen_conn:handle(). Handle for a specific +%%% SSH/SFTP connection. +%%% @type ssh_sftp_return() = term(). A return value from an ssh_sftp function. + +-module(ct_ssh). + +%% SSH Functions +-export([connect/1, connect/2, connect/3, + disconnect/1, + session_open/1, session_open/2, + session_close/2, + send/3, send/4, send/5, + receive_response/2, receive_response/3, receive_response/4, + send_and_receive/3, send_and_receive/4, send_and_receive/5, + send_and_receive/6, + exec/2, exec/3, exec/4, + subsystem/3, subsystem/4]). + +%% STFP Functions +-export([sftp_connect/1, + + read_file/2, write_file/3, list_dir/2, open/3, opendir/2, + close/2, read/3, pread/4, aread/3, apread/4, write/3, + pwrite/4, awrite/3, apwrite/4, position/3, read_file_info/2, + get_file_info/2, read_link_info/2, write_file_info/3, + read_link/2, make_symlink/3, rename/3, delete/2, make_dir/2, + del_dir/2, + + read_file/3, write_file/4, list_dir/3, open/4, opendir/3, + close/3, read/4, pread/5, aread/4, apread/5, write/4, + pwrite/5, awrite/4, apwrite/5, position/4, read_file_info/3, + get_file_info/3, read_link_info/3, write_file_info/4, + read_link/3, make_symlink/4, rename/4, delete/3, make_dir/3, + del_dir/3]). + +%% Callbacks +-export([init/3, handle_msg/2, reconnect/2, terminate/2, close/1]). + +-define(DEFAULT_TIMEOUT, 10000). + +-record(state, {ssh_ref, conn_type, target}). + + +%%%----------------------------------------------------------------- +%%%------------------------ SSH COMMANDS --------------------------- + +%%%----------------------------------------------------------------- +%%% @spec connect(KeyOrName) -> {ok,Handle} | {error,Reason} +%%% @equiv connect(KeyOrName,host,[]) +connect(KeyOrName) -> + connect(KeyOrName, host). + +%%%----------------------------------------------------------------- +%%% @spec connect(KeyOrName,ConnType) -> {ok,Handle} | {error,Reason} +%%% @equiv connect(KeyOrName,ConnType,[]) +connect(KeyOrName, ConnType) when is_atom(ConnType) -> + connect(KeyOrName, ConnType, []); + +%%%----------------------------------------------------------------- +%%% @spec connect(KeyOrName,ExtraOpts) -> {ok,Handle} | {error,Reason} +%%% @equiv connect(KeyOrName,host,ExtraOpts) +connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) -> + connect(KeyOrName, host, ExtraOpts). + +%%%----------------------------------------------------------------- +%%% @spec connect(KeyOrName,ConnType,ExtraOpts) -> +%%% {ok,Handle} | {error,Reason} +%%% KeyOrName = Key | Name +%%% Key = atom() +%%% Name = ct:target_name() +%%% ConnType = ssh | sftp | host +%%% ExtraOpts = ssh_connect_options() +%%% Handle = handle() +%%% Reason = term() +%%% +%%% @doc Open an SSH or SFTP connection using the information +%%% associated with <code>KeyOrName</code>. +%%% +%%% <p>If <code>Name</code> (an alias name for <code>Key</code>), +%%% is used to identify the connection, this name may +%%% be used as connection reference for subsequent calls. +%%% It's only possible to have one open connection at a time +%%% associated with <code>Name</code>. If <code>Key</code> is +%%% used, the returned handle must be used for subsequent calls +%%% (multiple connections may be opened using the config +%%% data specified by <code>Key</code>).</p> +%%% +%%% <p><code>ConnType</code> will always override the type +%%% specified in the address tuple in the configuration data (and +%%% in <code>ExtraOpts</code>). So it is possible to for example +%%% open an sftp connection directly using data originally +%%% specifying an ssh connection. The value <code>host</code> +%%% means the connection type specified by the host option +%%% (either in the configuration data or in <code>ExtraOpts</code>) +%%% will be used.</p> +%%% +%%% <p><code>ExtraOpts</code> (optional) are extra SSH options +%%% to be added to the config data for <code>KeyOrName</code>. +%%% The extra options will override any existing options with the +%%% same key in the config data. For details on valid SSH +%%% options, see the documentation for the OTP ssh application.</p> +connect(KeyOrName, ConnType, ExtraOpts) -> + case ct:get_config(KeyOrName) of + undefined -> + log(heading(connect,KeyOrName), "Failed: ~p\n", + [{not_available,KeyOrName}]), + {error,{not_available,KeyOrName}}; + SSHData -> + AllOpts = ExtraOpts++SSHData, + {ConnType1,Addr,AllOpts1} = + case ConnType of + host -> + case proplists:get_value(ssh, AllOpts) of + undefined -> + case proplists:get_value(sftp, AllOpts) of + undefined -> + log(heading(connect,KeyOrName), + "No host information specified!\n",[]); + SFTPAddr -> + {sftp,SFTPAddr,AllOpts} + end; + SSHAddr -> + {ssh,SSHAddr,AllOpts} + end; + _ -> + case proplists:get_value(ConnType, AllOpts) of + undefined when ConnType == ssh -> + case proplists:get_value(sftp, AllOpts) of + undefined -> + {ssh,undefined,AllOpts}; + SFTPAddr -> + log(heading(connect,KeyOrName), + "Note: Opening ssh connection to sftp host.\n", + []), + {ssh,SFTPAddr, + [{ssh,SFTPAddr}|proplists:delete(sftp, AllOpts)]} + end; + undefined when ConnType == sftp -> + case proplists:get_value(ssh, AllOpts) of + undefined -> + {sftp,undefined,AllOpts}; + SSHAddr -> + log(heading(connect,KeyOrName), + "Note: Opening sftp connection to ssh host.\n", + []), + {sftp,SSHAddr, + [{sftp,SSHAddr}|proplists:delete(ssh, AllOpts)]} + end; + SSHorSFTPAddr -> + {ConnType,SSHorSFTPAddr,AllOpts} + end + end, + case {Addr,proplists:get_value(port, AllOpts1)} of + {undefined,_} -> + log(heading(connect,KeyOrName), "Failed: ~p\n", + [{not_available,{KeyOrName,ConnType1}}]), + {error,{not_available,{KeyOrName,ConnType1}}}; + {_,undefined} -> + log(heading(connect,KeyOrName), + "Opening ~w connection to ~p:22\n", + [ConnType1,Addr]), + ct_gen_conn:start(KeyOrName, {ConnType1,Addr,22}, + AllOpts1, ?MODULE); + {_,Port} -> + log(heading(connect,KeyOrName), + "Opening ~w connection to ~p:~w\n", + [ConnType1,Addr,Port]), + ct_gen_conn:start(KeyOrName, {ConnType1,Addr,Port}, + AllOpts1, ?MODULE) + end + end. + +%%%----------------------------------------------------------------- +%%% @spec disconnect(SSH) -> ok | {error,Reason} +%%% SSH = connection() +%%% Reason = term() +%%% +%%% @doc Close an SSH/SFTP connection. +disconnect(SSH) -> + case get_handle(SSH) of + {ok,Pid} -> + log(heading(disconnect,SSH), "Handle: ~p", [Pid]), + case ct_gen_conn:stop(Pid) of + {error,{process_down,Pid,noproc}} -> + {error,already_closed}; + Result -> + Result + end; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% @spec session_open(SSH) -> {ok,ChannelId} | {error, Reason} +%%% @equiv session_open(SSH,DefaultTimeout) +session_open(SSH) -> + call(SSH, {session_open,?DEFAULT_TIMEOUT}). + +%%%----------------------------------------------------------------- +%%% @spec session_open(SSH,Timeout) -> {ok,ChannelId} | {error, Reason} +%%% SSH = connection() +%%% Timeout = integer() +%%% ChannelId = integer() +%%% Reason = term() +%%% +%%% @doc Opens a channel for an SSH session. +session_open(SSH, Timeout) -> + call(SSH, {session_open,Timeout}). + +%%%----------------------------------------------------------------- +%%% @spec session_close(SSH,ChannelId) -> ok | {error, Reason} +%%% SSH = connection() +%%% ChannelId = integer() +%%% Reason = term() +%%% +%%% @doc Closes an SSH session channel. +session_close(SSH, ChannelId) -> + call(SSH, {session_close,ChannelId}). + +%%%----------------------------------------------------------------- +%%% @spec exec(SSH,Command) -> {ok,Data} | {error,Reason} +%%% @equiv exec(SSH,Command,DefaultTimeout) +exec(SSH, Command) -> + exec(SSH, undefined, Command, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec exec(SSH,Command,Timeout) -> {ok,Data} | {error,Reason} +%%% SSH = connection() +%%% Command = string() +%%% Timeout = integer() +%%% Data = list() +%%% Reason = term() +%%% +%%% @doc Requests server to perform <code>Command</code>. A session +%%% channel is opened automatically for the request. +%%% <code>Data</code> is received from the server as a result +%%% of the command. +exec(SSH, Command, Timeout) when is_list(Command) -> + exec(SSH, undefined, Command, Timeout); + +%%%----------------------------------------------------------------- +%%% @spec exec(SSH,ChannelId,Command) -> {ok,Data} | {error,Reason} +%%% @equiv exec(SSH,ChannelId,Command,DefaultTimeout) +exec(SSH, ChannelId, Command) when is_integer(ChannelId) -> + exec(SSH, ChannelId, Command, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec exec(SSH,ChannelId,Command,Timeout) -> {ok,Data} | {error,Reason} +%%% SSH = connection() +%%% ChannelId = integer() +%%% Command = string() +%%% Timeout = integer() +%%% Data = list() +%%% Reason = term() +%%% +%%% @doc Requests server to perform <code>Command</code>. A previously +%%% opened session channel is used for the request. +%%% <code>Data</code> is received from the server as a result +%%% of the command. +exec(SSH, ChannelId, Command, Timeout) -> + call(SSH, {exec,ChannelId,Command,Timeout}). + +%%%----------------------------------------------------------------- +%%% @spec receive_response(SSH,ChannelId) -> {ok,Data} | {error,Reason} +%%% @equiv receive_response(SSH,ChannelId,close) +receive_response(SSH, ChannelId) -> + receive_response(SSH, ChannelId, close, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec receive_response(SSH,ChannelId,End) -> {ok,Data} | {error,Reason} +%%% @equiv receive_response(SSH,ChannelId,End,DefaultTimeout) +receive_response(SSH, ChannelId, End) when is_function(End) -> + receive_response(SSH, ChannelId, End, ?DEFAULT_TIMEOUT); + +%%%----------------------------------------------------------------- +%%% @spec receive_response(SSH,ChannelId,Timeout) -> {ok,Data} | {error,Reason} +%%% @equiv receive_response(SSH,ChannelId,close,Timeout) +receive_response(SSH, ChannelId, Timeout) when is_integer(Timeout) -> + receive_response(SSH, ChannelId, close, Timeout). + +%%%----------------------------------------------------------------- +%%% @spec receive_response(SSH,ChannelId,End,Timeout) -> +%%% {ok,Data} | {timeout,Data} | {error,Reason} +%%% SSH = connection() +%%% ChannelId = integer() +%%% End = Fun | close | timeout +%%% Timeout = integer() +%%% Data = list() +%%% Reason = term() +%%% +%%% @doc Receives expected data from server on the specified +%%% session channel. +%%% +%%% <p>If <code>End == close</code>, data is returned +%%% to the caller when the channel is closed by the +%%% server. If a timeout occurs before this happens, +%%% the function returns <code>{timeout,Data}</code> +%%% (where <code>Data</code> is the data received so far). +%%% If <code>End == timeout</code>, a timeout is expected +%%% and <code>{ok,Data}</code> is returned both in the case +%%% of a timeout and when the channel is closed. If +%%% <code>End</code> is a fun, this fun will be +%%% called with one argument - the data value in a received +%%% <code>ssh_cm</code> message (see ssh_connection(3)). The +%%% fun should return <code>true</code> to end the receiving +%%% operation (and have the so far collected data returned), or +%%% <code>false</code> to wait for more data from the server. +%%% (Note that even if a fun is supplied, the function returns +%%% immediately if the server closes the channel).</p> +receive_response(SSH, ChannelId, End, Timeout) -> + call(SSH, {receive_response,ChannelId,End,Timeout}). + +%%%----------------------------------------------------------------- +%%% @spec send(SSH,ChannelId,Data) -> ok | {error,Reason} +%%% @equiv send(SSH,ChannelId,0,Data,DefaultTimeout) +send(SSH, ChannelId, Data) -> + send(SSH, ChannelId, 0, Data, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec send(SSH,ChannelId,Data,Timeout) -> ok | {error,Reason} +%%% @equiv send(SSH,ChannelId,0,Data,Timeout) +send(SSH, ChannelId, Data, Timeout) when is_integer(Timeout) -> + send(SSH, ChannelId, 0, Data, Timeout); + +%%%----------------------------------------------------------------- +%%% @spec send(SSH,ChannelId,Type,Data) -> ok | {error,Reason} +%%% @equiv send(SSH,ChannelId,Type,Data,DefaultTimeout) +send(SSH, ChannelId, Type, Data) when is_integer(Type) -> + send(SSH, ChannelId, Type, Data, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec send(SSH,ChannelId,Type,Data,Timeout) -> ok | {error,Reason} +%%% SSH = connection() +%%% ChannelId = integer() +%%% Type = integer() +%%% Data = list() +%%% Timeout = integer() +%%% Reason = term() +%%% +%%% @doc Send data to server on specified session channel. +send(SSH, ChannelId, Type, Data, Timeout) -> + call(SSH, {send,ChannelId,Type,Data,Timeout}). + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Data) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,Data,close) +send_and_receive(SSH, ChannelId, Data) -> + send_and_receive(SSH, ChannelId, 0, Data, close, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Data,End) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,0,Data,End,DefaultTimeout) +send_and_receive(SSH, ChannelId, Data, End) when is_function(End) -> + send_and_receive(SSH, ChannelId, 0, Data, End, ?DEFAULT_TIMEOUT); + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Data,Timeout) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,0,Data,close,Timeout) +send_and_receive(SSH, ChannelId, Data, Timeout) when is_integer(Timeout) -> + send_and_receive(SSH, ChannelId, 0, Data, close, Timeout); + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Type,Data) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,Type,Data,close,DefaultTimeout) +send_and_receive(SSH, ChannelId, Type, Data) when is_integer(Type) -> + send_and_receive(SSH, ChannelId, Type, Data, close, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Data,End,Timeout) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,0,Data,End,Timeout) +send_and_receive(SSH, ChannelId, Data, End, Timeout) when is_integer(Timeout) -> + send_and_receive(SSH, ChannelId, 0, Data, End, Timeout); + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Type,Data,Timeout) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,Type,Data,close,Timeout) +send_and_receive(SSH, ChannelId, Type, Data, Timeout) when is_integer(Type) -> + send_and_receive(SSH, ChannelId, Type, Data, close, Timeout); + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Type,Data,End) -> +%%% {ok,Data} | {error,Reason} +%%% @equiv send_and_receive(SSH,ChannelId,Type,Data,End,DefaultTimeout) +send_and_receive(SSH, ChannelId, Type, Data, End) when is_function(End) -> + send_and_receive(SSH, ChannelId, Type, Data, End, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec send_and_receive(SSH,ChannelId,Type,Data,End,Timeout) -> +%%% {ok,Data} | {error,Reason} +%%% SSH = connection() +%%% ChannelId = integer() +%%% Type = integer() +%%% Data = list() +%%% End = Fun | close | timeout +%%% Timeout = integer() +%%% Reason = term() +%%% +%%% @doc Send data to server on specified session channel and wait +%%% to receive the server response. +%%% +%%% <p>See <code>receive_response/4</code> for details on the +%%% <code>End</code> argument.</p> +send_and_receive(SSH, ChannelId, Type, Data, End, Timeout) -> + call(SSH, {send_and_receive,ChannelId,Type,Data,End,Timeout}). + +%%%----------------------------------------------------------------- +%%% @spec subsystem(SSH,ChannelId,Subsystem) -> Status | {error,Reason} +%%% @equiv subsystem(SSH,ChannelId,Subsystem,DefaultTimeout) +subsystem(SSH, ChannelId, Subsystem) -> + subsystem(SSH, ChannelId, Subsystem, ?DEFAULT_TIMEOUT). + +%%%----------------------------------------------------------------- +%%% @spec subsystem(SSH,ChannelId,Subsystem,Timeout) -> +%%% Status | {error,Reason} +%%% SSH = connection() +%%% ChannelId = integer() +%%% Subsystem = string() +%%% Timeout = integer() +%%% Status = success | failure +%%% Reason = term() +%%% +%%% @doc Sends a request to execute a predefined subsystem. +subsystem(SSH, ChannelId, Subsystem, Timeout) -> + call(SSH, {subsystem,ChannelId,Subsystem,Timeout}). + + +%%%----------------------------------------------------------------- +%%%------------------------ SFTP COMMANDS -------------------------- + +%%%----------------------------------------------------------------- +%%% @spec sftp_connect(SSH) -> {ok,Server} | {error,Reason} +%%% SSH = connection() +%%% Server = pid() +%%% Reason = term() +%%% @doc Starts an SFTP session on an already existing SSH connection. +%%% <code>Server</code> identifies the new session and must be +%%% specified whenever SFTP requests are to be sent. +sftp_connect(SSH) -> + call(SSH, sftp_connect). + +%%%----------------------------------------------------------------- +%%% @spec read_file(SSH, File) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_file(SSH, File) -> + call(SSH, {read_file,sftp,File}). +%%%----------------------------------------------------------------- +%%% @spec read_file(SSH, Server, File) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_file(SSH, Server, File) -> + call(SSH, {read_file,Server,File}). + +%%%----------------------------------------------------------------- +%%% @spec write_file(SSH, File, Iolist) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +write_file(SSH, File, Iolist) -> + call(SSH, {write_file,sftp,File,Iolist}). +%%%----------------------------------------------------------------- +%%% @spec write_file(SSH, Server, File, Iolist) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +write_file(SSH, Server, File, Iolist) -> + call(SSH, {write_file,Server,File,Iolist}). + +%%%----------------------------------------------------------------- +%%% @spec list_dir(SSH, Path) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +list_dir(SSH, Path) -> + call(SSH, {list_dir,sftp,Path}). +%%%----------------------------------------------------------------- +%%% @spec list_dir(SSH, Server, Path) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +list_dir(SSH, Server, Path) -> + call(SSH, {list_dir,Server,Path}). + +%%%----------------------------------------------------------------- +%%% @spec open(SSH, File, Mode) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +open(SSH, File, Mode) -> + call(SSH, {open,sftp,File,Mode}). +%%%----------------------------------------------------------------- +%%% @spec open(SSH, Server, File, Mode) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +open(SSH, Server, File, Mode) -> + call(SSH, {open,Server,File,Mode}). + +%%%----------------------------------------------------------------- +%%% @spec opendir(SSH, Path) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +opendir(SSH, Path) -> + call(SSH, {opendir,sftp,Path}). +%%%----------------------------------------------------------------- +%%% @spec opendir(SSH, Server, Path) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +opendir(SSH, Server, Path) -> + call(SSH, {opendir,Server,Path}). + +%%%----------------------------------------------------------------- +%%% @spec close(SSH, Handle) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +close(SSH, Handle) -> + call(SSH, {close,sftp,Handle}). +%%%----------------------------------------------------------------- +%%% @spec close(SSH, Server, Handle) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +close(SSH, Server, Handle) -> + call(SSH, {close,Server,Handle}). + +%%%----------------------------------------------------------------- +%%% @spec read(SSH, Handle, Len) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read(SSH, Handle, Len) -> + call(SSH, {read,sftp,Handle,Len}). +%%%----------------------------------------------------------------- +%%% @spec read(SSH, Server, Handle, Len) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read(SSH, Server, Handle, Len) -> + call(SSH, {read,Server,Handle,Len}). + +%%%----------------------------------------------------------------- +%%% @spec pread(SSH, Handle, Position, Length) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +pread(SSH, Handle, Position, Length) -> + call(SSH, {pread,sftp,Handle,Position,Length}). +%%%----------------------------------------------------------------- +%%% @spec pread(SSH, Server, Handle, Position, Length) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +pread(SSH, Server, Handle, Position, Length) -> + call(SSH, {pread,Server,Handle,Position,Length}). + +%%%----------------------------------------------------------------- +%%% @spec aread(SSH, Handle, Len) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +aread(SSH, Handle, Len) -> + call(SSH, {aread,sftp,Handle,Len}). +%%%----------------------------------------------------------------- +%%% @spec aread(SSH, Server, Handle, Len) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +aread(SSH, Server, Handle, Len) -> + call(SSH, {aread,Server,Handle,Len}). + +%%%----------------------------------------------------------------- +%%% @spec apread(SSH, Handle, Position, Length) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +apread(SSH, Handle, Position, Length) -> + call(SSH, {apread,sftp,Handle,Position,Length}). +%%%----------------------------------------------------------------- +%%% @spec apread(SSH, Server, Handle, Position, Length) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +apread(SSH, Server, Handle, Position, Length) -> + call(SSH, {apread,Server,Handle,Position,Length}). + +%%%----------------------------------------------------------------- +%%% @spec write(SSH, Handle, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +write(SSH, Handle, Data) -> + call(SSH, {write,sftp,Handle,Data}). +%%%----------------------------------------------------------------- +%%% @spec write(SSH, Server, Handle, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +write(SSH, Server, Handle, Data) -> + call(SSH, {write,Server,Handle,Data}). + +%%%----------------------------------------------------------------- +%%% @spec pwrite(SSH, Handle, Position, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +pwrite(SSH, Handle, Position, Data) -> + call(SSH, {pwrite,sftp,Handle,Position,Data}). +%%%----------------------------------------------------------------- +%%% @spec pwrite(SSH, Server, Handle, Position, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +pwrite(SSH, Server, Handle, Position, Data) -> + call(SSH, {pwrite,Server,Handle,Position,Data}). + +%%%----------------------------------------------------------------- +%%% @spec awrite(SSH, Handle, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +awrite(SSH, Handle, Data) -> + call(SSH, {awrite,sftp,Handle, Data}). +%%%----------------------------------------------------------------- +%%% @spec awrite(SSH, Server, Handle, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +awrite(SSH, Server, Handle, Data) -> + call(SSH, {awrite,Server,Handle, Data}). + +%%%----------------------------------------------------------------- +%%% @spec apwrite(SSH, Handle, Position, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +apwrite(SSH, Handle, Position, Data) -> + call(SSH, {apwrite,sftp,Handle,Position,Data}). +%%%----------------------------------------------------------------- +%%% @spec apwrite(SSH, Server, Handle, Position, Data) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +apwrite(SSH, Server, Handle, Position, Data) -> + call(SSH, {apwrite,Server,Handle,Position,Data}). + +%%%----------------------------------------------------------------- +%%% @spec position(SSH, Handle, Location) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +position(SSH, Handle, Location) -> + call(SSH, {position,sftp,Handle,Location}). +%%%----------------------------------------------------------------- +%%% @spec position(SSH, Server, Handle, Location) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +position(SSH, Server, Handle, Location) -> + call(SSH, {position,Server,Handle,Location}). + +%%%----------------------------------------------------------------- +%%% @spec read_file_info(SSH, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_file_info(SSH, Name) -> + call(SSH, {read_file_info,sftp,Name}). +%%%----------------------------------------------------------------- +%%% @spec read_file_info(SSH, Server, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_file_info(SSH, Server, Name) -> + call(SSH, {read_file_info,Server,Name}). + +%%%----------------------------------------------------------------- +%%% @spec get_file_info(SSH, Handle) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +get_file_info(SSH, Handle) -> + call(SSH, {get_file_info,sftp,Handle}). +%%%----------------------------------------------------------------- +%%% @spec get_file_info(SSH, Server, Handle) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +get_file_info(SSH, Server, Handle) -> + call(SSH, {get_file_info,Server,Handle}). + +%%%----------------------------------------------------------------- +%%% @spec read_link_info(SSH, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_link_info(SSH, Name) -> + call(SSH, {read_link_info,sftp,Name}). +%%%----------------------------------------------------------------- +%%% @spec read_link_info(SSH, Server, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_link_info(SSH, Server, Name) -> + call(SSH, {read_link_info,Server,Name}). + +%%%----------------------------------------------------------------- +%%% @spec write_file_info(SSH, Name, Info) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +write_file_info(SSH, Name, Info) -> + call(SSH, {write_file_info,sftp,Name,Info}). +%%%----------------------------------------------------------------- +%%% @spec write_file_info(SSH, Server, Name, Info) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +write_file_info(SSH, Server, Name, Info) -> + call(SSH, {write_file_info,Server,Name,Info}). + +%%%----------------------------------------------------------------- +%%% @spec read_link(SSH, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_link(SSH, Name) -> + call(SSH, {read_link,sftp,Name}). +%%%----------------------------------------------------------------- +%%% @spec read_link(SSH, Server, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +read_link(SSH, Server, Name) -> + call(SSH, {read_link,Server,Name}). + +%%%----------------------------------------------------------------- +%%% @spec make_symlink(SSH, Name, Target) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +make_symlink(SSH, Name, Target) -> + call(SSH, {make_symlink,sftp,Name,Target}). +%%%----------------------------------------------------------------- +%%% @spec make_symlink(SSH, Server, Name, Target) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +make_symlink(SSH, Server, Name, Target) -> + call(SSH, {make_symlink,Server,Name,Target}). + +%%%----------------------------------------------------------------- +%%% @spec rename(SSH, OldName, NewName) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +rename(SSH, OldName, NewName) -> + call(SSH, {rename,sftp,OldName,NewName}). +%%%----------------------------------------------------------------- +%%% @spec rename(SSH, Server, OldName, NewName) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +rename(SSH, Server, OldName, NewName) -> + call(SSH, {rename,Server,OldName,NewName}). + +%%%----------------------------------------------------------------- +%%% @spec delete(SSH, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +delete(SSH, Name) -> + call(SSH, {delete,sftp,Name}). +%%%----------------------------------------------------------------- +%%% @spec delete(SSH, Server, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +delete(SSH, Server, Name) -> + call(SSH, {delete,Server,Name}). + +%%%----------------------------------------------------------------- +%%% @spec make_dir(SSH, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +make_dir(SSH, Name) -> + call(SSH, {make_dir,sftp,Name}). +%%%----------------------------------------------------------------- +%%% @spec make_dir(SSH, Server, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +make_dir(SSH, Server, Name) -> + call(SSH, {make_dir,Server,Name}). + +%%%----------------------------------------------------------------- +%%% @spec del_dir(SSH, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +del_dir(SSH, Name) -> + call(SSH, {del_dir,sftp,Name}). +%%%----------------------------------------------------------------- +%%% @spec del_dir(SSH, Server, Name) -> Result +%%% SSH = connection() +%%% Result = ssh_sftp_return() | {error,Reason} +%%% Reason = term() +%%% @doc For info and other types, see ssh_sftp(3). +del_dir(SSH, Server, Name) -> + call(SSH, {del_dir,Server,Name}). + + +%%%================================================================= +%%% Callback functions +%%%================================================================= + +%% @hidden +init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> + User = proplists:get_value(user, AllOpts), + Password = case proplists:get_value(password, AllOpts) of + undefined -> ""; + Pwd -> Pwd + end, + AllOpts1 = case proplists:get_value(connect_timeout, AllOpts) of + undefined -> + [{connect_timeout,?DEFAULT_TIMEOUT}|AllOpts]; + _ -> + AllOpts + end, + Options = + lists:foldl(fun({ssh,_},Opts) -> Opts; + ({sftp,_},Opts) -> Opts; + ({port,_},Opts) -> Opts; + ({silently_accept_hosts,_},Opts) -> Opts; + ({user_interaction,_},Opts) -> Opts; + (Opt={Key,_},Opts) -> + case lists:keymember(Key, 1, Opts) of + true -> Opts; + false -> [Opt|Opts] + end; + (_,Opts) -> Opts + end, [], AllOpts1), + FinalOptions = [{silently_accept_hosts,true}, + {user_interaction,false} | Options], + crypto:start(), + ssh:start(), + Result = case ConnType of + ssh -> + ssh:connect(Addr, Port, FinalOptions); + sftp -> + ssh_sftp:connect(Addr, Port, FinalOptions) + end, + case Result of + {ok,SSHRef} -> + log(heading(init,KeyOrName), + "Opened ~w connection:\nHost: ~p (~p)\nUser: ~p\nPassword: ~p\n", + [ConnType,Addr,Port,User,lists:duplicate(length(Password),$*)]), + {ok,SSHRef,#state{ssh_ref=SSHRef, conn_type=ConnType, + target=KeyOrName}}; + Error -> + Error + end. + +%% @hidden +handle_msg(sftp_connect, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]), + {ssh_sftp:connect(SSHRef),State}; + +handle_msg({session_open,TO}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", [SSHRef,TO]), + {ssh_connection:session_channel(SSHRef, TO),State}; + +handle_msg({session_close,Chn}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]), + {ssh_connection:close(SSHRef, Chn),State}; + +handle_msg({exec,Chn,Command,TO}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + Chn1 = + if Chn == undefined -> + log(heading(exec,Target), + "Opening channel for exec, SSH Ref: ~p", [SSHRef]), + case ssh_connection:session_channel(SSHRef, TO) of + {ok,C} -> C; + CErr -> CErr + end; + true -> + Chn + end, + case Chn1 of + {error,_} = ChnError -> + log(heading(exec,Target), "Opening channel failed: ~p", [ChnError]), + {ChnError,State}; + _ -> + log(heading(exec,Target), + "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p", + [SSHRef,Chn1,Command,TO]), + case ssh_connection:exec(SSHRef, Chn1, Command, TO) of + success -> + Result = do_recv_response(SSHRef, Chn1, [], close, TO), + ssh_connection:close(SSHRef, Chn1), + {Result,State}; + Other -> + {{error,Other},State} + end + end; + +handle_msg({receive_response,Chn,End,TO}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(receive_response,Target), + "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]), + Result = do_recv_response(SSHRef, Chn, [], End, TO), + {Result,State}; + +handle_msg({send,Chn,Type,Data,TO}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(send,Target), + "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" + "Data: ~p", [SSHRef,Chn,Type,TO,Data]), + Result = ssh_connection:send(SSHRef, Chn, Type, Data, TO), + {Result,State}; + +handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(send_and_receive,Target), + "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" + "Data: ~p", [SSHRef,Chn,Type,TO,Data]), + case ssh_connection:send(SSHRef, Chn, Type, Data, TO) of + ok -> + Result = do_recv_response(SSHRef, Chn, [], End, TO), + {Result,State}; + Error -> + {Error,State} + end; + +handle_msg({subsystem,Chn,Subsystem,TO}, State) -> + #state{ssh_ref=SSHRef, target=Target} = State, + log(heading(subsystem,Target), + "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p", + [SSHRef,Chn,Subsystem,TO]), + Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO), + {Result,State}; + +%% --- SFTP Commands --- + +handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:read_file(ref(Srv,SSHRef), File),S}; + +handle_msg({write_file,Srv,File,Iolist}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:write_file(ref(Srv,SSHRef), File, Iolist),S}; + +handle_msg({list_dir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:list_dir(ref(Srv,SSHRef), Path),S}; + +handle_msg({open,Srv,File,Mode}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:open(ref(Srv,SSHRef), File, Mode),S}; + +handle_msg({opendir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:opendir(ref(Srv,SSHRef), Path),S}; + +handle_msg({close,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:close(ref(Srv,SSHRef), Handle),S}; + +handle_msg({read,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:read(ref(Srv,SSHRef), Handle, Len),S}; + +handle_msg({pread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:pread(ref(Srv,SSHRef),Handle,Position,Length),S}; + +handle_msg({aread,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:aread(ref(Srv,SSHRef), Handle, Len),S}; + +handle_msg({apread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:apread(ref(Srv,SSHRef), Handle, Position, Length),S}; + +handle_msg({write,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:write(ref(Srv,SSHRef), Handle, Data),S}; + +handle_msg({pwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:pwrite(ref(Srv,SSHRef), Handle, Position, Data),S}; + +handle_msg({awrite,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:awrite(ref(Srv,SSHRef), Handle, Data),S}; + +handle_msg({apwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:apwrite(ref(Srv,SSHRef), Handle, Position, Data),S}; + +handle_msg({position,Srv,Handle,Location}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:position(ref(Srv,SSHRef), Handle, Location),S}; + +handle_msg({read_file_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:read_file_info(ref(Srv,SSHRef), Name),S}; + +handle_msg({get_file_info,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:get_file_info(ref(Srv,SSHRef), Handle),S}; + +handle_msg({read_link_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:read_link_info(ref(Srv,SSHRef), Name),S}; + +handle_msg({write_file_info,Srv,Name,Info}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:write_file_info(ref(Srv,SSHRef), Name, Info),S}; + +handle_msg({read_link,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:read_link(ref(Srv,SSHRef), Name),S}; + +handle_msg({make_symlink,Srv,Name,Target}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:make_symlink(ref(Srv,SSHRef), Name, Target),S}; + +handle_msg({rename,Srv,OldName,NewName}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:rename(ref(Srv,SSHRef), OldName, NewName),S}; + +handle_msg({delete,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:delete(ref(Srv,SSHRef), Name),S}; + +handle_msg({make_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:make_dir(ref(Srv,SSHRef), Name),S}; + +handle_msg({del_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> + log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + {ssh_sftp:del_dir(ref(Srv,SSHRef), Name),S}. + +%% @hidden +reconnect(_Addr,_State) -> + {error,no_reconnection_of_ssh}. + +%% @hidden +close(SSHRef) -> + disconnect(SSHRef). + +%% @hidden +terminate(SSHRef, State) -> + case State#state.conn_type of + ssh -> + log(heading(disconnect_ssh,State#state.target), + "SSH Ref: ~p",[SSHRef]), + ssh:close(SSHRef); + sftp -> + log(heading(disconnect_sftp,State#state.target), + "SFTP Ref: ~p",[SSHRef]), + ssh_sftp:stop(SSHRef) + end. + + +%%%================================================================= +%%% Internal functions + +%%%----------------------------------------------------------------- +%%% +do_recv_response(SSH, Chn, Data, End, Timeout) -> + receive + + {ssh_cm, SSH, {open,Chn,RemoteChn,{session}}} -> + debug("RECVD open"), + {ok,{open,Chn,RemoteChn,{session}}}; + + {ssh_cm, SSH, {closed,Chn}} -> + ssh_connection:close(SSH, Chn), + debug("CLSD~n~p ~p", [SSH,Chn]), + {ok,Data}; + + {ssh_cm, SSH, {data,Chn,_,NewData}} -> + ssh_connection:adjust_window(SSH, Chn, size(NewData)), + debug("RECVD~n~p", [binary_to_list(NewData)]), + DataAcc = Data ++ binary_to_list(NewData), + if is_function(End) -> + case End(DataAcc) of + true -> + {ok,DataAcc}; + false -> + do_recv_response(SSH, Chn, DataAcc, End, Timeout) + end; + true -> + do_recv_response(SSH, Chn, DataAcc, End, Timeout) + end; + + {ssh_cm, SSH, {eof,Chn}} -> + debug("RECVD EOF~n~p ~p", [SSH,Chn]), + {ok,Data}; + + {ssh_cm, SSH, {exit_signal,Chn,Signal,Err,_Lang}} -> + debug("RECVD exit_signal~n~p ~p~n~p ~p", [SSH,Chn,Signal,Err]), + do_recv_response(SSH, Chn, Data, End, Timeout); +%% {ok,{exit_signal,Chn,Signal,Err,_Lang}}; + + {ssh_cm, SSH, {exit_status,Chn,Status}} -> + debug("RECVD exit_status~n~p ~p~n~p", [SSH,Chn,Status]), + do_recv_response(SSH, Chn, Data, End, Timeout); +%% {ok,{exit_status,Chn,_Status}}; + + +%% --- INTERACTIVE MESSAGES - NOT HANDLED --- +%% +%% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} -> +%% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p", +%% [SSH,Chn,WantReply]), +%% ssh_connection:reply_request(SSH, WantReply, success, Chn), +%% do_recv_response(SSH, Chn, Data, End, Timeout); + +%% {ssh_cm, SSH, {shell,WantReply}} -> +%% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p", +%% [SSH,Chn,WantReply]), +%% ssh_connection:reply_request(SSH, WantReply, success, Chn), +%% do_recv_response(SSH,Chn,Data,End,Timeout); + +%% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} -> +%% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p", +%% [SSH,Chn,WantReply,Pty]), +%% ssh_connection:reply_request(SSH, WantReply, success, Chn), +%% do_recv_response(SSH, Chn, Data, End, Timeout); + +%% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} -> +%% debug("RECVD WINCH"), +%% {ok,WCh}; + + Other -> + debug("UNEXPECTED MESSAGE~n~p ~p~n~p", [SSH,Chn,Other]), + do_recv_response(SSH, Chn, Data, End, Timeout) + + after Timeout -> + case End of + timeout -> + {ok,Data}; + _ -> + {timeout,Data} + end + end. + +%%%----------------------------------------------------------------- +%%% +get_handle(SSH) when is_pid(SSH) -> + {ok,SSH}; +get_handle(SSH) -> + case ct_util:get_connections(SSH, ?MODULE) of + {ok,[{Pid,_}]} -> + {ok,Pid}; + {ok,[]} -> + connect(SSH); + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% +call(SSH, Msg) -> + case get_handle(SSH) of + {ok,Pid} -> + ct_gen_conn:call(Pid, Msg); + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% +ref(sftp, SSHRef) -> SSHRef; +ref(Server, _) -> Server. + +%%%----------------------------------------------------------------- +%%% +mod(Cmd) -> + [Op,_Server|Args] = tuple_to_list(Cmd), + list_to_tuple([Op|Args]). + +%%%----------------------------------------------------------------- +%%% +heading(Function, Ref) -> + io_lib:format("ct_ssh:~w ~p",[Function,Ref]). + +%%%----------------------------------------------------------------- +%%% +log(Heading, Str, Args) -> + ct_gen_conn:log(Heading, Str, Args). + + +%%%----------------------------------------------------------------- +%%% +debug(Str) -> + debug(Str, []). + +debug(_Str, _Args) -> +%% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args), + ok. |