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

[Question] How to read both stderr and stdout? #40

Open graywolf opened 7 months ago

graywolf commented 7 months ago

In the documentation I see that I can pick what output I want to read using channel-set-stream!. I can get both by using tr mode, but they are joined into single stream (as documented). I would like to be able to read both, but as separate streams.

It that possible?

My use case is to run remote command, capture the stdout for further processing, but display stderr to the user.

artyom-poptsov commented 7 months ago

As Guile-SSH uses libssh under the hood, it inherited some libssh API nuances. Although channel-set-stream! procedure does not directly mapped to any libssh procedure, it changes the data source for the subsequent reads from a channel port. In libssh ssh_channel_read procedure has special boolean paramter is_stderr that allows to specify what stream to read -- it can be stdout (0) or stderr (1), but not both. So when you read a Guile-SSH channel, the data will be requested from the specified stream.

However, I think you can read both stdout and stderr in the following way:

  1. Read all data that you need from a channel using stdout stream (set by default.)
  2. Switch to stderr stream by calling channel-set-stream!.
  3. Read all stderr data from the channel.
  4. Handle stdout and stderr as you wish in your program.

Please check if this algorithm works for you. -avp

graywolf commented 7 months ago

Yes, this approach does work for me, I reached the same conclusion as you suggested in the mean time. But it only works for "small" data (I think). I am not too versed in the intricacies of ssh protocol, but given the approach above working, there has to be some buffering going on. And those buffers have to be limited in size (if nothing else, by RAM).

So, if we imagine a situation when program keeps writing to both stdout and stderr, I assume it would get stuck sooner or later due to stderr buffer filling up. Is that correct?

To prevent that I believe reading from both at the same time is required, but it seems that is not currently possible. Does it make sense to explore it further? I can imagine some channel-get-stream-ports procedure that would return separate ports for stdout and stderr.

What are your thoughts regarding this?