diff --git a/CHANGELOG.md b/CHANGELOG.md index 10e48d363..4de245716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ IMPROVEMENTS: - commands/provision: Add `--no-parallel` option to disable provider parallelization if the provider supports it. [GH-2404] - commands/ssh: SSH compression is enabled by default. [GH-2456] + - commands/ssh: Inline commands specified with "-c" are now executed + using OpenSSH rather than pure-Ruby SSH. It is MUCH faster, and + stdin works! - providers/virtualbox: Enable symlinks for VirtualBox 4.1. [GH-2414] - providers/virtualbox: default VM name now includes milliseconds with a random number to try to avoid conflicts in CI environments. [GH-2482] diff --git a/lib/vagrant/action/builtin/ssh_run.rb b/lib/vagrant/action/builtin/ssh_run.rb index 1969600eb..8b4457211 100644 --- a/lib/vagrant/action/builtin/ssh_run.rb +++ b/lib/vagrant/action/builtin/ssh_run.rb @@ -1,5 +1,7 @@ require "log4r" +require "vagrant/util/ssh" + module Vagrant module Action module Builtin @@ -13,26 +15,24 @@ module Vagrant end def call(env) - command = env[:ssh_run_command] + # Grab the SSH info from the machine + info = env[:machine].ssh_info - @logger.debug("Executing command: #{command}") - exit_status = 0 - exit_status = env[:machine].communicate.execute(command, :error_check => false) do |type, data| - # Determine the proper channel to send the output onto depending - # on the type of data we are receiving. - channel = type == :stdout ? :out : :error + # If the result is nil, then the machine is telling us that it is + # not yet ready for SSH, so we raise this exception. + raise Errors::SSHNotReady if info.nil? - # Print the output as it comes in, but don't prefix it and don't - # force a new line so that the output is properly preserved however - # it may be formatted. - env[:ui].info(data.to_s, - :prefix => false, - :new_line => false, - :channel => channel) + if info[:private_key_path] + # Check the SSH key permissions + Util::SSH.check_key_permissions(Pathname.new(info[:private_key_path])) end - # Set the exit status on a known environmental variable - env[:ssh_run_exit_status] = exit_status + # Execute! + command = env[:ssh_run_command] + opts = env[:ssh_opts] || {} + opts[:extra_args] = ["-t", command] + opts[:subprocess] = true + env[:ssh_run_exit_status] = Util::SSH.exec(info, opts) # Call the next middleware @app.call(env) diff --git a/lib/vagrant/util/ssh.rb b/lib/vagrant/util/ssh.rb index 91c0546fa..afbce4866 100644 --- a/lib/vagrant/util/ssh.rb +++ b/lib/vagrant/util/ssh.rb @@ -1,5 +1,7 @@ require "log4r" +require 'childprocess' + require "vagrant/util/file_mode" require "vagrant/util/platform" require "vagrant/util/safe_exec" @@ -148,8 +150,20 @@ module Vagrant ENV["nodosfilewarning"] = "1" if Platform.cygwin? # Invoke SSH with all our options - LOGGER.info("Invoking SSH: #{command_options.inspect}") - SafeExec.exec("ssh", *command_options) + if !opts[:subprocess] + LOGGER.info("Invoking SSH: #{command_options.inspect}") + SafeExec.exec("ssh", *command_options) + return + end + + # If we're still here, it means we're supposed to subprocess + # out to ssh rather than exec it. + LOGGER.info("Executing SSH in subprocess: #{command_options.inspect}") + process = ChildProcess.build("ssh", *command_options) + process.io.inherit! + process.start + process.wait + return process.exit_code end end end