@c -*-texinfo-*-
@c This file is part of Guile-SSH Reference Manual.
@c Copyright (C) 2014-2021 Artyom V. Poptsov
@c See the file guile-ssh.texi for copying conditions.

@node Channels
@section Channels

@menu
* Channel Management::
* Port Forwarding::
@end menu

@c -----------------------------------------------------------------------------
@node Channel Management
@subsection Channel Management

@cindex data transferring
@tindex channel

The @code{(ssh channel)} module provides facilities to create
Guile-SSH channels and manipulating of them.

Channels are implemented as GNU Guile ports.  Therefore they can be
used with regular I/O procedures such as @code{display}, @code{write},
@code{read-line} and friends (@pxref{Input and Output,,, guile, The
GNU Guile Reference Manual}).  This section describes operations that
are specific for the channels.

When a channel is closed by the remote side, the local side detects it;
reading from such channel gives an EOF object when all data is read.

Note that when the parent session from which a channel is made is freed the
channel is freed as well.

@deffn {Scheme Procedure} channel? x
Return @code{#t} if @var{x} is a Guile-SSH channel, @code{#f}
otherwise.
@end deffn

@deffn {Scheme Procedure} make-channel session [mode]
Allocate a new Guile-SSH channel for the @var{session} (@pxref{Sessions}).

@var{flags} are determine what kind of a channel should be created.  Possible
modes are: @code{OPEN_READ}, @code{OPEN_WRITE}, @code{OPEN_BOTH}.  They allow
to create either an input channel, output channel or input/output channel
respectively.
@end deffn

@deffn {Scheme Procedure} channel-open-session channel
Open a session channel.  This procedure actually turn the
@var{channel} into an open port available for I/O operations.  Throw
@code{guile-ssh-error} on error.  Return value is undefined.
@end deffn

@deffn {Scheme Procedure} channel-open? channel
Return @code{#t} if a @var{channel} is open, #f otherwise.  Note that this procedure
also returns @code{#f} when the remote side is closed.
@end deffn

@deffn {Scheme Procedure} channel-request-exec channel command
@cindex non-interactive SSH session
@cindex command execution
Run a shell @var{command} without an interactive shell.  The @var{channel}
must be open.  Throw @code{guile-ssh-error} on error.  Return value is
undefined.

This procedure is a low-level one and you should use remote pipes instead (@pxref{Remote Pipes}).

@strong{Note} that the procedure only can be used to execute a single command
on the remote host, so you should close the channel after
@code{channel-request-exec}.  If you want to execute another command then you
must open a new channel and use it.

Example:

@lisp
(let ((channel (make-channel session)))
  (channel-open-session channel)
  (channel-request-exec channel "uname")
  (read-line channel))
@result{} "Linux"
@end lisp

@end deffn

@deffn {Scheme Procedure} channel-request-pty channel
Request a @acronym{PTY} (pseudo terminal).  Throw @code{guile-ssh-error} on
error.  The @var{channel} must be open.  Return value is undefined.
@end deffn

@deffn {Scheme Procedure} channel-request-shell channel
Request a shell.  The @var{channel} must be open.  Throw
@code{guile-ssh-error} on error.  Return value is undefined.
@end deffn

@deffn {Scheme Procedure} channel-request-env channel variable value
@cindex setting of environment variables
Set an environment @var{variable} to @var{value}.  Throw
@code{guile-ssh-error} on error.  The @var{channel} must be open.  Return
value is undefined.
@end deffn

@deffn {Scheme Procedure} channel-request-send-exit-status channel exit-status
Send an @var{exit-status} to the remote process (as described in RFC 4254,
section 6.10).  Only SSH-v2 is supported.  Return value is undefined.

The @var{channel} needs to be closed with after this message.
@end deffn

@deffn {Scheme Procedure} channel-set-pty-size! channel columns rows
Change size of the @acronym{PTY} to @var{columns} and @var{rows}.  The
@var{channel} must be open.  Return value is undefined.
@end deffn

@deffn {Scheme Procedure} channel-set-stream! channel stream
Set default @var{stream} for @var{channel}.  @var{stream} must be one of the
following symbols: @code{stdout} (default), @code{stderr}.  The @var{channel}
must be open.  Throw @code{guile-ssh-error} on error.  Return value is
undefined.

Example:

@lisp
(channel-set-stream! channel 'stderr)
@end lisp
@end deffn

@deffn {Scheme Procedure} channel-get-stream channel
Get current stream name from @var{channel}.  The @var{channel} must be open.
Throw @code{guile-ssh-error} on error.  Return one of the following symbols:
@code{stdout}, @code{stderr}.

Example:

@lisp
(channel-get-stream channel)
@result{} 'stderr
@end lisp
@end deffn

@deffn {Scheme Procedure} channel-get-session channel
Get the session to which belongs the @var{channel}.  Throw
@code{guile-ssh-error} on an error.  Return the session.
@end deffn

@deffn {Scheme Procedure} channel-send-eof channel
Send an end of file (EOF) on the @var{channel}.  This action doesn't close the
@var{channel}; you may still read from it but not write.  Throw
@code{guile-ssh-error} on an error.  Return value is undefined.

Example:

@lisp
(use-modules (ice-9 rdelim)
             ;; Guile-SSH modules.
             (ssh auth)
             (ssh popen)
             (ssh session)
             (ssh channel))

;; Make a session
(define s (make-session #:host "example.org"))

;; Connect to the server
(connect! s)

;; Authenticate
(userauth-agent! s)

;; Open a remote pipe to 'wc' command running on
;; the server.
(let ((p (open-remote-pipe s "wc" OPEN_BOTH)))

  ;; Send data to 'wc' command using the remote pipe.
  (display "Hello Scheme World!" p)

  ;; 'wc' reads data until EOF and writes its result
  ;; afterwards.
  (channel-send-eof p)

  ;; Read the 'wc' output.
  (read-line p))
@result{} "      0       3      19"
@end lisp

@end deffn

@deffn {Scheme Procedure} channel-eof? channel
Return @code{#t} if remote has sent @acronym{EOF}, @code{#f} otherwise.  Throw
@code{guile-ssh-error} if the channel has been closed and freed.
@end deffn

@deffn {Scheme Procedure} channel-get-exit-status channel
Get the exit status of the @var{channel} (error code from the executed
instruction).  The @var{channel} must be open.  Return the exist status, or
@code{#f} if no exit status has been returned (yet).  Throw
@code{guile-ssh-error} on error.
@end deffn

@c -----------------------------------------------------------------------------
@node Port Forwarding
@subsection Port Forwarding

@cindex Port forwarding

Low-level API from @code{(ssh channel)} module to manage SSH port
forwarding. These procedures @strong{do not} bind the ports and do not
automatically forward the content of a socket to the channel.  You should
either implement binding and data forwarding in your application or use the
tunnel API (@pxref{Tunnels, Guile-SSH tunnel API})

@deffn {Scheme Procedure} channel-open-forward channel [#:source-host=''localhost''] #:local-port #:remote-host [#:remote-port=local-port]
Open a (local) TCP/IP forwarding @var{channel}.  Connect to a
@var{remote-host} and @var{remote-port}, and use @var{source-host} and
@var{local-port} as origination of connections.

The procedure returns one of the following symbols:
@table @samp
@item ok
Success.
@item again
We are in the nonblocking mode and the call to be done again.
@item error
An error occurred.
@end table

The local port forwarding works as follows:

@example
local-host               remote-host
,...............,        ,.................
:               :        :                :
:  [a browser]  :        : [a web server] :
:       |       :        :        A       :
:       |       :        :        |       :
:   port 8080   :        :     port 80    :
:       |       :        :        |       :
:       V       :        :        |       :
:  [SSH client]===========>[SSH server]   :
:               :        :                :
'...............'        '................'
@end example

Where port 8080 is an arbitrary @var{local-port} and port 80 is a
@var{remote-port}.

Also in our case, ``SSH client'' is an application that uses Guile-SSH and
calls @code{channel-open-forward}.

Example:

@lisp
(channel-open-forward channel
                      #:local-port  8080
                      #:remote-host "www.example.org"
                      #:remote-port 80)
@end lisp
@end deffn

@deffn {Scheme Procedure} channel-listen-forward session [#:address=#f] [#:port=0]
Start a TCP/IP reverse (remote) port forwarding.  Send the ``tcpip-forward''
global request using @var{session} to ask the server to begin listening for
inbound connections on the specified @var{address} and @var{port}.

If @var{address} is not specified (or set to @code{#f}) then the server binds
all addresses on all protocol families supported by the server.  When 0 is
passed as a @var{port} then server allocates the next unprivileged port.

The procedure returns two values: the first value is the result of the
operation, and the second value is the bound port number; if @var{port} was
set to 0 then the procedure returns the chosen port number.

The result of the operation can be one of the following symbols:
@table @samp
@item ok
Success.
@item again
We are in the nonblocking mode and the call to be done again.
@item error
An error occurred.
@end table

Reverse port forwarding looks as follows:

@example
local-host                remote-host
,................,        ,.................
:                :        :                :
: [a web server] :        :  [a browser]   :
:        A       :        :       |        :
:        |       :        :       |        :
:     port 80    :        :   port 8080    :
:        |       :        :       |        :
:        |       :        :       V        :
:   [SSH client]<===========[SSH server]   :
:                :        :                :
'................'        '................'
@end example

@end deffn

@deffn {Scheme Procedure} channel-accept-forward session [timeout=0]
Accept an incoming TCP/IP forwarding channel and get information about
incoming connection.  Return two values: the first value is the incoming
channel, and the second value is a port number on which the connection was
issued.
@end deffn

@deffn {Scheme Procedure} channel-cancel-forward session address port
Send ``cancel-tcpip-forward'' global request to @var{session} to ask the
server to cancel a ``tcpip-forward'' request on the bound @var{address} and
@var{port}.

The result of the operation can be one of the following symbols:
@table @samp
@item ok
Success.
@item again
We are in the nonblocking mode and the call to be done again.
@item error
An error occurred.
@end table

Here's an example Guile program that uses @code{channel-cancel-forward} to
cancel reverse port forwarding on a server:

@lisp
#!/usr/bin/guile \
-e main
!#

(use-modules (ssh session)
             (ssh auth)
             (ssh channel))

(define (main args)
  (let ((session (make-session #:user          "alice"
                               #:host          "127.0.0.1"
                               #:port          22
                               #:log-verbosity 'rare)))
    (connect! session)
    (userauth-agent! session)

    ;; Send "tcpip-forward" request to an SSH server
    (channel-listen-forward session #:address "localhost" #:port 12345)

    ;; Accept incoming reverse port forwarding requests with
    ;; 'channel-accept-forward' in some kind of loop...

    ;; Cancel the issued "tcpip-forward" request with
    ;; "cancel-tcpip-forward" request
    (channel-cancel-forward session "localhost" 12345)))
@end lisp
@end deffn

@c Local Variables:
@c TeX-master: "guile-ssh.texi"
@c End:
