f224fc5ea5
Subprocessed being interrupted resulted in it looking like those commands were executing successfully but with zero output. Interrupting the sudo prompt would result in any command running in sudo returning nothing and looking like it had succeeded. There was some clean up code in the lxc provider that nuked vagrant container state in the .vagrant directory if it looked like the container no longer existed based on the result of lxc-ls. Interrupting this check resulted in it looking like the container not existing, resulting in the provider code nuking the lxc dir in .vagrant. Voila
95 lines
2.9 KiB
Ruby
95 lines
2.9 KiB
Ruby
module Vagrant
|
|
module LXC
|
|
class SudoWrapper
|
|
# Include this so we can use `Subprocess` more easily.
|
|
include Vagrant::Util::Retryable
|
|
|
|
attr_reader :wrapper_path
|
|
|
|
def initialize(wrapper_path = nil)
|
|
@wrapper_path = wrapper_path
|
|
@logger = Log4r::Logger.new("vagrant::lxc::sudo_wrapper")
|
|
end
|
|
|
|
def run(*command)
|
|
options = command.last.is_a?(Hash) ? command.last : {}
|
|
|
|
# Avoid running LXC commands with a restrictive umask.
|
|
# Otherwise disasters occur, like the container root directory
|
|
# having permissions `rwxr-x---` which prevents the `vagrant`
|
|
# user from accessing its own home directory; among other
|
|
# problems, SSH cannot then read `authorized_keys`!
|
|
old_mask = File.umask
|
|
File.umask(old_mask & 022) # allow all `r` and `x` bits
|
|
|
|
begin
|
|
if @wrapper_path && !options[:no_wrapper]
|
|
command.unshift @wrapper_path
|
|
execute *(['sudo'] + command)
|
|
else
|
|
execute *(['sudo', '/usr/bin/env'] + command)
|
|
end
|
|
ensure
|
|
File.umask(old_mask)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
# TODO: Review code below this line, it was pretty much a copy and
|
|
# paste from VirtualBox base driver and has no tests
|
|
def execute(*command, &block)
|
|
# Get the options hash if it exists
|
|
opts = {}
|
|
opts = command.pop if command.last.is_a?(Hash)
|
|
|
|
tries = 0
|
|
tries = 3 if opts[:retryable]
|
|
|
|
sleep = opts.fetch(:sleep, 1)
|
|
|
|
# Variable to store our execution result
|
|
r = nil
|
|
|
|
retryable(:on => LXC::Errors::ExecuteError, :tries => tries, :sleep => sleep) do
|
|
# Execute the command
|
|
r = raw(*command, &block)
|
|
|
|
# If the command was a failure, then raise an exception that is
|
|
# nicely handled by Vagrant.
|
|
if r.exit_code != 0
|
|
if @interrupted
|
|
raise LXC::Errors::SubprocessInterruptError, command.inspect
|
|
else
|
|
raise LXC::Errors::ExecuteError,
|
|
command: command.inspect, stderr: r.stderr, stdout: r.stdout, exitcode: r.exit_code
|
|
end
|
|
end
|
|
end
|
|
|
|
# Return the output, making sure to replace any Windows-style
|
|
# newlines with Unix-style.
|
|
stdout = r.stdout.gsub("\r\n", "\n")
|
|
if opts[:show_stderr]
|
|
{ :stdout => stdout, :stderr => r.stderr.gsub("\r\n", "\n") }
|
|
else
|
|
stdout
|
|
end
|
|
end
|
|
|
|
def raw(*command, &block)
|
|
int_callback = lambda do
|
|
@interrupted = true
|
|
@logger.info("Interrupted.")
|
|
end
|
|
|
|
# Append in the options for subprocess
|
|
command << { :notify => [:stdout, :stderr] }
|
|
|
|
Vagrant::Util::Busy.busy(int_callback) do
|
|
Vagrant::Util::Subprocess.execute(*command, &block)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|