mixlib-shellout 3.1.7 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06626b906743fd932b0921f90d24e9aeee3c51fbdecfec571ee9547e1ac701ef
4
- data.tar.gz: c2255e8cf8b72ad727fb076e2eafafef51cdf7b0f17fef0d0e526424dc986c25
3
+ metadata.gz: 82102f99cbf59a6ec48f6b7685e470ecd94bc629e27ae83b36a3eaa7e02ace33
4
+ data.tar.gz: 83d3a8aeb7bf139a209440bc8877b63f524b7d30c783b99af2c7406d237618ef
5
5
  SHA512:
6
- metadata.gz: a8bbe1757af6c210b789decff30f35126ae1348481461a3b05eac662a295ec849fe293ede7d361b0598d04799e10e8aea89e18eea048de03a303b0a077d2b471
7
- data.tar.gz: 0f5cce16bfa678c486ee46ee1c84f218507825070ef86a3eb8a6498d3b98277982ea9c457e10a8cde131969e5cdf1251b02fa16f36cecc6afdfcc835b72fd2c2
6
+ metadata.gz: 201ec59f8e4dd8f20443e9d78f880037cd65f91a890321647d95347d29bf478968554548efc311d2a1da82ae3a7c00220cb27021c13d21c8b2ecc28bdfbd20cc
7
+ data.tar.gz: 3c1caed9698c8820fb70b2245b134e303fcb55343323ea1c51350d625ceeee5f9c3ec8a68d5f81355aafad3c30d89a244316576b674a13eb922f7b0d8fb10948
@@ -248,7 +248,7 @@ module Mixlib
248
248
  # running or died without setting an exit status (e.g., terminated by
249
249
  # `kill -9`).
250
250
  def exitstatus
251
- @status&.exitstatus
251
+ @status && @status.exitstatus
252
252
  end
253
253
 
254
254
  # Run the command, writing the command's standard out and standard error
@@ -157,7 +157,7 @@ module Mixlib
157
157
  end
158
158
 
159
159
  def __io_for_live_stream
160
- if !STDOUT.closed? && __log.trace?
160
+ if STDOUT.tty? && !__config[:daemon] && __log.debug?
161
161
  STDOUT
162
162
  else
163
163
  nil
@@ -370,11 +370,11 @@ module Mixlib
370
370
  return if attempt_reap
371
371
 
372
372
  @terminate_reason = "Command exceeded allowed execution time, process terminated"
373
- logger&.error("Command exceeded allowed execution time, sending TERM")
373
+ logger.error("Command exceeded allowed execution time, sending TERM") if logger
374
374
  Process.kill(:TERM, child_pgid)
375
375
  sleep 3
376
376
  attempt_reap
377
- logger&.error("Command exceeded allowed execution time, sending KILL")
377
+ logger.error("Command exceeded allowed execution time, sending KILL") if logger
378
378
  Process.kill(:KILL, child_pgid)
379
379
  reap
380
380
 
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "3.1.7".freeze
3
+ VERSION = "3.2.0".freeze
4
4
  end
5
5
  end
@@ -89,7 +89,7 @@ module Mixlib
89
89
  # Start the process
90
90
  #
91
91
  process, profile, token = Process.create3(create_process_args)
92
- logger&.debug(format_process(process, app_name, command_line, timeout))
92
+ logger.debug(format_process(process, app_name, command_line, timeout)) if logger
93
93
  begin
94
94
  # Start pushing data into input
95
95
  stdin_write << input if input
@@ -124,7 +124,7 @@ module Mixlib
124
124
  kill_process_tree(process.process_id, wmi, logger)
125
125
  Process.kill(:KILL, process.process_id)
126
126
  rescue SystemCallError
127
- logger&.warn("Failed to kill timed out process #{process.process_id}")
127
+ logger.warn("Failed to kill timed out process #{process.process_id}") if logger
128
128
  end
129
129
 
130
130
  raise Mixlib::ShellOut::CommandTimeout, [
@@ -398,16 +398,20 @@ module Mixlib
398
398
 
399
399
  def kill_process(instance, logger)
400
400
  child_pid = instance.wmi_ole_object.processid
401
- logger&.debug([
401
+ if logger
402
+ logger.debug([
402
403
  "killing child process #{child_pid}::",
403
404
  "#{instance.wmi_ole_object.Name} of parent #{pid}",
404
405
  ].join)
406
+ end
405
407
  Process.kill(:KILL, instance.wmi_ole_object.processid)
406
408
  rescue SystemCallError
407
- logger&.debug([
409
+ if logger
410
+ logger.debug([
408
411
  "Failed to kill child process #{child_pid}::",
409
412
  "#{instance.wmi_ole_object.Name} of parent #{pid}",
410
413
  ].join)
414
+ end
411
415
  end
412
416
 
413
417
  def format_process(process, app_name, command_line, timeout)
@@ -18,9 +18,11 @@
18
18
  #
19
19
 
20
20
  require "win32/process"
21
+ require "ffi/win32/extensions"
21
22
 
22
23
  # Add new constants for Logon
23
24
  module Process::Constants
25
+ private
24
26
 
25
27
  LOGON32_LOGON_INTERACTIVE = 0x00000002
26
28
  LOGON32_LOGON_BATCH = 0x00000004
@@ -44,6 +46,8 @@ module Process::Constants
44
46
  WIN32_PROFILETYPE_PT_MANDATORY = 0x04
45
47
  WIN32_PROFILETYPE_PT_ROAMING_PREEXISTING = 0x08
46
48
 
49
+ # The environment block list ends with two nulls (\0\0).
50
+ ENVIRONMENT_BLOCK_ENDS = "\0\0".freeze
47
51
  end
48
52
 
49
53
  # Structs required for data handling
@@ -77,6 +81,12 @@ module Process::Functions
77
81
  attach_pfunc :UnloadUserProfile,
78
82
  %i{handle handle}, :bool
79
83
 
84
+ attach_pfunc :CreateEnvironmentBlock,
85
+ %i{pointer ulong bool}, :bool
86
+
87
+ attach_pfunc :DestroyEnvironmentBlock,
88
+ %i{pointer}, :bool
89
+
80
90
  ffi_lib :advapi32
81
91
 
82
92
  attach_pfunc :LogonUserW,
@@ -147,13 +157,15 @@ module Process
147
157
  si_hash = {}
148
158
 
149
159
  # If the startup_info key is present, validate its subkeys
150
- hash["startup_info"]&.each do |key, val|
151
- key = key.to_s.downcase
152
- unless valid_si_keys.include?(key)
153
- raise ArgumentError, "invalid startup_info key '#{key}'"
154
- end
160
+ if hash["startup_info"]
161
+ hash["startup_info"].each do |key, val|
162
+ key = key.to_s.downcase
163
+ unless valid_si_keys.include?(key)
164
+ raise ArgumentError, "invalid startup_info key '#{key}'"
165
+ end
155
166
 
156
- si_hash[key] = val
167
+ si_hash[key] = val
168
+ end
157
169
  end
158
170
 
159
171
  # The +command_line+ key is mandatory unless the +app_name+ key
@@ -169,9 +181,25 @@ module Process
169
181
 
170
182
  env = nil
171
183
 
184
+ # Retrieve the environment variables for the specified user.
185
+ if hash["with_logon"]
186
+ logon, passwd, domain = format_creds_from_hash(hash)
187
+ logon_type = hash["elevated"] ? LOGON32_LOGON_BATCH : LOGON32_LOGON_INTERACTIVE
188
+ token = logon_user(logon, domain, passwd, logon_type)
189
+ logon_ptr = FFI::MemoryPointer.from_string(logon)
190
+ profile = PROFILEINFO.new.tap do |dat|
191
+ dat[:dwSize] = dat.size
192
+ dat[:dwFlags] = 1
193
+ dat[:lpUserName] = logon_ptr
194
+ end
195
+
196
+ load_user_profile(token, profile.pointer)
197
+ env_list = retrieve_environment_variables(token)
198
+ end
199
+
172
200
  # The env string should be passed as a string of ';' separated paths.
173
201
  if hash["environment"]
174
- env = hash["environment"]
202
+ env = env_list.nil? ? hash["environment"] : merge_env_variables(env_list, hash["environment"])
175
203
 
176
204
  unless env.respond_to?(:join)
177
205
  env = hash["environment"].split(File::PATH_SEPARATOR)
@@ -393,6 +421,33 @@ module Process
393
421
  true
394
422
  end
395
423
 
424
+ # Retrieves the environment variables for the specified user.
425
+ #
426
+ # @param env_pointer [Pointer] The environment block is an array of null-terminated Unicode strings.
427
+ # @param token [Integer] User token handle.
428
+ # @return [Boolean] true if successfully retrieves the environment variables for the specified user.
429
+ #
430
+ def create_environment_block(env_pointer, token)
431
+ unless CreateEnvironmentBlock(env_pointer, token, false)
432
+ raise SystemCallError.new("CreateEnvironmentBlock", FFI.errno)
433
+ end
434
+
435
+ true
436
+ end
437
+
438
+ # Frees environment variables created by the CreateEnvironmentBlock function.
439
+ #
440
+ # @param env_pointer [Pointer] The environment block is an array of null-terminated Unicode strings.
441
+ # @return [Boolean] true if successfully frees environment variables created by the CreateEnvironmentBlock function.
442
+ #
443
+ def destroy_environment_block(env_pointer)
444
+ unless DestroyEnvironmentBlock(env_pointer)
445
+ raise SystemCallError.new("DestroyEnvironmentBlock", FFI.errno)
446
+ end
447
+
448
+ true
449
+ end
450
+
396
451
  def create_process_as_user(token, app, cmd, process_security,
397
452
  thread_security, inherit, creation_flags, env, cwd, startinfo, procinfo)
398
453
 
@@ -527,5 +582,51 @@ module Process
527
582
  [ logon, passwd, domain ]
528
583
  end
529
584
 
585
+ # Retrieves the environment variables for the specified user.
586
+ #
587
+ # @param token [Integer] User token handle.
588
+ # @return env_list [Array<String>] Environment variables of specified user.
589
+ #
590
+ def retrieve_environment_variables(token)
591
+ env_list = []
592
+ env_pointer = FFI::MemoryPointer.new(:pointer)
593
+ create_environment_block(env_pointer, token)
594
+ str_ptr = env_pointer.read_pointer
595
+ offset = 0
596
+ loop do
597
+ new_str_pointer = str_ptr + offset
598
+ break if new_str_pointer.read_string(2) == ENVIRONMENT_BLOCK_ENDS
599
+
600
+ environment = new_str_pointer.read_wstring
601
+ env_list << environment
602
+ offset = offset + environment.length * 2 + 2
603
+ end
604
+
605
+ # To free the buffer when we have finished with the environment block
606
+ destroy_environment_block(str_ptr)
607
+ env_list
608
+ end
609
+
610
+ # Merge environment variables of specified user and current environment variables.
611
+ #
612
+ # @param fetched_env [Array<String>] environment variables of specified user.
613
+ # @param current_env [Array<String>] current environment variables.
614
+ # @return [Array<String>] Merged environment variables.
615
+ #
616
+ def merge_env_variables(fetched_env, current_env)
617
+ env_hash_1 = environment_list_to_hash(fetched_env)
618
+ env_hash_2 = environment_list_to_hash(current_env)
619
+ merged_env = env_hash_2.merge(env_hash_1)
620
+ merged_env.map { |k, v| "#{k}=#{v}" }
621
+ end
622
+
623
+ # Convert an array to a hash.
624
+ #
625
+ # @param env_var [Array<String>] Environment variables.
626
+ # @return [Hash] Converted an array to hash.
627
+ #
628
+ def environment_list_to_hash(env_var)
629
+ Hash[ env_var.map { |pair| pair.split("=", 2) } ]
630
+ end
530
631
  end
531
632
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-shellout
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.7
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-29 00:00:00.000000000 Z
11
+ date: 2020-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-utils