More discovery after printf debugging: it appears that acquiring the stderr stream increases the refcount on the stdio stream, so executing close on the stream won't actually solve my problem either, because the only way to work around close taking out both stdin and stdout is to allocate stderr. I think the only reasonable solution is to add a thin wrapper around libssh2_channel_send_eof to ssh2, and perhaps change the close stream function to call libssh2_channel_send_eof/libssh2_channel_close. On Thu, 2020-10-29 at 13:39 -0300, Calvin Buckley wrote: > After reading the ssh2 module source, I think the close function is > buggy in a way that causes the second issue. The first issue is > probably fundamental because of how the module leaks the ssh > abstraction to the stream level. > > (I'm using master to compare with.) > > At ssh2_fopen_wrappers.c+132, it does checks for EOF at the read > level, > but does nothing with this information. I suspect that this should be > send_eof to close stdin. On the following line, it frees the channel. > I > don't see any libssh2_channel_close code in the stream closing > function. There is no EOF sending code anywhere else as far as I > could > read. > > Annoyingly, this does mean that if fixed, this would probably mean > closing the main stdio stream will close both stdin and stdout, > requiring redirecting stdout into stderr to read it. The > bidirectional > stream doesn't really have any semantics to close stdin without > closing > stdout; I suspect there'd have to be another ssh2-specific function > to > close stdin by sending EOF. > > (Oh, and I did bump up to Fedora's 7.4.11 package. Didn't change > anything, obviously.) > > `On Wed, 2020-10-28 at 15:16 -0300, Calvin Buckley wrote: > > I've been having issues trying to use a command over ssh2_exec. The > > command takes input over standard input, waits for it to be closed, > > then writes back to standard output. Unfortunate, the ssh2 API > > seems > > deficient in multiple ways: > > > > First, the stream returned by ssh2_exec is for /both/ standard > > input > > and error. Closing means theoretically closing them both. This > > isn't > > /too/ bad, because I believe my program doesn't use standard error, > > so > > I just reassign the FDs at the shell level so that standard output > > is > > now on the separate standard error stream. > > > > Second, closing the stream doesn't actually seem to close standard > > input. The program (and cat, for the example below) still seems to > > think it should read from standard input and thus keeps standard > > output > > (well, error) open. > > > > This is annoying because it effectively means stream_get_contents > > is > > useless for me, because it'll block forever or with non-blocking, > > spit > > out a bunch of spurious zeros as it gets nothing constantly. cat > > will > > write back after the flush, but not immediately (some spurious zero > > reads, then a read with data, then more spurious), and my program > > relies on having standard input closed before it'll write. > > > > It appears others have tried to deal with this, but failed: > > https://stackoverflow.com/questions/19898351/php-close-input-channel-of-bidirectional-stream-from-ssh2-exec > > > > > > > > As with that person, I would /really/ not want to create a > > temporary > > file on the remote server if I can get away with it. > > > > I'm using PHP 7.4.8 w/ the PECL SSH2 extension that's shipped by > > Fedora. > > > > -- code -- > > > > <?php > > > > $server = "redact"; > > $user = "redact"; > > $password = "redact"; > > > > // no error handling in the sample program (real one does) > > $conn = ssh2_connect($server, 22); > > ssh2_auth_password($conn, $user, $password); > > > > // redirect stdout to stderr, because closing stdin also closes > > stdout > > // use cat as a surrogate (the program I'm calling takes all of > > stdin > > // and writes back to stdout on stdin EOF; cat will have to do to > > show > > // the broken behaviour) > > $ssh_stdio = ssh2_exec($conn, "cat 1>&2"); > > $ssh_stderr = ssh2_fetch_stream($ssh_stdio, SSH2_STREAM_STDERR); > > stream_set_blocking($ssh_stdio, true); > > stream_set_blocking($ssh_stderr, true); > > $written = fwrite($ssh_stdio, "the quick brown fox"); > > // attempt to close stdin (using stderr afterwards, explained > > above) > > fflush($ssh_stdio); > > fclose($ssh_stdio); > > // stderr blocking behaviour: > > // blocking: blocks forever because cat doesn't know it closed? > > // non-blocking: gets an empty string because cat hasn't written > > // back in time > > $out = stream_get_contents($ssh_stderr); > > var_dump($out); > >