Remote Pipes
============

(ssh popen) provides API for working with remote pipes, akin to (ice-9 popen) procedures (see Pipes (The GNU Guile Reference Manual))

MODE argument allows to specify what kind of pipe should be created. Allowed values are: OPEN_READ, OPEN_WRITE, OPEN_BOTH.

There is an additional value, OPEN_PTY, that allows to request a pseudo terminal.  The terminal is needed to run such commands as top.  Thus, to run top on the remote side you need to open a remote pipe with "t" flag set.

Note that when a PTY is used, a server merges stderr stream with stdout.

Values of the aforementioned constants:

OPEN_READ
     ``r''

OPEN_WRITE
     ``w''

OPEN_BOTH
     ``r+''

OPEN_PTY
     ``t''

 -- Scheme Procedure: open-remote-pipe session command mode
     Execute a COMMAND on the remote host using a SESSION with a pipe to it.  Returns newly created channel port with the specified MODE.

 -- Scheme Procedure: open-remote-pipe* session mode prog [args...]
     Execute PROG on the remote host with the given ARGS using a SESSION with a pipe to it.  Returns newly created channel port with the specified MODE.

 -- Scheme Procedure: open-remote-input-pipe session command
 -- Scheme Procedure: open-remote-input-pipe* session prog [args...]
     Equvalent to open-remote-pipe and open-remote-pipe* respectively with mode OPEN_READ.

 -- Scheme Procedure: open-remote-output-pipe session command
 -- Scheme Procedure: open-remote-output-pipe* session prog [args...]
     Equvalent to open-remote-pipe and open-remote-pipe* respectively with mode OPEN_WRITE.

Examples
--------

Simple cases
............

Here's a self-explanatory little script that executes uname -o command on the local host and prints the result:

     #!/usr/bin/guile \
     -e main -s
     !#

     (use-modules (ice-9 rdelim)             ; {read,write}-line
                  ;; Guile-SSH
                  (ssh session)
                  (ssh auth)
                  (ssh popen))               ; remote pipes

     (define (main args)
       ;; Make a session with local machine and the current user.
       (let ((session (make-session #:host "localhost")))

         ;; Connect the session and perform the authentication.
         (connect! session)
         (authenticate-server session)
         (userauth-agent! session)

         ;; Execute the command on the remote side and get the input pipe
         ;; to it.
         (let ((channel (open-remote-input-pipe session "uname -o")))
           ;; Read and display the result.
           (write-line (read-line channel)))))

Executing a command with a pseudo terminal
..........................................

Surely we aren't limited to one-line outputs; for example, we can watch top command executing on a remote side locally, by reading data from the channel in a loop:

     (define OPEN_PTY_READ (string-append OPEN_PTY OPEN_READ))

     (let ((channel (open-remote-pipe* session OPEN_PTY_READ
                                       "top" "-u avp")))
       (let r ((line (read-line channel)))
         (unless (eof-object? line)
           (write-line line)
           (r (read-line channel)))))

Or we can do the same, but this time with streams:

     (use-modules (srfi srfi-41)             ; streams
                  (ssh session)
                  (ssh auth)
                  (ssh popen))

     (define (pipe->stream p)
       (stream-let loop ((c (read-char p)))
         (if (eof-object? c)
             (begin
               (close-input-port p)
               stream-null)
             (stream-cons c (loop (read-char p))))))

     (define OPEN_PTY_READ (string-append OPEN_PTY OPEN_READ))

     (define (main args)
       (let ((s (make-session #:host "example.org")))
         (connect! s)
         (userauth-agent! s)
         (let ((rs (pipe->stream (open-remote-pipe* s OPEN_PTY_READ
                                                    "top" "-u avp"))))
           (stream-for-each display rs))))

Controlling the pseudo terminal size
....................................

To set the size of a pseudo terminal, one may use channel-set-pty-size! from (ssh channel).  For example:

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

     (define OPEN_PTY_READ (string-append OPEN_PTY OPEN_READ))

     ;; Opening of a Guile-SSH session goes here ...

     (let ((p (open-remote-pipe* session OPEN_PTY_READ "top" "-u avp")))
       (channel-set-pty-size! p 80 50)
       ;; Reading output from a port ...
       )