mixlib-shellout 1.0.0-x86-mingw32 → 1.1.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/README.md CHANGED
@@ -28,6 +28,14 @@ Run a command as the `www` user with no extra ENV settings from `/tmp`
28
28
  cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp')
29
29
  cmd.run_command # etc.
30
30
 
31
+ ## STDIN Example
32
+ Invoke crontab to edit user cron:
33
+
34
+ # :input only supports simple strings
35
+ crontab_lines = [ "* * * * * /bin/true", "* * * * * touch /tmp/here" ]
36
+ crontab = Mixlib::ShellOut.new("crontab -l -u #{@new_resource.user}", :input => crontab_lines.join("\n"))
37
+ crontab.run_command
38
+
31
39
  ## Platform Support
32
40
  Mixlib::ShellOut does a standard fork/exec on Unix, and uses the Win32
33
41
  API on Windows. There is not currently support for JRuby.
@@ -55,6 +55,11 @@ module Mixlib
55
55
  # the command's output will be echoed to STDOUT.
56
56
  attr_accessor :live_stream
57
57
 
58
+ # ShellOut will push data from :input down the stdin of the subprocss.
59
+ # Normally set via options passed to new.
60
+ # Default: nil
61
+ attr_accessor :input
62
+
58
63
  # If a logger is set, ShellOut will log a message before it executes the
59
64
  # command.
60
65
  attr_accessor :logger
@@ -140,6 +145,7 @@ module Mixlib
140
145
  def initialize(*command_args)
141
146
  @stdout, @stderr = '', ''
142
147
  @live_stream = nil
148
+ @input = nil
143
149
  @log_level = :debug
144
150
  @log_tag = nil
145
151
  @environment = DEFAULT_ENVIRONMENT
@@ -267,6 +273,8 @@ module Mixlib
267
273
  self.valid_exit_codes = Array(setting)
268
274
  when 'live_stream'
269
275
  self.live_stream = setting
276
+ when 'input'
277
+ self.input = setting
270
278
  when 'logger'
271
279
  self.logger = setting
272
280
  when 'log_level'
@@ -5,4 +5,3 @@ module Mixlib
5
5
  class InvalidCommandOption < RuntimeError; end
6
6
  end
7
7
  end
8
-
@@ -40,6 +40,8 @@ module Mixlib
40
40
  @result = nil
41
41
  @execution_time = 0
42
42
 
43
+ write_to_child_stdin
44
+
43
45
  # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
44
46
  # when calling IO.select and IO#read. Some OS Vendors are not interested
45
47
  # in updating their ruby packages (Apple, *cough*) and we *have to*
@@ -114,10 +116,14 @@ module Mixlib
114
116
  end
115
117
 
116
118
  def initialize_ipc
117
- @stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe
119
+ @stdin_pipe, @stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe, IO.pipe
118
120
  @process_status_pipe.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
119
121
  end
120
122
 
123
+ def child_stdin
124
+ @stdin_pipe[1]
125
+ end
126
+
121
127
  def child_stdout
122
128
  @stdout_pipe[0]
123
129
  end
@@ -131,23 +137,25 @@ module Mixlib
131
137
  end
132
138
 
133
139
  def close_all_pipes
140
+ child_stdin.close unless child_stdin.closed?
134
141
  child_stdout.close unless child_stdout.closed?
135
142
  child_stderr.close unless child_stderr.closed?
136
143
  child_process_status.close unless child_process_status.closed?
137
144
  end
138
145
 
139
- # replace stdout, and stderr with pipes to the parent, and close the
140
- # reader side of the error marshaling side channel. Close STDIN so when we
141
- # exec, the new program will know it's never getting input ever.
146
+ # Replace stdout, and stderr with pipes to the parent, and close the
147
+ # reader side of the error marshaling side channel.
148
+ #
149
+ # If there is no input, close STDIN so when we exec,
150
+ # the new program will know it's never getting input ever.
142
151
  def configure_subprocess_file_descriptors
143
152
  process_status_pipe.first.close
144
153
 
145
154
  # HACK: for some reason, just STDIN.close isn't good enough when running
146
155
  # under ruby 1.9.2, so make it good enough:
147
- stdin_reader, stdin_writer = IO.pipe
148
- stdin_writer.close
149
- STDIN.reopen stdin_reader
150
- stdin_reader.close
156
+ stdin_pipe.last.close
157
+ STDIN.reopen stdin_pipe.first
158
+ stdin_pipe.first.close unless input
151
159
 
152
160
  stdout_pipe.first.close
153
161
  STDOUT.reopen stdout_pipe.last
@@ -158,14 +166,18 @@ module Mixlib
158
166
  stderr_pipe.last.close
159
167
 
160
168
  STDOUT.sync = STDERR.sync = true
169
+ STDIN.sync = true if input
161
170
  end
162
171
 
163
172
  def configure_parent_process_file_descriptors
164
173
  # Close the sides of the pipes we don't care about
174
+ stdin_pipe.first.close
175
+ stdin_pipe.last.close unless input
165
176
  stdout_pipe.last.close
166
177
  stderr_pipe.last.close
167
178
  process_status_pipe.last.close
168
179
  # Get output as it happens rather than buffered
180
+ child_stdin.sync = true if input
169
181
  child_stdout.sync = true
170
182
  child_stderr.sync = true
171
183
 
@@ -178,6 +190,13 @@ module Mixlib
178
190
  @open_pipes ||= [child_stdout, child_stderr]
179
191
  end
180
192
 
193
+ # Keep this unbuffered for now
194
+ def write_to_child_stdin
195
+ return unless input
196
+ child_stdin << input
197
+ child_stdin.close # Kick things off
198
+ end
199
+
181
200
  def read_stdout_to_buffer
182
201
  while chunk = child_stdout.read_nonblock(READ_SIZE)
183
202
  @stdout << chunk
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "1.0.0"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -1,7 +1,8 @@
1
1
  #--
2
2
  # Author:: Daniel DeLeo (<dan@opscode.com>)
3
3
  # Author:: John Keiser (<jkeiser@opscode.com>)
4
- # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # Author:: Ho-Sheng Hsiao (<hosh@opscode.com>)
5
+ # Copyright:: Copyright (c) 2011, 2012 Opscode, Inc.
5
6
  # License:: Apache License, Version 2.0
6
7
  #
7
8
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +23,8 @@ require 'windows/handle'
22
23
  require 'windows/process'
23
24
  require 'windows/synchronize'
24
25
 
26
+ require 'mixlib/shellout/windows/core_ext'
27
+
25
28
  module Mixlib
26
29
  class ShellOut
27
30
  module Windows
@@ -50,7 +53,7 @@ module Mixlib
50
53
  #
51
54
  # Set cwd, environment, appname, etc.
52
55
  #
53
- app_name, command_line = command_to_run
56
+ app_name, command_line = command_to_run(self.command)
54
57
  create_process_args = {
55
58
  :app_name => app_name,
56
59
  :command_line => command_line,
@@ -69,6 +72,11 @@ module Mixlib
69
72
  #
70
73
  process = Process.create(create_process_args)
71
74
  begin
75
+ # Start pushing data into input
76
+ stdin_write << input if input
77
+
78
+ # Close pipe to kick things off
79
+ stdin_write.close
72
80
 
73
81
  #
74
82
  # Wait for the process to finish, consuming output as we go
@@ -90,7 +98,7 @@ module Mixlib
90
98
  when WAIT_TIMEOUT
91
99
  # Kill the process
92
100
  if (Time.now - start_wait) > timeout
93
- raise Mixlib::ShellOut::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
101
+ raise Mixlib::ShellOut::CommandTimeout, "command timed out:\n#{format_for_exception}"
94
102
  end
95
103
 
96
104
  consume_output(open_streams, stdout_read, stderr_read)
@@ -154,33 +162,52 @@ module Mixlib
154
162
  return true
155
163
  end
156
164
 
157
- IS_BATCH_FILE = /\.bat|\.cmd$/i
165
+ IS_BATCH_FILE = /\.bat"?$|\.cmd"?$/i
158
166
 
159
- def command_to_run
160
- if command =~ /^\s*"(.*)"/
161
- # If we have quotes, do an exact match
162
- candidate = $1
163
- else
164
- # Otherwise check everything up to the first space
165
- candidate = command[0,command.index(/\s/) || command.length].strip
166
- end
167
+ def command_to_run(command)
168
+ return _run_under_cmd(command) if Utils.should_run_under_cmd?(command)
169
+
170
+ candidate = candidate_executable_for_command(command)
167
171
 
168
172
  # Don't do searching for empty commands. Let it fail when it runs.
169
- if candidate.length == 0
170
- return [ nil, command ]
171
- end
173
+ return [ nil, command ] if candidate.length == 0
172
174
 
173
175
  # Check if the exe exists directly. Otherwise, search PATH.
174
- exe = find_exe_at_location(candidate)
175
- if exe.nil? && exe !~ /[\\\/]/
176
- exe = which(command[0,command.index(/\s/) || command.length])
177
- end
176
+ exe = Utils.find_executable(candidate)
177
+ exe = Utils.which(unquoted_executable_path(command)) if exe.nil? && exe !~ /[\\\/]/
178
178
 
179
+ # Batch files MUST use cmd; and if we couldn't find the command we're looking for,
180
+ # we assume it must be a cmd builtin.
179
181
  if exe.nil? || exe =~ IS_BATCH_FILE
180
- # Batch files MUST use cmd; and if we couldn't find the command we're looking for, we assume it must be a cmd builtin.
181
- [ ENV['COMSPEC'], "cmd /c #{command}" ]
182
+ _run_under_cmd(command)
183
+ else
184
+ _run_directly(command, exe)
185
+ end
186
+ end
187
+
188
+
189
+ # cmd does not parse multiple quotes well unless the whole thing is wrapped up in quotes.
190
+ # https://github.com/opscode/mixlib-shellout/pull/2#issuecomment-4837859
191
+ # http://ss64.com/nt/syntax-esc.html
192
+ def _run_under_cmd(command)
193
+ [ ENV['COMSPEC'], "cmd /c \"#{command}\"" ]
194
+ end
195
+
196
+ def _run_directly(command, exe)
197
+ [ exe, command ]
198
+ end
199
+
200
+ def unquoted_executable_path(command)
201
+ command[0,command.index(/\s/) || command.length]
202
+ end
203
+
204
+ def candidate_executable_for_command(command)
205
+ if command =~ /^\s*"(.*?)"/
206
+ # If we have quotes, do an exact match
207
+ $1
182
208
  else
183
- [ exe, command ]
209
+ # Otherwise check everything up to the first space
210
+ unquoted_executable_path(command).strip
184
211
  end
185
212
  end
186
213
 
@@ -200,389 +227,74 @@ module Mixlib
200
227
  result
201
228
  end
202
229
 
203
- def pathext
204
- @pathext ||= ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') + [''] : ['']
205
- end
206
-
207
- def which(cmd)
208
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
209
- exe = find_exe_at_location("#{path}/${cmd}")
210
- return exe if exe
211
- end
212
- return nil
213
- end
214
-
215
- def find_exe_at_location(path)
216
- return path if File.executable? path
217
- pathext.each do |ext|
218
- exe = "#{path}#{ext}"
219
- return exe if File.executable? exe
230
+ module Utils
231
+ # api: semi-private
232
+ # If there are special characters parsable by cmd.exe (such as file redirection), then
233
+ # this method should return true.
234
+ #
235
+ # This parser is based on
236
+ # https://github.com/ruby/ruby/blob/9073db5cb1d3173aff62be5b48d00f0fb2890991/win32/win32.c#L1437
237
+ def self.should_run_under_cmd?(command)
238
+ return true if command =~ /^@/
239
+
240
+ quote = nil
241
+ env = false
242
+ env_first_char = false
243
+
244
+ command.dup.each_char do |c|
245
+ case c
246
+ when "'", '"'
247
+ if (!quote)
248
+ quote = c
249
+ elsif quote == c
250
+ quote = nil
251
+ end
252
+ next
253
+ when '>', '<', '|', '&', "\n"
254
+ return true unless quote
255
+ when '%'
256
+ return true if env
257
+ env = env_first_char = true
258
+ next
259
+ else
260
+ next unless env
261
+ if env_first_char
262
+ env_first_char = false
263
+ env = false and next if c !~ /[A-Za-z_]/
264
+ end
265
+ env = false if c !~ /[A-Za-z1-9_]/
266
+ end
267
+ end
268
+ return false
220
269
  end
221
- return nil
222
- end
223
- end # class
224
- end
225
- end
226
-
227
- #
228
- # Override module Windows::Process.CreateProcess to fix bug when
229
- # using both app_name and command_line
230
- #
231
- module Windows
232
- module Process
233
- API.new('CreateProcess', 'SPPPLLLPPP', 'B')
234
- end
235
- end
236
-
237
- #
238
- # Override Win32::Process.create to take a proper environment hash
239
- # so that variables can contain semicolons
240
- # (submitted patch to owner)
241
- #
242
- module Process
243
- def create(args)
244
- unless args.kind_of?(Hash)
245
- raise TypeError, 'Expecting hash-style keyword arguments'
246
- end
247
-
248
- valid_keys = %w/
249
- app_name command_line inherit creation_flags cwd environment
250
- startup_info thread_inherit process_inherit close_handles with_logon
251
- domain password
252
- /
253
-
254
- valid_si_keys = %/
255
- startf_flags desktop title x y x_size y_size x_count_chars
256
- y_count_chars fill_attribute sw_flags stdin stdout stderr
257
- /
258
-
259
- # Set default values
260
- hash = {
261
- 'app_name' => nil,
262
- 'creation_flags' => 0,
263
- 'close_handles' => true
264
- }
265
-
266
- # Validate the keys, and convert symbols and case to lowercase strings.
267
- args.each{ |key, val|
268
- key = key.to_s.downcase
269
- unless valid_keys.include?(key)
270
- raise ArgumentError, "invalid key '#{key}'"
271
- end
272
- hash[key] = val
273
- }
274
270
 
275
- si_hash = {}
276
-
277
- # If the startup_info key is present, validate its subkeys
278
- if hash['startup_info']
279
- hash['startup_info'].each{ |key, val|
280
- key = key.to_s.downcase
281
- unless valid_si_keys.include?(key)
282
- raise ArgumentError, "invalid startup_info key '#{key}'"
283
- end
284
- si_hash[key] = val
285
- }
286
- end
287
-
288
- # The +command_line+ key is mandatory unless the +app_name+ key
289
- # is specified.
290
- unless hash['command_line']
291
- if hash['app_name']
292
- hash['command_line'] = hash['app_name']
293
- hash['app_name'] = nil
294
- else
295
- raise ArgumentError, 'command_line or app_name must be specified'
296
- end
297
- end
298
-
299
- # The environment string should be passed as an array of A=B paths, or
300
- # as a string of ';' separated paths.
301
- if hash['environment']
302
- env = hash['environment']
303
- if !env.respond_to?(:join)
304
- # Backwards compat for ; separated paths
305
- env = hash['environment'].split(File::PATH_SEPARATOR)
306
- end
307
- # The argument format is a series of null-terminated strings, with an additional null terminator.
308
- env = env.map { |e| e + "\0" }.join("") + "\0"
309
- if hash['with_logon']
310
- env = env.multi_to_wide(e)
311
- end
312
- env = [env].pack('p*').unpack('L').first
313
- else
314
- env = nil
315
- end
316
-
317
- startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
318
- startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
319
- procinfo = [0,0,0,0].pack('LLLL')
320
-
321
- # Process SECURITY_ATTRIBUTE structure
322
- process_security = 0
323
- if hash['process_inherit']
324
- process_security = [0,0,0].pack('LLL')
325
- process_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
326
- process_security[8,4] = [1].pack('L') # TRUE
327
- end
328
-
329
- # Thread SECURITY_ATTRIBUTE structure
330
- thread_security = 0
331
- if hash['thread_inherit']
332
- thread_security = [0,0,0].pack('LLL')
333
- thread_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
334
- thread_security[8,4] = [1].pack('L') # TRUE
335
- end
336
-
337
- # Automatically handle stdin, stdout and stderr as either IO objects
338
- # or file descriptors. This won't work for StringIO, however.
339
- ['stdin', 'stdout', 'stderr'].each{ |io|
340
- if si_hash[io]
341
- if si_hash[io].respond_to?(:fileno)
342
- handle = get_osfhandle(si_hash[io].fileno)
343
- else
344
- handle = get_osfhandle(si_hash[io])
271
+ def self.pathext
272
+ @pathext ||= ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') + [''] : ['']
345
273
  end
346
274
 
347
- if handle == INVALID_HANDLE_VALUE
348
- raise Error, get_last_error
275
+ # which() mimicks the Unix which command
276
+ # FIXME: it is not working
277
+ def self.which(cmd)
278
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
279
+ exe = find_executable("#{path}/#{cmd}")
280
+ return exe if exe
281
+ end
282
+ return nil
349
283
  end
350
284
 
351
- # Most implementations of Ruby on Windows create inheritable
352
- # handles by default, but some do not. RF bug #26988.
353
- bool = SetHandleInformation(
354
- handle,
355
- HANDLE_FLAG_INHERIT,
356
- HANDLE_FLAG_INHERIT
357
- )
285
+ # Windows has a different notion of what "executable" means
286
+ # The OS will search through valid the extensions and look
287
+ # for a binary there.
288
+ def self.find_executable(path)
289
+ return path if File.executable? path
358
290
 
359
- raise Error, get_last_error unless bool
360
-
361
- si_hash[io] = handle
362
- si_hash['startf_flags'] ||= 0
363
- si_hash['startf_flags'] |= STARTF_USESTDHANDLES
364
- hash['inherit'] = true
291
+ pathext.each do |ext|
292
+ exe = "#{path}#{ext}"
293
+ return exe if File.executable? exe
294
+ end
295
+ return nil
296
+ end
365
297
  end
366
- }
367
-
368
- # The bytes not covered here are reserved (null)
369
- unless si_hash.empty?
370
- startinfo[0,4] = [startinfo.size].pack('L')
371
- startinfo[8,4] = [si_hash['desktop']].pack('p*') if si_hash['desktop']
372
- startinfo[12,4] = [si_hash['title']].pack('p*') if si_hash['title']
373
- startinfo[16,4] = [si_hash['x']].pack('L') if si_hash['x']
374
- startinfo[20,4] = [si_hash['y']].pack('L') if si_hash['y']
375
- startinfo[24,4] = [si_hash['x_size']].pack('L') if si_hash['x_size']
376
- startinfo[28,4] = [si_hash['y_size']].pack('L') if si_hash['y_size']
377
- startinfo[32,4] = [si_hash['x_count_chars']].pack('L') if si_hash['x_count_chars']
378
- startinfo[36,4] = [si_hash['y_count_chars']].pack('L') if si_hash['y_count_chars']
379
- startinfo[40,4] = [si_hash['fill_attribute']].pack('L') if si_hash['fill_attribute']
380
- startinfo[44,4] = [si_hash['startf_flags']].pack('L') if si_hash['startf_flags']
381
- startinfo[48,2] = [si_hash['sw_flags']].pack('S') if si_hash['sw_flags']
382
- startinfo[56,4] = [si_hash['stdin']].pack('L') if si_hash['stdin']
383
- startinfo[60,4] = [si_hash['stdout']].pack('L') if si_hash['stdout']
384
- startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
385
- end
386
-
387
- if hash['with_logon']
388
- logon = multi_to_wide(hash['with_logon'])
389
- domain = multi_to_wide(hash['domain'])
390
- app = hash['app_name'].nil? ? nil : multi_to_wide(hash['app_name'])
391
- cmd = hash['command_line'].nil? ? nil : multi_to_wide(hash['command_line'])
392
- cwd = multi_to_wide(hash['cwd'])
393
- passwd = multi_to_wide(hash['password'])
394
-
395
- hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
396
-
397
- process_ran = CreateProcessWithLogonW(
398
- logon, # User
399
- domain, # Domain
400
- passwd, # Password
401
- LOGON_WITH_PROFILE, # Logon flags
402
- app, # App name
403
- cmd, # Command line
404
- hash['creation_flags'], # Creation flags
405
- env, # Environment
406
- cwd, # Working directory
407
- startinfo, # Startup Info
408
- procinfo # Process Info
409
- )
410
- else
411
- process_ran = CreateProcess(
412
- hash['app_name'], # App name
413
- hash['command_line'], # Command line
414
- process_security, # Process attributes
415
- thread_security, # Thread attributes
416
- hash['inherit'], # Inherit handles?
417
- hash['creation_flags'], # Creation flags
418
- env, # Environment
419
- hash['cwd'], # Working directory
420
- startinfo, # Startup Info
421
- procinfo # Process Info
422
- )
423
- end
424
-
425
- # TODO: Close stdin, stdout and stderr handles in the si_hash unless
426
- # they're pointing to one of the standard handles already. [Maybe]
427
- if !process_ran
428
- raise_last_error("CreateProcess()")
429
- end
430
-
431
- # Automatically close the process and thread handles in the
432
- # PROCESS_INFORMATION struct unless explicitly told not to.
433
- if hash['close_handles']
434
- CloseHandle(procinfo[0,4].unpack('L').first)
435
- CloseHandle(procinfo[4,4].unpack('L').first)
436
- end
437
-
438
- ProcessInfo.new(
439
- procinfo[0,4].unpack('L').first, # hProcess
440
- procinfo[4,4].unpack('L').first, # hThread
441
- procinfo[8,4].unpack('L').first, # hProcessId
442
- procinfo[12,4].unpack('L').first # hThreadId
443
- )
444
- end
445
-
446
- def self.raise_last_error(operation)
447
- error_string = "#{operation} failed: #{get_last_error}"
448
- last_error_code = GetLastError()
449
- if ERROR_CODE_MAP.has_key?(last_error_code)
450
- raise ERROR_CODE_MAP[last_error_code], error_string
451
- else
452
- raise Error, error_string
453
- end
298
+ end # class
454
299
  end
455
-
456
- # List from ruby/win32/win32.c
457
- ERROR_CODE_MAP = {
458
- ERROR_INVALID_FUNCTION => Errno::EINVAL,
459
- ERROR_FILE_NOT_FOUND => Errno::ENOENT,
460
- ERROR_PATH_NOT_FOUND => Errno::ENOENT,
461
- ERROR_TOO_MANY_OPEN_FILES => Errno::EMFILE,
462
- ERROR_ACCESS_DENIED => Errno::EACCES,
463
- ERROR_INVALID_HANDLE => Errno::EBADF,
464
- ERROR_ARENA_TRASHED => Errno::ENOMEM,
465
- ERROR_NOT_ENOUGH_MEMORY => Errno::ENOMEM,
466
- ERROR_INVALID_BLOCK => Errno::ENOMEM,
467
- ERROR_BAD_ENVIRONMENT => Errno::E2BIG,
468
- ERROR_BAD_FORMAT => Errno::ENOEXEC,
469
- ERROR_INVALID_ACCESS => Errno::EINVAL,
470
- ERROR_INVALID_DATA => Errno::EINVAL,
471
- ERROR_INVALID_DRIVE => Errno::ENOENT,
472
- ERROR_CURRENT_DIRECTORY => Errno::EACCES,
473
- ERROR_NOT_SAME_DEVICE => Errno::EXDEV,
474
- ERROR_NO_MORE_FILES => Errno::ENOENT,
475
- ERROR_WRITE_PROTECT => Errno::EROFS,
476
- ERROR_BAD_UNIT => Errno::ENODEV,
477
- ERROR_NOT_READY => Errno::ENXIO,
478
- ERROR_BAD_COMMAND => Errno::EACCES,
479
- ERROR_CRC => Errno::EACCES,
480
- ERROR_BAD_LENGTH => Errno::EACCES,
481
- ERROR_SEEK => Errno::EIO,
482
- ERROR_NOT_DOS_DISK => Errno::EACCES,
483
- ERROR_SECTOR_NOT_FOUND => Errno::EACCES,
484
- ERROR_OUT_OF_PAPER => Errno::EACCES,
485
- ERROR_WRITE_FAULT => Errno::EIO,
486
- ERROR_READ_FAULT => Errno::EIO,
487
- ERROR_GEN_FAILURE => Errno::EACCES,
488
- ERROR_LOCK_VIOLATION => Errno::EACCES,
489
- ERROR_SHARING_VIOLATION => Errno::EACCES,
490
- ERROR_WRONG_DISK => Errno::EACCES,
491
- ERROR_SHARING_BUFFER_EXCEEDED => Errno::EACCES,
492
- # ERROR_BAD_NETPATH => Errno::ENOENT,
493
- # ERROR_NETWORK_ACCESS_DENIED => Errno::EACCES,
494
- # ERROR_BAD_NET_NAME => Errno::ENOENT,
495
- ERROR_FILE_EXISTS => Errno::EEXIST,
496
- ERROR_CANNOT_MAKE => Errno::EACCES,
497
- ERROR_FAIL_I24 => Errno::EACCES,
498
- ERROR_INVALID_PARAMETER => Errno::EINVAL,
499
- ERROR_NO_PROC_SLOTS => Errno::EAGAIN,
500
- ERROR_DRIVE_LOCKED => Errno::EACCES,
501
- ERROR_BROKEN_PIPE => Errno::EPIPE,
502
- ERROR_DISK_FULL => Errno::ENOSPC,
503
- ERROR_INVALID_TARGET_HANDLE => Errno::EBADF,
504
- ERROR_INVALID_HANDLE => Errno::EINVAL,
505
- ERROR_WAIT_NO_CHILDREN => Errno::ECHILD,
506
- ERROR_CHILD_NOT_COMPLETE => Errno::ECHILD,
507
- ERROR_DIRECT_ACCESS_HANDLE => Errno::EBADF,
508
- ERROR_NEGATIVE_SEEK => Errno::EINVAL,
509
- ERROR_SEEK_ON_DEVICE => Errno::EACCES,
510
- ERROR_DIR_NOT_EMPTY => Errno::ENOTEMPTY,
511
- # ERROR_DIRECTORY => Errno::ENOTDIR,
512
- ERROR_NOT_LOCKED => Errno::EACCES,
513
- ERROR_BAD_PATHNAME => Errno::ENOENT,
514
- ERROR_MAX_THRDS_REACHED => Errno::EAGAIN,
515
- # ERROR_LOCK_FAILED => Errno::EACCES,
516
- ERROR_ALREADY_EXISTS => Errno::EEXIST,
517
- ERROR_INVALID_STARTING_CODESEG => Errno::ENOEXEC,
518
- ERROR_INVALID_STACKSEG => Errno::ENOEXEC,
519
- ERROR_INVALID_MODULETYPE => Errno::ENOEXEC,
520
- ERROR_INVALID_EXE_SIGNATURE => Errno::ENOEXEC,
521
- ERROR_EXE_MARKED_INVALID => Errno::ENOEXEC,
522
- ERROR_BAD_EXE_FORMAT => Errno::ENOEXEC,
523
- ERROR_ITERATED_DATA_EXCEEDS_64k => Errno::ENOEXEC,
524
- ERROR_INVALID_MINALLOCSIZE => Errno::ENOEXEC,
525
- ERROR_DYNLINK_FROM_INVALID_RING => Errno::ENOEXEC,
526
- ERROR_IOPL_NOT_ENABLED => Errno::ENOEXEC,
527
- ERROR_INVALID_SEGDPL => Errno::ENOEXEC,
528
- ERROR_AUTODATASEG_EXCEEDS_64k => Errno::ENOEXEC,
529
- ERROR_RING2SEG_MUST_BE_MOVABLE => Errno::ENOEXEC,
530
- ERROR_RELOC_CHAIN_XEEDS_SEGLIM => Errno::ENOEXEC,
531
- ERROR_INFLOOP_IN_RELOC_CHAIN => Errno::ENOEXEC,
532
- ERROR_FILENAME_EXCED_RANGE => Errno::ENOENT,
533
- ERROR_NESTING_NOT_ALLOWED => Errno::EAGAIN,
534
- # ERROR_PIPE_LOCAL => Errno::EPIPE,
535
- ERROR_BAD_PIPE => Errno::EPIPE,
536
- ERROR_PIPE_BUSY => Errno::EAGAIN,
537
- ERROR_NO_DATA => Errno::EPIPE,
538
- ERROR_PIPE_NOT_CONNECTED => Errno::EPIPE,
539
- ERROR_OPERATION_ABORTED => Errno::EINTR,
540
- # ERROR_NOT_ENOUGH_QUOTA => Errno::ENOMEM,
541
- ERROR_MOD_NOT_FOUND => Errno::ENOENT,
542
- WSAEINTR => Errno::EINTR,
543
- WSAEBADF => Errno::EBADF,
544
- # WSAEACCES => Errno::EACCES,
545
- WSAEFAULT => Errno::EFAULT,
546
- WSAEINVAL => Errno::EINVAL,
547
- WSAEMFILE => Errno::EMFILE,
548
- WSAEWOULDBLOCK => Errno::EWOULDBLOCK,
549
- WSAEINPROGRESS => Errno::EINPROGRESS,
550
- WSAEALREADY => Errno::EALREADY,
551
- WSAENOTSOCK => Errno::ENOTSOCK,
552
- WSAEDESTADDRREQ => Errno::EDESTADDRREQ,
553
- WSAEMSGSIZE => Errno::EMSGSIZE,
554
- WSAEPROTOTYPE => Errno::EPROTOTYPE,
555
- WSAENOPROTOOPT => Errno::ENOPROTOOPT,
556
- WSAEPROTONOSUPPORT => Errno::EPROTONOSUPPORT,
557
- WSAESOCKTNOSUPPORT => Errno::ESOCKTNOSUPPORT,
558
- WSAEOPNOTSUPP => Errno::EOPNOTSUPP,
559
- WSAEPFNOSUPPORT => Errno::EPFNOSUPPORT,
560
- WSAEAFNOSUPPORT => Errno::EAFNOSUPPORT,
561
- WSAEADDRINUSE => Errno::EADDRINUSE,
562
- WSAEADDRNOTAVAIL => Errno::EADDRNOTAVAIL,
563
- WSAENETDOWN => Errno::ENETDOWN,
564
- WSAENETUNREACH => Errno::ENETUNREACH,
565
- WSAENETRESET => Errno::ENETRESET,
566
- WSAECONNABORTED => Errno::ECONNABORTED,
567
- WSAECONNRESET => Errno::ECONNRESET,
568
- WSAENOBUFS => Errno::ENOBUFS,
569
- WSAEISCONN => Errno::EISCONN,
570
- WSAENOTCONN => Errno::ENOTCONN,
571
- WSAESHUTDOWN => Errno::ESHUTDOWN,
572
- WSAETOOMANYREFS => Errno::ETOOMANYREFS,
573
- # WSAETIMEDOUT => Errno::ETIMEDOUT,
574
- WSAECONNREFUSED => Errno::ECONNREFUSED,
575
- WSAELOOP => Errno::ELOOP,
576
- WSAENAMETOOLONG => Errno::ENAMETOOLONG,
577
- WSAEHOSTDOWN => Errno::EHOSTDOWN,
578
- WSAEHOSTUNREACH => Errno::EHOSTUNREACH,
579
- # WSAEPROCLIM => Errno::EPROCLIM,
580
- # WSAENOTEMPTY => Errno::ENOTEMPTY,
581
- WSAEUSERS => Errno::EUSERS,
582
- WSAEDQUOT => Errno::EDQUOT,
583
- WSAESTALE => Errno::ESTALE,
584
- WSAEREMOTE => Errno::EREMOTE
585
- }
586
-
587
- module_function :create
588
300
  end
@@ -0,0 +1,385 @@
1
+ #--
2
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
3
+ # Author:: John Keiser (<jkeiser@opscode.com>)
4
+ # Copyright:: Copyright (c) 2011, 2012 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'win32/process'
21
+ require 'windows/handle'
22
+ require 'windows/process'
23
+ require 'windows/synchronize'
24
+
25
+ # Override module Windows::Process.CreateProcess to fix bug when
26
+ # using both app_name and command_line
27
+ #
28
+ module Windows
29
+ module Process
30
+ API.new('CreateProcess', 'SPPPLLLPPP', 'B')
31
+ end
32
+ end
33
+
34
+ #
35
+ # Override Win32::Process.create to take a proper environment hash
36
+ # so that variables can contain semicolons
37
+ # (submitted patch to owner)
38
+ #
39
+ module Process
40
+ def create(args)
41
+ unless args.kind_of?(Hash)
42
+ raise TypeError, 'Expecting hash-style keyword arguments'
43
+ end
44
+
45
+ valid_keys = %w/
46
+ app_name command_line inherit creation_flags cwd environment
47
+ startup_info thread_inherit process_inherit close_handles with_logon
48
+ domain password
49
+ /
50
+
51
+ valid_si_keys = %/
52
+ startf_flags desktop title x y x_size y_size x_count_chars
53
+ y_count_chars fill_attribute sw_flags stdin stdout stderr
54
+ /
55
+
56
+ # Set default values
57
+ hash = {
58
+ 'app_name' => nil,
59
+ 'creation_flags' => 0,
60
+ 'close_handles' => true
61
+ }
62
+
63
+ # Validate the keys, and convert symbols and case to lowercase strings.
64
+ args.each{ |key, val|
65
+ key = key.to_s.downcase
66
+ unless valid_keys.include?(key)
67
+ raise ArgumentError, "invalid key '#{key}'"
68
+ end
69
+ hash[key] = val
70
+ }
71
+
72
+ si_hash = {}
73
+
74
+ # If the startup_info key is present, validate its subkeys
75
+ if hash['startup_info']
76
+ hash['startup_info'].each{ |key, val|
77
+ key = key.to_s.downcase
78
+ unless valid_si_keys.include?(key)
79
+ raise ArgumentError, "invalid startup_info key '#{key}'"
80
+ end
81
+ si_hash[key] = val
82
+ }
83
+ end
84
+
85
+ # The +command_line+ key is mandatory unless the +app_name+ key
86
+ # is specified.
87
+ unless hash['command_line']
88
+ if hash['app_name']
89
+ hash['command_line'] = hash['app_name']
90
+ hash['app_name'] = nil
91
+ else
92
+ raise ArgumentError, 'command_line or app_name must be specified'
93
+ end
94
+ end
95
+
96
+ # The environment string should be passed as an array of A=B paths, or
97
+ # as a string of ';' separated paths.
98
+ if hash['environment']
99
+ env = hash['environment']
100
+ if !env.respond_to?(:join)
101
+ # Backwards compat for ; separated paths
102
+ env = hash['environment'].split(File::PATH_SEPARATOR)
103
+ 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
+
114
+ startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
115
+ startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
116
+ procinfo = [0,0,0,0].pack('LLLL')
117
+
118
+ # Process SECURITY_ATTRIBUTE structure
119
+ process_security = 0
120
+ if hash['process_inherit']
121
+ process_security = [0,0,0].pack('LLL')
122
+ process_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
123
+ process_security[8,4] = [1].pack('L') # TRUE
124
+ end
125
+
126
+ # Thread SECURITY_ATTRIBUTE structure
127
+ thread_security = 0
128
+ if hash['thread_inherit']
129
+ thread_security = [0,0,0].pack('LLL')
130
+ thread_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
131
+ thread_security[8,4] = [1].pack('L') # TRUE
132
+ end
133
+
134
+ # Automatically handle stdin, stdout and stderr as either IO objects
135
+ # or file descriptors. This won't work for StringIO, however.
136
+ ['stdin', 'stdout', 'stderr'].each{ |io|
137
+ if si_hash[io]
138
+ if si_hash[io].respond_to?(:fileno)
139
+ handle = get_osfhandle(si_hash[io].fileno)
140
+ else
141
+ handle = get_osfhandle(si_hash[io])
142
+ end
143
+
144
+ if handle == INVALID_HANDLE_VALUE
145
+ raise Error, get_last_error
146
+ end
147
+
148
+ # Most implementations of Ruby on Windows create inheritable
149
+ # handles by default, but some do not. RF bug #26988.
150
+ bool = SetHandleInformation(
151
+ handle,
152
+ HANDLE_FLAG_INHERIT,
153
+ HANDLE_FLAG_INHERIT
154
+ )
155
+
156
+ raise Error, get_last_error unless bool
157
+
158
+ si_hash[io] = handle
159
+ si_hash['startf_flags'] ||= 0
160
+ si_hash['startf_flags'] |= STARTF_USESTDHANDLES
161
+ hash['inherit'] = true
162
+ end
163
+ }
164
+
165
+ # The bytes not covered here are reserved (null)
166
+ unless si_hash.empty?
167
+ startinfo[0,4] = [startinfo.size].pack('L')
168
+ startinfo[8,4] = [si_hash['desktop']].pack('p*') if si_hash['desktop']
169
+ startinfo[12,4] = [si_hash['title']].pack('p*') if si_hash['title']
170
+ startinfo[16,4] = [si_hash['x']].pack('L') if si_hash['x']
171
+ startinfo[20,4] = [si_hash['y']].pack('L') if si_hash['y']
172
+ startinfo[24,4] = [si_hash['x_size']].pack('L') if si_hash['x_size']
173
+ startinfo[28,4] = [si_hash['y_size']].pack('L') if si_hash['y_size']
174
+ startinfo[32,4] = [si_hash['x_count_chars']].pack('L') if si_hash['x_count_chars']
175
+ startinfo[36,4] = [si_hash['y_count_chars']].pack('L') if si_hash['y_count_chars']
176
+ startinfo[40,4] = [si_hash['fill_attribute']].pack('L') if si_hash['fill_attribute']
177
+ startinfo[44,4] = [si_hash['startf_flags']].pack('L') if si_hash['startf_flags']
178
+ startinfo[48,2] = [si_hash['sw_flags']].pack('S') if si_hash['sw_flags']
179
+ startinfo[56,4] = [si_hash['stdin']].pack('L') if si_hash['stdin']
180
+ startinfo[60,4] = [si_hash['stdout']].pack('L') if si_hash['stdout']
181
+ startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
182
+ end
183
+
184
+ if hash['with_logon']
185
+ logon = multi_to_wide(hash['with_logon'])
186
+ domain = multi_to_wide(hash['domain'])
187
+ app = hash['app_name'].nil? ? nil : multi_to_wide(hash['app_name'])
188
+ cmd = hash['command_line'].nil? ? nil : multi_to_wide(hash['command_line'])
189
+ cwd = multi_to_wide(hash['cwd'])
190
+ passwd = multi_to_wide(hash['password'])
191
+
192
+ hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
193
+
194
+ process_ran = CreateProcessWithLogonW(
195
+ logon, # User
196
+ domain, # Domain
197
+ passwd, # Password
198
+ LOGON_WITH_PROFILE, # Logon flags
199
+ app, # App name
200
+ cmd, # Command line
201
+ hash['creation_flags'], # Creation flags
202
+ env, # Environment
203
+ cwd, # Working directory
204
+ startinfo, # Startup Info
205
+ procinfo # Process Info
206
+ )
207
+ else
208
+ process_ran = CreateProcess(
209
+ hash['app_name'], # App name
210
+ hash['command_line'], # Command line
211
+ process_security, # Process attributes
212
+ thread_security, # Thread attributes
213
+ hash['inherit'], # Inherit handles?
214
+ hash['creation_flags'], # Creation flags
215
+ env, # Environment
216
+ hash['cwd'], # Working directory
217
+ startinfo, # Startup Info
218
+ procinfo # Process Info
219
+ )
220
+ end
221
+
222
+ # TODO: Close stdin, stdout and stderr handles in the si_hash unless
223
+ # they're pointing to one of the standard handles already. [Maybe]
224
+ if !process_ran
225
+ raise_last_error("CreateProcess()")
226
+ end
227
+
228
+ # Automatically close the process and thread handles in the
229
+ # PROCESS_INFORMATION struct unless explicitly told not to.
230
+ if hash['close_handles']
231
+ CloseHandle(procinfo[0,4].unpack('L').first)
232
+ CloseHandle(procinfo[4,4].unpack('L').first)
233
+ end
234
+
235
+ ProcessInfo.new(
236
+ procinfo[0,4].unpack('L').first, # hProcess
237
+ procinfo[4,4].unpack('L').first, # hThread
238
+ procinfo[8,4].unpack('L').first, # hProcessId
239
+ procinfo[12,4].unpack('L').first # hThreadId
240
+ )
241
+ end
242
+
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
+ module_function :create
385
+ end
metadata CHANGED
@@ -1,82 +1,76 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mixlib-shellout
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
4
5
  prerelease:
5
- version: 1.0.0
6
6
  platform: x86-mingw32
7
- authors:
7
+ authors:
8
8
  - Opscode
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2012-02-28 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2012-08-06 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: rspec
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &11516480 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
24
22
  type: :development
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: win32-process
28
23
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *11516480
25
+ - !ruby/object:Gem::Dependency
26
+ name: win32-process
27
+ requirement: &11586780 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
29
+ requirements:
32
30
  - - ~>
33
- - !ruby/object:Gem::Version
31
+ - !ruby/object:Gem::Version
34
32
  version: 0.6.5
35
33
  type: :runtime
36
- version_requirements: *id002
34
+ prerelease: false
35
+ version_requirements: *11586780
37
36
  description: Run external commands on Unix or Windows
38
37
  email: info@opscode.com
39
38
  executables: []
40
-
41
39
  extensions: []
42
-
43
- extra_rdoc_files:
40
+ extra_rdoc_files:
44
41
  - README.md
45
42
  - LICENSE
46
- files:
43
+ files:
47
44
  - LICENSE
48
45
  - README.md
46
+ - lib/mixlib/shellout.rb
47
+ - lib/mixlib/shellout/windows/core_ext.rb
49
48
  - lib/mixlib/shellout/exceptions.rb
50
- - lib/mixlib/shellout/unix.rb
51
49
  - lib/mixlib/shellout/version.rb
50
+ - lib/mixlib/shellout/unix.rb
52
51
  - lib/mixlib/shellout/windows.rb
53
- - lib/mixlib/shellout.rb
54
52
  homepage: http://wiki.opscode.com/
55
53
  licenses: []
56
-
57
54
  post_install_message:
58
55
  rdoc_options: []
59
-
60
- require_paths:
56
+ require_paths:
61
57
  - lib
62
- required_ruby_version: !ruby/object:Gem::Requirement
58
+ required_ruby_version: !ruby/object:Gem::Requirement
63
59
  none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: "0"
68
- required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
65
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: "0"
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
74
70
  requirements: []
75
-
76
71
  rubyforge_project:
77
- rubygems_version: 1.8.10
72
+ rubygems_version: 1.8.17
78
73
  signing_key:
79
74
  specification_version: 3
80
75
  summary: Run external commands on Unix or Windows
81
76
  test_files: []
82
-