11
2- # Copyright 2012-2018 Chef Software, Inc.
2+ # Copyright:: Copyright (c) Chef Software Inc.
33#
44# Licensed under the Apache License, Version 2.0 (the "License");
55# you may not use this file except in compliance with the License.
@@ -66,20 +66,25 @@ def initialize(project)
6666 def run!
6767 measure ( "Health check time" ) do
6868 log . info ( log_key ) { "Running health on #{ project . name } " }
69- bad_libs = case Ohai [ "platform" ]
70- when "mac_os_x"
71- health_check_otool
72- when "aix"
73- health_check_aix
74- when "windows"
75- # TODO: objdump -p will provided a very limited check of
76- # explicit dependencies on windows. Most dependencies are
77- # implicit and hence not detected.
78- log . warn ( log_key ) { "Skipping dependency health checks on Windows." }
79- { }
80- else
81- health_check_ldd
82- end
69+ bad_libs , good_libs =
70+ case Ohai [ "platform" ]
71+ when "mac_os_x"
72+ health_check_otool
73+ when "aix"
74+ health_check_aix
75+ when "windows"
76+ # TODO: objdump -p will provided a very limited check of
77+ # explicit dependencies on windows. Most dependencies are
78+ # implicit and hence not detected.
79+ log . warn ( log_key ) { "Skipping dependency health checks on Windows." }
80+ [ { } , { } ]
81+ when "solaris2"
82+ health_check_solaris
83+ when "freebsd" , "openbsd" , "netbsd"
84+ health_check_freebsd
85+ else
86+ health_check_linux
87+ end
8388
8489 unresolved = [ ]
8590 unreliable = [ ]
@@ -167,6 +172,10 @@ def run!
167172 raise HealthCheckFailed
168173 end
169174
175+ if good_libs . keys . length == 0 && !windows?
176+ raise "Internal error: no good libraries were found"
177+ end
178+
170179 conflict_map = { }
171180
172181 conflict_map = relocation_check if relocation_checkable?
@@ -280,19 +289,20 @@ def relocation_check
280289 def health_check_otool
281290 current_library = nil
282291 bad_libs = { }
292+ good_libs = { }
283293
284- read_shared_libs ( "find #{ project . install_dir } / -type f | egrep '\. (dylib|bundle)$' | xargs otool -L" ) do |line |
294+ read_shared_libs ( "find #{ project . install_dir } / -type f | egrep '\. (dylib|bundle)$'" , " xargs otool -L") do |line |
285295 case line
286296 when /^(.+):$/
287297 current_library = Regexp . last_match [ 1 ]
288298 when /^\s +(.+) \( .+\) $/
289299 linked = Regexp . last_match [ 1 ]
290300 name = File . basename ( linked )
291- bad_libs = check_for_bad_library ( bad_libs , current_library , name , linked )
301+ bad_libs , good_libs = check_for_bad_library ( bad_libs , good_libs , current_library , name , linked )
292302 end
293303 end
294304
295- bad_libs
305+ [ bad_libs , good_libs ]
296306 end
297307
298308 #
@@ -304,57 +314,125 @@ def health_check_otool
304314 def health_check_aix
305315 current_library = nil
306316 bad_libs = { }
317+ good_libs = { }
307318
308- read_shared_libs ( "find #{ project . install_dir } / -type f | xargs file | grep \" RISC System \" | awk -F: '{print $1}' | xargs -n 1 ldd" ) do |line |
319+ read_shared_libs ( "find #{ project . install_dir } / -type f | xargs file | grep \" XCOFF \" | awk -F: '{print $1}'" , " xargs -n 1 ldd") do |line |
309320 case line
310321 when /^(.+) needs:$/
311322 current_library = Regexp . last_match [ 1 ]
312323 log . debug ( log_key ) { "Analyzing dependencies for #{ current_library } " }
313324 when /^\s +(.+)$/
314325 name = Regexp . last_match [ 1 ]
315326 linked = Regexp . last_match [ 1 ]
316- bad_libs = check_for_bad_library ( bad_libs , current_library , name , linked )
327+ ( bad_libs , good_libs ) = check_for_bad_library ( bad_libs , good_libs , current_library , name , linked )
317328 when /File is not an executable XCOFF file/ # ignore non-executable files
318329 else
319330 log . warn ( log_key ) { "Line did not match for #{ current_library } \n #{ line } " }
320331 end
321332 end
322333
323- bad_libs
334+ [ bad_libs , good_libs ]
324335 end
325336
326337 #
327- # Run healthchecks against ldd .
338+ # Run healthchecks on Solaris .
328339 #
329340 # @return [Hash<String, Hash<String, Hash<String, Int>>>]
330341 # the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count)
331342 #
332- def health_check_ldd
333- regexp_ends = ".*(" + IGNORED_ENDINGS . map { | e | e . gsub ( / \. / , '\.' ) } . join ( "|" ) + ")$"
334- regexp_patterns = IGNORED_PATTERNS . map { | e | ".*" + e . gsub ( %r{/} , '\/' ) + ".*" } . join ( "|" )
335- regexp = regexp_ends + "|" + regexp_patterns
343+ def health_check_solaris
344+ current_library = nil
345+ bad_libs = { }
346+ good_libs = { }
336347
348+ read_shared_libs ( "find #{ project . install_dir } / -type f | xargs file | grep \" ELF\" | awk -F: '{print $1}' | sed -e 's/:$//'" , "xargs -n 1 ldd" ) do |line |
349+ case line
350+ when /^(.+):$/
351+ current_library = Regexp . last_match [ 1 ]
352+ log . debug ( log_key ) { "Analyzing dependencies for #{ current_library } " }
353+ when /^\s +(.+) \= \> \s +(.+)( \( .+\) )?$/
354+ name = Regexp . last_match [ 1 ]
355+ linked = Regexp . last_match [ 2 ]
356+ ( bad_libs , good_libs ) = check_for_bad_library ( bad_libs , good_libs , current_library , name , linked )
357+ when /^\s +(.+) \( .+\) $/
358+ next
359+ when /^\s +statically linked$/
360+ next
361+ when /^\s +not a dynamic executable$/ # ignore non-executable files
362+ else
363+ log . warn ( log_key ) do
364+ "Line did not match for #{ current_library } \n #{ line } "
365+ end
366+ end
367+ end
368+
369+ [ bad_libs , good_libs ]
370+ end
371+
372+ #
373+ # Run healthchecks on FreeBSD
374+ #
375+ # @return [Hash<String, Hash<String, Hash<String, Int>>>]
376+ # the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count)
377+ #
378+ def health_check_freebsd
337379 current_library = nil
338380 bad_libs = { }
381+ good_libs = { }
339382
340- read_shared_libs ( "find #{ project . install_dir } / -type f -regextype posix-extended ! -regex ' #{ regexp } ' | xargs ldd" ) do |line |
383+ read_shared_libs ( "find #{ project . install_dir } / -type f | xargs file | grep \" ELF \" | awk -F: '{print $1 }' | sed -e 's/:$//'" , " xargs ldd") do |line |
341384 case line
342385 when /^(.+):$/
343386 current_library = Regexp . last_match [ 1 ]
344387 log . debug ( log_key ) { "Analyzing dependencies for #{ current_library } " }
345388 when /^\s +(.+) \= \> \s +(.+)( \( .+\) )?$/
346389 name = Regexp . last_match [ 1 ]
347390 linked = Regexp . last_match [ 2 ]
348- bad_libs = check_for_bad_library ( bad_libs , current_library , name , linked )
391+ ( bad_libs , good_libs ) = check_for_bad_library ( bad_libs , good_libs , current_library , name , linked )
349392 when /^\s +(.+) \( .+\) $/
350393 next
351394 when /^\s +statically linked$/
352395 next
353- when /^\s +libjvm.so/
396+ when /^\s +not a dynamic executable$/ # ignore non-executable files
397+ else
398+ log . warn ( log_key ) do
399+ "Line did not match for #{ current_library } \n #{ line } "
400+ end
401+ end
402+ end
403+
404+ [ bad_libs , good_libs ]
405+ end
406+
407+ #
408+ # Run healthchecks against ldd.
409+ #
410+ # @return [Hash<String, Hash<String, Hash<String, Int>>>]
411+ # the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count)
412+ #
413+ def health_check_linux
414+ current_library = nil
415+ bad_libs = { }
416+ good_libs = { }
417+
418+ read_shared_libs ( "find #{ project . install_dir } / -type f" , "xargs ldd" ) do |line |
419+ case line
420+ when /^(.+):$/
421+ current_library = Regexp . last_match [ 1 ]
422+ log . debug ( log_key ) { "Analyzing dependencies for #{ current_library } " }
423+ when /^\s +(.+) \= \> \s +(.+)( \( .+\) )?$/
424+ name = Regexp . last_match [ 1 ]
425+ linked = Regexp . last_match [ 2 ]
426+ ( bad_libs , good_libs ) = check_for_bad_library ( bad_libs , good_libs , current_library , name , linked )
427+ when /^\s +(.+) \( .+\) $/
354428 next
355- when /^\s +libjava.so /
429+ when /^\s +statically linked$ /
356430 next
357- when /^\s +libmawt.so/
431+ when /^\s +libjvm.so/ # FIXME: should remove if it doesn't blow up server
432+ next
433+ when /^\s +libjava.so/ # FIXME: should remove if it doesn't blow up server
434+ next
435+ when /^\s +libmawt.so/ # FIXME: should remove if it doesn't blow up server
358436 next
359437 when /^\s +not a dynamic executable$/ # ignore non-executable files
360438 else
@@ -364,7 +442,7 @@ def health_check_ldd
364442 end
365443 end
366444
367- bad_libs
445+ [ bad_libs , good_libs ]
368446 end
369447
370448 private
@@ -399,10 +477,40 @@ def whitelist_files
399477 # @yield [String]
400478 # each line
401479 #
402- def read_shared_libs ( command )
403- cmd = shellout ( command )
404- cmd . stdout . each_line do |line |
405- yield line
480+ def read_shared_libs ( find_command , ldd_command , &output_proc )
481+ #
482+ # construct the list of files to check
483+ #
484+
485+ find_output = shellout! ( find_command ) . stdout . lines
486+
487+ find_output . reject! { |file | IGNORED_ENDINGS . any? { |ending | file . end_with? ( "#{ ending } \n " ) } }
488+
489+ find_output . reject! { |file | IGNORED_SUBSTRINGS . any? { |substr | file . include? ( substr ) } }
490+
491+ if find_output . empty?
492+ # probably the find_command is busted, it should never be empty or why are you using omnibus?
493+ raise "Internal Error: Health Check found no lines"
494+ end
495+
496+ if find_output . any? { |file | file !~ Regexp . new ( project . install_dir ) }
497+ # every file in the find output should be within the install_dir
498+ raise "Internal Error: Health Check lines not matching the install_dir"
499+ end
500+
501+ #
502+ # feed the list of files to the "ldd" command
503+ #
504+
505+ # this command will typically fail if the last file isn't a valid lib/binary which happens often
506+ ldd_output = shellout ( ldd_command , input : find_output . join ) . stdout
507+
508+ #
509+ # do the output process to determine if the files are good or bad
510+ #
511+
512+ ldd_output . each_line do |line |
513+ output_proc . call ( line )
406514 end
407515 end
408516
@@ -420,7 +528,7 @@ def read_shared_libs(command)
420528 #
421529 # @return the modified bad_library hash
422530 #
423- def check_for_bad_library ( bad_libs , current_library , name , linked )
531+ def check_for_bad_library ( bad_libs , good_libs , current_library , name , linked )
424532 safe = nil
425533
426534 whitelist_libs = case Ohai [ "platform" ]
@@ -463,10 +571,11 @@ def check_for_bad_library(bad_libs, current_library, name, linked)
463571 bad_libs [ current_library ] [ name ] [ linked ] = 1
464572 end
465573 else
574+ good_libs [ current_library ] = true
466575 log . debug ( log_key ) { " -> PASSED: #{ name } is either whitelisted or safely provided." }
467576 end
468577
469- bad_libs
578+ [ bad_libs , good_libs ]
470579 end
471580 end
472581end
0 commit comments