Class: Facter::Core::Execution::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/facter/core/execution/base.rb

Direct Known Subclasses

Posix, Windows

Instance Method Summary collapse

Instance Method Details

#execute(command, options = {}) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/facter/core/execution/base.rb', line 30

def execute(command, options = {})

  on_fail = options.fetch(:on_fail, :raise)

  # Set LC_ALL and LANG to force i18n to C for the duration of this exec; this ensures that any code that parses the
  # output of the command can expect it to be in a consistent / predictable format / locale
  with_env 'LC_ALL' => 'C', 'LANG' => 'C' do

    expanded_command = expand_command(command)

    if expanded_command.nil?
      if on_fail == :raise
        raise Facter::Core::Execution::ExecutionFailure.new, "Could not execute '#{command}': command not found"
      else
        return on_fail
      end
    end

    out = ''

    begin
      wait_for_child = true
      out = %x{#{expanded_command}}.chomp
      wait_for_child = false
    rescue => detail
      if on_fail == :raise
        raise Facter::Core::Execution::ExecutionFailure.new, "Failed while executing '#{expanded_command}': #{detail.message}"
      else
        return on_fail
      end
    ensure
      if wait_for_child
        # We need to ensure that if this command exits early then any spawned
        # children will be reaped. Process execution is frequently
        # terminated using Timeout.timeout but since the timeout isn't in
        # this scope we can't rescue the raised exception. The best that
        # we can do is determine if the child has exited, and if it hasn't
        # then we need to spawn a thread to wait for the child.
        #
        # Due to the limitations of Ruby 1.8 there aren't good ways to
        # asynchronously run a command and grab the PID of that command
        # using the standard library. The best we can do is blindly wait
        # on all processes and hope for the best. This issue is described
        # at https://tickets.puppetlabs.com/browse/FACT-150
        Thread.new { Process.waitall }
      end
    end

    out
  end
end

#with_env(values) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/facter/core/execution/base.rb', line 3

def with_env(values)
  old = {}
  values.each do |var, value|
    # save the old value if it exists
    if old_val = ENV[var]
      old[var] = old_val
    end
    # set the new (temporary) value for the environment variable
    ENV[var] = value
  end
  # execute the caller's block, capture the return value
  rv = yield
# use an ensure block to make absolutely sure we restore the variables
ensure
  # restore the old values
  values.each do |var, value|
    if old.include?(var)
      ENV[var] = old[var]
    else
      # if there was no old value, delete the key from the current environment variables hash
      ENV.delete(var)
    end
  end
  # return the captured return value
  rv
end