summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml14
-rw-r--r--CHANGELOG.md4
-rw-r--r--Gemfile3
-rw-r--r--README.md85
-rw-r--r--appveyor.yml3
-rw-r--r--lib/mixlib/shellout.rb4
-rw-r--r--lib/mixlib/shellout/unix.rb4
-rw-r--r--lib/mixlib/shellout/version.rb2
-rw-r--r--lib/mixlib/shellout/windows.rb54
-rw-r--r--lib/mixlib/shellout/windows/core_ext.rb12
-rw-r--r--mixlib-shellout.gemspec8
-rw-r--r--spec/mixlib/shellout/windows_spec.rb48
-rw-r--r--spec/mixlib/shellout_spec.rb6
-rw-r--r--spec/support/platform_helpers.rb9
14 files changed, 176 insertions, 80 deletions
diff --git a/.travis.yml b/.travis.yml
index 02a71ae..ea98a8b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,15 @@
+language: ruby
+cache: bundler
+
+sudo: false
+
rvm:
- - 1.9.3
- - 2.0.0
- - 2.1.2
+ - 2.1.9
+ - 2.2.5
+ - 2.3.1
+
+before_install:
+ - gem update bundler
branches:
only:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b41faba..e5689a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# mixlib-shellout Changelog
+## Release 2.2.6
+
+* Fix regression introduced in 2.2.2 by changing `CreateProcessAsUserW` to use a `:int` instead of `:bool` for the `inherit` flag to fix `shell_out` on windows from a service context
+
## Release 2.2.5
* [**tschuy**:](https://github.com/tschuy)
diff --git a/Gemfile b/Gemfile
index d080412..1f468d9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,4 +9,7 @@ end
group(:development) do
gem 'pry'
+ gem 'pry-byebug'
+ gem 'pry-stack_explorer'
+ gem 'rb-readline'
end
diff --git a/README.md b/README.md
index 2cfe5b0..85146d8 100644
--- a/README.md
+++ b/README.md
@@ -1,54 +1,87 @@
# Mixlib::ShellOut
-Provides a simplified interface to shelling out yet still collecting both
-standard out and standard error and providing full control over environment,
-working directory, uid, gid, etc.
+[![Build Status Master](https://travis-ci.org/chef/mixlib-shellout.svg?branch=master)](https://travis-ci.org/chef/mixlib-shellout) [![Build Status Master](https://ci.appveyor.com/api/projects/status/github/chef/mixlib-shellout?branch=master&svg=true&passingText=master%20-%20Ok&pendingText=master%20-%20Pending&failingText=master%20-%20Failing)](https://ci.appveyor.com/project/Chef/mixlib-shellout/branch/master) [![Gem Version](https://badge.fury.io/rb/mixlib-shellout.svg)](https://badge.fury.io/rb/mixlib-shellout)
+
+Provides a simplified interface to shelling out while still collecting both standard out and standard error and providing full control over environment, working directory, uid, gid, etc.
No means for passing input to the subprocess is provided.
## Example
+### Simple Shellout
Invoke find(1) to search for .rb files:
- find = Mixlib::ShellOut.new("find . -name '*.rb'")
- find.run_command
+```ruby
+ require 'mixlib/shellout'
+ find = Mixlib::ShellOut.new("find . -name '*.rb'")
+ find.run_command
+```
If all went well, the results are on `stdout`
- puts find.stdout
+```ruby
+ puts find.stdout
+```
`find(1)` prints diagnostic info to STDERR:
- puts "error messages" + find.stderr
+```ruby
+ puts "error messages" + find.stderr
+```
Raise an exception if it didn't exit with 0
- find.error!
+```ruby
+ find.error!
+```
+
+### Advanced Shellout
+In addition to the command to run there are other options that can be set to change the shellout behavior. The complete list of options can be found here: https://github.com/chef/mixlib-shellout/blob/master/lib/mixlib/shellout.rb
-Run a command as the `www` user with no extra ENV settings from `/tmp`
+Run a command as the `www` user with no extra ENV settings from `/tmp` with a 1s timeout
- cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp')
- cmd.run_command # etc.
+```ruby
+ cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp', :timeout => 1)
+ cmd.run_command # etc.
+```
-## STDIN Example
+### STDIN Example
Invoke crontab to edit user cron:
- # :input only supports simple strings
- crontab_lines = [ "* * * * * /bin/true", "* * * * * touch /tmp/here" ]
- crontab = Mixlib::ShellOut.new("crontab -l -u #{@new_resource.user}", :input => crontab_lines.join("\n"))
- crontab.run_command
+```ruby
+ # :input only supports simple strings
+ crontab_lines = [ "* * * * * /bin/true", "* * * * * touch /tmp/here" ]
+ crontab = Mixlib::ShellOut.new("crontab -l -u #{@new_resource.user}", :input => crontab_lines.join("\n"))
+ crontab.run_command
+```
-## Windows Impersonation Example
+### Windows Impersonation Example
Invoke "whoami.exe" to demonstrate running a command as another user:
- whoami = Mixlib::ShellOut.new("whoami.exe", :user => "username", :domain => "DOMAIN", :password => "password")
- whoami.run_command
+```ruby
+ whoami = Mixlib::ShellOut.new("whoami.exe", :user => "username", :domain => "DOMAIN", :password => "password")
+ whoami.run_command
+```
## Platform Support
-Mixlib::ShellOut does a standard fork/exec on Unix, and uses the Win32
-API on Windows. There is not currently support for JRuby.
-
-## License
-Apache 2 Licensed. See LICENSE for full details.
+Mixlib::ShellOut does a standard fork/exec on Unix, and uses the Win32 API on Windows. There is not currently support for JRuby.
## See Also
-* `Process.spawn` in Ruby 1.9
-* [https://github.com/rtomayko/posix-spawn](https://github.com/rtomayko/posix-spawn)
+- `Process.spawn` in Ruby 1.9
+- [https://github.com/rtomayko/posix-spawn](https://github.com/rtomayko/posix-spawn)
+
+## License
+- Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
+- License:: Apache License, Version 2.0
+
+```text
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
diff --git a/appveyor.yml b/appveyor.yml
index de4779c..0ea6535 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -6,8 +6,7 @@ platform:
environment:
matrix:
- - ruby_version: "200"
- - ruby_version: "193"
+ - ruby_version: "21"
clone_folder: c:\projects\mixlib-shellout
clone_depth: 1
diff --git a/lib/mixlib/shellout.rb b/lib/mixlib/shellout.rb
index 6f8730f..2fdbcfd 100644
--- a/lib/mixlib/shellout.rb
+++ b/lib/mixlib/shellout.rb
@@ -1,6 +1,6 @@
#--
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2010, 2011 Opscode, Inc.
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2010-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/mixlib/shellout/unix.rb b/lib/mixlib/shellout/unix.rb
index dd22cbe..734a597 100644
--- a/lib/mixlib/shellout/unix.rb
+++ b/lib/mixlib/shellout/unix.rb
@@ -1,6 +1,6 @@
#--
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2010, 2011 Opscode, Inc.
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2010-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/mixlib/shellout/version.rb b/lib/mixlib/shellout/version.rb
index b8f65e8..e59d531 100644
--- a/lib/mixlib/shellout/version.rb
+++ b/lib/mixlib/shellout/version.rb
@@ -1,5 +1,5 @@
module Mixlib
class ShellOut
- VERSION = "2.2.5"
+ VERSION = "2.2.7"
end
end
diff --git a/lib/mixlib/shellout/windows.rb b/lib/mixlib/shellout/windows.rb
index 14aca55..5b13dad 100644
--- a/lib/mixlib/shellout/windows.rb
+++ b/lib/mixlib/shellout/windows.rb
@@ -1,8 +1,8 @@
#--
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Author:: John Keiser (<jkeiser@opscode.com>)
-# Author:: Ho-Sheng Hsiao (<hosh@opscode.com>)
-# Copyright:: Copyright (c) 2011, 2012 Opscode, Inc.
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
+# Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -321,31 +321,51 @@ module Mixlib
File.executable?(path) && !File.directory?(path)
end
+ def self.system_required_processes
+ [
+ 'System Idle Process',
+ 'System',
+ 'spoolsv.exe',
+ 'lsass.exe',
+ 'csrss.exe',
+ 'smss.exe',
+ 'svchost.exe'
+ ]
+ end
+
+ def self.unsafe_process?(name, logger)
+ return false unless system_required_processes.include? name
+ logger.debug(
+ "A request to kill a critical system process - #{name} - was received and skipped."
+ )
+ true
+ end
+
# recursively kills all child processes of given pid
# calls itself querying for children child procs until
# none remain. Important that a single WmiLite instance
# is passed in since each creates its own WMI rpc process
def self.kill_process_tree(pid, wmi, logger)
wmi.query("select * from Win32_Process where ParentProcessID=#{pid}").each do |instance|
+ next if unsafe_process?(instance.wmi_ole_object.name, logger)
child_pid = instance.wmi_ole_object.processid
kill_process_tree(child_pid, wmi, logger)
- begin
- logger.debug([
- "killing child process #{child_pid}::",
- "#{instance.wmi_ole_object.Name} of parent #{pid}"
- ].join) if logger
- kill_process(instance)
- rescue Errno::EIO, SystemCallError
- logger.debug([
- "Failed to kill child process #{child_pid}::",
- "#{instance.wmi_ole_object.Name} of parent #{pid}"
- ].join) if logger
- end
+ kill_process(instance, logger)
end
end
- def self.kill_process(instance)
+ def self.kill_process(instance, logger)
+ child_pid = instance.wmi_ole_object.processid
+ logger.debug([
+ "killing child process #{child_pid}::",
+ "#{instance.wmi_ole_object.Name} of parent #{pid}"
+ ].join) if logger
Process.kill(:KILL, instance.wmi_ole_object.processid)
+ rescue Errno::EIO, SystemCallError
+ logger.debug([
+ "Failed to kill child process #{child_pid}::",
+ "#{instance.wmi_ole_object.Name} of parent #{pid}"
+ ].join) if logger
end
def self.format_process(process, app_name, command_line, timeout)
diff --git a/lib/mixlib/shellout/windows/core_ext.rb b/lib/mixlib/shellout/windows/core_ext.rb
index d889778..2fe2bf2 100644
--- a/lib/mixlib/shellout/windows/core_ext.rb
+++ b/lib/mixlib/shellout/windows/core_ext.rb
@@ -1,7 +1,7 @@
#--
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Author:: John Keiser (<jkeiser@opscode.com>)
-# Copyright:: Copyright (c) 2011, 2012 Opscode, Inc.
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,7 +42,7 @@ module Process::Functions
[:buffer_in, :buffer_in, :buffer_in, :ulong, :ulong, :pointer], :bool
attach_pfunc :CreateProcessAsUserW,
- [:ulong, :buffer_in, :buffer_in, :pointer, :pointer, :bool,
+ [:ulong, :buffer_in, :buffer_inout, :pointer, :pointer, :int,
:ulong, :buffer_in, :buffer_in, :pointer, :pointer], :bool
ffi_lib :user32
@@ -268,8 +268,8 @@ module Process
# If running in the service windows station must do a log on to get
# to the interactive desktop. Running process user account must have
# the 'Replace a process level token' permission. This is necessary as
- # the logon (which happens with CreateProcessWithLogon) must have an
- # interactive windows station to attach to, which is created with the
+ # the logon (which happens with CreateProcessWithLogon) must have an
+ # interactive windows station to attach to, which is created with the
# LogonUser cann with the LOGON32_LOGON_INTERACTIVE flag.
if winsta_name =~ /^Service-0x0-.*$/i
token = FFI::MemoryPointer.new(:ulong)
diff --git a/mixlib-shellout.gemspec b/mixlib-shellout.gemspec
index e406c74..4aa568f 100644
--- a/mixlib-shellout.gemspec
+++ b/mixlib-shellout.gemspec
@@ -8,11 +8,11 @@ Gem::Specification.new do |s|
s.extra_rdoc_files = ["README.md", "LICENSE" ]
s.summary = "Run external commands on Unix or Windows"
s.description = s.summary
- s.author = "Opscode"
- s.email = "info@opscode.com"
- s.homepage = "http://wiki.opscode.com/"
+ s.author = "Chef Software Inc."
+ s.email = "info@chef.io"
+ s.homepage = "https://www.chef.io/"
- s.required_ruby_version = ">= 1.9.3"
+ s.required_ruby_version = ">= 2.1"
s.add_development_dependency "rspec", "~> 3.0"
diff --git a/spec/mixlib/shellout/windows_spec.rb b/spec/mixlib/shellout/windows_spec.rb
index dd8a80b..8b40fde 100644
--- a/spec/mixlib/shellout/windows_spec.rb
+++ b/spec/mixlib/shellout/windows_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'Mixlib::ShellOut::Windows', :windows_only do
-
+
describe 'Utils' do
describe '.should_run_under_cmd?' do
subject { Mixlib::ShellOut::Windows::Utils.should_run_under_cmd?(command) }
@@ -108,6 +108,46 @@ describe 'Mixlib::ShellOut::Windows', :windows_only do
end
end
end
+
+ describe '.kill_process_tree' do
+ let(:utils) { Mixlib::ShellOut::Windows::Utils }
+ let(:wmi) { Object.new }
+ let(:wmi_ole_object) { Object.new }
+ let(:wmi_process) { Object.new }
+ let(:logger) { Object.new }
+
+ before do
+ allow(wmi).to receive(:query).and_return([wmi_process])
+ allow(wmi_process).to receive(:wmi_ole_object).and_return(wmi_ole_object)
+ allow(logger).to receive(:debug)
+ end
+
+ context 'with a protected system process in the process tree' do
+ before do
+ allow(wmi_ole_object).to receive(:name).and_return('csrss.exe')
+ allow(wmi_ole_object).to receive(:processid).and_return(100)
+ end
+
+ it 'does not attempt to kill csrss.exe' do
+ expect(utils).to_not receive(:kill_process)
+ utils.kill_process_tree(200, wmi, logger)
+ end
+ end
+
+ context 'with a non-system-critical process in the process tree' do
+ before do
+ allow(wmi_ole_object).to receive(:name).and_return('blah.exe')
+ allow(wmi_ole_object).to receive(:processid).and_return(300)
+ end
+
+ it 'does attempt to kill blah.exe' do
+ expect(utils).to receive(:kill_process).with(wmi_process, logger)
+ expect(utils).to receive(:kill_process_tree).with(200, wmi, logger).and_call_original
+ expect(utils).to receive(:kill_process_tree).with(300, wmi, logger)
+ utils.kill_process_tree(200, wmi, logger)
+ end
+ end
+ end
end
# Caveat: Private API methods are subject to change without notice.
@@ -170,7 +210,7 @@ describe 'Mixlib::ShellOut::Windows', :windows_only do
allow(ENV).to receive(:[]).with('COMSPEC').and_return('C:\Windows\system32\cmd.exe')
allow(File).to receive(:executable?).and_return(false)
allow(File).to receive(:executable?).with(executable_path).and_return(true)
- allow(File).to receive(:directory?).and_return(false)
+ allow(File).to receive(:directory?).and_return(false)
end
it 'should return with full path with extension' do
@@ -181,7 +221,7 @@ describe 'Mixlib::ShellOut::Windows', :windows_only do
before do
# File.executable? returns true for directories
allow(File).to receive(:executable?).with(cmd).and_return(true)
- allow(File).to receive(:directory?).with(cmd).and_return(true)
+ allow(File).to receive(:directory?).with(cmd).and_return(true)
end
it 'should return with full path with extension' do
@@ -196,6 +236,8 @@ describe 'Mixlib::ShellOut::Windows', :windows_only do
let(:cmd_exe) { "C:\\Windows\\system32\\cmd.exe" }
let(:cmd) { "#{executable_path}" }
+ before { ENV['ComSpec'] = 'C:\Windows\system32\cmd.exe' }
+
context 'with .bat file' do
let(:executable_path) { '"C:\Program Files\Application\Start.bat"' }
diff --git a/spec/mixlib/shellout_spec.rb b/spec/mixlib/shellout_spec.rb
index 7e93054..38915f3 100644
--- a/spec/mixlib/shellout_spec.rb
+++ b/spec/mixlib/shellout_spec.rb
@@ -17,10 +17,7 @@ describe Mixlib::ShellOut do
let(:ruby_code) { raise 'define let(:ruby_code)' }
let(:options) { nil }
- # On some testing environments, we have gems that creates a deprecation notice sent
- # out on STDERR. To fix that, we disable gems on Ruby 1.9.2
- let(:ruby_eval) { lambda { |code| "ruby #{disable_gems} -e '#{code}'" } }
- let(:disable_gems) { ( ruby_19? ? '--disable-gems' : '') }
+ let(:ruby_eval) { lambda { |code| "ruby -e '#{code}'" } }
context 'when instantiating' do
subject { shell_cmd }
@@ -1144,7 +1141,6 @@ describe Mixlib::ShellOut do
context 'on unix', :unix_only do
def ruby_wo_shell(code)
parts = %w[ruby]
- parts << "--disable-gems" if ruby_19?
parts << "-e"
parts << code
end
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index c730b4d..bbf7d15 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -1,12 +1,3 @@
-
-def ruby_19?
- !!(RUBY_VERSION =~ /^1.9/)
-end
-
-def ruby_18?
- !!(RUBY_VERSION =~ /^1.8/)
-end
-
def windows?
!!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
end