SSH#sudo! more compatible with all commands, example in details.

As pointed out by Charles Duffy, commands such as the following
didn't work in the previous version:

    bash -c 'foo; bar'

Due to the way sudo! used `echo` to output lines into a tempfile.
This is now fixed.
This commit is contained in:
Mitchell Hashimoto 2011-01-24 19:58:32 -08:00
parent 14f66b1bc2
commit afcbeb9e7b
1 changed files with 42 additions and 32 deletions

View File

@ -31,18 +31,23 @@ module Vagrant
# this command is tailor-made to be compliant with older versions
# of `sudo`.
def sudo!(commands, options=nil, &block)
# First, make a temporary file to store the script
filename = exec!("mktemp /tmp/vagrant-command-#{'X' * 10}")
session.open_channel do |ch|
ch.exec("sudo -i sh") do |ch2, success|
# Output each command as if they were entered on the command line
[commands].flatten.each do |command|
ch2.send_data "#{command}\n"
end
# Output each command into the temporary file
[commands].flatten.each do |command|
exec!("echo #{command} >> #{filename}")
# Remember to exit or we'll hang!
ch2.send_data "exit\n"
# Setup the callbacks with our options so we get all the
# stdout/stderr and error checking goodies
setup_channel_callbacks(ch2, commands, options, block)
end
ch.wait
end
# Finally, execute the file, passing in the parameters since this
# is the expected command to run.
exec!("sudo chmod +x #{filename}")
exec!("sudo -i #{filename}", options, &block)
end
# Executes a given command on the SSH session and blocks until
@ -52,6 +57,22 @@ module Vagrant
def exec!(command, options=nil, &block)
options = { :error_check => true }.merge(options || {})
retryable(:tries => 5, :on => IOError, :sleep => 0.5) do
metach = session.open_channel do |channel|
channel.exec(command) do |ch, success|
raise "could not execute command: #{command.inspect}" unless success
setup_channel_callbacks(ch, command, options, block)
end
end
metach.wait
metach[:result]
end
end
# Sets up the channel callbacks to properly check exit statuses and
# callback on stdout/stderr.
def setup_channel_callbacks(channel, command, options, block)
block ||= Proc.new do |ch, type, data|
check_exit_status(data, command, options) if type == :exit_status && options[:error_check]
@ -59,30 +80,19 @@ module Vagrant
ch[:result] << data if [:stdout, :stderr].include?(type)
end
retryable(:tries => 5, :on => IOError, :sleep => 0.5) do
metach = session.open_channel do |channel|
channel.exec(command) do |ch, success|
raise "could not execute command: #{command.inspect}" unless success
# Output stdout data to the block
channel.on_data do |ch2, data|
block.call(ch2, :stdout, data)
end
# Output stdout data to the block
channel.on_data do |ch2, data|
block.call(ch2, :stdout, data)
end
# Output stderr data to the block
channel.on_extended_data do |ch2, type, data|
block.call(ch2, :stderr, data)
end
# Output stderr data to the block
channel.on_extended_data do |ch2, type, data|
block.call(ch2, :stderr, data)
end
# Output exit status information to the block
channel.on_request("exit-status") do |ch2, data|
block.call(ch2, :exit_status, data.read_long)
end
end
end
metach.wait
metach[:result]
# Output exit status information to the block
channel.on_request("exit-status") do |ch2, data|
block.call(ch2, :exit_status, data.read_long)
end
end