Determine in a platform-specific way whether a path is absolute. This defaults to the local platform if none is specified.
# File lib/facter/util/resolution.rb, line 73 def self.absolute_path?(path, platform=nil) # Escape once for the string literal, and once for the regex. slash = '[\\/]' name = '[^\\/]+' regexes = { :windows => %^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!, :posix => %^/!, } platform ||= Facter::Util::Config.is_windows? ? :windows : :posix !! (path =~ regexes[platform]) end
Execute a program and return the output of that program.
Returns nil if the program can’t be found, or if there is a problem executing the code.
# File lib/facter/util/resolution.rb, line 151 def self.exec(code, interpreter = nil) Facter.warnonce "The interpreter parameter to 'exec' is deprecated and will be removed in a future version." if interpreter ## Set 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 "LANG" => "C" do if expanded_code = expand_command(code) # if we can find the binary, we'll run the command with the expanded path to the binary code = expanded_code else # if we cannot find the binary return nil on posix. On windows we'll still try to run the # command in case it is a shell-builtin. In case it is not, windows will raise Errno::ENOENT return nil unless Facter::Util::Config.is_windows? return nil if absolute_path?(code) end out = nil begin out = %{#{code}}.chomp Facter.warnonce "Using Facter::Util::Resolution.exec with a shell built-in is deprecated. Most built-ins can be replaced with native ruby commands. If you really have to run a built-in, pass \"cmd /c your_builtin\" as a command (command responsible for this message was \"#{code}\")" unless expanded_code rescue Errno::ENOENT => detail # command not found on Windows return nil rescue => detail Facter.warn(detail) return nil end if out == "" return nil else return out end end end
Expand the executable of a commandline to an absolute path. The executable is the first word of the commandline. If the executable contains spaces, it has be but in double quotes to be properly recognized.
Returns the commandline with the expanded binary or nil if the binary can’t be found. If the path to the binary contains quotes, the whole binary is put in quotes.
# File lib/facter/util/resolution.rb, line 93 def self.expand_command(command) if match = /^"(.+?)"(?:\s+(.*))?/.match(command) exe, arguments = match.captures exe = which(exe) and [ "\"#{exe}\"", arguments ].compact.join(" ") elsif match = /^'(.+?)'(?:\s+(.*))?/.match(command) and not Facter::Util::Config.is_windows? exe, arguments = match.captures exe = which(exe) and [ "'#{exe}'", arguments ].compact.join(" ") else exe, arguments = command.split(/ /,2) if exe = which(exe) # the binary was not quoted which means it contains no spaces. But the # full path to the binary may do so. exe = "\"#{exe}\"" if exe =~ /\s/ and Facter::Util::Config.is_windows? exe = "'#{exe}'" if exe =~ /\s/ and not Facter::Util::Config.is_windows? [ exe, arguments ].compact.join(" ") end end end
Create a new resolution mechanism.
# File lib/facter/util/resolution.rb, line 201 def initialize(name) @name = name @confines = [] @value = nil @timeout = 0 @weight = nil end
Returns the locations to be searched when looking for a binary. This is currently determined by the PATH environment variable plus /sbin and /usr/sbin when run on unix
# File lib/facter/util/resolution.rb, line 20 def self.search_paths if Facter::Util::Config.is_windows? ENV['PATH'].split(File::PATH_SEPARATOR) else # Make sure facter is usable even for non-root users. Most commands # in /sbin (like ifconfig) can be run as non priviledged users as # long as they do not modify anything - which we do not do with facter ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/sbin', '/usr/sbin' ] end end
Determine the full path to a binary. If the supplied filename does not already describe an absolute path then different locations (determined by self.search_paths) will be searched for a match.
Returns nil if no matching executable can be found otherwise returns the expanded pathname.
# File lib/facter/util/resolution.rb, line 37 def self.which(bin) if absolute_path?(bin) return bin if File.executable?(bin) if Facter::Util::Config.is_windows? and File.extname(bin).empty? exts = ENV['PATHEXT'] exts = exts ? exts.split(File::PATH_SEPARATOR) : ].COM .EXE .BAT .CMD] exts.each do |ext| destext = bin + ext if File.executable?(destext) Facter.warnonce("Using Facter::Util::Resolution.which with an absolute path like #{bin} but no fileextension is deprecated. Please add the correct extension (#{ext})") return destext end end end else search_paths.each do |dir| dest = File.join(dir, bin) if Facter::Util::Config.is_windows? dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File.extname(dest).empty? exts = ENV['PATHEXT'] exts = exts ? exts.split(File::PATH_SEPARATOR) : ].COM .EXE .BAT .CMD] exts.each do |ext| destext = dest + ext return destext if File.executable?(destext) end end end return dest if File.executable?(dest) end end nil end
Call this method with a block of code for which you would like to temporarily modify one or more environment variables; the specified values will be set for the duration of your block, after which the original values (if any) will be restored.
a Hash containing the key/value pairs of any environment variables that you
would like to temporarily override
# File lib/facter/util/resolution.rb, line 119 def self.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
Add a new confine to the resolution mechanism.
# File lib/facter/util/resolution.rb, line 190 def confine(confines) confines.each do |fact, values| @confines.push Facter::Util::Confine.new(fact, *values) end end
flush executes the block, if any, stored by the {on_flush} method
@see Facter::Util::Fact#flush @see Facter::Util::Resolution#on_flush
@api private
# File lib/facter/util/resolution.rb, line 266 def flush @on_flush_block.call if @on_flush_block end
# File lib/facter/util/resolution.rb, line 196 def has_weight(weight) @weight = weight end
# File lib/facter/util/resolution.rb, line 275 def interpreter=(interp) Facter.warnonce "The 'Facter::Util::Resolution.interpreter=' method is deprecated and will be removed in a future version." @interpreter = interp end
We need this as a getter for ‘timeout’, because some versions of ruby seem to already have a ‘timeout’ method and we can’t seem to override the instance methods, somehow.
# File lib/facter/util/resolution.rb, line 221 def limit @timeout end
on_flush accepts a block and executes the block when the resolution's value is flushed. This makes it possible to model a single, expensive system call inside of a Ruby object and then define multiple dynamic facts which resolve by sending messages to the model instance. If one of the dynamic facts is flushed then it can, in turn, flush the data stored in the model instance to keep all of the dynamic facts in sync without making multiple, expensive, system calls.
Please see the Solaris zones fact for an example of how this feature may be used.
@see Facter::Util::Fact#flush @see Facter::Util::Resolution#flush
@api public
# File lib/facter/util/resolution.rb, line 255 def on_flush(&block) @on_flush_block = block end
Set our code for returning a value.
# File lib/facter/util/resolution.rb, line 226 def setcode(string = nil, interp = nil, &block) Facter.warnonce "The interpreter parameter to 'setcode' is deprecated and will be removed in a future version." if interp if string @code = string @interpreter = interp || INTERPRETER else unless block_given? raise ArgumentError, "You must pass either code or a block" end @code = block end end
Is this resolution mechanism suitable on the system in question?
# File lib/facter/util/resolution.rb, line 281 def suitable? unless defined? @suitable @suitable = ! @confines.detect { |confine| ! confine.true? } end return @suitable end
# File lib/facter/util/resolution.rb, line 289 def to_s return self.value() end
How we get a value for our resolution mechanism.
# File lib/facter/util/resolution.rb, line 294 def value return @value if @value result = nil return result if @code == nil starttime = Time.now.to_f begin Timeout.timeout(limit) do if @code.is_a?(Proc) result = @code.call() else result = Facter::Util::Resolution.exec(@code) end end rescue Timeout::Error => detail Facter.warn "Timed out seeking value for %s" % self.name # This call avoids zombies -- basically, create a thread that will # dezombify all of the child processes that we're ignoring because # of the timeout. Thread.new { Process.waitall } return nil rescue => details Facter.warn "Could not retrieve %s: %s" % [self.name, details] return nil end finishtime = Time.now.to_f ms = (finishtime - starttime) * 1000 Facter.show_time "#{self.name}: #{"%.2f" % ms}ms" return nil if result == "" return result end
Generated with the Darkfish Rdoc Generator 2.