mixlib-shellout 2.2.7-universal-mingw32 → 2.3.0-universal-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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb04212922b107765ee2b1cca2075e89839e81aa
4
- data.tar.gz: f288d3d4b96149359fa173f6eb4be591b9870e76
3
+ metadata.gz: a40ea886f43696d31f832a9c84c6d71a381e3657
4
+ data.tar.gz: ca25e83f24eed9d3271baf9c2e9c79c831a21781
5
5
  SHA512:
6
- metadata.gz: 59f1799bed35a86f297121daf7f45d5f45241dafeb15db758265f14564e09dd66de3331f0a7f95535663d4d5b7a266f5638ce32be6767fbed1e83fea84360132
7
- data.tar.gz: d112373309d2f7ccc90c5077c4b56900abe27891ca767ba7cef4bc67831c14163babdc730b7db8b8acca6e223bf78e8377757a124e4dc5ce762fdcc33c8fb22a
6
+ metadata.gz: 185e8d0875b6670c9b85c653262c0f9a082bd47cf0fd455c14a90210e45b229d352cd5b30449d0b4b3a25c6fccd310ca8751d2acb0d7734c6da728a0c9f0d32b
7
+ data.tar.gz: 998acc35a68b4953a162495d1ed24229b1e361c918b25a255527d9564d269980e52e5eacc1b4999f7d73ab6e9332f5115386c9cff3a702f845f69a109772b7a5
data/Gemfile CHANGED
@@ -1,15 +1,15 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec :name => "mixlib-shellout"
4
4
 
5
5
  group(:test) do
6
6
  gem "rspec_junit_formatter"
7
- gem 'rake'
7
+ gem "rake"
8
8
  end
9
9
 
10
10
  group(:development) do
11
- gem 'pry'
12
- gem 'pry-byebug'
13
- gem 'pry-stack_explorer'
14
- gem 'rb-readline'
11
+ gem "pry"
12
+ gem "pry-byebug"
13
+ gem "pry-stack_explorer"
14
+ gem "rb-readline"
15
15
  end
data/README.md CHANGED
@@ -65,9 +65,13 @@ Invoke "whoami.exe" to demonstrate running a command as another user:
65
65
  Mixlib::ShellOut does a standard fork/exec on Unix, and uses the Win32 API on Windows. There is not currently support for JRuby.
66
66
 
67
67
  ## See Also
68
- - `Process.spawn` in Ruby 1.9
68
+ - `Process.spawn` in Ruby 1.9+
69
69
  - [https://github.com/rtomayko/posix-spawn](https://github.com/rtomayko/posix-spawn)
70
70
 
71
+ ## Contributing
72
+
73
+ For information on contributing to this project see <https://github.com/chef/chef/blob/master/CONTRIBUTING.md>
74
+
71
75
  ## License
72
76
  - Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
73
77
  - License:: Apache License, Version 2.0
data/Rakefile CHANGED
@@ -1,24 +1,16 @@
1
- require 'rspec/core/rake_task'
2
- require 'rubygems/package_task'
3
- require 'mixlib/shellout/version'
1
+ require "bundler"
2
+ require "rspec/core/rake_task"
4
3
 
5
- Dir[File.expand_path("../*gemspec", __FILE__)].reverse.each do |gemspec_path|
6
- gemspec = eval(IO.read(gemspec_path))
7
- Gem::PackageTask.new(gemspec).define
8
- end
4
+ Bundler::GemHelper.install_tasks name: "mixlib-shellout"
5
+
6
+ require "chefstyle"
7
+ require "rubocop/rake_task"
8
+ desc "Run Ruby style checks"
9
+ RuboCop::RakeTask.new(:style)
9
10
 
10
11
  desc "Run all specs in spec directory"
11
12
  RSpec::Core::RakeTask.new(:spec) do |t|
12
- t.pattern = FileList['spec/**/*_spec.rb']
13
- end
14
-
15
- desc "Build it and ship it"
16
- task ship: [:clobber_package, :gem] do
17
- # sh("git tag #{Mixlib::ShellOut::VERSION}")
18
- sh("git push opscode --tags")
19
- Dir[File.expand_path("../pkg/*.gem", __FILE__)].reverse.each do |built_gem|
20
- sh("gem push #{built_gem}")
21
- end
13
+ t.pattern = FileList["spec/**/*_spec.rb"]
22
14
  end
23
15
 
24
- task default: :spec
16
+ task default: [:spec, :style]
@@ -16,10 +16,10 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'etc'
20
- require 'tmpdir'
21
- require 'fcntl'
22
- require 'mixlib/shellout/exceptions'
19
+ require "etc"
20
+ require "tmpdir"
21
+ require "fcntl"
22
+ require "mixlib/shellout/exceptions"
23
23
 
24
24
  module Mixlib
25
25
 
@@ -29,10 +29,10 @@ module Mixlib
29
29
  DEFAULT_READ_TIMEOUT = 600
30
30
 
31
31
  if RUBY_PLATFORM =~ /mswin|mingw32|windows/
32
- require 'mixlib/shellout/windows'
32
+ require "mixlib/shellout/windows"
33
33
  include ShellOut::Windows
34
34
  else
35
- require 'mixlib/shellout/unix'
35
+ require "mixlib/shellout/unix"
36
36
  include ShellOut::Unix
37
37
  end
38
38
 
@@ -109,6 +109,9 @@ module Mixlib
109
109
 
110
110
  attr_reader :stdin_pipe, :stdout_pipe, :stderr_pipe, :process_status_pipe
111
111
 
112
+ # Runs windows process with elevated privileges. Required for Powershell commands which need elevated privileges
113
+ attr_accessor :elevated
114
+
112
115
  # === Arguments:
113
116
  # Takes a single command, or a list of command fragments. These are used
114
117
  # as arguments to Kernel.exec. See the Kernel.exec documentation for more
@@ -133,7 +136,7 @@ module Mixlib
133
136
  # * +timeout+: a Numeric value for the number of seconds to wait on the
134
137
  # child process before raising an Exception. This is calculated as the
135
138
  # total amount of time that ShellOut waited on the child process without
136
- # receiving any output (i.e., IO.select returned nil). Default is 60
139
+ # receiving any output (i.e., IO.select returned nil). Default is 600
137
140
  # seconds. Note: the stdlib Timeout library is not used.
138
141
  # * +input+: A String of data to be passed to the subcommand. This is
139
142
  # written to the child process' stdin stream before the process is
@@ -162,7 +165,7 @@ module Mixlib
162
165
  # cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp')
163
166
  # cmd.run_command # etc.
164
167
  def initialize(*command_args)
165
- @stdout, @stderr, @process_status = '', '', ''
168
+ @stdout, @stderr, @process_status = "", "", ""
166
169
  @live_stdout = @live_stderr = nil
167
170
  @input = nil
168
171
  @log_level = :debug
@@ -172,6 +175,7 @@ module Mixlib
172
175
  @valid_exit_codes = [0]
173
176
  @terminate_reason = nil
174
177
  @timeout = nil
178
+ @elevated = false
175
179
 
176
180
  if command_args.last.is_a?(Hash)
177
181
  parse_options(command_args.pop)
@@ -212,7 +216,7 @@ module Mixlib
212
216
  def gid
213
217
  return group.kind_of?(Integer) ? group : Etc.getgrnam(group.to_s).gid if group
214
218
  return Etc.getpwuid(uid).gid if using_login?
215
- return nil
219
+ nil
216
220
  end
217
221
 
218
222
  def timeout
@@ -253,7 +257,7 @@ module Mixlib
253
257
  # within +timeout+ seconds (default: 600s)
254
258
  def run_command
255
259
  if logger
256
- log_message = (log_tag.nil? ? "" : "#@log_tag ") << "sh(#@command)"
260
+ log_message = (log_tag.nil? ? "" : "#{@log_tag} ") << "sh(#{@command})"
257
261
  logger.send(log_level, log_message)
258
262
  end
259
263
  super
@@ -284,15 +288,15 @@ module Mixlib
284
288
  # is highly encouraged.
285
289
  # === Raises
286
290
  # ShellCommandFailed always
287
- def invalid!(msg=nil)
291
+ def invalid!(msg = nil)
288
292
  msg ||= "Command produced unexpected results"
289
293
  raise ShellCommandFailed, msg + "\n" + format_for_exception
290
294
  end
291
295
 
292
296
  def inspect
293
- "<#{self.class.name}##{object_id}: command: '#@command' process_status: #{@status.inspect} " +
294
- "stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " +
295
- "environment: #{@environment.inspect} timeout: #{timeout} user: #@user group: #@group working_dir: #@cwd >"
297
+ "<#{self.class.name}##{object_id}: command: '#{@command}' process_status: #{@status.inspect} " +
298
+ "stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " +
299
+ "environment: #{@environment.inspect} timeout: #{timeout} user: #{@user} group: #{@group} working_dir: #{@cwd} >"
296
300
  end
297
301
 
298
302
  private
@@ -300,45 +304,47 @@ module Mixlib
300
304
  def parse_options(opts)
301
305
  opts.each do |option, setting|
302
306
  case option.to_s
303
- when 'cwd'
307
+ when "cwd"
304
308
  self.cwd = setting
305
- when 'domain'
309
+ when "domain"
306
310
  self.domain = setting
307
- when 'password'
311
+ when "password"
308
312
  self.password = setting
309
- when 'user'
313
+ when "user"
310
314
  self.user = setting
311
315
  self.with_logon = setting
312
- when 'group'
316
+ when "group"
313
317
  self.group = setting
314
- when 'umask'
318
+ when "umask"
315
319
  self.umask = setting
316
- when 'timeout'
320
+ when "timeout"
317
321
  self.timeout = setting
318
- when 'returns'
322
+ when "returns"
319
323
  self.valid_exit_codes = Array(setting)
320
- when 'live_stream'
324
+ when "live_stream"
321
325
  self.live_stdout = self.live_stderr = setting
322
- when 'live_stdout'
326
+ when "live_stdout"
323
327
  self.live_stdout = setting
324
- when 'live_stderr'
328
+ when "live_stderr"
325
329
  self.live_stderr = setting
326
- when 'input'
330
+ when "input"
327
331
  self.input = setting
328
- when 'logger'
332
+ when "logger"
329
333
  self.logger = setting
330
- when 'log_level'
334
+ when "log_level"
331
335
  self.log_level = setting
332
- when 'log_tag'
336
+ when "log_tag"
333
337
  self.log_tag = setting
334
- when 'environment', 'env'
338
+ when "environment", "env"
335
339
  if setting
336
- self.environment = Hash[setting.map{|(k,v)| [k.to_s,v]}]
340
+ self.environment = Hash[setting.map { |(k, v)| [k.to_s, v] }]
337
341
  else
338
342
  self.environment = {}
339
343
  end
340
- when 'login'
344
+ when "login"
341
345
  self.login = setting
346
+ when "elevated"
347
+ self.elevated = setting
342
348
  else
343
349
  raise InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
344
350
  end
@@ -1,7 +1,9 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- class ShellCommandFailed < RuntimeError; end
4
- class CommandTimeout < RuntimeError; end
5
- class InvalidCommandOption < RuntimeError; end
3
+ class Error < RuntimeError; end
4
+ class ShellCommandFailed < Error; end
5
+ class CommandTimeout < Error; end
6
+ class InvalidCommandOption < Error; end
7
+ class EmptyWindowsCommand < Error; end
6
8
  end
7
9
  end
@@ -27,23 +27,25 @@ module Mixlib
27
27
 
28
28
  # Option validation that is unix specific
29
29
  def validate_options(opts)
30
- # No options to validate, raise exceptions here if needed
30
+ if opts[:elevated]
31
+ raise InvalidCommandOption, "Option `elevated` is supported for Powershell commands only"
32
+ end
31
33
  end
32
34
 
33
35
  # Whether we're simulating a login shell
34
36
  def using_login?
35
- return login && user
37
+ login && user
36
38
  end
37
39
 
38
40
  # Helper method for sgids
39
41
  def all_seconderies
40
42
  ret = []
41
43
  Etc.endgrent
42
- while ( g = Etc.getgrent ) do
44
+ while ( g = Etc.getgrent )
43
45
  ret << g
44
46
  end
45
47
  Etc.endgrent
46
- return ret
48
+ ret
47
49
  end
48
50
 
49
51
  # The secondary groups that the subprocess will switch to.
@@ -52,7 +54,7 @@ module Mixlib
52
54
  def sgids
53
55
  return nil unless using_login?
54
56
  user_name = Etc.getpwuid(uid).name
55
- all_seconderies.select{|g| g.mem.include?(user_name)}.map{|g|g.gid}
57
+ all_seconderies.select { |g| g.mem.include?(user_name) }.map { |g| g.gid }
56
58
  end
57
59
 
58
60
  # The environment variables that are deduced from simulating logon
@@ -63,12 +65,12 @@ module Mixlib
63
65
  # According to `man su`, the set fields are:
64
66
  # $HOME, $SHELL, $USER, $LOGNAME, $PATH, and $IFS
65
67
  # Values are copied from "shadow" package in Ubuntu 14.10
66
- {'HOME'=>entry.dir, 'SHELL'=>entry.shell, 'USER'=>entry.name, 'LOGNAME'=>entry.name, 'PATH'=>'/sbin:/bin:/usr/sbin:/usr/bin', 'IFS'=>"\t\n"}
68
+ { "HOME" => entry.dir, "SHELL" => entry.shell, "USER" => entry.name, "LOGNAME" => entry.name, "PATH" => "/sbin:/bin:/usr/sbin:/usr/bin", "IFS" => "\t\n" }
67
69
  end
68
70
 
69
71
  # Merges the two environments for the process
70
72
  def process_environment
71
- logon_environment.merge(self.environment)
73
+ logon_environment.merge(environment)
72
74
  end
73
75
 
74
76
  # Run the command, writing the command's standard out and standard error
@@ -170,7 +172,7 @@ module Mixlib
170
172
 
171
173
  def set_environment
172
174
  # user-set variables should override the login ones
173
- process_environment.each do |env_var,value|
175
+ process_environment.each do |env_var, value|
174
176
  ENV[env_var] = value
175
177
  end
176
178
  end
@@ -335,14 +337,14 @@ module Mixlib
335
337
  set_cwd
336
338
 
337
339
  begin
338
- command.kind_of?(Array) ? exec(*command, :close_others=>true) : exec(command, :close_others=>true)
340
+ command.kind_of?(Array) ? exec(*command, :close_others => true) : exec(command, :close_others => true)
339
341
 
340
- raise 'forty-two' # Should never get here
342
+ raise "forty-two" # Should never get here
341
343
  rescue Exception => e
342
344
  Marshal.dump(e, process_status_pipe.last)
343
345
  process_status_pipe.last.flush
344
346
  end
345
- process_status_pipe.last.close unless (process_status_pipe.last.closed?)
347
+ process_status_pipe.last.close unless process_status_pipe.last.closed?
346
348
  exit!
347
349
  end
348
350
  end
@@ -351,16 +353,14 @@ module Mixlib
351
353
  # If it's there, un-marshal it and raise. If it's not there,
352
354
  # assume everything went well.
353
355
  def propagate_pre_exec_failure
354
- begin
355
- attempt_buffer_read until child_process_status.eof?
356
- e = Marshal.load(@process_status)
357
- raise(Exception === e ? e : "unknown failure: #{e.inspect}")
358
- rescue ArgumentError # If we get an ArgumentError error, then the exec was successful
359
- true
360
- ensure
361
- child_process_status.close
362
- open_pipes.delete(child_process_status)
363
- end
356
+ attempt_buffer_read until child_process_status.eof?
357
+ e = Marshal.load(@process_status)
358
+ raise(Exception === e ? e : "unknown failure: #{e.inspect}")
359
+ rescue ArgumentError # If we get an ArgumentError error, then the exec was successful
360
+ true
361
+ ensure
362
+ child_process_status.close
363
+ open_pipes.delete(child_process_status)
364
364
  end
365
365
 
366
366
  def reap_errant_child
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "2.2.7"
3
+ VERSION = "2.3.0"
4
4
  end
5
5
  end
@@ -18,8 +18,8 @@
18
18
  # limitations under the License.
19
19
  #
20
20
 
21
- require 'win32/process'
22
- require 'mixlib/shellout/windows/core_ext'
21
+ require "win32/process"
22
+ require "mixlib/shellout/windows/core_ext"
23
23
 
24
24
  module Mixlib
25
25
  class ShellOut
@@ -32,10 +32,12 @@ module Mixlib
32
32
 
33
33
  # Option validation that is windows specific
34
34
  def validate_options(opts)
35
- if opts[:user]
36
- unless opts[:password]
37
- raise InvalidCommandOption, "You must supply both a username and password when supplying a user in windows"
38
- end
35
+ if opts[:user] && !opts[:password]
36
+ raise InvalidCommandOption, "You must supply a password when supplying a user in windows"
37
+ end
38
+
39
+ if opts[:elevated] && opts[:elevated] != true && opts[:elevated] != false
40
+ raise InvalidCommandOption, "Invalid value passed for `elevated`. Please provide true/false."
39
41
  end
40
42
  end
41
43
 
@@ -56,29 +58,30 @@ module Mixlib
56
58
  #
57
59
  # Set cwd, environment, appname, etc.
58
60
  #
59
- app_name, command_line = command_to_run(self.command)
61
+ app_name, command_line = command_to_run(command)
60
62
  create_process_args = {
61
63
  :app_name => app_name,
62
64
  :command_line => command_line,
63
65
  :startup_info => {
64
66
  :stdout => stdout_write,
65
67
  :stderr => stderr_write,
66
- :stdin => stdin_read
68
+ :stdin => stdin_read,
67
69
  },
68
- :environment => inherit_environment.map { |k,v| "#{k}=#{v}" },
69
- :close_handles => false
70
+ :environment => inherit_environment.map { |k, v| "#{k}=#{v}" },
71
+ :close_handles => false,
70
72
  }
71
73
  create_process_args[:cwd] = cwd if cwd
72
74
  # default to local account database if domain is not specified
73
75
  create_process_args[:domain] = domain.nil? ? "." : domain
74
76
  create_process_args[:with_logon] = with_logon if with_logon
75
77
  create_process_args[:password] = password if password
78
+ create_process_args[:elevated] = elevated if elevated
76
79
 
77
80
  #
78
81
  # Start the process
79
82
  #
80
83
  process = Process.create(create_process_args)
81
- logger.debug(Utils.format_process(process, app_name, command_line, timeout)) if logger
84
+ logger.debug(format_process(process, app_name, command_line, timeout)) if logger
82
85
  begin
83
86
  # Start pushing data into input
84
87
  stdin_write << input if input
@@ -90,26 +93,26 @@ module Mixlib
90
93
  # Wait for the process to finish, consuming output as we go
91
94
  #
92
95
  start_wait = Time.now
93
- while true
96
+ loop do
94
97
  wait_status = WaitForSingleObject(process.process_handle, 0)
95
98
  case wait_status
96
99
  when WAIT_OBJECT_0
97
100
  # Get process exit code
98
- exit_code = [0].pack('l')
101
+ exit_code = [0].pack("l")
99
102
  unless GetExitCodeProcess(process.process_handle, exit_code)
100
103
  raise get_last_error
101
104
  end
102
105
  @status = ThingThatLooksSortOfLikeAProcessStatus.new
103
- @status.exitstatus = exit_code.unpack('l').first
106
+ @status.exitstatus = exit_code.unpack("l").first
104
107
 
105
108
  return self
106
109
  when WAIT_TIMEOUT
107
110
  # Kill the process
108
111
  if (Time.now - start_wait) > timeout
109
112
  begin
110
- require 'wmi-lite/wmi'
113
+ require "wmi-lite/wmi"
111
114
  wmi = WmiLite::Wmi.new
112
- Utils.kill_process_tree(process.process_id, wmi, logger)
115
+ kill_process_tree(process.process_id, wmi, logger)
113
116
  Process.kill(:KILL, process.process_id)
114
117
  rescue Errno::EIO, SystemCallError
115
118
  logger.warn("Failed to kill timed out process #{process.process_id}") if logger
@@ -118,13 +121,13 @@ module Mixlib
118
121
  raise Mixlib::ShellOut::CommandTimeout, [
119
122
  "command timed out:",
120
123
  format_for_exception,
121
- Utils.format_process(process, app_name, command_line, timeout)
124
+ format_process(process, app_name, command_line, timeout),
122
125
  ].join("\n")
123
126
  end
124
127
 
125
128
  consume_output(open_streams, stdout_read, stderr_read)
126
129
  else
127
- raise "Unknown response from WaitForSingleObject(#{process.process_handle}, #{timeout*1000}): #{wait_status}"
130
+ raise "Unknown response from WaitForSingleObject(#{process.process_handle}, #{timeout * 1000}): #{wait_status}"
128
131
  end
129
132
 
130
133
  end
@@ -146,8 +149,6 @@ module Mixlib
146
149
  end
147
150
  end
148
151
 
149
- private
150
-
151
152
  class ThingThatLooksSortOfLikeAProcessStatus
152
153
  attr_accessor :exitstatus
153
154
  def success?
@@ -155,6 +156,8 @@ module Mixlib
155
156
  end
156
157
  end
157
158
 
159
+ private
160
+
158
161
  def consume_output(open_streams, stdout_read, stderr_read)
159
162
  return false if open_streams.length == 0
160
163
  ready = IO.select(open_streams, nil, nil, READ_WAIT_TIME)
@@ -182,65 +185,59 @@ module Mixlib
182
185
  end
183
186
  end
184
187
 
185
- return true
188
+ true
186
189
  end
187
190
 
188
- IS_BATCH_FILE = /\.bat"?$|\.cmd"?$/i
189
-
190
191
  def command_to_run(command)
191
- return _run_under_cmd(command) if Utils.should_run_under_cmd?(command)
192
+ return run_under_cmd(command) if should_run_under_cmd?(command)
192
193
 
193
194
  candidate = candidate_executable_for_command(command)
194
195
 
195
- # Don't do searching for empty commands. Let it fail when it runs.
196
- return [ nil, command ] if candidate.length == 0
196
+ if candidate.length == 0
197
+ raise Mixlib::ShellOut::EmptyWindowsCommand, "could not parse script/executable out of command: `#{command}`"
198
+ end
197
199
 
198
200
  # Check if the exe exists directly. Otherwise, search PATH.
199
- exe = Utils.find_executable(candidate)
200
- exe = Utils.which(unquoted_executable_path(command)) if exe.nil? && exe !~ /[\\\/]/
201
-
202
- # Batch files MUST use cmd; and if we couldn't find the command we're looking for,
203
- # we assume it must be a cmd builtin.
204
- if exe.nil? || exe =~ IS_BATCH_FILE
205
- _run_under_cmd(command)
201
+ exe = which(candidate)
202
+ if exe_needs_cmd?(exe)
203
+ run_under_cmd(command)
206
204
  else
207
- _run_directly(command, exe)
205
+ [ exe, command ]
208
206
  end
209
207
  end
210
208
 
209
+ # Batch files MUST use cmd; and if we couldn't find the command we're looking for,
210
+ # we assume it must be a cmd builtin.
211
+ def exe_needs_cmd?(exe)
212
+ !exe || exe =~ /\.bat"?$|\.cmd"?$/i
213
+ end
214
+
211
215
  # cmd does not parse multiple quotes well unless the whole thing is wrapped up in quotes.
212
216
  # https://github.com/opscode/mixlib-shellout/pull/2#issuecomment-4837859
213
217
  # http://ss64.com/nt/syntax-esc.html
214
- def _run_under_cmd(command)
215
- [ ENV['COMSPEC'], "cmd /c \"#{command}\"" ]
216
- end
217
-
218
- def _run_directly(command, exe)
219
- [ exe, command ]
220
- end
221
-
222
- def unquoted_executable_path(command)
223
- command[0,command.index(/\s/) || command.length]
218
+ def run_under_cmd(command)
219
+ [ ENV["COMSPEC"], "cmd /c \"#{command}\"" ]
224
220
  end
225
221
 
222
+ # FIXME: this extracts ARGV[0] but is it correct?
226
223
  def candidate_executable_for_command(command)
227
224
  if command =~ /^\s*"(.*?)"/
228
225
  # If we have quotes, do an exact match
229
226
  $1
230
227
  else
231
228
  # Otherwise check everything up to the first space
232
- unquoted_executable_path(command).strip
229
+ command[0, command.index(/\s/) || command.length].strip
233
230
  end
234
231
  end
235
232
 
236
233
  def inherit_environment
237
234
  result = {}
238
- ENV.each_pair do |k,v|
235
+ ENV.each_pair do |k, v|
239
236
  result[k] = v
240
237
  end
241
238
 
242
- environment.each_pair do |k,v|
243
- if v == nil
239
+ environment.each_pair do |k, v|
240
+ if v.nil?
244
241
  result.delete(k)
245
242
  else
246
243
  result[k] = v
@@ -249,134 +246,134 @@ module Mixlib
249
246
  result
250
247
  end
251
248
 
252
- module Utils
253
- # api: semi-private
254
- # If there are special characters parsable by cmd.exe (such as file redirection), then
255
- # this method should return true.
256
- #
257
- # This parser is based on
258
- # https://github.com/ruby/ruby/blob/9073db5cb1d3173aff62be5b48d00f0fb2890991/win32/win32.c#L1437
259
- def self.should_run_under_cmd?(command)
260
- return true if command =~ /^@/
261
-
262
- quote = nil
263
- env = false
264
- env_first_char = false
265
-
266
- command.dup.each_char do |c|
267
- case c
268
- when "'", '"'
269
- if (!quote)
270
- quote = c
271
- elsif quote == c
272
- quote = nil
273
- end
274
- next
275
- when '>', '<', '|', '&', "\n"
276
- return true unless quote
277
- when '%'
278
- return true if env
279
- env = env_first_char = true
280
- next
281
- else
282
- next unless env
283
- if env_first_char
284
- env_first_char = false
285
- env = false and next if c !~ /[A-Za-z_]/
286
- end
287
- env = false if c !~ /[A-Za-z1-9_]/
249
+ # api: semi-private
250
+ # If there are special characters parsable by cmd.exe (such as file redirection), then
251
+ # this method should return true.
252
+ #
253
+ # This parser is based on
254
+ # https://github.com/ruby/ruby/blob/9073db5cb1d3173aff62be5b48d00f0fb2890991/win32/win32.c#L1437
255
+ def should_run_under_cmd?(command)
256
+ return true if command =~ /^@/
257
+
258
+ quote = nil
259
+ env = false
260
+ env_first_char = false
261
+
262
+ command.dup.each_char do |c|
263
+ case c
264
+ when "'", '"'
265
+ if !quote
266
+ quote = c
267
+ elsif quote == c
268
+ quote = nil
288
269
  end
270
+ next
271
+ when ">", "<", "|", "&", "\n"
272
+ return true unless quote
273
+ when "%"
274
+ return true if env
275
+ env = env_first_char = true
276
+ next
277
+ else
278
+ next unless env
279
+ if env_first_char
280
+ env_first_char = false
281
+ (env = false) && next if c !~ /[A-Za-z_]/
282
+ end
283
+ env = false if c !~ /[A-Za-z1-9_]/
289
284
  end
290
- return false
291
- end
292
-
293
- def self.pathext
294
- @pathext ||= ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') + [''] : ['']
295
285
  end
286
+ false
287
+ end
296
288
 
297
- # which() mimicks the Unix which command
298
- # FIXME: it is not working
299
- def self.which(cmd)
300
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
301
- exe = find_executable("#{path}/#{cmd}")
302
- return exe if exe
303
- end
304
- return nil
289
+ # FIXME: reduce code duplication with chef/chef
290
+ def which(cmd)
291
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") + [""] : [""]
292
+ # windows always searches '.' first
293
+ exts.each do |ext|
294
+ filename = "#{cmd}#{ext}"
295
+ return filename if File.executable?(filename) && !File.directory?(filename)
305
296
  end
306
-
307
- # Windows has a different notion of what "executable" means
308
- # The OS will search through valid the extensions and look
309
- # for a binary there.
310
- def self.find_executable(path)
311
- return path if executable? path
312
-
313
- pathext.each do |ext|
314
- exe = "#{path}#{ext}"
315
- return exe if executable? exe
297
+ # only search through the path if the Filename does not contain separators
298
+ if File.basename(cmd) == cmd
299
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
300
+ paths.each do |path|
301
+ exts.each do |ext|
302
+ filename = File.join(path, "#{cmd}#{ext}")
303
+ return filename if File.executable?(filename) && !File.directory?(filename)
304
+ end
316
305
  end
317
- return nil
318
- end
319
-
320
- def self.executable?(path)
321
- File.executable?(path) && !File.directory?(path)
322
306
  end
307
+ false
308
+ end
323
309
 
324
- def self.system_required_processes
325
- [
326
- 'System Idle Process',
327
- 'System',
328
- 'spoolsv.exe',
329
- 'lsass.exe',
330
- 'csrss.exe',
331
- 'smss.exe',
332
- 'svchost.exe'
333
- ]
334
- end
310
+ def system_required_processes
311
+ [
312
+ "System Idle Process",
313
+ "System",
314
+ "spoolsv.exe",
315
+ "lsass.exe",
316
+ "csrss.exe",
317
+ "smss.exe",
318
+ "svchost.exe",
319
+ ]
320
+ end
335
321
 
336
- def self.unsafe_process?(name, logger)
337
- return false unless system_required_processes.include? name
338
- logger.debug(
339
- "A request to kill a critical system process - #{name} - was received and skipped."
340
- )
341
- true
342
- end
322
+ def unsafe_process?(name, logger)
323
+ return false unless system_required_processes.include? name
324
+ logger.debug(
325
+ "A request to kill a critical system process - #{name} - was received and skipped."
326
+ )
327
+ true
328
+ end
343
329
 
344
- # recursively kills all child processes of given pid
345
- # calls itself querying for children child procs until
346
- # none remain. Important that a single WmiLite instance
347
- # is passed in since each creates its own WMI rpc process
348
- def self.kill_process_tree(pid, wmi, logger)
349
- wmi.query("select * from Win32_Process where ParentProcessID=#{pid}").each do |instance|
350
- next if unsafe_process?(instance.wmi_ole_object.name, logger)
351
- child_pid = instance.wmi_ole_object.processid
352
- kill_process_tree(child_pid, wmi, logger)
353
- kill_process(instance, logger)
354
- end
330
+ # recursively kills all child processes of given pid
331
+ # calls itself querying for children child procs until
332
+ # none remain. Important that a single WmiLite instance
333
+ # is passed in since each creates its own WMI rpc process
334
+ def kill_process_tree(pid, wmi, logger)
335
+ wmi.query("select * from Win32_Process where ParentProcessID=#{pid}").each do |instance|
336
+ next if unsafe_process?(instance.wmi_ole_object.name, logger)
337
+ child_pid = instance.wmi_ole_object.processid
338
+ kill_process_tree(child_pid, wmi, logger)
339
+ kill_process(instance, logger)
355
340
  end
341
+ end
356
342
 
357
- def self.kill_process(instance, logger)
358
- child_pid = instance.wmi_ole_object.processid
343
+ def kill_process(instance, logger)
344
+ child_pid = instance.wmi_ole_object.processid
345
+ if logger
359
346
  logger.debug([
360
347
  "killing child process #{child_pid}::",
361
- "#{instance.wmi_ole_object.Name} of parent #{pid}"
362
- ].join) if logger
363
- Process.kill(:KILL, instance.wmi_ole_object.processid)
364
- rescue Errno::EIO, SystemCallError
348
+ "#{instance.wmi_ole_object.Name} of parent #{pid}",
349
+ ].join)
350
+ end
351
+ Process.kill(:KILL, instance.wmi_ole_object.processid)
352
+ rescue Errno::EIO, SystemCallError
353
+ if logger
365
354
  logger.debug([
366
355
  "Failed to kill child process #{child_pid}::",
367
- "#{instance.wmi_ole_object.Name} of parent #{pid}"
368
- ].join) if logger
356
+ "#{instance.wmi_ole_object.Name} of parent #{pid}",
357
+ ].join)
369
358
  end
359
+ end
360
+
361
+ def format_process(process, app_name, command_line, timeout)
362
+ msg = []
363
+ msg << "ProcessId: #{process.process_id}"
364
+ msg << "app_name: #{app_name}"
365
+ msg << "command_line: #{command_line}"
366
+ msg << "timeout: #{timeout}"
367
+ msg.join("\n")
368
+ end
370
369
 
371
- def self.format_process(process, app_name, command_line, timeout)
372
- msg = []
373
- msg << "ProcessId: #{process.process_id}"
374
- msg << "app_name: #{app_name}"
375
- msg << "command_line: #{command_line}"
376
- msg << "timeout: #{timeout}"
377
- msg.join("\n")
370
+ # DEPRECATED do not use
371
+ class Utils
372
+ include Mixlib::ShellOut::Windows
373
+ def self.should_run_under_cmd?(cmd)
374
+ Mixlib::ShellOut::Windows::Utils.new.send(:should_run_under_cmd?, cmd)
378
375
  end
379
376
  end
380
- end # class
377
+ end
381
378
  end
382
379
  end