diff --git a/lib/vagrant/util/subprocess.rb b/lib/vagrant/util/subprocess.rb index 9aa2358be..31d184d9a 100644 --- a/lib/vagrant/util/subprocess.rb +++ b/lib/vagrant/util/subprocess.rb @@ -90,6 +90,8 @@ module Vagrant process.io.stdout = stdout_writer process.io.stderr = stderr_writer process.duplex = true + process.leader = true if @options[:detach] + process.detach = true if @options[:detach] # Special installer-related things if Vagrant.in_installer? @@ -158,6 +160,12 @@ module Vagrant raise LaunchError.new(ex.message) end + # If running with the detach option, no need to capture IO or + # ensure program exists. + if @options[:detach] + return + end + # Make sure the stdin does not buffer process.io.stdin.sync = true @@ -261,7 +269,7 @@ module Vagrant # Return an exit status container return Result.new(process.exit_code, io_data[:stdout], io_data[:stderr]) ensure - if process && process.alive? + if process && process.alive? && !@options[:detach] # Make sure no matter what happens, the process exits process.stop(2) end diff --git a/plugins/hosts/linux/cap/rdp.rb b/plugins/hosts/linux/cap/rdp.rb index 23e742b8b..a31b92077 100644 --- a/plugins/hosts/linux/cap/rdp.rb +++ b/plugins/hosts/linux/cap/rdp.rb @@ -47,8 +47,9 @@ module VagrantPlugins args += rdp_info[:extra_args] if rdp_info[:extra_args] end + # require "pry-byebug"; binding.pry # Finally, run the client. - Vagrant::Util::Subprocess.execute(rdp_client, *args) + Vagrant::Util::Subprocess.execute(rdp_client, *args, {:detach => true}) end end end diff --git a/plugins/hosts/windows/cap/rdp.rb b/plugins/hosts/windows/cap/rdp.rb index 009dac377..35805c026 100644 --- a/plugins/hosts/windows/cap/rdp.rb +++ b/plugins/hosts/windows/cap/rdp.rb @@ -22,7 +22,7 @@ module VagrantPlugins args = rdp_info[:extra_args] + args end # Launch it - Vagrant::Util::Subprocess.execute("mstsc", *args) + Vagrant::Util::Subprocess.execute("mstsc", *args, {:detach => true}) end end end diff --git a/test/unit/vagrant/util/subprocess_test.rb b/test/unit/vagrant/util/subprocess_test.rb index 81da0e635..0ff5835c8 100644 --- a/test/unit/vagrant/util/subprocess_test.rb +++ b/test/unit/vagrant/util/subprocess_test.rb @@ -2,6 +2,9 @@ require File.expand_path("../../../base", __FILE__) require "vagrant/util/subprocess" describe Vagrant::Util::Subprocess do + let(:ls_test_commands) {[ described_class.new("ls"), described_class.new("ls", {:detach => true}) ]} + let(:sleep_test_commands) {[ described_class.new("sleep", "5"), described_class.new("sleep", "5", {:detach => true}) ]} + describe '#execute' do before do # ensure we have `cat` and `echo` in our PATH so that we can run these @@ -47,6 +50,11 @@ describe Vagrant::Util::Subprocess do expect(result.stdout).to eq(data) end + it 'does not wait for process if detach option specified' do + cat = described_class.new('cat', {:detach => true}) + expect(cat.execute).to eq(nil) + end + context "running within AppImage" do let(:appimage_ld_path) { nil } let(:exec_path) { "/exec/path" } @@ -102,22 +110,22 @@ describe Vagrant::Util::Subprocess do end describe "#running?" do - context "when subprocess has not been started" do - it "should return false" do - sp = described_class.new("ls") + it "should return false when subprocess has not been started" do + ls_test_commands.each do |sp| expect(sp.running?).to be(false) end end - context "when subprocess has completed" do - it "should return false" do - sp = described_class.new("ls") + + it "should return false when subprocess has completed" do + ls_test_commands.each do |sp| sp.execute + sleep(0.1) expect(sp.running?).to be(false) end end - context "when subprocess is running" do - it "should return true" do - sp = described_class.new("sleep", "5") + + it "should return true when subprocess is running" do + sleep_test_commands.each do |sp| thread = Thread.new{ sp.execute } sleep(0.3) expect(sp.running?).to be(true) @@ -130,34 +138,40 @@ describe Vagrant::Util::Subprocess do describe "#stop" do context "when subprocess has not been started" do it "should return false" do - sp = described_class.new("ls") - expect(sp.stop).to be(false) + ls_test_commands.each do |sp| + expect(sp.stop).to be(false) + end end end context "when subprocess has already completed" do it "should return false" do - sp = described_class.new("ls") - sp.execute - expect(sp.stop).to be(false) + ls_test_commands.each do |sp| + sp.execute + sleep(0.1) + expect(sp.stop).to be(false) + end end end context "when subprocess is running" do - let(:sp){ described_class.new("sleep", "1") } it "should return true" do - thread = Thread.new{ sp.execute } - sleep(0.1) - expect(sp.stop).to be(true) - thread.join + sleep_test_commands.each do |sp| + thread = Thread.new{ sp.execute } + sleep(0.1) + expect(sp.stop).to be(true) + thread.join + end end it "should stop the process" do - thread = Thread.new{ sp.execute } - sleep(0.1) - sp.stop - expect(sp.running?).to be(false) - thread.join + sleep_test_commands.each do |sp| + thread = Thread.new{ sp.execute } + sleep(0.1) + sp.stop + expect(sp.running?).to be(false) + thread.join + end end end end