From 6009492e7382db79e827349d973f13234658185b Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Tue, 31 Jul 2018 10:39:26 -0700 Subject: [PATCH] Detect AppImage and update executable environment if required If starting a process while running from within AppImage adjust the LD_LIBRARY_PATH of the subprocess when the executable exists outside of the AppImage. This prevents issues of invalid dynamic library lookups when the AppImage contains common named libraries. --- lib/vagrant/util/subprocess.rb | 14 +++++- test/unit/vagrant/util/subprocess_test.rb | 52 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/util/subprocess.rb b/lib/vagrant/util/subprocess.rb index 7efb1a436..789364b3b 100644 --- a/lib/vagrant/util/subprocess.rb +++ b/lib/vagrant/util/subprocess.rb @@ -93,7 +93,7 @@ module Vagrant # Special installer-related things if Vagrant.in_installer? - installer_dir = ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"].to_s.downcase + installer_dir = Vagrant.installer_embedded_dir.to_s.downcase # If we're in an installer on Mac and we're executing a command # in the installer context, then force DYLD_LIBRARY_PATH to look @@ -123,6 +123,18 @@ module Vagrant @logger.info("Command not in installer, restoring original environment...") jailbreak(process.environment) end + + # If running within an AppImage and calling external executable. When + # executable is external set the LD_LIBRARY_PATH to host values. + if ENV["VAGRANT_APPIMAGE"] + embed_path = Pathname.new(Vagrant.installer_embedded_dir).expand_path.to_s + exec_path = Pathname.new(@command[0]).expand_path.to_s + if !exec_path.start_with?(embed_path) && ENV["VAGRANT_APPIMAGE_LD_LIBRARY_PATH"] + @logger.info("Detected AppImage environment and request to external binary. Updating library path.") + @logger.debug("Setting LD_LIBRARY_PATH to #{ENV["VAGRANT_APPIMAGE_LD_LIBRARY_PATH"]}") + process.environment["LD_LIBRARY_PATH"] = ENV["VAGRANT_APPIMAGE_LD_LIBRARY_PATH"].to_s + end + end else @logger.info("Vagrant not running in installer, restoring original environment...") jailbreak(process.environment) diff --git a/test/unit/vagrant/util/subprocess_test.rb b/test/unit/vagrant/util/subprocess_test.rb index 144635fde..8b8dc81ba 100644 --- a/test/unit/vagrant/util/subprocess_test.rb +++ b/test/unit/vagrant/util/subprocess_test.rb @@ -46,6 +46,58 @@ describe Vagrant::Util::Subprocess do # we should see our data as the output from `cat` expect(result.stdout).to eq(data) end + + context "running within AppImage" do + let(:appimage_ld_path) { nil } + let(:exec_path) { "/exec/path" } + let(:appimage_path) { "/appimage" } + let(:process) { double("process", io: process_io, environment: process_env) } + let(:process_io) { double("process_io") } + let(:process_env) { double("process_env") } + let(:subject) { described_class.new(exec_path) } + + before do + allow(process).to receive(:start) + allow(process).to receive(:duplex=) + allow(process).to receive(:alive?).and_return(false) + allow(process).to receive(:exited?).and_return(true) + allow(process).to receive(:poll_for_exit).and_return(0) + allow(process).to receive(:exit_code).and_return(0) + allow(process_io).to receive(:stdout=) + allow(process_io).to receive(:stderr=) + allow(process_io).to receive(:stdin).and_return(double("io_stdin", "sync=" => true)) + allow(process_env).to receive(:[]=) + allow(ENV).to receive(:[]).with("VAGRANT_INSTALLER_ENV").and_return("1") + allow(ENV).to receive(:[]).with("VAGRANT_APPIMAGE").and_return("1") + allow(ENV).to receive(:[]).with("VAGRANT_APPIMAGE_LD_LIBRARY_PATH").and_return(appimage_ld_path) + allow(File).to receive(:file?).with(exec_path).and_return(true) + allow(ChildProcess).to receive(:build).and_return(process) + allow(Vagrant).to receive(:installer_embedded_dir).and_return(appimage_path) + allow(Vagrant).to receive(:user_data_path).and_return("") + end + + after { subject.execute } + + it "should not update LD_LIBRARY_PATH when environment variable is not set" do + expect(process_env).not_to receive(:[]=).with("LD_LIBRARY_PATH", anything) + end + + context "when APPIMAGE_LD_LIBRARY_PATH environment variable is set" do + let(:appimage_ld_path) { "APPIMAGE_SYSTEM_LIBS" } + + it "should set LD_LIBRARY_PATH when executable is not within appimage" do + expect(process_env).to receive(:[]=).with("LD_LIBRARY_PATH", appimage_ld_path) + end + + context "when executable is located within AppImage" do + let(:exec_path) { "#{appimage_path}/exec/path" } + + it "should not set LD_LIBRARY_PATH" do + expect(process_env).not_to receive(:[]=).with("LD_LIBRARY_PATH", anything) + end + end + end + end end describe "#running?" do