summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/mixlib/shellout/windows.rb37
-rw-r--r--mixlib-shellout.gemspec1
-rw-r--r--spec/mixlib/shellout_spec.rb17
3 files changed, 51 insertions, 4 deletions
diff --git a/lib/mixlib/shellout/windows.rb b/lib/mixlib/shellout/windows.rb
index 6003ae8..fe7fd19 100644
--- a/lib/mixlib/shellout/windows.rb
+++ b/lib/mixlib/shellout/windows.rb
@@ -20,8 +20,14 @@
require 'win32/process'
require 'mixlib/shellout/windows/core_ext'
+require 'mixlib/log'
module Mixlib
+ class ShelloutLog
+ extend Mixlib::Log
+ init("#{ENV['temp']}/shellout.txt")
+ end
+
class ShellOut
module Windows
@@ -43,6 +49,7 @@ module Mixlib
# Missing lots of features from the UNIX version, such as
# uid, etc.
def run_command
+ logger = ShelloutLog.logger
#
# Create pipes to capture stdout and stderr,
@@ -90,6 +97,7 @@ module Mixlib
# Wait for the process to finish, consuming output as we go
#
start_wait = Time.now
+ logger.warn(Utils.format_process(process, app_name, command_line, timeout)) if logger
while true
wait_status = WaitForSingleObject(process.process_handle, 0)
case wait_status
@@ -107,12 +115,15 @@ module Mixlib
# Kill the process
if (Time.now - start_wait) > timeout
begin
+ require 'wmi-lite/wmi'
+ wmi = WmiLite::Wmi.new
+ Utils.kill_process_tree(process.process_id, wmi, logger)
Process.kill(:KILL, process.process_id)
- rescue Errno::EIO
+ rescue Errno::EIO, SystemCallError
logger.warn("Failed to kill timed out process #{process.process_id}") if logger
end
- raise Mixlib::ShellOut::CommandTimeout, "command timed out:\n#{format_for_exception}"
+ raise Mixlib::ShellOut::CommandTimeout, "command timed out:\n#{format_for_exception}\n#{Utils.format_process(process, app_name, command_line, timeout)}"
end
consume_output(open_streams, stdout_read, stderr_read)
@@ -313,6 +324,28 @@ module Mixlib
def self.executable?(path)
File.executable?(path) && !File.directory?(path)
end
+
+ def self.kill_process_tree(pid, wmi, logger)
+ wmi.query("select ProcessID, Name from Win32_Process where ParentProcessID=#{pid}").each do |instance|
+ child_pid = instance.wmi_ole_object.processid
+ kill_process_tree(child_pid, wmi, logger)
+ begin
+ logger.warn("killing child process #{child_pid}::#{instance.wmi_ole_object.Name} of parent #{pid}") if logger
+ Process.kill(:KILL, child_pid)
+ rescue Errno::EIO, SystemCallError
+ logger.warn("Failed to kill child process #{child_pid}::#{instance.wmi_ole_object.Name} of parent #{pid}") if logger
+ end
+ end
+ end
+
+ def self.format_process(process, app_name, command_line, timeout)
+ msg = []
+ msg << "ProcessId: #{process.process_id}"
+ msg << "app_name: #{app_name}"
+ msg << "command_line: #{command_line}"
+ msg << "timeout: #{timeout}"
+ msg.join("\n")
+ end
end
end # class
end
diff --git a/mixlib-shellout.gemspec b/mixlib-shellout.gemspec
index e406c74..b02514b 100644
--- a/mixlib-shellout.gemspec
+++ b/mixlib-shellout.gemspec
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 1.9.3"
+ s.add_dependency "wmi-lite", "~> 1.0"
s.add_development_dependency "rspec", "~> 3.0"
s.bindir = "bin"
diff --git a/spec/mixlib/shellout_spec.rb b/spec/mixlib/shellout_spec.rb
index 1c5ce31..6b57619 100644
--- a/spec/mixlib/shellout_spec.rb
+++ b/spec/mixlib/shellout_spec.rb
@@ -621,7 +621,7 @@ describe Mixlib::ShellOut do
end
context "when running under Windows", :windows_only do
- let(:cmd) { 'whoami.exe' }
+ let(:cmd) { '%windir%/system32/whoami.exe' }
let(:running_user) { shell_cmd.run_command.stdout.strip.downcase }
context "when no user is set" do
@@ -629,7 +629,7 @@ describe Mixlib::ShellOut do
# to match how whoami returns the information
it "should run as current user" do
- expect(running_user).to eql("#{ENV['COMPUTERNAME'].downcase}\\#{ENV['USERNAME'].downcase}")
+ expect(running_user).to eql("#{ENV['USERDOMAIN'].downcase}\\#{ENV['USERNAME'].downcase}")
end
end
@@ -1116,11 +1116,24 @@ describe Mixlib::ShellOut do
'powershell -c "sleep 10"'
end
+ before do
+ require "wmi-lite/wmi"
+ allow(WmiLite::Wmi).to receive(:new)
+ allow(Mixlib::ShellOut::Windows::Utils).to receive(:kill_process_tree)
+ end
+
it "should raise CommandTimeout" do
Timeout::timeout(5) do
expect { executed_cmd }.to raise_error(Mixlib::ShellOut::CommandTimeout)
end
end
+
+ context 'and child processes should be killed' do
+ it 'kills the child processes' do
+ expect(Mixlib::ShellOut::Windows::Utils).to receive(:kill_process_tree)
+ expect { executed_cmd }.to raise_error(Mixlib::ShellOut::CommandTimeout)
+ end
+ end
end
context 'on unix', :unix_only do