class Omnibus::HealthCheck

Constants

AIX_WHITELIST_LIBS
ARCH_WHITELIST_LIBS
FREEBSD_WHITELIST_LIBS
MAC_WHITELIST_LIBS
SMARTOS_WHITELIST_LIBS
SOLARIS_WHITELIST_LIBS
WHITELIST_LIBS

Public Class Methods

check_for_bad_library(install_dir, bad_libs, whitelist_files, current_library, name, linked) click to toggle source
# File lib/omnibus/health_check.rb, line 227
def self.check_for_bad_library(install_dir, bad_libs, whitelist_files, current_library, name, linked)
  safe = nil

  whitelist_libs = case OHAI.platform
                   when 'arch'
                     ARCH_WHITELIST_LIBS
                   when 'mac_os_x'
                     MAC_WHITELIST_LIBS
                   when 'solaris2'
                     SOLARIS_WHITELIST_LIBS
                   when 'smartos'
                     SMARTOS_WHITELIST_LIBS
                   when 'freebsd'
                     FREEBSD_WHITELIST_LIBS
                   when 'aix'
                     AIX_WHITELIST_LIBS
                   else
                     WHITELIST_LIBS
                   end
  whitelist_libs.each do |reg|
    safe ||= true if reg.match(name)
  end
  whitelist_files.each do |reg|
    safe ||= true if reg.match(current_library)
  end

  log "  --> Dependency: #{name}" if ARGV[0] == "verbose"
  log "  --> Provided by: #{linked}" if ARGV[0] == "verbose"

  if !safe && linked !~ Regexp.new(install_dir)
    log "    -> FAILED: #{current_library} has unsafe dependencies" if ARGV[0] == "verbose"
    bad_libs[current_library] ||= {}
    bad_libs[current_library][name] ||= {}
    if bad_libs[current_library][name].has_key?(linked)
      bad_libs[current_library][name][linked] += 1
    else
      bad_libs[current_library][name][linked] = 1
    end
  else
    log "    -> PASSED: #{name} is either whitelisted or safely provided." if ARGV[0] == "verbose"
  end

  bad_libs
end
health_check_aix(install_dir, whitelist_files) click to toggle source
# File lib/omnibus/health_check.rb, line 272
def self.health_check_aix(install_dir, whitelist_files)
  #
  # ShellOut has GC turned off during execution, so when we're
  # executing extremely long commands with lots of output, we
  # should be mindful that the string concatentation for building
  # #stdout will hurt memory usage drastically
  #
  ldd_cmd = "find #{install_dir}/ -type f | xargs file | grep \"RISC System\" | awk -F: '{print $1}' | xargs -n 1 ldd > ldd.out 2>/dev/null"

  log "Executing `#{ldd_cmd}`"
  shell = Mixlib::ShellOut.new(ldd_cmd, :timeout => 3600)
  shell.run_command

  ldd_output = File.read('ldd.out')

  current_library = nil
  bad_libs = {}

  ldd_output.each_line do |line|
    case line
    when /^(.+) needs:$/
      current_library = $1
      log "*** Analysing dependencies for #{current_library}" if ARGV[0] == "verbose"
    when /^\s+(.+)$/
      name = $1
      linked = $1
      bad_libs = check_for_bad_library(install_dir, bad_libs, whitelist_files, current_library, name, linked)
    when /File is not an executable XCOFF file/ # ignore non-executable files
    else
      log "*** Line did not match for #{current_library}\n#{line}"
    end
  end

  File.delete('ldd.out')
  bad_libs
end
health_check_ldd(install_dir, whitelist_files) click to toggle source
# File lib/omnibus/health_check.rb, line 309
def self.health_check_ldd(install_dir, whitelist_files)
  #
  # ShellOut has GC turned off during execution, so when we're
  # executing extremely long commands with lots of output, we
  # should be mindful that the string concatentation for building
  # #stdout will hurt memory usage drastically
  #
  ldd_cmd = "find #{install_dir}/ -type f | xargs ldd > ldd.out 2>/dev/null"

  log "Executing `#{ldd_cmd}`"
  shell = Mixlib::ShellOut.new(ldd_cmd, :timeout => 3600)
  shell.run_command

  ldd_output = File.read('ldd.out')

  current_library = nil
  bad_libs = {}

  ldd_output.each_line do |line|
    case line
    when /^(.+):$/
      current_library = $1
      log "*** Analysing dependencies for #{current_library}" if ARGV[0] == "verbose"
    when /^\s+(.+) \=\>\s+(.+)( \(.+\))?$/
      name = $1
      linked = $2
      bad_libs = check_for_bad_library(install_dir, bad_libs, whitelist_files, current_library, name, linked)
    when /^\s+(.+) \(.+\)$/
      next
    when /^\s+statically linked$/
      next
    when /^\s+libjvm.so/
      next
    when /^\s+libjava.so/
      next
    when /^\s+libmawt.so/
      next
    when /^\s+not a dynamic executable$/ # ignore non-executable files
    else
      log "*** Line did not match for #{current_library}\n#{line}"
    end
  end

  File.delete('ldd.out')
  bad_libs
end
health_check_otool(install_dir, whitelist_files) click to toggle source
# File lib/omnibus/health_check.rb, line 200
def self.health_check_otool(install_dir, whitelist_files)
  otool_cmd = "find #{install_dir}/ -type f | egrep '\.(dylib|bundle)$' | xargs otool -L > otool.out 2>/dev/null"
  log "Executing `#{otool_cmd}`"
  shell = Mixlib::ShellOut.new(otool_cmd, :timeout => 3600)
  shell.run_command

  otool_output = File.read('otool.out')

  current_library = nil
  bad_libs = {}

  otool_output.each_line do |line|
    case line
    when /^(.+):$/
      current_library = $1
    when /^\s+(.+) \(.+\)$/
      linked = $1
      name = File.basename(linked)
      bad_libs = check_for_bad_library(install_dir, bad_libs, whitelist_files, current_library, name, linked)
    end
  end

  File.delete('otool.out')

  bad_libs
end
log(msg) click to toggle source
# File lib/omnibus/health_check.rb, line 141
def self.log(msg)
  puts "[health_check] #{msg}"
end
run(install_dir, whitelist_files = []) click to toggle source
# File lib/omnibus/health_check.rb, line 145
def self.run(install_dir, whitelist_files = [])
  case OHAI.platform
  when "mac_os_x"
    bad_libs = health_check_otool(install_dir, whitelist_files)
  when "aix"
    bad_libs = health_check_aix(install_dir, whitelist_files)
  else
    bad_libs = health_check_ldd(install_dir, whitelist_files)
  end

  unresolved = []
  unreliable = []
  detail = []

  if bad_libs.keys.length > 0
    bad_libs.each do |name, lib_hash|
      lib_hash.each do |lib, linked_libs|
        linked_libs.each do |linked, count|
          if linked =~ /not found/
            unresolved << lib unless unresolved.include? lib
          else
            unreliable << linked unless unreliable.include? linked
          end
          detail << "#{name}|#{lib}|#{linked}|#{count}"
        end
      end
    end
    log "*** Health Check Failed, Summary follows:"
    bad_omnibus_libs, bad_omnibus_bins = bad_libs.keys.partition { |k| k.include? "embedded/lib" }
    log "*** The following Omnibus-built libraries have unsafe or unmet dependencies:"
    bad_omnibus_libs.each { |lib| log "    --> #{lib}" }
    log "*** The following Omnibus-built binaries have unsafe or unmet dependencies:"
    bad_omnibus_bins.each { |bin| log "    --> #{bin}" }
    if unresolved.length > 0
      log "*** The following requirements could not be resolved:"
      unresolved.each { |lib| log "    --> #{lib}"}
    end
    if unreliable.length > 0
      log "*** The following libraries cannot be guaranteed to be on target systems:"
      unreliable.each { |lib| log "    --> #{lib}"}
    end
    log "*** The precise failures were:"
    detail.each do |line|
      item, dependency, location, count = line.split('|')
      reason = location =~ /not found/ ? "Unresolved dependency" : "Unsafe dependency"
      log "    --> #{item}"
      log "    DEPENDS ON: #{dependency}"
      log "      COUNT: #{count}"
      log "      PROVIDED BY: #{location}"
      log "      FAILED BECAUSE: #{reason}"
    end
    raise "Health Check Failed"
  end
end