summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Wrock <matt@mattwrock.com>2015-10-19 16:30:59 -0700
committerMatt Wrock <matt@mattwrock.com>2015-10-19 16:30:59 -0700
commit46d565c170eea893c95b5cb7d64378557938d309 (patch)
treecaf6f53d226a272edc17f238ead5157eb6fe1370
parentd9c4da3170cb6561d2148674ed9cce10baf76600 (diff)
parent24e7ea98594f5bf72ca3d9741c2bb600f53d8b84 (diff)
downloadmixlib-shellout-46d565c170eea893c95b5cb7d64378557938d309.tar.gz
merging latest master
-rw-r--r--CHANGELOG.md5
-rw-r--r--lib/mixlib/shellout/version.rb2
-rw-r--r--lib/mixlib/shellout/windows/core_ext.rb493
-rw-r--r--mixlib-shellout-windows.gemspec2
-rw-r--r--mixlib-shellout.gemspec3
5 files changed, 252 insertions, 253 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e100c0..864767e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# mixlib-shellout Changelog
+## Release 2.2.2
+
+* Ship gemspec and Gemfiles to facilate testing.
+* Fix #111 by pulling in an updated version of win-32/process and correctly patching Process::create.
+
## Release 2.2.1
* Fix executable resolution on Windows when a directory exists with the same name as the command to run
diff --git a/lib/mixlib/shellout/version.rb b/lib/mixlib/shellout/version.rb
index 2301857..4e968e8 100644
--- a/lib/mixlib/shellout/version.rb
+++ b/lib/mixlib/shellout/version.rb
@@ -1,5 +1,5 @@
module Mixlib
class ShellOut
- VERSION = "2.0.1"
+ VERSION = "2.2.2"
end
end
diff --git a/lib/mixlib/shellout/windows/core_ext.rb b/lib/mixlib/shellout/windows/core_ext.rb
index 6dfed2f..d889778 100644
--- a/lib/mixlib/shellout/windows/core_ext.rb
+++ b/lib/mixlib/shellout/windows/core_ext.rb
@@ -21,6 +21,8 @@ require 'win32/process'
# Add new constants for Logon
module Process::Constants
+ private
+
LOGON32_LOGON_INTERACTIVE = 0x00000002
LOGON32_PROVIDER_DEFAULT = 0x00000000
UOI_NAME = 0x00000002
@@ -34,16 +36,6 @@ end
# Define the functions needed to check with Service windows station
module Process::Functions
- module FFI::Library
- # Wrapper method for attach_function + private
- def attach_pfunc(*args)
- attach_function(*args)
- private args[0]
- end
- end
-
- extend FFI::Library
-
ffi_lib :advapi32
attach_pfunc :LogonUserW,
@@ -64,315 +56,316 @@ end
# Override Process.create to check for running in the Service window station and doing
# a full logon with LogonUser, instead of a CreateProcessWithLogon
+# Cloned from https://github.com/djberg96/win32-process/blob/ffi/lib/win32/process.rb
+# as of 2015-10-15 from commit cc066e5df25048f9806a610f54bf5f7f253e86f7
module Process
- include Process::Constants
- include Process::Structs
- def create(args)
- unless args.kind_of?(Hash)
- raise TypeError, 'hash keyword arguments expected'
- end
-
- valid_keys = %w[
- app_name command_line inherit creation_flags cwd environment
- startup_info thread_inherit process_inherit close_handles with_logon
- domain password
- ]
-
- valid_si_keys = %w[
- startf_flags desktop title x y x_size y_size x_count_chars
- y_count_chars fill_attribute sw_flags stdin stdout stderr
- ]
-
- # Set default values
- hash = {
- 'app_name' => nil,
- 'creation_flags' => 0,
- 'close_handles' => true
- }
-
- # Validate the keys, and convert symbols and case to lowercase strings.
- args.each{ |key, val|
- key = key.to_s.downcase
- unless valid_keys.include?(key)
- raise ArgumentError, "invalid key '#{key}'"
+ # Explicitly reopen singleton class so that class/constant declarations from
+ # extensions are visible in Modules.nesting.
+ class << self
+ def create(args)
+ unless args.kind_of?(Hash)
+ raise TypeError, 'hash keyword arguments expected'
end
- hash[key] = val
- }
- si_hash = {}
+ valid_keys = %w[
+ app_name command_line inherit creation_flags cwd environment
+ startup_info thread_inherit process_inherit close_handles with_logon
+ domain password
+ ]
+
+ valid_si_keys = %w[
+ startf_flags desktop title x y x_size y_size x_count_chars
+ y_count_chars fill_attribute sw_flags stdin stdout stderr
+ ]
+
+ # Set default values
+ hash = {
+ 'app_name' => nil,
+ 'creation_flags' => 0,
+ 'close_handles' => true
+ }
- # If the startup_info key is present, validate its subkeys
- if hash['startup_info']
- hash['startup_info'].each{ |key, val|
+ # Validate the keys, and convert symbols and case to lowercase strings.
+ args.each{ |key, val|
key = key.to_s.downcase
- unless valid_si_keys.include?(key)
- raise ArgumentError, "invalid startup_info key '#{key}'"
+ unless valid_keys.include?(key)
+ raise ArgumentError, "invalid key '#{key}'"
end
- si_hash[key] = val
+ hash[key] = val
}
- end
- # The +command_line+ key is mandatory unless the +app_name+ key
- # is specified.
- unless hash['command_line']
- if hash['app_name']
- hash['command_line'] = hash['app_name']
- hash['app_name'] = nil
- else
- raise ArgumentError, 'command_line or app_name must be specified'
- end
- end
-
- env = nil
+ si_hash = {}
- # The env string should be passed as a string of ';' separated paths.
- if hash['environment']
- env = hash['environment']
+ # If the startup_info key is present, validate its subkeys
+ if hash['startup_info']
+ hash['startup_info'].each{ |key, val|
+ key = key.to_s.downcase
+ unless valid_si_keys.include?(key)
+ raise ArgumentError, "invalid startup_info key '#{key}'"
+ end
+ si_hash[key] = val
+ }
+ end
- unless env.respond_to?(:join)
- env = hash['environment'].split(File::PATH_SEPARATOR)
+ # The +command_line+ key is mandatory unless the +app_name+ key
+ # is specified.
+ unless hash['command_line']
+ if hash['app_name']
+ hash['command_line'] = hash['app_name']
+ hash['app_name'] = nil
+ else
+ raise ArgumentError, 'command_line or app_name must be specified'
+ end
end
- env = env.map{ |e| e + 0.chr }.join('') + 0.chr
- env.to_wide_string! if hash['with_logon']
- end
+ env = nil
- # Process SECURITY_ATTRIBUTE structure
- process_security = nil
+ # The env string should be passed as a string of ';' separated paths.
+ if hash['environment']
+ env = hash['environment']
- if hash['process_inherit']
- process_security = SECURITY_ATTRIBUTES.new
- process_security[:nLength] = 12
- process_security[:bInheritHandle] = true
- end
+ unless env.respond_to?(:join)
+ env = hash['environment'].split(File::PATH_SEPARATOR)
+ end
- # Thread SECURITY_ATTRIBUTE structure
- thread_security = nil
+ env = env.map{ |e| e + 0.chr }.join('') + 0.chr
+ env.to_wide_string! if hash['with_logon']
+ end
- if hash['thread_inherit']
- thread_security = SECURITY_ATTRIBUTES.new
- thread_security[:nLength] = 12
- thread_security[:bInheritHandle] = true
- end
+ # Process SECURITY_ATTRIBUTE structure
+ process_security = nil
- # Automatically handle stdin, stdout and stderr as either IO objects
- # or file descriptors. This won't work for StringIO, however. It also
- # will not work on JRuby because of the way it handles internal file
- # descriptors.
- #
- ['stdin', 'stdout', 'stderr'].each{ |io|
- if si_hash[io]
- if si_hash[io].respond_to?(:fileno)
- handle = get_osfhandle(si_hash[io].fileno)
- else
- handle = get_osfhandle(si_hash[io])
- end
+ if hash['process_inherit']
+ process_security = SECURITY_ATTRIBUTES.new
+ process_security[:nLength] = 12
+ process_security[:bInheritHandle] = 1
+ end
- if handle == INVALID_HANDLE_VALUE
- ptr = FFI::MemoryPointer.new(:int)
+ # Thread SECURITY_ATTRIBUTE structure
+ thread_security = nil
- if windows_version >= 6 && get_errno(ptr) == 0
- errno = ptr.read_int
+ if hash['thread_inherit']
+ thread_security = SECURITY_ATTRIBUTES.new
+ thread_security[:nLength] = 12
+ thread_security[:bInheritHandle] = 1
+ end
+
+ # Automatically handle stdin, stdout and stderr as either IO objects
+ # or file descriptors. This won't work for StringIO, however. It also
+ # will not work on JRuby because of the way it handles internal file
+ # descriptors.
+ #
+ ['stdin', 'stdout', 'stderr'].each{ |io|
+ if si_hash[io]
+ if si_hash[io].respond_to?(:fileno)
+ handle = get_osfhandle(si_hash[io].fileno)
else
- errno = FFI.errno
+ handle = get_osfhandle(si_hash[io])
end
- raise SystemCallError.new("get_osfhandle", errno)
- end
+ if handle == INVALID_HANDLE_VALUE
+ ptr = FFI::MemoryPointer.new(:int)
- # Most implementations of Ruby on Windows create inheritable
- # handles by default, but some do not. RF bug #26988.
- bool = SetHandleInformation(
- handle,
- HANDLE_FLAG_INHERIT,
- HANDLE_FLAG_INHERIT
- )
+ if windows_version >= 6 && get_errno(ptr) == 0
+ errno = ptr.read_int
+ else
+ errno = FFI.errno
+ end
- raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool
+ raise SystemCallError.new("get_osfhandle", errno)
+ end
- si_hash[io] = handle
- si_hash['startf_flags'] ||= 0
- si_hash['startf_flags'] |= STARTF_USESTDHANDLES
- hash['inherit'] = true
- end
- }
-
- procinfo = PROCESS_INFORMATION.new
- startinfo = STARTUPINFO.new
-
- unless si_hash.empty?
- startinfo[:cb] = startinfo.size
- startinfo[:lpDesktop] = si_hash['desktop'] if si_hash['desktop']
- startinfo[:lpTitle] = si_hash['title'] if si_hash['title']
- startinfo[:dwX] = si_hash['x'] if si_hash['x']
- startinfo[:dwY] = si_hash['y'] if si_hash['y']
- startinfo[:dwXSize] = si_hash['x_size'] if si_hash['x_size']
- startinfo[:dwYSize] = si_hash['y_size'] if si_hash['y_size']
- startinfo[:dwXCountChars] = si_hash['x_count_chars'] if si_hash['x_count_chars']
- startinfo[:dwYCountChars] = si_hash['y_count_chars'] if si_hash['y_count_chars']
- startinfo[:dwFillAttribute] = si_hash['fill_attribute'] if si_hash['fill_attribute']
- startinfo[:dwFlags] = si_hash['startf_flags'] if si_hash['startf_flags']
- startinfo[:wShowWindow] = si_hash['sw_flags'] if si_hash['sw_flags']
- startinfo[:cbReserved2] = 0
- startinfo[:hStdInput] = si_hash['stdin'] if si_hash['stdin']
- startinfo[:hStdOutput] = si_hash['stdout'] if si_hash['stdout']
- startinfo[:hStdError] = si_hash['stderr'] if si_hash['stderr']
- end
+ # Most implementations of Ruby on Windows create inheritable
+ # handles by default, but some do not. RF bug #26988.
+ bool = SetHandleInformation(
+ handle,
+ HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT
+ )
- app = nil
- cmd = nil
+ raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool
- # Convert strings to wide character strings if present
- if hash['app_name']
- app = hash['app_name'].to_wide_string
- end
-
- if hash['command_line']
- cmd = hash['command_line'].to_wide_string
- end
+ si_hash[io] = handle
+ si_hash['startf_flags'] ||= 0
+ si_hash['startf_flags'] |= STARTF_USESTDHANDLES
+ hash['inherit'] = true
+ end
+ }
- if hash['cwd']
- cwd = hash['cwd'].to_wide_string
- end
+ procinfo = PROCESS_INFORMATION.new
+ startinfo = STARTUPINFO.new
+
+ unless si_hash.empty?
+ startinfo[:cb] = startinfo.size
+ startinfo[:lpDesktop] = si_hash['desktop'] if si_hash['desktop']
+ startinfo[:lpTitle] = si_hash['title'] if si_hash['title']
+ startinfo[:dwX] = si_hash['x'] if si_hash['x']
+ startinfo[:dwY] = si_hash['y'] if si_hash['y']
+ startinfo[:dwXSize] = si_hash['x_size'] if si_hash['x_size']
+ startinfo[:dwYSize] = si_hash['y_size'] if si_hash['y_size']
+ startinfo[:dwXCountChars] = si_hash['x_count_chars'] if si_hash['x_count_chars']
+ startinfo[:dwYCountChars] = si_hash['y_count_chars'] if si_hash['y_count_chars']
+ startinfo[:dwFillAttribute] = si_hash['fill_attribute'] if si_hash['fill_attribute']
+ startinfo[:dwFlags] = si_hash['startf_flags'] if si_hash['startf_flags']
+ startinfo[:wShowWindow] = si_hash['sw_flags'] if si_hash['sw_flags']
+ startinfo[:cbReserved2] = 0
+ startinfo[:hStdInput] = si_hash['stdin'] if si_hash['stdin']
+ startinfo[:hStdOutput] = si_hash['stdout'] if si_hash['stdout']
+ startinfo[:hStdError] = si_hash['stderr'] if si_hash['stderr']
+ end
- inherit = hash['inherit'] || false
+ app = nil
+ cmd = nil
- if hash['with_logon']
- logon = hash['with_logon'].to_wide_string
+ # Convert strings to wide character strings if present
+ if hash['app_name']
+ app = hash['app_name'].to_wide_string
+ end
- if hash['password']
- passwd = hash['password'].to_wide_string
- else
- raise ArgumentError, 'password must be specified if with_logon is used'
+ if hash['command_line']
+ cmd = hash['command_line'].to_wide_string
end
- if hash['domain']
- domain = hash['domain'].to_wide_string
+ if hash['cwd']
+ cwd = hash['cwd'].to_wide_string
end
- hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
+ inherit = hash['inherit'] ? 1 : 0
- winsta_name = FFI::MemoryPointer.new(:char, 256)
- return_size = FFI::MemoryPointer.new(:ulong)
+ if hash['with_logon']
+ logon = hash['with_logon'].to_wide_string
- bool = GetUserObjectInformationA(
- GetProcessWindowStation(), # Window station handle
- UOI_NAME, # Information to get
- winsta_name, # Buffer to receive information
- winsta_name.size, # Size of buffer
- return_size # Size filled into buffer
- )
+ if hash['password']
+ passwd = hash['password'].to_wide_string
+ else
+ raise ArgumentError, 'password must be specified if with_logon is used'
+ end
- unless bool
- raise SystemCallError.new("GetUserObjectInformationA", FFI.errno)
- end
+ if hash['domain']
+ domain = hash['domain'].to_wide_string
+ end
+
+ hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
- winsta_name = winsta_name.read_string(return_size.read_ulong)
-
- # 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
- # LogonUser cann with the LOGON32_LOGON_INTERACTIVE flag.
- if winsta_name =~ /^Service-0x0-.*$/i
- token = FFI::MemoryPointer.new(:ulong)
-
- bool = LogonUserW(
- logon, # User
- domain, # Domain
- passwd, # Password
- LOGON32_LOGON_INTERACTIVE, # Logon Type
- LOGON32_PROVIDER_DEFAULT, # Logon Provider
- token # User token handle
+ winsta_name = FFI::MemoryPointer.new(:char, 256)
+ return_size = FFI::MemoryPointer.new(:ulong)
+
+ bool = GetUserObjectInformationA(
+ GetProcessWindowStation(), # Window station handle
+ UOI_NAME, # Information to get
+ winsta_name, # Buffer to receive information
+ winsta_name.size, # Size of buffer
+ return_size # Size filled into buffer
)
unless bool
- raise SystemCallError.new("LogonUserW", FFI.errno)
+ raise SystemCallError.new("GetUserObjectInformationA", FFI.errno)
end
- token = token.read_ulong
+ winsta_name = winsta_name.read_string(return_size.read_ulong)
+
+ # 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
+ # LogonUser cann with the LOGON32_LOGON_INTERACTIVE flag.
+ if winsta_name =~ /^Service-0x0-.*$/i
+ token = FFI::MemoryPointer.new(:ulong)
+
+ bool = LogonUserW(
+ logon, # User
+ domain, # Domain
+ passwd, # Password
+ LOGON32_LOGON_INTERACTIVE, # Logon Type
+ LOGON32_PROVIDER_DEFAULT, # Logon Provider
+ token # User token handle
+ )
+
+ unless bool
+ raise SystemCallError.new("LogonUserW", FFI.errno)
+ end
- begin
- bool = CreateProcessAsUserW(
- token, # User token handle
+ token = token.read_ulong
+
+ begin
+ bool = CreateProcessAsUserW(
+ token, # User token handle
+ app, # App name
+ cmd, # Command line
+ process_security, # Process attributes
+ thread_security, # Thread attributes
+ inherit, # Inherit handles
+ hash['creation_flags'], # Creation Flags
+ env, # Environment
+ cwd, # Working directory
+ startinfo, # Startup Info
+ procinfo # Process Info
+ )
+ ensure
+ CloseHandle(token)
+ end
+
+ unless bool
+ raise SystemCallError.new("CreateProcessAsUserW (You must hold the 'Replace a process level token' permission)", FFI.errno)
+ end
+ else
+ bool = CreateProcessWithLogonW(
+ logon, # User
+ domain, # Domain
+ passwd, # Password
+ LOGON_WITH_PROFILE, # Logon flags
app, # App name
cmd, # Command line
- process_security, # Process attributes
- thread_security, # Thread attributes
- inherit, # Inherit handles
- hash['creation_flags'], # Creation Flags
+ hash['creation_flags'], # Creation flags
env, # Environment
cwd, # Working directory
startinfo, # Startup Info
procinfo # Process Info
)
- ensure
- CloseHandle(token)
- end
- unless bool
- raise SystemCallError.new("CreateProcessAsUserW (You must hold the 'Replace a process level token' permission)", FFI.errno)
+ unless bool
+ raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
+ end
end
else
- bool = CreateProcessWithLogonW(
- logon, # User
- domain, # Domain
- passwd, # Password
- LOGON_WITH_PROFILE, # Logon flags
+ bool = CreateProcessW(
app, # App name
cmd, # Command line
+ process_security, # Process attributes
+ thread_security, # Thread attributes
+ inherit, # Inherit handles?
hash['creation_flags'], # Creation flags
env, # Environment
cwd, # Working directory
startinfo, # Startup Info
procinfo # Process Info
)
- end
- unless bool
- raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
+ unless bool
+ raise SystemCallError.new("CreateProcessW", FFI.errno)
+ end
end
- else
- bool = CreateProcessW(
- app, # App name
- cmd, # Command line
- process_security, # Process attributes
- thread_security, # Thread attributes
- inherit, # Inherit handles?
- hash['creation_flags'], # Creation flags
- env, # Environment
- cwd, # Working directory
- startinfo, # Startup Info
- procinfo # Process Info
- )
- unless bool
- raise SystemCallError.new("CreateProcessW", FFI.errno)
+ # Automatically close the process and thread handles in the
+ # PROCESS_INFORMATION struct unless explicitly told not to.
+ if hash['close_handles']
+ CloseHandle(procinfo[:hProcess])
+ CloseHandle(procinfo[:hThread])
+ # Clear these fields so callers don't attempt to close the handle
+ # which can result in the wrong handle being closed or an
+ # exception in some circumstances.
+ procinfo[:hProcess] = 0
+ procinfo[:hThread] = 0
end
- end
- # Automatically close the process and thread handles in the
- # PROCESS_INFORMATION struct unless explicitly told not to.
- if hash['close_handles']
- CloseHandle(procinfo[:hProcess]) if procinfo[:hProcess]
- CloseHandle(procinfo[:hThread]) if procinfo[:hThread]
-
- # Set fields to nil so callers don't attempt to close the handle
- # which can result in the wrong handle being closed or an
- # exception in some circumstances
- procinfo[:hProcess] = nil
- procinfo[:hThread] = nil
+ ProcessInfo.new(
+ procinfo[:hProcess],
+ procinfo[:hThread],
+ procinfo[:dwProcessId],
+ procinfo[:dwThreadId]
+ )
end
-
- ProcessInfo.new(
- procinfo[:hProcess],
- procinfo[:hThread],
- procinfo[:dwProcessId],
- procinfo[:dwThreadId]
- )
end
-
- module_function :create
end
diff --git a/mixlib-shellout-windows.gemspec b/mixlib-shellout-windows.gemspec
index 8e178de..fc370e7 100644
--- a/mixlib-shellout-windows.gemspec
+++ b/mixlib-shellout-windows.gemspec
@@ -2,6 +2,6 @@ gemspec = eval(File.read(File.expand_path("../mixlib-shellout.gemspec", __FILE__
gemspec.platform = Gem::Platform.new(["universal", "mingw32"])
-gemspec.add_dependency "win32-process", "~> 0.7.5"
+gemspec.add_dependency "win32-process", "~> 0.8.2"
gemspec
diff --git a/mixlib-shellout.gemspec b/mixlib-shellout.gemspec
index ed23fd1..b02514b 100644
--- a/mixlib-shellout.gemspec
+++ b/mixlib-shellout.gemspec
@@ -20,5 +20,6 @@ Gem::Specification.new do |s|
s.bindir = "bin"
s.executables = []
s.require_path = 'lib'
- s.files = %w(LICENSE README.md) + Dir.glob("lib/**/*")
+ s.files = %w(Gemfile Rakefile LICENSE README.md) + Dir.glob("*.gemspec") +
+ Dir.glob("lib/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
end