Bug #10494
closedioctl returns EINVAL instead of ENOTTY for pipes on older linux, breaking piped test suite
Description
Yeah I know the title is a mouthful. I'll try to explain.
BACKGROUND:
Starting sometime recently, MRI's test suite started to use a custom version of test/unit contained in test/lib. Inside unit.rb, there's code that uses io/console to determine the window size for some fancy formatting.
https://github.com/ruby/ruby/blob/trunk/test/lib/test/unit.rb#L404
The surrounding rescue captures LoadError (io/console doesn't exist), NoMethodError (winsize is not defined), ENOTTY (fd is not a tty), and EBADF (fd is no longer valid), and provides suitable fallback code for console width.
PROBLEM:
However, when we ran the MRI suite with JRuby on Travis, we got the following error:
[exec] Run options: -q --
[exec]
[exec] # Running tests:
[exec]
[exec]
[exec] Errno::EINVAL: Invalid argument - ioctl(TIOCGWINSZ) winsize at /home/travis/build/jruby/jruby/lib/ruby/stdlib/io/console.rb:120
[exec] terminal_width at /home/travis/build/jruby/jruby/test/mri/lib/test/unit.rb:404
[exec] put_status at /home/travis/build/jruby/jruby/test/mri/lib/test/unit.rb:433
This is from within a Maven build target we use to run our test suites with the proper environment. We could not reproduce this on current Linux versions.
CAUSE:
For those unfamiliar with the JVM, the standard process-launching APIs always set the child's stdio to pipes that you can then read, a la popen in MRI. Long story short, it turns out that Linux incorrectly returned EINVAL instead of ENOTTY when calling ioctl with a pipe up until this fix in May of 2012: https://lkml.org/lkml/2012/5/25/142
This is after the release of Ubuntu 12.04 that Travis runs, and I'm guessing they have not updated the kernels.
REPRODUCTION (for Linux kernels prior to this patch):
I was able to reproduce this EINVAL on a Travis dev box (thanks asarih!) with both JRuby and MRI using the following command line:
$ rvm ruby-2.1 do ruby -rio/console -e 'p $stdout.winsize' | cat
-e:1:in `winsize': Invalid argument (Errno::EINVAL)
from -e:1:in `<main>'
SOLUTION:
????????
I'm not sure.
-
We could just patch the test library. I have done this for JRuby here: https://github.com/jruby/jruby/commit/29122372da5ba2f359239aa916e074a57fe96b70
-
We could modify io/console #winsize and other methods to raise this incorrect EINVAL as ENOTTY, since we should know in each of those methods that we have the right request and data structure (ergo EINVAL should not happen on non-buggy systems)
We need to do one or the other for the test suite to be runnable on these earlier Linux kernels when stdout is a pipe.