I'm trying to manage a SSH connection to a network device via the PTY module, with a code similar to this:
cmd_line = "ssh coltrane@love.supreme.com"
begin
PTY.spawn(cmd_line) do |r_f,w_f,pid|
...
rescue PTY::ChildExited => cended
...
end
The whole I/O works pretty well, however I don't know how to get the exit status of the child process.
For instance, if the connection is broken or simply times out, the spawned process will terminate with an error code, but this code does not seem to be returned in the $?
special variable.
TLDR
Use 1.9.2 and wait on the PTY process to correctly set $?
Full Story
On 1.9.2 you can capture the exit status for PTY by calling wait on the PTY pid. This works out almost all the time (AFAIK). The only exceptions I know of are with edge cases like exiting immediately or issuing an empty string for a command (see http://redmine.ruby-lang.org/issues/5253).
For example:
Now run the test:
On 1.8.7 you need to do a bit more. In older rubies PTY would often exit with PTY::ChildExited errors, even when you wait for the PTY process to finish. As a result, if you run the tests as written you get this:
Notice ALMOST all the tests bomb with a ChildExited error, but one (incidentally the one representing the most realistic use of PTY) succeeds as expected. Surely this erratic behavior is a bug and, as already shown, it has been fixed in 1.9.2.
There is a partial workaround, however. You can specifically handle the ChildExited errors using something like this:
Insert that, run the tests again, and you get results consistent with 1.9.2, with the BIG caveat that $? will not be set correctly (unlike 1.9.2). Specifically if you were to add this test:
You get success on 1.9.2 and you get failure on 1.8.7. In the 1.8.7 case the PTY completes via the ChildExited error -- the Process.wait never gets called and thus never sets $?. Instead the $? from the 'system "true"' persists and you get 0 instead of 1 as the exit status.
The behavior of $? is hard to follow and has more caveats that I won't get into (ie sometimes the PTY will complete via the Process.wait).
In abstract: In Linux The parent should wait()s for this child to know the exit status of his child.
C code:
I'm sorry.I don't have experience with ruby to provide you with some code. Best wishes :)
Ok, here are some possible solutions for this problem :
use ruby 1.9.2 PTY.check() method
wrap the command line in a script
Unfortunately I can't use the latest version of ruby as so I used the wrapper solution, that echoes $? to a file at the end of the wrapper script. The exit code is read when the spawned child exits.
Of course if something interrupts the execution of the wrapper script itself, then we'll never get the result file ...
But at least this workaround can be used for 1.8.7/1.9.1 versions of Ruby