artyom-poptsov / guile-ssh

Guile-SSH is a library that provides access to the SSH protocol for GNU Guile programs.
https://memory-heap.org/~avp/projects/guile-ssh
GNU General Public License v3.0
65 stars 13 forks source link

(ssh popen) merges stdout and stderr #2

Closed davexunit closed 6 years ago

davexunit commented 8 years ago

When reading text over a remote pipe, the input text I am reading contains both the text written to standard output as well as standard input. I noticed this because I messed up my .bashrc and referenced a function I did not have defined, which caused bash to write to stderr.

The code I'm using is this:

(use-modules (ice-9 rdelim)
             (ssh session)
             (ssh auth)
             (ssh popen))

(define session (make-session #:host "some-host"))

(connect! session)
(authenticate-server session)
(userauth-agent! session)

(let ((channel (open-remote-input-pipe session "uname -o")))
  ;; Read and display the result.
  (write-line (read-line channel)))

I would expect this to print "GNU/Linux", but instead it prints something like "home/dave/.bashrc: line 50: foo: command not found". The OpenSSH command-line client does not have this issue.

$ ssh some-host uname -o > /tmp/foo
/home/dave/.bashrc: line 50: foo: command not found
$ cat /tmp/foo
GNU/Linux
artyom-poptsov commented 8 years ago

Hi,

thanks for reporting. I'm investigating the problem; meanwhile you could use channel-get-exit-status from (ssh channel) as a workaround to check whether the command execution was successful or not. For example:

(let ((channel (open-remote-input-pipe session "uname -o")))
  (if (zero? (channel-get-exit-status channel))
      (write-line (read-line channel))
      (write-line (string-append "ERROR: " (read-line channel))
                  (current-error-port))))

-- Artyom

davexunit commented 8 years ago

In my case, the command is successful, it just happens to have output written to stderr.

artyom-poptsov commented 8 years ago

Hello David,

the bug should be fixed now in 71a6e1e on the master branch, please check if it works for you.

Thanks!

-- Artyom

artyom-poptsov commented 8 years ago

Hello,

here's an example of error handling:

(use-modules (ice-9 rdelim)
             (ssh channel)
             (ssh session)
             (ssh auth)
             (ssh popen))

(let ((session (make-session #:host "localhost")))
  (connect! session)
  (userauth-agent! session)
  (let ((channel (open-remote-input-pipe* session "uname" "--badfood")))
    (if (zero? (channel-get-exit-status channel))
        (write-line (read-line channel))
        (begin
         (channel-set-stream! channel 'stderr)
         (format #t "ERROR: ~a~%" (read-line channel))))))

Also there's a more generic solution committed in 2ab0287 on the 'master' branch; now it's possible to explicitly ask a server for a pseudo terminal to run commands such as top (although when PTY is used the server merges stderr and stdout.) See the latest (guile-ssh) Remote Pipes documentation for details.

-- Artyom

artyom-poptsov commented 6 years ago

I'm closing the issue as the bug was fixed long ago.

-- Artyom