Bug #22438
openmkmf.rb do not work with /home mounted with noexec
Description
When trying to install a vagrant plugin (here vagrant-libvirt), it uses the file mkmf.rb (here /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb) and this fail with the following error message:
Vagrant failed to install the requested plugin because development tools
are required for installation but are not currently installed on this
machine. Please install development tools and then try this command
again.
It is the user facing error message that is from the following underlying command:
/opt/vagrant/embedded/bin/ruby --verbose extconf.rb
checking for pkg-config for libvirt... /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:480:in `try_do': The compiler failed to generate an executable file. (RuntimeError)
You have to install development tools first.
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:573:in `try_link0'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:591:in `try_link'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:683:in `try_ldflags'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:1896:in `block in pkg_config'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:983:in `block in checking_for'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:344:in `block (2 levels) in postpone'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:314:in `open'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:344:in `block in postpone'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:314:in `open'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:340:in `postpone'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:982:in `checking_for'
from /opt/vagrant/embedded/lib/ruby/3.3.0/mkmf.rb:1862:in `pkg_config'
from extconf.rb:5:in `<main>'
The mentioned try_do function is where the issue is.
Here is its code (as the ruby version 3.3.8):
def try_do(src, command, *opts, &b)
unless have_devel?
raise <<MSG
The compiler failed to generate an executable file.
You have to install development tools first.
MSG
end
begin
src = create_tmpsrc(src, &b)
xsystem(command, *opts)
ensure
log_src(src)
end
end
Which is called by try_link0 function that has the following code:
def try_link0(src, opt="", *opts, &b) # :nodoc:
exe = CONFTEST+$EXEEXT
cmd = link_command("", opt)
if $universal
require 'tmpdir'
Dir.mktmpdir("mkmf_", oldtmpdir = ENV["TMPDIR"]) do |tmpdir|
begin
ENV["TMPDIR"] = tmpdir
try_do(src, cmd, *opts, &b)
ensure
ENV["TMPDIR"] = oldtmpdir
end
end
else
try_do(src, cmd, *opts, &b)
end and File.executable?(exe) or return nil
exe
ensure
MakeMakefile.rm_rf(*Dir["#{CONFTEST}*"]-[exe])
end
So you can see, if my understanding is correct, that the environment variableTMPDIR, despite being set, is never used/honored to execute the command.
The command is still running in the current working directory, and the TMPDIR is ignored/useless.
In my case the current working directory is ~/.vagrant.d/gems/3.3.8/gems/ruby-libvirt-0.8.4/ext/libvirt which is in my /home mounted with noexec.
This makes File.executable?(exe) (exe is the compiled file) returning false because the source and the compiled file (even if they are just to test) end up in my working directory and not in the temporary directory specified with the environment variable TMPDIR which have exec permission.
So I suggest to always use a temporary directory to execute all the tests contained in this code file mkmf.rb and to actually honor TMPDIR when defined.
No data to display