mixlib-shellout 1.1.0-x86-mingw32 → 1.2.0.rc.0-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +0 -0
- data/README.md +6 -0
- data/lib/mixlib/shellout.rb +14 -2
- data/lib/mixlib/shellout/exceptions.rb +0 -0
- data/lib/mixlib/shellout/unix.rb +39 -6
- data/lib/mixlib/shellout/version.rb +1 -1
- data/lib/mixlib/shellout/windows.rb +13 -0
- data/lib/mixlib/shellout/windows/core_ext.rb +215 -237
- metadata +40 -14
data/LICENSE
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -36,6 +36,12 @@ Invoke crontab to edit user cron:
|
|
36
36
|
crontab = Mixlib::ShellOut.new("crontab -l -u #{@new_resource.user}", :input => crontab_lines.join("\n"))
|
37
37
|
crontab.run_command
|
38
38
|
|
39
|
+
## Windows Impersonation Example
|
40
|
+
Invoke crontab to edit user cron:
|
41
|
+
|
42
|
+
whomai = Mixlib::ShellOut.new("whoami.exe", :user => "username", :domain => "DOMAIN", :password => "password")
|
43
|
+
whoami.run_command
|
44
|
+
|
39
45
|
## Platform Support
|
40
46
|
Mixlib::ShellOut does a standard fork/exec on Unix, and uses the Win32
|
41
47
|
API on Windows. There is not currently support for JRuby.
|
data/lib/mixlib/shellout.rb
CHANGED
@@ -39,6 +39,9 @@ module Mixlib
|
|
39
39
|
|
40
40
|
# User the command will run as. Normally set via options passed to new
|
41
41
|
attr_accessor :user
|
42
|
+
attr_accessor :domain
|
43
|
+
attr_accessor :password
|
44
|
+
attr_accessor :with_logon
|
42
45
|
|
43
46
|
# Group the command will run as. Normally set via options passed to new
|
44
47
|
attr_accessor :group
|
@@ -213,7 +216,7 @@ module Mixlib
|
|
213
216
|
# * Errno::ENOENT when the command is not available on the system (or not
|
214
217
|
# in the current $PATH)
|
215
218
|
# * CommandTimeout when the command does not complete
|
216
|
-
# within +timeout+ seconds (default:
|
219
|
+
# within +timeout+ seconds (default: 600s)
|
217
220
|
def run_command
|
218
221
|
if logger
|
219
222
|
log_message = (log_tag.nil? ? "" : "#@log_tag ") << "sh(#@command)"
|
@@ -261,8 +264,13 @@ module Mixlib
|
|
261
264
|
case option.to_s
|
262
265
|
when 'cwd'
|
263
266
|
self.cwd = setting
|
267
|
+
when 'domain'
|
268
|
+
self.domain = setting
|
269
|
+
when 'password'
|
270
|
+
self.password = setting
|
264
271
|
when 'user'
|
265
272
|
self.user = setting
|
273
|
+
self.with_logon = setting
|
266
274
|
when 'group'
|
267
275
|
self.group = setting
|
268
276
|
when 'umask'
|
@@ -288,8 +296,12 @@ module Mixlib
|
|
288
296
|
raise InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
|
289
297
|
end
|
290
298
|
end
|
291
|
-
end
|
292
299
|
|
300
|
+
validate_options(opts)
|
301
|
+
end
|
293
302
|
|
303
|
+
def validate_options(opts)
|
304
|
+
super
|
305
|
+
end
|
294
306
|
end
|
295
307
|
end
|
File without changes
|
data/lib/mixlib/shellout/unix.rb
CHANGED
@@ -20,6 +20,11 @@ module Mixlib
|
|
20
20
|
class ShellOut
|
21
21
|
module Unix
|
22
22
|
|
23
|
+
# Option validation that is unix specific
|
24
|
+
def validate_options(opts)
|
25
|
+
# No options to validate, raise exceptions here if needed
|
26
|
+
end
|
27
|
+
|
23
28
|
# Run the command, writing the command's standard out and standard error
|
24
29
|
# to +stdout+ and +stderr+, and saving its exit status object to +status+
|
25
30
|
# === Returns
|
@@ -30,11 +35,20 @@ module Mixlib
|
|
30
35
|
# * Errno::ENOENT when the command is not available on the system (or not
|
31
36
|
# in the current $PATH)
|
32
37
|
# * Chef::Exceptions::CommandTimeout when the command does not complete
|
33
|
-
# within +timeout+ seconds (default:
|
38
|
+
# within +timeout+ seconds (default: 600s)
|
34
39
|
def run_command
|
35
40
|
@child_pid = fork_subprocess
|
36
41
|
|
37
42
|
configure_parent_process_file_descriptors
|
43
|
+
|
44
|
+
# Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
|
45
|
+
# when calling IO.select and IO#read. Some OS Vendors are not interested
|
46
|
+
# in updating their ruby packages (Apple, *cough*) and we *have to*
|
47
|
+
# make it work. So I give you this epic hack:
|
48
|
+
GC.disable
|
49
|
+
|
50
|
+
# CHEF-3390: Marshall.load on Ruby < 1.8.7p369 also has a GC bug related
|
51
|
+
# to Marshall.load, so try disabling GC first.
|
38
52
|
propagate_pre_exec_failure
|
39
53
|
|
40
54
|
@result = nil
|
@@ -42,11 +56,6 @@ module Mixlib
|
|
42
56
|
|
43
57
|
write_to_child_stdin
|
44
58
|
|
45
|
-
# Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
|
46
|
-
# when calling IO.select and IO#read. Some OS Vendors are not interested
|
47
|
-
# in updating their ruby packages (Apple, *cough*) and we *have to*
|
48
|
-
# make it work. So I give you this epic hack:
|
49
|
-
GC.disable
|
50
59
|
until @status
|
51
60
|
ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
|
52
61
|
unless ready
|
@@ -169,6 +178,28 @@ module Mixlib
|
|
169
178
|
STDIN.sync = true if input
|
170
179
|
end
|
171
180
|
|
181
|
+
# When a new process is started with chef, it shares the file
|
182
|
+
# descriptors of the parent. We clean the file descriptors
|
183
|
+
# coming from the parent to prevent unintended locking if parent
|
184
|
+
# is killed.
|
185
|
+
# NOTE: After some discussions we've decided to iterate on file
|
186
|
+
# descriptors upto 256. We believe this is a reasonable upper
|
187
|
+
# limit in a chef environment. If we have issues in the future this
|
188
|
+
# number could be made to be configurable or updated based on
|
189
|
+
# the ulimit based on platform.
|
190
|
+
def clean_parent_file_descriptors
|
191
|
+
# Don't clean $stdin, $stdout, $stderr, process_status_pipe.
|
192
|
+
3.upto(256) do |n|
|
193
|
+
# We are checking the fd for error pipe before attempting to
|
194
|
+
# create a file because error pipe will auto close when we
|
195
|
+
# try to create a file since it's set to CLOEXEC.
|
196
|
+
if n != @process_status_pipe.last.to_i
|
197
|
+
fd = File.for_fd(n) rescue nil
|
198
|
+
fd.close if fd
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
172
203
|
def configure_parent_process_file_descriptors
|
173
204
|
# Close the sides of the pipes we don't care about
|
174
205
|
stdin_pipe.first.close
|
@@ -222,6 +253,8 @@ module Mixlib
|
|
222
253
|
fork do
|
223
254
|
configure_subprocess_file_descriptors
|
224
255
|
|
256
|
+
clean_parent_file_descriptors
|
257
|
+
|
225
258
|
set_group
|
226
259
|
set_user
|
227
260
|
set_environment
|
@@ -35,6 +35,15 @@ module Mixlib
|
|
35
35
|
|
36
36
|
TIME_SLICE = 0.05
|
37
37
|
|
38
|
+
# Option validation that is windows specific
|
39
|
+
def validate_options(opts)
|
40
|
+
if opts[:user]
|
41
|
+
unless opts[:password]
|
42
|
+
raise InvalidCommandOption, "You must supply both a username and password when supplying a user in windows"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
38
47
|
#--
|
39
48
|
# Missing lots of features from the UNIX version, such as
|
40
49
|
# uid, etc.
|
@@ -66,6 +75,10 @@ module Mixlib
|
|
66
75
|
:close_handles => false
|
67
76
|
}
|
68
77
|
create_process_args[:cwd] = cwd if cwd
|
78
|
+
# default to local account database if domain is not specified
|
79
|
+
create_process_args[:domain] = domain.nil? ? "." : domain
|
80
|
+
create_process_args[:with_logon] = with_logon if with_logon
|
81
|
+
create_process_args[:password] = password if password
|
69
82
|
|
70
83
|
#
|
71
84
|
# Start the process
|
@@ -18,40 +18,65 @@
|
|
18
18
|
#
|
19
19
|
|
20
20
|
require 'win32/process'
|
21
|
-
require 'windows/handle'
|
22
|
-
require 'windows/process'
|
23
|
-
require 'windows/synchronize'
|
24
21
|
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
# Add new constants for Logon
|
23
|
+
module Process::Constants
|
24
|
+
LOGON32_LOGON_INTERACTIVE = 0x00000002
|
25
|
+
LOGON32_PROVIDER_DEFAULT = 0x00000000
|
26
|
+
UOI_NAME = 0x00000002
|
27
|
+
end
|
28
|
+
|
29
|
+
# Define the functions needed to check with Service windows station
|
30
|
+
module Process::Functions
|
31
|
+
module FFI::Library
|
32
|
+
# Wrapper method for attach_function + private
|
33
|
+
def attach_pfunc(*args)
|
34
|
+
attach_function(*args)
|
35
|
+
private args[0]
|
36
|
+
end
|
31
37
|
end
|
38
|
+
|
39
|
+
extend FFI::Library
|
40
|
+
|
41
|
+
ffi_lib :advapi32
|
42
|
+
|
43
|
+
attach_pfunc :LogonUserW,
|
44
|
+
[:buffer_in, :buffer_in, :buffer_in, :ulong, :ulong, :pointer], :bool
|
45
|
+
|
46
|
+
attach_pfunc :CreateProcessAsUserW,
|
47
|
+
[:ulong, :buffer_in, :buffer_in, :pointer, :pointer, :bool,
|
48
|
+
:ulong, :buffer_in, :buffer_in, :pointer, :pointer], :bool
|
49
|
+
|
50
|
+
ffi_lib :user32
|
51
|
+
|
52
|
+
attach_pfunc :GetProcessWindowStation,
|
53
|
+
[], :ulong
|
54
|
+
|
55
|
+
attach_pfunc :GetUserObjectInformationA,
|
56
|
+
[:ulong, :uint, :buffer_out, :ulong, :pointer], :bool
|
32
57
|
end
|
33
58
|
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# so that variables can contain semicolons
|
37
|
-
# (submitted patch to owner)
|
38
|
-
#
|
59
|
+
# Override Process.create to check for running in the Service window station and doing
|
60
|
+
# a full logon with LogonUser, instead of a CreateProcessWithLogon
|
39
61
|
module Process
|
62
|
+
include Process::Constants
|
63
|
+
include Process::Structs
|
64
|
+
|
40
65
|
def create(args)
|
41
66
|
unless args.kind_of?(Hash)
|
42
|
-
raise TypeError, '
|
67
|
+
raise TypeError, 'hash keyword arguments expected'
|
43
68
|
end
|
44
69
|
|
45
|
-
valid_keys = %w
|
70
|
+
valid_keys = %w[
|
46
71
|
app_name command_line inherit creation_flags cwd environment
|
47
72
|
startup_info thread_inherit process_inherit close_handles with_logon
|
48
73
|
domain password
|
49
|
-
|
74
|
+
]
|
50
75
|
|
51
|
-
valid_si_keys =
|
76
|
+
valid_si_keys = %w[
|
52
77
|
startf_flags desktop title x y x_size y_size x_count_chars
|
53
78
|
y_count_chars fill_attribute sw_flags stdin stdout stderr
|
54
|
-
|
79
|
+
]
|
55
80
|
|
56
81
|
# Set default values
|
57
82
|
hash = {
|
@@ -93,46 +118,43 @@ module Process
|
|
93
118
|
end
|
94
119
|
end
|
95
120
|
|
96
|
-
|
97
|
-
|
121
|
+
env = nil
|
122
|
+
|
123
|
+
# The env string should be passed as a string of ';' separated paths.
|
98
124
|
if hash['environment']
|
99
125
|
env = hash['environment']
|
100
|
-
|
101
|
-
|
126
|
+
|
127
|
+
unless env.respond_to?(:join)
|
102
128
|
env = hash['environment'].split(File::PATH_SEPARATOR)
|
103
129
|
end
|
104
|
-
# The argument format is a series of null-terminated strings, with an additional null terminator.
|
105
|
-
env = env.map { |e| e + "\0" }.join("") + "\0"
|
106
|
-
if hash['with_logon']
|
107
|
-
env = env.multi_to_wide(e)
|
108
|
-
end
|
109
|
-
env = [env].pack('p*').unpack('L').first
|
110
|
-
else
|
111
|
-
env = nil
|
112
|
-
end
|
113
130
|
|
114
|
-
|
115
|
-
|
116
|
-
|
131
|
+
env = env.map{ |e| e + 0.chr }.join('') + 0.chr
|
132
|
+
env.to_wide_string! if hash['with_logon']
|
133
|
+
end
|
117
134
|
|
118
135
|
# Process SECURITY_ATTRIBUTE structure
|
119
|
-
process_security =
|
136
|
+
process_security = nil
|
137
|
+
|
120
138
|
if hash['process_inherit']
|
121
|
-
process_security =
|
122
|
-
process_security[
|
123
|
-
process_security[
|
139
|
+
process_security = SECURITY_ATTRIBUTES.new
|
140
|
+
process_security[:nLength] = 12
|
141
|
+
process_security[:bInheritHandle] = true
|
124
142
|
end
|
125
143
|
|
126
144
|
# Thread SECURITY_ATTRIBUTE structure
|
127
|
-
thread_security =
|
145
|
+
thread_security = nil
|
146
|
+
|
128
147
|
if hash['thread_inherit']
|
129
|
-
thread_security =
|
130
|
-
thread_security[
|
131
|
-
thread_security[
|
148
|
+
thread_security = SECURITY_ATTRIBUTES.new
|
149
|
+
thread_security[:nLength] = 12
|
150
|
+
thread_security[:bInheritHandle] = true
|
132
151
|
end
|
133
152
|
|
134
153
|
# Automatically handle stdin, stdout and stderr as either IO objects
|
135
|
-
# or file descriptors.
|
154
|
+
# or file descriptors. This won't work for StringIO, however. It also
|
155
|
+
# will not work on JRuby because of the way it handles internal file
|
156
|
+
# descriptors.
|
157
|
+
#
|
136
158
|
['stdin', 'stdout', 'stderr'].each{ |io|
|
137
159
|
if si_hash[io]
|
138
160
|
if si_hash[io].respond_to?(:fileno)
|
@@ -142,7 +164,15 @@ module Process
|
|
142
164
|
end
|
143
165
|
|
144
166
|
if handle == INVALID_HANDLE_VALUE
|
145
|
-
|
167
|
+
ptr = FFI::MemoryPointer.new(:int)
|
168
|
+
|
169
|
+
if windows_version >= 6 && get_errno(ptr) == 0
|
170
|
+
errno = ptr.read_int
|
171
|
+
else
|
172
|
+
errno = FFI.errno
|
173
|
+
end
|
174
|
+
|
175
|
+
raise SystemCallError.new("get_osfhandle", errno)
|
146
176
|
end
|
147
177
|
|
148
178
|
# Most implementations of Ruby on Windows create inheritable
|
@@ -153,7 +183,7 @@ module Process
|
|
153
183
|
HANDLE_FLAG_INHERIT
|
154
184
|
)
|
155
185
|
|
156
|
-
raise
|
186
|
+
raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool
|
157
187
|
|
158
188
|
si_hash[io] = handle
|
159
189
|
si_hash['startf_flags'] ||= 0
|
@@ -162,224 +192,172 @@ module Process
|
|
162
192
|
end
|
163
193
|
}
|
164
194
|
|
165
|
-
|
195
|
+
procinfo = PROCESS_INFORMATION.new
|
196
|
+
startinfo = STARTUPINFO.new
|
197
|
+
|
166
198
|
unless si_hash.empty?
|
167
|
-
startinfo[
|
168
|
-
startinfo[
|
169
|
-
startinfo[
|
170
|
-
startinfo[
|
171
|
-
startinfo[
|
172
|
-
startinfo[
|
173
|
-
startinfo[
|
174
|
-
startinfo[
|
175
|
-
startinfo[
|
176
|
-
startinfo[
|
177
|
-
startinfo[
|
178
|
-
startinfo[
|
179
|
-
startinfo[
|
180
|
-
startinfo[
|
181
|
-
startinfo[
|
199
|
+
startinfo[:cb] = startinfo.size
|
200
|
+
startinfo[:lpDesktop] = si_hash['desktop'] if si_hash['desktop']
|
201
|
+
startinfo[:lpTitle] = si_hash['title'] if si_hash['title']
|
202
|
+
startinfo[:dwX] = si_hash['x'] if si_hash['x']
|
203
|
+
startinfo[:dwY] = si_hash['y'] if si_hash['y']
|
204
|
+
startinfo[:dwXSize] = si_hash['x_size'] if si_hash['x_size']
|
205
|
+
startinfo[:dwYSize] = si_hash['y_size'] if si_hash['y_size']
|
206
|
+
startinfo[:dwXCountChars] = si_hash['x_count_chars'] if si_hash['x_count_chars']
|
207
|
+
startinfo[:dwYCountChars] = si_hash['y_count_chars'] if si_hash['y_count_chars']
|
208
|
+
startinfo[:dwFillAttribute] = si_hash['fill_attribute'] if si_hash['fill_attribute']
|
209
|
+
startinfo[:dwFlags] = si_hash['startf_flags'] if si_hash['startf_flags']
|
210
|
+
startinfo[:wShowWindow] = si_hash['sw_flags'] if si_hash['sw_flags']
|
211
|
+
startinfo[:cbReserved2] = 0
|
212
|
+
startinfo[:hStdInput] = si_hash['stdin'] if si_hash['stdin']
|
213
|
+
startinfo[:hStdOutput] = si_hash['stdout'] if si_hash['stdout']
|
214
|
+
startinfo[:hStdError] = si_hash['stderr'] if si_hash['stderr']
|
215
|
+
end
|
216
|
+
|
217
|
+
app = nil
|
218
|
+
cmd = nil
|
219
|
+
|
220
|
+
# Convert strings to wide character strings if present
|
221
|
+
if hash['app_name']
|
222
|
+
app = hash['app_name'].to_wide_string
|
223
|
+
end
|
224
|
+
|
225
|
+
if hash['command_line']
|
226
|
+
cmd = hash['command_line'].to_wide_string
|
227
|
+
end
|
228
|
+
|
229
|
+
if hash['cwd']
|
230
|
+
cwd = hash['cwd'].to_wide_string
|
182
231
|
end
|
183
232
|
|
233
|
+
inherit = hash['inherit'] || false
|
234
|
+
|
184
235
|
if hash['with_logon']
|
185
|
-
logon
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
236
|
+
logon = hash['with_logon'].to_wide_string
|
237
|
+
|
238
|
+
if hash['password']
|
239
|
+
passwd = hash['password'].to_wide_string
|
240
|
+
else
|
241
|
+
raise ArgumentError, 'password must be specified if with_logon is used'
|
242
|
+
end
|
243
|
+
|
244
|
+
if hash['domain']
|
245
|
+
domain = hash['domain'].to_wide_string
|
246
|
+
end
|
191
247
|
|
192
248
|
hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
|
193
249
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
cwd, # Working directory
|
204
|
-
startinfo, # Startup Info
|
205
|
-
procinfo # Process Info
|
250
|
+
winsta_name = FFI::MemoryPointer.new(:char, 256)
|
251
|
+
return_size = FFI::MemoryPointer.new(:ulong)
|
252
|
+
|
253
|
+
bool = GetUserObjectInformationA(
|
254
|
+
GetProcessWindowStation(), # Window station handle
|
255
|
+
UOI_NAME, # Information to get
|
256
|
+
winsta_name, # Buffer to receive information
|
257
|
+
winsta_name.size, # Size of buffer
|
258
|
+
return_size # Size filled into buffer
|
206
259
|
)
|
260
|
+
|
261
|
+
unless bool
|
262
|
+
raise SystemCallError.new("GetUserObjectInformationA", FFI.errno)
|
263
|
+
end
|
264
|
+
|
265
|
+
winsta_name = winsta_name.read_string(return_size.read_ulong)
|
266
|
+
|
267
|
+
# If running in the service windows station must do a log on to get
|
268
|
+
# to the interactive desktop. Running process user account must have
|
269
|
+
# the 'Replace a process level token' permission. This is necessary as
|
270
|
+
# the logon (which happens with CreateProcessWithLogon) must have an
|
271
|
+
# interactive windows station to attach to, which is created with the
|
272
|
+
# LogonUser cann with the LOGON32_LOGON_INTERACTIVE flag.
|
273
|
+
if winsta_name =~ /^Service-0x0-.*$/i
|
274
|
+
token = FFI::MemoryPointer.new(:ulong)
|
275
|
+
|
276
|
+
bool = LogonUserW(
|
277
|
+
logon, # User
|
278
|
+
domain, # Domain
|
279
|
+
passwd, # Password
|
280
|
+
LOGON32_LOGON_INTERACTIVE, # Logon Type
|
281
|
+
LOGON32_PROVIDER_DEFAULT, # Logon Provider
|
282
|
+
token # User token handle
|
283
|
+
)
|
284
|
+
|
285
|
+
unless bool
|
286
|
+
raise SystemCallError.new("LogonUserW", FFI.errno)
|
287
|
+
end
|
288
|
+
|
289
|
+
token = token.read_ulong
|
290
|
+
|
291
|
+
bool = CreateProcessAsUserW(
|
292
|
+
token, # User token handle
|
293
|
+
app, # App name
|
294
|
+
cmd, # Command line
|
295
|
+
process_security, # Process attributes
|
296
|
+
thread_security, # Thread attributes
|
297
|
+
inherit, # Inherit handles
|
298
|
+
hash['creation_flags'], # Creation Flags
|
299
|
+
env, # Environment
|
300
|
+
cwd, # Working directory
|
301
|
+
startinfo, # Startup Info
|
302
|
+
procinfo # Process Info
|
303
|
+
)
|
304
|
+
|
305
|
+
unless bool
|
306
|
+
raise SystemCallError.new("CreateProcessAsUserW (You must hold the 'Replace a process level token' permission)", FFI.errno)
|
307
|
+
end
|
308
|
+
else
|
309
|
+
bool = CreateProcessWithLogonW(
|
310
|
+
logon, # User
|
311
|
+
domain, # Domain
|
312
|
+
passwd, # Password
|
313
|
+
LOGON_WITH_PROFILE, # Logon flags
|
314
|
+
app, # App name
|
315
|
+
cmd, # Command line
|
316
|
+
hash['creation_flags'], # Creation flags
|
317
|
+
env, # Environment
|
318
|
+
cwd, # Working directory
|
319
|
+
startinfo, # Startup Info
|
320
|
+
procinfo # Process Info
|
321
|
+
)
|
322
|
+
end
|
323
|
+
|
324
|
+
unless bool
|
325
|
+
raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
|
326
|
+
end
|
207
327
|
else
|
208
|
-
|
209
|
-
|
210
|
-
|
328
|
+
bool = CreateProcessW(
|
329
|
+
app, # App name
|
330
|
+
cmd, # Command line
|
211
331
|
process_security, # Process attributes
|
212
332
|
thread_security, # Thread attributes
|
213
|
-
|
333
|
+
inherit, # Inherit handles?
|
214
334
|
hash['creation_flags'], # Creation flags
|
215
335
|
env, # Environment
|
216
|
-
|
336
|
+
cwd, # Working directory
|
217
337
|
startinfo, # Startup Info
|
218
338
|
procinfo # Process Info
|
219
339
|
)
|
220
|
-
end
|
221
340
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
raise_last_error("CreateProcess()")
|
341
|
+
unless bool
|
342
|
+
raise SystemCallError.new("CreateProcessW", FFI.errno)
|
343
|
+
end
|
226
344
|
end
|
227
345
|
|
228
346
|
# Automatically close the process and thread handles in the
|
229
347
|
# PROCESS_INFORMATION struct unless explicitly told not to.
|
230
348
|
if hash['close_handles']
|
231
|
-
CloseHandle(procinfo[
|
232
|
-
CloseHandle(procinfo[
|
349
|
+
CloseHandle(procinfo[:hProcess])
|
350
|
+
CloseHandle(procinfo[:hThread])
|
351
|
+
CloseHandle(token)
|
233
352
|
end
|
234
353
|
|
235
354
|
ProcessInfo.new(
|
236
|
-
procinfo[
|
237
|
-
procinfo[
|
238
|
-
procinfo[
|
239
|
-
procinfo[
|
355
|
+
procinfo[:hProcess],
|
356
|
+
procinfo[:hThread],
|
357
|
+
procinfo[:dwProcessId],
|
358
|
+
procinfo[:dwThreadId]
|
240
359
|
)
|
241
360
|
end
|
242
361
|
|
243
|
-
def self.raise_last_error(operation)
|
244
|
-
error_string = "#{operation} failed: #{get_last_error}"
|
245
|
-
last_error_code = GetLastError()
|
246
|
-
if ERROR_CODE_MAP.has_key?(last_error_code)
|
247
|
-
raise ERROR_CODE_MAP[last_error_code], error_string
|
248
|
-
else
|
249
|
-
raise Error, error_string
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
# List from ruby/win32/win32.c
|
254
|
-
ERROR_CODE_MAP = {
|
255
|
-
ERROR_INVALID_FUNCTION => Errno::EINVAL,
|
256
|
-
ERROR_FILE_NOT_FOUND => Errno::ENOENT,
|
257
|
-
ERROR_PATH_NOT_FOUND => Errno::ENOENT,
|
258
|
-
ERROR_TOO_MANY_OPEN_FILES => Errno::EMFILE,
|
259
|
-
ERROR_ACCESS_DENIED => Errno::EACCES,
|
260
|
-
ERROR_INVALID_HANDLE => Errno::EBADF,
|
261
|
-
ERROR_ARENA_TRASHED => Errno::ENOMEM,
|
262
|
-
ERROR_NOT_ENOUGH_MEMORY => Errno::ENOMEM,
|
263
|
-
ERROR_INVALID_BLOCK => Errno::ENOMEM,
|
264
|
-
ERROR_BAD_ENVIRONMENT => Errno::E2BIG,
|
265
|
-
ERROR_BAD_FORMAT => Errno::ENOEXEC,
|
266
|
-
ERROR_INVALID_ACCESS => Errno::EINVAL,
|
267
|
-
ERROR_INVALID_DATA => Errno::EINVAL,
|
268
|
-
ERROR_INVALID_DRIVE => Errno::ENOENT,
|
269
|
-
ERROR_CURRENT_DIRECTORY => Errno::EACCES,
|
270
|
-
ERROR_NOT_SAME_DEVICE => Errno::EXDEV,
|
271
|
-
ERROR_NO_MORE_FILES => Errno::ENOENT,
|
272
|
-
ERROR_WRITE_PROTECT => Errno::EROFS,
|
273
|
-
ERROR_BAD_UNIT => Errno::ENODEV,
|
274
|
-
ERROR_NOT_READY => Errno::ENXIO,
|
275
|
-
ERROR_BAD_COMMAND => Errno::EACCES,
|
276
|
-
ERROR_CRC => Errno::EACCES,
|
277
|
-
ERROR_BAD_LENGTH => Errno::EACCES,
|
278
|
-
ERROR_SEEK => Errno::EIO,
|
279
|
-
ERROR_NOT_DOS_DISK => Errno::EACCES,
|
280
|
-
ERROR_SECTOR_NOT_FOUND => Errno::EACCES,
|
281
|
-
ERROR_OUT_OF_PAPER => Errno::EACCES,
|
282
|
-
ERROR_WRITE_FAULT => Errno::EIO,
|
283
|
-
ERROR_READ_FAULT => Errno::EIO,
|
284
|
-
ERROR_GEN_FAILURE => Errno::EACCES,
|
285
|
-
ERROR_LOCK_VIOLATION => Errno::EACCES,
|
286
|
-
ERROR_SHARING_VIOLATION => Errno::EACCES,
|
287
|
-
ERROR_WRONG_DISK => Errno::EACCES,
|
288
|
-
ERROR_SHARING_BUFFER_EXCEEDED => Errno::EACCES,
|
289
|
-
# ERROR_BAD_NETPATH => Errno::ENOENT,
|
290
|
-
# ERROR_NETWORK_ACCESS_DENIED => Errno::EACCES,
|
291
|
-
# ERROR_BAD_NET_NAME => Errno::ENOENT,
|
292
|
-
ERROR_FILE_EXISTS => Errno::EEXIST,
|
293
|
-
ERROR_CANNOT_MAKE => Errno::EACCES,
|
294
|
-
ERROR_FAIL_I24 => Errno::EACCES,
|
295
|
-
ERROR_INVALID_PARAMETER => Errno::EINVAL,
|
296
|
-
ERROR_NO_PROC_SLOTS => Errno::EAGAIN,
|
297
|
-
ERROR_DRIVE_LOCKED => Errno::EACCES,
|
298
|
-
ERROR_BROKEN_PIPE => Errno::EPIPE,
|
299
|
-
ERROR_DISK_FULL => Errno::ENOSPC,
|
300
|
-
ERROR_INVALID_TARGET_HANDLE => Errno::EBADF,
|
301
|
-
ERROR_INVALID_HANDLE => Errno::EINVAL,
|
302
|
-
ERROR_WAIT_NO_CHILDREN => Errno::ECHILD,
|
303
|
-
ERROR_CHILD_NOT_COMPLETE => Errno::ECHILD,
|
304
|
-
ERROR_DIRECT_ACCESS_HANDLE => Errno::EBADF,
|
305
|
-
ERROR_NEGATIVE_SEEK => Errno::EINVAL,
|
306
|
-
ERROR_SEEK_ON_DEVICE => Errno::EACCES,
|
307
|
-
ERROR_DIR_NOT_EMPTY => Errno::ENOTEMPTY,
|
308
|
-
# ERROR_DIRECTORY => Errno::ENOTDIR,
|
309
|
-
ERROR_NOT_LOCKED => Errno::EACCES,
|
310
|
-
ERROR_BAD_PATHNAME => Errno::ENOENT,
|
311
|
-
ERROR_MAX_THRDS_REACHED => Errno::EAGAIN,
|
312
|
-
# ERROR_LOCK_FAILED => Errno::EACCES,
|
313
|
-
ERROR_ALREADY_EXISTS => Errno::EEXIST,
|
314
|
-
ERROR_INVALID_STARTING_CODESEG => Errno::ENOEXEC,
|
315
|
-
ERROR_INVALID_STACKSEG => Errno::ENOEXEC,
|
316
|
-
ERROR_INVALID_MODULETYPE => Errno::ENOEXEC,
|
317
|
-
ERROR_INVALID_EXE_SIGNATURE => Errno::ENOEXEC,
|
318
|
-
ERROR_EXE_MARKED_INVALID => Errno::ENOEXEC,
|
319
|
-
ERROR_BAD_EXE_FORMAT => Errno::ENOEXEC,
|
320
|
-
ERROR_ITERATED_DATA_EXCEEDS_64k => Errno::ENOEXEC,
|
321
|
-
ERROR_INVALID_MINALLOCSIZE => Errno::ENOEXEC,
|
322
|
-
ERROR_DYNLINK_FROM_INVALID_RING => Errno::ENOEXEC,
|
323
|
-
ERROR_IOPL_NOT_ENABLED => Errno::ENOEXEC,
|
324
|
-
ERROR_INVALID_SEGDPL => Errno::ENOEXEC,
|
325
|
-
ERROR_AUTODATASEG_EXCEEDS_64k => Errno::ENOEXEC,
|
326
|
-
ERROR_RING2SEG_MUST_BE_MOVABLE => Errno::ENOEXEC,
|
327
|
-
ERROR_RELOC_CHAIN_XEEDS_SEGLIM => Errno::ENOEXEC,
|
328
|
-
ERROR_INFLOOP_IN_RELOC_CHAIN => Errno::ENOEXEC,
|
329
|
-
ERROR_FILENAME_EXCED_RANGE => Errno::ENOENT,
|
330
|
-
ERROR_NESTING_NOT_ALLOWED => Errno::EAGAIN,
|
331
|
-
# ERROR_PIPE_LOCAL => Errno::EPIPE,
|
332
|
-
ERROR_BAD_PIPE => Errno::EPIPE,
|
333
|
-
ERROR_PIPE_BUSY => Errno::EAGAIN,
|
334
|
-
ERROR_NO_DATA => Errno::EPIPE,
|
335
|
-
ERROR_PIPE_NOT_CONNECTED => Errno::EPIPE,
|
336
|
-
ERROR_OPERATION_ABORTED => Errno::EINTR,
|
337
|
-
# ERROR_NOT_ENOUGH_QUOTA => Errno::ENOMEM,
|
338
|
-
ERROR_MOD_NOT_FOUND => Errno::ENOENT,
|
339
|
-
WSAEINTR => Errno::EINTR,
|
340
|
-
WSAEBADF => Errno::EBADF,
|
341
|
-
# WSAEACCES => Errno::EACCES,
|
342
|
-
WSAEFAULT => Errno::EFAULT,
|
343
|
-
WSAEINVAL => Errno::EINVAL,
|
344
|
-
WSAEMFILE => Errno::EMFILE,
|
345
|
-
WSAEWOULDBLOCK => Errno::EWOULDBLOCK,
|
346
|
-
WSAEINPROGRESS => Errno::EINPROGRESS,
|
347
|
-
WSAEALREADY => Errno::EALREADY,
|
348
|
-
WSAENOTSOCK => Errno::ENOTSOCK,
|
349
|
-
WSAEDESTADDRREQ => Errno::EDESTADDRREQ,
|
350
|
-
WSAEMSGSIZE => Errno::EMSGSIZE,
|
351
|
-
WSAEPROTOTYPE => Errno::EPROTOTYPE,
|
352
|
-
WSAENOPROTOOPT => Errno::ENOPROTOOPT,
|
353
|
-
WSAEPROTONOSUPPORT => Errno::EPROTONOSUPPORT,
|
354
|
-
WSAESOCKTNOSUPPORT => Errno::ESOCKTNOSUPPORT,
|
355
|
-
WSAEOPNOTSUPP => Errno::EOPNOTSUPP,
|
356
|
-
WSAEPFNOSUPPORT => Errno::EPFNOSUPPORT,
|
357
|
-
WSAEAFNOSUPPORT => Errno::EAFNOSUPPORT,
|
358
|
-
WSAEADDRINUSE => Errno::EADDRINUSE,
|
359
|
-
WSAEADDRNOTAVAIL => Errno::EADDRNOTAVAIL,
|
360
|
-
WSAENETDOWN => Errno::ENETDOWN,
|
361
|
-
WSAENETUNREACH => Errno::ENETUNREACH,
|
362
|
-
WSAENETRESET => Errno::ENETRESET,
|
363
|
-
WSAECONNABORTED => Errno::ECONNABORTED,
|
364
|
-
WSAECONNRESET => Errno::ECONNRESET,
|
365
|
-
WSAENOBUFS => Errno::ENOBUFS,
|
366
|
-
WSAEISCONN => Errno::EISCONN,
|
367
|
-
WSAENOTCONN => Errno::ENOTCONN,
|
368
|
-
WSAESHUTDOWN => Errno::ESHUTDOWN,
|
369
|
-
WSAETOOMANYREFS => Errno::ETOOMANYREFS,
|
370
|
-
# WSAETIMEDOUT => Errno::ETIMEDOUT,
|
371
|
-
WSAECONNREFUSED => Errno::ECONNREFUSED,
|
372
|
-
WSAELOOP => Errno::ELOOP,
|
373
|
-
WSAENAMETOOLONG => Errno::ENAMETOOLONG,
|
374
|
-
WSAEHOSTDOWN => Errno::EHOSTDOWN,
|
375
|
-
WSAEHOSTUNREACH => Errno::EHOSTUNREACH,
|
376
|
-
# WSAEPROCLIM => Errno::EPROCLIM,
|
377
|
-
# WSAENOTEMPTY => Errno::ENOTEMPTY,
|
378
|
-
WSAEUSERS => Errno::EUSERS,
|
379
|
-
WSAEDQUOT => Errno::EDQUOT,
|
380
|
-
WSAESTALE => Errno::ESTALE,
|
381
|
-
WSAEREMOTE => Errno::EREMOTE
|
382
|
-
}
|
383
|
-
|
384
362
|
module_function :create
|
385
363
|
end
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixlib-shellout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.2.0.rc.0
|
5
|
+
prerelease: 6
|
6
6
|
platform: x86-mingw32
|
7
7
|
authors:
|
8
8
|
- Opscode
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-05-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,18 +21,44 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: ap
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
25
46
|
- !ruby/object:Gem::Dependency
|
26
47
|
name: win32-process
|
27
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
28
49
|
none: false
|
29
50
|
requirements:
|
30
51
|
- - ~>
|
31
52
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
53
|
+
version: 0.7.0
|
33
54
|
type: :runtime
|
34
55
|
prerelease: false
|
35
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.7.0
|
36
62
|
description: Run external commands on Unix or Windows
|
37
63
|
email: info@opscode.com
|
38
64
|
executables: []
|
@@ -43,12 +69,12 @@ extra_rdoc_files:
|
|
43
69
|
files:
|
44
70
|
- LICENSE
|
45
71
|
- README.md
|
46
|
-
- lib/mixlib/shellout.rb
|
47
|
-
- lib/mixlib/shellout/windows/core_ext.rb
|
48
72
|
- lib/mixlib/shellout/exceptions.rb
|
49
|
-
- lib/mixlib/shellout/version.rb
|
50
73
|
- lib/mixlib/shellout/unix.rb
|
74
|
+
- lib/mixlib/shellout/version.rb
|
75
|
+
- lib/mixlib/shellout/windows/core_ext.rb
|
51
76
|
- lib/mixlib/shellout/windows.rb
|
77
|
+
- lib/mixlib/shellout.rb
|
52
78
|
homepage: http://wiki.opscode.com/
|
53
79
|
licenses: []
|
54
80
|
post_install_message:
|
@@ -64,12 +90,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
91
|
none: false
|
66
92
|
requirements:
|
67
|
-
- - ! '
|
93
|
+
- - ! '>'
|
68
94
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
95
|
+
version: 1.3.1
|
70
96
|
requirements: []
|
71
97
|
rubyforge_project:
|
72
|
-
rubygems_version: 1.8.
|
98
|
+
rubygems_version: 1.8.23
|
73
99
|
signing_key:
|
74
100
|
specification_version: 3
|
75
101
|
summary: Run external commands on Unix or Windows
|