Channels
========

Channel Management
------------------

The (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 display, write, read-line and friends (see Input and Output (The GNU Guile Reference Manual)).  This section describes operations that are specific for the channels.

 -- Scheme Procedure: channel? x
     Return #t if X is a Guile-SSH channel, #f otherwise.

 -- Scheme Procedure: make-channel session [mode]
     Allocate a new Guile-SSH channel for the SESSION (see Sessions).

     FLAGS are determine what kind of a channel should be created.  Possible modes are: OPEN_READ, OPEN_WRITE, OPEN_BOTH.  They allow to create either an input channel, output channel or input/output channel respectively.

 -- Scheme Procedure: channel-open-session channel
     Open a session channel.  This procedure actually turn the CHANNEL into an open port available for I/O operations.  Throw guile-ssh-error on error.  Return value is undefined.

 -- Scheme Procedure: channel-request-exec channel command
     Run a shell COMMAND without an interactive shell.  The CHANNEL must be open.  Throw guile-ssh-error on error.  Return value is undefined.

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

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

     Example:

          (let ((channel (make-channel session)))
            (channel-open-session channel)
            (channel-request-exec channel "uname")
            (read-line channel))
          => "Linux"

 -- Scheme Procedure: channel-request-pty channel
     Request a PTY (pseudo terminal).  Throw guile-ssh-error on error.  The CHANNEL must be open.  Return value is undefined.

 -- Scheme Procedure: channel-request-shell channel
     Request a shell.  The CHANNEL must be open.  Throw guile-ssh-error on error.  Return value is undefined.

 -- Scheme Procedure: channel-request-env channel variable value
     Set an environment VARIABLE to VALUE.  Throw guile-ssh-error on error.  The CHANNEL must be open.  Return value is undefined.

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

     The CHANNEL needs to be closed with after this message.

 -- Scheme Procedure: channel-set-pty-size! channel columns rows
     Change size of the PTY to COLUMNS and ROWS.  The CHANNEL must be open.  Return value is undefined.

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

     Example:

          (channel-set-stream! channel 'stderr)

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

     Example:

          (channel-get-stream channel)
          => 'stderr

 -- Scheme Procedure: channel-get-session channel
     Get the session to which belongs the CHANNEL.  Throw guile-ssh-error on an error.  Return the session.

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

     Example:

          (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))
          => "      0       3      19"

 -- Scheme Procedure: channel-eof? channel
     Return #t if remote has sent EOF, #f otherwise.  Throw guile-ssh-error if the channel has been closed and freed.

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

Port Forwarding
---------------

Low-level API from (ssh channel) module to manage SSH port forwarding. These procedures 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 (see Guile-SSH tunnel API)

 -- Scheme Procedure: channel-open-forward channel [#:source-host=''localhost''] #:local-port #:remote-host [#:remote-port=local-port]
     Open a (local) TCP/IP forwarding CHANNEL.  Connect to a REMOTE-HOST and REMOTE-PORT, and use SOURCE-HOST and LOCAL-PORT as origination of connections.

     The procedure returns one of the following symbols:

     ok
          Success.

     again
          We are in the nonblocking mode and the call to be done again.

     error
          An error occurred.

     The local port forwarding works as follows:

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

     Where port 8080 is an arbitrary LOCAL-PORT and port 80 is a REMOTE-PORT.

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

     Example:

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

 -- 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 SESSION to ask the server to begin listening for inbound connections on the specified ADDRESS and PORT.

     If ADDRESS is not specified (or set to #f) then the server binds all addresses on all protocol families supported by the server.  When 0 is passed as a 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 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:

     ok
          Success.

     again
          We are in the nonblocking mode and the call to be done again.

     error
          An error occurred.

     Reverse port forwarding looks as follows:

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

 -- 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.

 -- Scheme Procedure: channel-cancel-forward session address port
     Send ``cancel-tcpip-forward'' global request to SESSION to ask the server to cancel a ``tcpip-forward'' request on the bound ADDRESS and PORT.

     The result of the operation can be one of the following symbols:

     ok
          Success.

     again
          We are in the nonblocking mode and the call to be done again.

     error
          An error occurred.

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

          #!/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)))