From b5611e06f6bbee0caff56c30c06c55439eabd0a1 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Tue, 20 Dec 2016 17:26:01 -0800 Subject: Remove the deprecated run_command and popen4 methods This is very much legacy and has long since been replaced by shell_out Signed-off-by: Tim Smith --- lib/ohai/mixin/command.rb | 319 +--------------------------------------- spec/unit/mixin/command_spec.rb | 68 --------- 2 files changed, 1 insertion(+), 386 deletions(-) diff --git a/lib/ohai/mixin/command.rb b/lib/ohai/mixin/command.rb index 04a4f63f..10c93baf 100644 --- a/lib/ohai/mixin/command.rb +++ b/lib/ohai/mixin/command.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob () +# Author:: Tim Smith () # Copyright:: Copyright (c) 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # @@ -17,12 +18,7 @@ # require "ohai/exception" -require "ohai/config" require "ohai/log" -require "stringio" -require "tmpdir" -require "fcntl" -require "etc" require "mixlib/shellout" module Ohai @@ -48,319 +44,6 @@ module Ohai end module_function :shell_out - - def run_command(args = {}) - Ohai::Log.warn("Ohai::Mixin::Command run_command is deprecated and will be removed in Ohai 9.0.0") - if args.has_key?(:creates) - if File.exists?(args[:creates]) - Ohai::Log.debug("Skipping #{args[:command]} - creates #{args[:creates]} exists.") - return false - end - end - - stdout_string = nil - stderr_string = nil - - args[:cwd] ||= Dir.tmpdir - unless File.directory?(args[:cwd]) - raise Ohai::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory" - end - - status = nil - Dir.chdir(args[:cwd]) do - status, stdout_string, stderr_string = run_command_backend(args[:command], args[:timeout]) - - if stdout_string - Ohai::Log.debug("---- Begin #{args[:command]} STDOUT ----") - Ohai::Log.debug(stdout_string.strip) - Ohai::Log.debug("---- End #{args[:command]} STDOUT ----") - end - if stderr_string - Ohai::Log.debug("---- Begin #{args[:command]} STDERR ----") - Ohai::Log.debug(stderr_string.strip) - Ohai::Log.debug("---- End #{args[:command]} STDERR ----") - end - - args[:returns] ||= 0 - args[:no_status_check] ||= false - if status.exitstatus != args[:returns] && (not args[:no_status_check]) - raise Ohai::Exceptions::Exec, "#{args[:command_string]} returned #{status.exitstatus}, expected #{args[:returns]}" - else - Ohai::Log.debug("Ran #{args[:command_string]} (#{args[:command]}) returned #{status.exitstatus}") - end - end - return status, stdout_string, stderr_string - end - - module_function :run_command - - def run_command_unix(command, timeout) - stderr_string, stdout_string, status = "", "", nil - - exec_processing_block = lambda do |pid, stdin, stdout, stderr| - stdout_string, stderr_string = stdout.string.chomp, stderr.string.chomp - end - - if timeout - begin - Timeout.timeout(timeout) do - status = popen4(command, {}, &exec_processing_block) - end - rescue Timeout::Error => e - Chef::Log.error("#{command} exceeded timeout #{timeout}") - raise(e) - end - else - status = popen4(command, {}, &exec_processing_block) - end - return status, stdout_string, stderr_string - end - - def run_command_windows(command, timeout) - shellout_opts = {} - shellout_opts[:timeout] = timeout if timeout - - m = Mixlib::ShellOut.new(command, shellout_opts) - m.run_command - [m.status, m.stdout, m.stderr] - end - - if RUBY_PLATFORM =~ /mswin|mingw32|windows/ - alias :run_command_backend :run_command_windows - else - alias :run_command_backend :run_command_unix - end - # This is taken directly from Ara T Howard's Open4 library, and then - # modified to suit the needs of Ohai. Any bugs here are most likely - # my own, and not Ara's. - # - # The original appears in external/open4.rb in its unmodified form. - # - # Thanks Ara! - def popen4(cmd, args = {}, &b) - Ohai::Log.warn("Ohai::Mixin::Command popen4 is deprecated and will be removed in Ohai 9.0.0") - - # Disable garbage collection to work around possible bug in MRI - # Ruby 1.8 suffers from intermittent segfaults believed to be due to GC while IO.select - # See OHAI-330 / CHEF-2916 / CHEF-1305 - GC.disable - - # Waitlast - this is magic. - # - # Do we wait for the child process to die before we yield - # to the block, or after? That is the magic of waitlast. - # - # By default, we are waiting before we yield the block. - args[:waitlast] ||= false - - args[:user] ||= nil - unless args[:user].kind_of?(Integer) - args[:user] = Etc.getpwnam(args[:user]).uid if args[:user] - end - args[:group] ||= nil - unless args[:group].kind_of?(Integer) - args[:group] = Etc.getgrnam(args[:group]).gid if args[:group] - end - args[:environment] ||= {} - - # Default on C locale so parsing commands output can be done - # independently of the node's default locale. - # "LC_ALL" could be set to nil, in which case we also must ignore it. - unless args[:environment].has_key?("LC_ALL") - args[:environment]["LC_ALL"] = "C" - end - - pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe - - verbose = $VERBOSE - begin - $VERBOSE = nil - ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) - - cid = fork do - Process.setsid - - pw.last.close - STDIN.reopen pw.first - pw.first.close - - pr.first.close - STDOUT.reopen pr.last - pr.last.close - - pe.first.close - STDERR.reopen pe.last - pe.last.close - - STDOUT.sync = STDERR.sync = true - - if args[:group] - Process.egid = args[:group] - Process.gid = args[:group] - end - - if args[:user] - Process.euid = args[:user] - Process.uid = args[:user] - end - - args[:environment].each do |key, value| - ENV[key] = value - end - - if args[:umask] - umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777) - File.umask(umask) - end - - begin - if cmd.kind_of?(Array) - exec(*cmd) - else - exec(cmd) - end - raise "forty-two" - rescue Exception => e - Marshal.dump(e, ps.last) - ps.last.flush - end - ps.last.close unless ps.last.closed? - exit! - end - ensure - $VERBOSE = verbose - end - - [pw.first, pr.last, pe.last, ps.last].each { |fd| fd.close } - - begin - e = Marshal.load ps.first - pw.last.close - pr.first.close - pe.first.close - Process.wait(cid) - raise(Exception === e ? e : "unknown failure!") - rescue EOFError # If we get an EOF error, then the exec was successful - 42 - ensure - ps.first.close - end - - pw.last.sync = true - - pi = [pw.last, pr.first, pe.first] - - if b - begin - if args[:waitlast] - b[cid, *pi] - # send EOF so that if the child process is reading from STDIN - # it will actually finish up and exit - pi[0].close_write - Process.waitpid2(cid).last - else - # This took some doing. - # The trick here is to close STDIN - # Then set our end of the childs pipes to be O_NONBLOCK - # Then wait for the child to die, which means any IO it - # wants to do must be done - it's dead. If it isn't, - # it's because something totally skanky is happening, - # and we don't care. - o = StringIO.new - e = StringIO.new - - #pi[0].close - - stdout = pi[1] - stderr = pi[2] - - stdout.sync = true - stderr.sync = true - - stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK) - stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK) - - stdout_finished = false - stderr_finished = false - - results = nil - - while !stdout_finished || !stderr_finished - begin - channels_to_watch = [] - channels_to_watch << stdout if !stdout_finished - channels_to_watch << stderr if !stderr_finished - ready = IO.select(channels_to_watch, nil, nil, 1.0) - rescue Errno::EAGAIN - ensure - results = Process.waitpid2(cid, Process::WNOHANG) - if results - stdout_finished = true - stderr_finished = true - end - end - - if ready && ready.first.include?(stdout) - line = results ? stdout.gets(nil) : stdout.gets - if line - o.write(line) - else - stdout_finished = true - end - end - if ready && ready.first.include?(stderr) - line = results ? stderr.gets(nil) : stderr.gets - if line - e.write(line) - else - stderr_finished = true - end - end - end - results = Process.waitpid2(cid) unless results - o.rewind - e.rewind - - # **OHAI-275** - # The way we read from the pipes causes ruby to mark the strings - # as ASCII-8BIT (i.e., binary), but the content should be encoded - # as the default external encoding. For example, a command may - # return data encoded as UTF-8, but the strings will be marked as - # ASCII-8BIT. Later, when you attempt to print the values as - # UTF-8, Ruby will try to convert them and fail, raising an - # error. - # - # Ruby always marks strings as binary when read from IO in - # incomplete chunks, since you may have split the data within a - # multibyte char. In our case, we concat the chunks back - # together, so any multibyte chars will be reassembled. - # - # Note that all of this applies only to Ruby 1.9, which we check - # for by making sure that the Encoding class exists and strings - # have encoding methods. - if "".respond_to?(:force_encoding) && defined?(Encoding) - o.string.force_encoding(Encoding.default_external) - o.string.encode!("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?") - e.string.force_encoding(Encoding.default_external) - e.string.encode!("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?") - end - b[cid, pi[0], o, e] - results.last - end - ensure - pi.each { |fd| fd.close unless fd.closed? } - end - else - [cid, pw.last, pr.first, pe.first] - end - rescue Errno::ENOENT - raise Ohai::Exceptions::Exec, "command #{cmd} doesn't exist or is not in the PATH" - ensure - # we disabled GC entering - GC.enable - end - - module_function :popen4 end end end diff --git a/spec/unit/mixin/command_spec.rb b/spec/unit/mixin/command_spec.rb index fb4ce87f..e9afa2cd 100644 --- a/spec/unit/mixin/command_spec.rb +++ b/spec/unit/mixin/command_spec.rb @@ -19,74 +19,6 @@ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper.rb") -describe Ohai::Mixin::Command, "popen4" do - break if RUBY_PLATFORM =~ /(win|w)32$/ - - it "should default all commands to be run in the POSIX standard C locale" do - Ohai::Mixin::Command.popen4("echo $LC_ALL") do |pid, stdin, stdout, stderr| - stdin.close - expect(stdout.read.strip).to eq("C") - end - end - - it "should respect locale when specified explicitly" do - Ohai::Mixin::Command.popen4("echo $LC_ALL", :environment => { "LC_ALL" => "es" }) do |pid, stdin, stdout, stderr| - stdin.close - expect(stdout.read.strip).to eq("es") - end - end - - if defined?(::Encoding) && "".respond_to?(:force_encoding) #i.e., ruby 1.9 - context "when external commands return UTF-8 strings and we are running under LANG=C encoding" do - before do - @saved_default_external = Encoding.default_external - @saved_default_internal = Encoding.default_internal - Encoding.default_external = Encoding::US_ASCII - Encoding.default_internal = Encoding::US_ASCII - end - - after do - Encoding.default_external = @saved_default_external - Encoding.default_internal = @saved_default_internal - end - - it "should force encode the string to UTF-8" do - extend Ohai::Mixin::Command - snowy = run_command(:command => ("echo '" + ("☃" * 8096) + "'"))[1] - expect(snowy.encoding).to eq(Encoding::UTF_8) - end - end - - it "should force encode the string to UTF-8" do - extend Ohai::Mixin::Command - snowy = run_command(:command => ("echo '" + ("☃" * 8096) + "'"))[1] - expect(snowy.encoding).to eq(Encoding::UTF_8) - end - end - - it "reaps zombie processes after exec fails [OHAI-455]" do - # NOTE: depending on ulimit settings, GC, etc., before the OHAI-455 patch, - # ohai could also exhaust the available file descriptors when creating this - # many zombie processes. A regression _could_ cause Errno::EMFILE but this - # probably won't be consistent on different environments. - created_procs = 0 - 100.times do - begin - Ohai::Mixin::Command.popen4("/bin/this-is-not-a-real-command") { |p, i, o, e| nil } - rescue Ohai::Exceptions::Exec - created_procs += 1 - end - end - expect(created_procs).to eq(100) - reaped_procs = 0 - begin - loop { Process.wait(-1); reaped_procs += 1 } - rescue Errno::ECHILD - end - expect(reaped_procs).to eq(0) - end -end - describe Ohai::Mixin::Command, "shell_out" do let(:cmd) { "sparkle-dream --version" } -- cgit v1.2.1