mixlib-shellout 2.2.6-universal-mingw32 → 2.2.7-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.
@@ -1,5 +1,5 @@
1
- module Mixlib
2
- class ShellOut
3
- VERSION = "2.2.6"
4
- end
5
- end
1
+ module Mixlib
2
+ class ShellOut
3
+ VERSION = "2.2.7"
4
+ end
5
+ end
@@ -1,362 +1,382 @@
1
- #--
2
- # Author:: Daniel DeLeo (<dan@opscode.com>)
3
- # Author:: John Keiser (<jkeiser@opscode.com>)
4
- # Author:: Ho-Sheng Hsiao (<hosh@opscode.com>)
5
- # Copyright:: Copyright (c) 2011, 2012 Opscode, Inc.
6
- # License:: Apache License, Version 2.0
7
- #
8
- # Licensed under the Apache License, Version 2.0 (the "License");
9
- # you may not use this file except in compliance with the License.
10
- # You may obtain a copy of the License at
11
- #
12
- # http://www.apache.org/licenses/LICENSE-2.0
13
- #
14
- # Unless required by applicable law or agreed to in writing, software
15
- # distributed under the License is distributed on an "AS IS" BASIS,
16
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- # See the License for the specific language governing permissions and
18
- # limitations under the License.
19
- #
20
-
21
- require 'win32/process'
22
- require 'mixlib/shellout/windows/core_ext'
23
-
24
- module Mixlib
25
- class ShellOut
26
- module Windows
27
-
28
- include Process::Functions
29
- include Process::Constants
30
-
31
- TIME_SLICE = 0.05
32
-
33
- # Option validation that is windows specific
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
39
- end
40
- end
41
-
42
- #--
43
- # Missing lots of features from the UNIX version, such as
44
- # uid, etc.
45
- def run_command
46
- #
47
- # Create pipes to capture stdout and stderr,
48
- #
49
- stdout_read, stdout_write = IO.pipe
50
- stderr_read, stderr_write = IO.pipe
51
- stdin_read, stdin_write = IO.pipe
52
- open_streams = [ stdout_read, stderr_read ]
53
-
54
- begin
55
-
56
- #
57
- # Set cwd, environment, appname, etc.
58
- #
59
- app_name, command_line = command_to_run(self.command)
60
- create_process_args = {
61
- :app_name => app_name,
62
- :command_line => command_line,
63
- :startup_info => {
64
- :stdout => stdout_write,
65
- :stderr => stderr_write,
66
- :stdin => stdin_read
67
- },
68
- :environment => inherit_environment.map { |k,v| "#{k}=#{v}" },
69
- :close_handles => false
70
- }
71
- create_process_args[:cwd] = cwd if cwd
72
- # default to local account database if domain is not specified
73
- create_process_args[:domain] = domain.nil? ? "." : domain
74
- create_process_args[:with_logon] = with_logon if with_logon
75
- create_process_args[:password] = password if password
76
-
77
- #
78
- # Start the process
79
- #
80
- process = Process.create(create_process_args)
81
- logger.debug(Utils.format_process(process, app_name, command_line, timeout)) if logger
82
- begin
83
- # Start pushing data into input
84
- stdin_write << input if input
85
-
86
- # Close pipe to kick things off
87
- stdin_write.close
88
-
89
- #
90
- # Wait for the process to finish, consuming output as we go
91
- #
92
- start_wait = Time.now
93
- while true
94
- wait_status = WaitForSingleObject(process.process_handle, 0)
95
- case wait_status
96
- when WAIT_OBJECT_0
97
- # Get process exit code
98
- exit_code = [0].pack('l')
99
- unless GetExitCodeProcess(process.process_handle, exit_code)
100
- raise get_last_error
101
- end
102
- @status = ThingThatLooksSortOfLikeAProcessStatus.new
103
- @status.exitstatus = exit_code.unpack('l').first
104
-
105
- return self
106
- when WAIT_TIMEOUT
107
- # Kill the process
108
- if (Time.now - start_wait) > timeout
109
- begin
110
- require 'wmi-lite/wmi'
111
- wmi = WmiLite::Wmi.new
112
- Utils.kill_process_tree(process.process_id, wmi, logger)
113
- Process.kill(:KILL, process.process_id)
114
- rescue Errno::EIO, SystemCallError
115
- logger.warn("Failed to kill timed out process #{process.process_id}") if logger
116
- end
117
-
118
- raise Mixlib::ShellOut::CommandTimeout, [
119
- "command timed out:",
120
- format_for_exception,
121
- Utils.format_process(process, app_name, command_line, timeout)
122
- ].join("\n")
123
- end
124
-
125
- consume_output(open_streams, stdout_read, stderr_read)
126
- else
127
- raise "Unknown response from WaitForSingleObject(#{process.process_handle}, #{timeout*1000}): #{wait_status}"
128
- end
129
-
130
- end
131
-
132
- ensure
133
- CloseHandle(process.thread_handle) if process.thread_handle
134
- CloseHandle(process.process_handle) if process.process_handle
135
- end
136
-
137
- ensure
138
- #
139
- # Consume all remaining data from the pipes until they are closed
140
- #
141
- stdout_write.close
142
- stderr_write.close
143
-
144
- while consume_output(open_streams, stdout_read, stderr_read)
145
- end
146
- end
147
- end
148
-
149
- private
150
-
151
- class ThingThatLooksSortOfLikeAProcessStatus
152
- attr_accessor :exitstatus
153
- def success?
154
- exitstatus == 0
155
- end
156
- end
157
-
158
- def consume_output(open_streams, stdout_read, stderr_read)
159
- return false if open_streams.length == 0
160
- ready = IO.select(open_streams, nil, nil, READ_WAIT_TIME)
161
- return true if ! ready
162
-
163
- if ready.first.include?(stdout_read)
164
- begin
165
- next_chunk = stdout_read.readpartial(READ_SIZE)
166
- @stdout << next_chunk
167
- @live_stdout << next_chunk if @live_stdout
168
- rescue EOFError
169
- stdout_read.close
170
- open_streams.delete(stdout_read)
171
- end
172
- end
173
-
174
- if ready.first.include?(stderr_read)
175
- begin
176
- next_chunk = stderr_read.readpartial(READ_SIZE)
177
- @stderr << next_chunk
178
- @live_stderr << next_chunk if @live_stderr
179
- rescue EOFError
180
- stderr_read.close
181
- open_streams.delete(stderr_read)
182
- end
183
- end
184
-
185
- return true
186
- end
187
-
188
- IS_BATCH_FILE = /\.bat"?$|\.cmd"?$/i
189
-
190
- def command_to_run(command)
191
- return _run_under_cmd(command) if Utils.should_run_under_cmd?(command)
192
-
193
- candidate = candidate_executable_for_command(command)
194
-
195
- # Don't do searching for empty commands. Let it fail when it runs.
196
- return [ nil, command ] if candidate.length == 0
197
-
198
- # 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)
206
- else
207
- _run_directly(command, exe)
208
- end
209
- end
210
-
211
- # cmd does not parse multiple quotes well unless the whole thing is wrapped up in quotes.
212
- # https://github.com/opscode/mixlib-shellout/pull/2#issuecomment-4837859
213
- # 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]
224
- end
225
-
226
- def candidate_executable_for_command(command)
227
- if command =~ /^\s*"(.*?)"/
228
- # If we have quotes, do an exact match
229
- $1
230
- else
231
- # Otherwise check everything up to the first space
232
- unquoted_executable_path(command).strip
233
- end
234
- end
235
-
236
- def inherit_environment
237
- result = {}
238
- ENV.each_pair do |k,v|
239
- result[k] = v
240
- end
241
-
242
- environment.each_pair do |k,v|
243
- if v == nil
244
- result.delete(k)
245
- else
246
- result[k] = v
247
- end
248
- end
249
- result
250
- end
251
-
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_]/
288
- end
289
- end
290
- return false
291
- end
292
-
293
- def self.pathext
294
- @pathext ||= ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') + [''] : ['']
295
- end
296
-
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
305
- 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
316
- end
317
- return nil
318
- end
319
-
320
- def self.executable?(path)
321
- File.executable?(path) && !File.directory?(path)
322
- end
323
-
324
- # recursively kills all child processes of given pid
325
- # calls itself querying for children child procs until
326
- # none remain. Important that a single WmiLite instance
327
- # is passed in since each creates its own WMI rpc process
328
- def self.kill_process_tree(pid, wmi, logger)
329
- wmi.query("select * from Win32_Process where ParentProcessID=#{pid}").each do |instance|
330
- child_pid = instance.wmi_ole_object.processid
331
- kill_process_tree(child_pid, wmi, logger)
332
- begin
333
- logger.debug([
334
- "killing child process #{child_pid}::",
335
- "#{instance.wmi_ole_object.Name} of parent #{pid}"
336
- ].join) if logger
337
- kill_process(instance)
338
- rescue Errno::EIO, SystemCallError
339
- logger.debug([
340
- "Failed to kill child process #{child_pid}::",
341
- "#{instance.wmi_ole_object.Name} of parent #{pid}"
342
- ].join) if logger
343
- end
344
- end
345
- end
346
-
347
- def self.kill_process(instance)
348
- Process.kill(:KILL, instance.wmi_ole_object.processid)
349
- end
350
-
351
- def self.format_process(process, app_name, command_line, timeout)
352
- msg = []
353
- msg << "ProcessId: #{process.process_id}"
354
- msg << "app_name: #{app_name}"
355
- msg << "command_line: #{command_line}"
356
- msg << "timeout: #{timeout}"
357
- msg.join("\n")
358
- end
359
- end
360
- end # class
361
- end
362
- end
1
+ #--
2
+ # Author:: Daniel DeLeo (<dan@chef.io>)
3
+ # Author:: John Keiser (<jkeiser@chef.io>)
4
+ # Author:: Ho-Sheng Hsiao (<hosh@chef.io>)
5
+ # Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'win32/process'
22
+ require 'mixlib/shellout/windows/core_ext'
23
+
24
+ module Mixlib
25
+ class ShellOut
26
+ module Windows
27
+
28
+ include Process::Functions
29
+ include Process::Constants
30
+
31
+ TIME_SLICE = 0.05
32
+
33
+ # Option validation that is windows specific
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
39
+ end
40
+ end
41
+
42
+ #--
43
+ # Missing lots of features from the UNIX version, such as
44
+ # uid, etc.
45
+ def run_command
46
+ #
47
+ # Create pipes to capture stdout and stderr,
48
+ #
49
+ stdout_read, stdout_write = IO.pipe
50
+ stderr_read, stderr_write = IO.pipe
51
+ stdin_read, stdin_write = IO.pipe
52
+ open_streams = [ stdout_read, stderr_read ]
53
+
54
+ begin
55
+
56
+ #
57
+ # Set cwd, environment, appname, etc.
58
+ #
59
+ app_name, command_line = command_to_run(self.command)
60
+ create_process_args = {
61
+ :app_name => app_name,
62
+ :command_line => command_line,
63
+ :startup_info => {
64
+ :stdout => stdout_write,
65
+ :stderr => stderr_write,
66
+ :stdin => stdin_read
67
+ },
68
+ :environment => inherit_environment.map { |k,v| "#{k}=#{v}" },
69
+ :close_handles => false
70
+ }
71
+ create_process_args[:cwd] = cwd if cwd
72
+ # default to local account database if domain is not specified
73
+ create_process_args[:domain] = domain.nil? ? "." : domain
74
+ create_process_args[:with_logon] = with_logon if with_logon
75
+ create_process_args[:password] = password if password
76
+
77
+ #
78
+ # Start the process
79
+ #
80
+ process = Process.create(create_process_args)
81
+ logger.debug(Utils.format_process(process, app_name, command_line, timeout)) if logger
82
+ begin
83
+ # Start pushing data into input
84
+ stdin_write << input if input
85
+
86
+ # Close pipe to kick things off
87
+ stdin_write.close
88
+
89
+ #
90
+ # Wait for the process to finish, consuming output as we go
91
+ #
92
+ start_wait = Time.now
93
+ while true
94
+ wait_status = WaitForSingleObject(process.process_handle, 0)
95
+ case wait_status
96
+ when WAIT_OBJECT_0
97
+ # Get process exit code
98
+ exit_code = [0].pack('l')
99
+ unless GetExitCodeProcess(process.process_handle, exit_code)
100
+ raise get_last_error
101
+ end
102
+ @status = ThingThatLooksSortOfLikeAProcessStatus.new
103
+ @status.exitstatus = exit_code.unpack('l').first
104
+
105
+ return self
106
+ when WAIT_TIMEOUT
107
+ # Kill the process
108
+ if (Time.now - start_wait) > timeout
109
+ begin
110
+ require 'wmi-lite/wmi'
111
+ wmi = WmiLite::Wmi.new
112
+ Utils.kill_process_tree(process.process_id, wmi, logger)
113
+ Process.kill(:KILL, process.process_id)
114
+ rescue Errno::EIO, SystemCallError
115
+ logger.warn("Failed to kill timed out process #{process.process_id}") if logger
116
+ end
117
+
118
+ raise Mixlib::ShellOut::CommandTimeout, [
119
+ "command timed out:",
120
+ format_for_exception,
121
+ Utils.format_process(process, app_name, command_line, timeout)
122
+ ].join("\n")
123
+ end
124
+
125
+ consume_output(open_streams, stdout_read, stderr_read)
126
+ else
127
+ raise "Unknown response from WaitForSingleObject(#{process.process_handle}, #{timeout*1000}): #{wait_status}"
128
+ end
129
+
130
+ end
131
+
132
+ ensure
133
+ CloseHandle(process.thread_handle) if process.thread_handle
134
+ CloseHandle(process.process_handle) if process.process_handle
135
+ end
136
+
137
+ ensure
138
+ #
139
+ # Consume all remaining data from the pipes until they are closed
140
+ #
141
+ stdout_write.close
142
+ stderr_write.close
143
+
144
+ while consume_output(open_streams, stdout_read, stderr_read)
145
+ end
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ class ThingThatLooksSortOfLikeAProcessStatus
152
+ attr_accessor :exitstatus
153
+ def success?
154
+ exitstatus == 0
155
+ end
156
+ end
157
+
158
+ def consume_output(open_streams, stdout_read, stderr_read)
159
+ return false if open_streams.length == 0
160
+ ready = IO.select(open_streams, nil, nil, READ_WAIT_TIME)
161
+ return true if ! ready
162
+
163
+ if ready.first.include?(stdout_read)
164
+ begin
165
+ next_chunk = stdout_read.readpartial(READ_SIZE)
166
+ @stdout << next_chunk
167
+ @live_stdout << next_chunk if @live_stdout
168
+ rescue EOFError
169
+ stdout_read.close
170
+ open_streams.delete(stdout_read)
171
+ end
172
+ end
173
+
174
+ if ready.first.include?(stderr_read)
175
+ begin
176
+ next_chunk = stderr_read.readpartial(READ_SIZE)
177
+ @stderr << next_chunk
178
+ @live_stderr << next_chunk if @live_stderr
179
+ rescue EOFError
180
+ stderr_read.close
181
+ open_streams.delete(stderr_read)
182
+ end
183
+ end
184
+
185
+ return true
186
+ end
187
+
188
+ IS_BATCH_FILE = /\.bat"?$|\.cmd"?$/i
189
+
190
+ def command_to_run(command)
191
+ return _run_under_cmd(command) if Utils.should_run_under_cmd?(command)
192
+
193
+ candidate = candidate_executable_for_command(command)
194
+
195
+ # Don't do searching for empty commands. Let it fail when it runs.
196
+ return [ nil, command ] if candidate.length == 0
197
+
198
+ # 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)
206
+ else
207
+ _run_directly(command, exe)
208
+ end
209
+ end
210
+
211
+ # cmd does not parse multiple quotes well unless the whole thing is wrapped up in quotes.
212
+ # https://github.com/opscode/mixlib-shellout/pull/2#issuecomment-4837859
213
+ # 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]
224
+ end
225
+
226
+ def candidate_executable_for_command(command)
227
+ if command =~ /^\s*"(.*?)"/
228
+ # If we have quotes, do an exact match
229
+ $1
230
+ else
231
+ # Otherwise check everything up to the first space
232
+ unquoted_executable_path(command).strip
233
+ end
234
+ end
235
+
236
+ def inherit_environment
237
+ result = {}
238
+ ENV.each_pair do |k,v|
239
+ result[k] = v
240
+ end
241
+
242
+ environment.each_pair do |k,v|
243
+ if v == nil
244
+ result.delete(k)
245
+ else
246
+ result[k] = v
247
+ end
248
+ end
249
+ result
250
+ end
251
+
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_]/
288
+ end
289
+ end
290
+ return false
291
+ end
292
+
293
+ def self.pathext
294
+ @pathext ||= ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') + [''] : ['']
295
+ end
296
+
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
305
+ 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
316
+ end
317
+ return nil
318
+ end
319
+
320
+ def self.executable?(path)
321
+ File.executable?(path) && !File.directory?(path)
322
+ end
323
+
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
335
+
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
343
+
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
355
+ end
356
+
357
+ def self.kill_process(instance, logger)
358
+ child_pid = instance.wmi_ole_object.processid
359
+ logger.debug([
360
+ "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
365
+ logger.debug([
366
+ "Failed to kill child process #{child_pid}::",
367
+ "#{instance.wmi_ole_object.Name} of parent #{pid}"
368
+ ].join) if logger
369
+ end
370
+
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")
378
+ end
379
+ end
380
+ end # class
381
+ end
382
+ end