mixlib-shellout 1.0.0 → 1.1.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
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.rc.1"
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,8 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-shellout
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 1.0.0
4
+ hash: -510023506
5
+ prerelease: 6
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ - rc
11
+ - 1
12
+ version: 1.1.0.rc.1
6
13
  platform: ruby
7
14
  authors:
8
15
  - Opscode
@@ -10,7 +17,7 @@ autorequire:
10
17
  bindir: bin
11
18
  cert_chain: []
12
19
 
13
- date: 2012-02-28 00:00:00 Z
20
+ date: 2012-08-06 00:00:00 Z
14
21
  dependencies:
15
22
  - !ruby/object:Gem::Dependency
16
23
  name: rspec
@@ -20,6 +27,9 @@ dependencies:
20
27
  requirements:
21
28
  - - ">="
22
29
  - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
23
33
  version: "0"
24
34
  type: :development
25
35
  version_requirements: *id001
@@ -35,11 +45,12 @@ extra_rdoc_files:
35
45
  files:
36
46
  - LICENSE
37
47
  - README.md
48
+ - lib/mixlib/shellout.rb
49
+ - lib/mixlib/shellout/windows/core_ext.rb
38
50
  - lib/mixlib/shellout/exceptions.rb
39
- - lib/mixlib/shellout/unix.rb
40
51
  - lib/mixlib/shellout/version.rb
52
+ - lib/mixlib/shellout/unix.rb
41
53
  - lib/mixlib/shellout/windows.rb
42
- - lib/mixlib/shellout.rb
43
54
  homepage: http://wiki.opscode.com/
44
55
  licenses: []
45
56
 
@@ -53,17 +64,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
64
  requirements:
54
65
  - - ">="
55
66
  - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
56
70
  version: "0"
57
71
  required_rubygems_version: !ruby/object:Gem::Requirement
58
72
  none: false
59
73
  requirements:
60
- - - ">="
74
+ - - ">"
61
75
  - !ruby/object:Gem::Version
62
- version: "0"
76
+ hash: 25
77
+ segments:
78
+ - 1
79
+ - 3
80
+ - 1
81
+ version: 1.3.1
63
82
  requirements: []
64
83
 
65
84
  rubyforge_project:
66
- rubygems_version: 1.8.10
85
+ rubygems_version: 1.8.15
67
86
  signing_key:
68
87
  specification_version: 3
69
88
  summary: Run external commands on Unix or Windows