Hello all, My scripts, which read stdout from ssh, weren't seeing EOF from the remote session.? It was being sent, but lost.? I tracked it down to the following code, in ssh.c, at ssh_session2_open: ??????? if (stdin_null_flag) { ??????????????? in = open(_PATH_DEVNULL, O_RDONLY); ??????? } else { ??????????????? in = dup(STDIN_FILENO); ??????? } ??????? out = dup(STDOUT_FILENO); ??????? err = dup(STDERR_FILENO); The remote session did close stdout.? The sshd from which it was spawned signaled to close stdout.? The ssh program received that signal and closed, well, something, but not stdout.? It closed a copy.? Importantly, it left a copy open, so my program got no EOF. Why not: ??????? if (stdin_null_flag) { ??????????????? in = open(_PATH_DEVNULL, O_RDONLY); ??????? } else { ??????????????? in = STDIN_FILENO; ??????? } ??????? out = STDOUT_FILENO; ??????? err = STDERR_FILENO; If not that, how is a program that reads from ssh's output ever going to see EOF? Thanks, David
Ping! On 10/10/17 05:18, David Newall wrote:> If not that, how is a program that reads from ssh's output ever going > to see EOF?Does anybody know?? It looks to me like the answer is "cannot", and I trust that nobody would claim that to be the preferred situation. I realize it's? not quite as simple as not duplicating the file descriptors, but I believe that is close to the answer.
On Tue, Oct 10, 2017 at 2:48 AM, David Newall <openssh at davidnewall.com> wrote:> Hello all, > > My scripts, which read stdout from ssh, weren't seeing EOF from the remote > session. It was being sent, but lost. >Hi David, How did you hit the problem? "ssh host echo hello | cat" works fine for me.
On Tue, 10 Oct 2017, David Newall wrote:> Hello all, > > My scripts, which read stdout from ssh, weren't seeing EOF from the > remote session.? It was being sent, but lost.? I tracked it down to the > following code, in ssh.c, at ssh_session2_open: > > ??????? if (stdin_null_flag) { > ??????????????? in = open(_PATH_DEVNULL, O_RDONLY); > ??????? } else { > ??????????????? in = dup(STDIN_FILENO); > ??????? } > ??????? out = dup(STDOUT_FILENO); > ??????? err = dup(STDERR_FILENO);This is probably because some things interact with stderr (and possibly stdout) separately than through the channels layer.> ??????? if (stdin_null_flag) { > ??????????????? in = open(_PATH_DEVNULL, O_RDONLY); > ??????? } else { > ??????????????? in = STDIN_FILENO; > ??????? } > ??????? out = STDOUT_FILENO; > ??????? err = STDERR_FILENO; > > If not that, how is a program that reads from ssh's output ever going to > see EOF?I don't think communicating closes on stdout separately to ssh itself terminating is a case we've considered supporting before. Maybe we could, but there are bound to be some subtleties beyond your suggested solution. At a minimum, I think we'd have to dup2 a fd to /dev/null to STDOUT_FILENO so writes to stdout (e.g. from ill-behaved libraries) have somewhere to go. I think we'd want to leave stderr the way it is too. -d
On 12/10/17 12:54, Clark Wang wrote:> How did you hit the problem? "ssh host echo hello | cat" works fine > for me.That's because ssh exited, not because of EOF. Consider the following: ?? ssh localhost 'exec cat > /dev/null' | { read l; echo ${l:-EOF}; } One should see "EOF" immediately (for values of immediate that include startup latency), but that's not what happens. I did have a better example.? When ssh was run with debug output, it showed ssh drain and close output when the remote end closed it's output.? Owing to ssh having two copies of the output descriptor, the downstream program did not see that EOF (it's not EOF until both file descriptors are closed.)? Now, irritatingly, I can't reproduce that.? Now, the remote program closes its output, the sshd parent closes its end of that pipe, but ssh smugly sits there, keeping output (both copies) open until the remote program exits.? Clearly there is more going on that just two descriptors to the same output pipe. It's wrong; ssh should not absorb EOF.
On 13/10/17 16:22, Damien Miller wrote: > At a minimum, I think we'd have to dup2 a fd to /dev/null to > STDOUT_FILENO so writes to stdout (e.g. from ill-behaved > libraries) have somewhere to go. Would that really be useful?? Output from Ill-behaved libraries, written fd 1, already go to the same place.? Don't forget, dup does not create a new file, it creates a duplicate handle to the same file.? I'm proposing getting rid of the duplicate.