mixlib-shellout 3.1.1-universal-mingw32 → 3.2.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
  SHA256:
3
- metadata.gz: 7113f252c59a3efdeea40d32a3198d5a9f12f75bc3f937f9c336dad70af2df35
4
- data.tar.gz: 1ce89dd828db10b56ab70efdf57c1aa696087dc2ada86b30d8b7678fe30676df
3
+ metadata.gz: 00f4730aaa3fe480f46217bfe963ebe9ca037f42a416ed437ae652ad01546991
4
+ data.tar.gz: 639bd615824b4f8b5c06e00f092f352f8e8bff6b6f6d58cb38926bccd82dc3bb
5
5
  SHA512:
6
- metadata.gz: 16e054814bab6099704d204b9473243ed594ebaf04f632287bc8fb5cbe73d35933596be255de9ee5b32f733c1163063c8606067750db52592dd53137c137e450
7
- data.tar.gz: 44adaa809681d53a81b9e1ffafe3f5c692037662149917a874f18dfb4a910e0613a8064b98b090f51810adb5e3603ba5c18bf96f20e84f2c81f47379858270f5
6
+ metadata.gz: 43abff4b66af104549f91ae03bd29bbd571b05a5795139fe38896773e9a966fb70bc4214b920ae3ccb7984164e55099205fa50ee105c96c570cb041be84ea396
7
+ data.tar.gz: 729981e52f21ddb356a4d530eaabc958502ca2d81c844285050eb7a1808d4f841784b25db072bfb36904daecef9d4bfc40056bb295b8634c4cd9b58fc83b7aaf
@@ -16,8 +16,8 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require "etc"
20
- require "tmpdir"
19
+ require "etc" unless defined?(Etc)
20
+ require "tmpdir" unless defined?(Dir.mktmpdir)
21
21
  require "fcntl"
22
22
  require_relative "shellout/exceptions"
23
23
 
@@ -122,7 +122,7 @@ module Mixlib
122
122
  # === Options:
123
123
  # If the last argument is a Hash, it is removed from the list of args passed
124
124
  # to exec and used as an options hash. The following options are available:
125
- # * +user+: the user the commmand should run as. if an integer is given, it is
125
+ # * +user+: the user the command should run as. if an integer is given, it is
126
126
  # used as a uid. A string is treated as a username and resolved to a uid
127
127
  # with Etc.getpwnam
128
128
  # * +group+: the group the command should run as. works similarly to +user+
@@ -16,33 +16,23 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require_relative "../shellout"
19
- require "chef-utils"
20
- require "chef-utils/dsl/path_sanity"
19
+ require "chef-utils" unless defined?(ChefUtils)
20
+ require "chef-utils/dsl/default_paths"
21
21
  require "chef-utils/internal"
22
22
 
23
23
  module Mixlib
24
24
  class ShellOut
25
25
  module Helper
26
26
  include ChefUtils::Internal
27
- include ChefUtils::DSL::PathSanity
27
+ include ChefUtils::DSL::DefaultPaths
28
28
 
29
- # PREFERRED APIS:
30
29
  #
31
- # all consumers should now call shell_out!/shell_out.
30
+ # These APIs are considered public for use in ohai and chef (by cookbooks and plugins, etc)
31
+ # but are considered private/experimental for now for the direct users of mixlib-shellout.
32
32
  #
33
- # the shell_out_compacted/shell_out_compacted! APIs are private but are intended for use
34
- # in rspec tests, and should ideally always be used to make code refactoring that do not
35
- # change behavior easier:
36
- #
37
- # allow(provider).to receive(:shell_out_compacted!).with("foo", "bar", "baz")
38
- # provider.shell_out!("foo", [ "bar", nil, "baz"])
39
- # provider.shell_out!(["foo", nil, "bar" ], ["baz"])
40
- #
41
- # note that shell_out_compacted also includes adding the magical timeout option to force
42
- # people to setup expectations on that value explicitly. it does not include the default_env
43
- # mangling in order to avoid users having to setup an expectation on anything other than
44
- # setting `default_env: false` and allow us to make tweak to the default_env without breaking
45
- # a thousand unit tests.
33
+ # You can see an example of how to handle the "dependency injection" in the rspec unit test.
34
+ # That backend API is left deliberately undocumented for now and may not follow SemVer and may
35
+ # break at any time (at least for the rest of 2020).
46
36
  #
47
37
 
48
38
  def shell_out(*args, **options)
@@ -98,15 +88,28 @@ module Mixlib
98
88
  "LC_ALL" => __config[:internal_locale],
99
89
  "LANGUAGE" => __config[:internal_locale],
100
90
  "LANG" => __config[:internal_locale],
101
- __env_path_name => sanitized_path,
91
+ __env_path_name => default_paths,
102
92
  }.update(options[env_key] || {})
103
93
  end
104
94
  options
105
95
  end
106
96
 
107
- # this SHOULD be used for setting up expectations in rspec, see banner comment at top.
97
+ # The shell_out_compacted/shell_out_compacted! APIs are private but are intended for use
98
+ # in rspec tests. They should always be used in rspec tests instead of shell_out to allow
99
+ # for less brittle rspec tests.
108
100
  #
109
- # the private constraint is meant to avoid code calling this directly, rspec expectations are fine.
101
+ # This expectation:
102
+ #
103
+ # allow(provider).to receive(:shell_out_compacted!).with("foo", "bar", "baz")
104
+ #
105
+ # Is met by many different possible calling conventions that mean the same thing:
106
+ #
107
+ # provider.shell_out!("foo", [ "bar", nil, "baz"])
108
+ # provider.shell_out!(["foo", nil, "bar" ], ["baz"])
109
+ #
110
+ # Note that when setting `default_env: false` that you should just setup an expectation on
111
+ # :shell_out_compacted for `default_env: false`, rather than the expanded env settings so
112
+ # that the default_env implementation can change without breaking unit tests.
110
113
  #
111
114
  def shell_out_compacted(*args, **options)
112
115
  options = __apply_default_env(options)
@@ -117,10 +120,6 @@ module Mixlib
117
120
  end
118
121
  end
119
122
 
120
- # this SHOULD be used for setting up expectations in rspec, see banner comment at top.
121
- #
122
- # the private constraint is meant to avoid code calling this directly, rspec expectations are fine.
123
- #
124
123
  def shell_out_compacted!(*args, **options)
125
124
  options = __apply_default_env(options)
126
125
  cmd = if options.empty?
@@ -132,23 +131,12 @@ module Mixlib
132
131
  cmd
133
132
  end
134
133
 
135
- # Helper for subclasses to reject nil out of an array. It allows
136
- # using the array form of shell_out (which avoids the need to surround arguments with
137
- # quote marks to deal with shells).
138
- #
139
- # Usage:
140
- # shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username))
141
- #
142
- # universal_options and useradd_options can be nil, empty array, empty string, strings or arrays
143
- # and the result makes sense.
144
- #
145
- # keeping this separate from shell_out!() makes it a bit easier to write expectations against the
146
- # shell_out args and be able to omit nils and such in the tests (and to test that the nils are
147
- # being rejected correctly).
134
+ # Helper for subclasses to reject nil out of an array. It allows using the array form of
135
+ # shell_out (which avoids the need to surround arguments with quote marks to deal with shells).
148
136
  #
149
137
  # @param args [String] variable number of string arguments
150
138
  # @return [Array] array of strings with nil and null string rejection
151
-
139
+ #
152
140
  def __clean_array(*args)
153
141
  args.flatten.compact.map(&:to_s)
154
142
  end
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "3.1.1".freeze
3
+ VERSION = "3.2.0".freeze
4
4
  end
5
5
  end
@@ -208,7 +208,7 @@ module Mixlib
208
208
  # 4. if the argument must be quoted by #1 and terminates in a sequence of backslashes then all the backlashes must themselves
209
209
  # be backslash excaped (double the backslashes).
210
210
  # 5. if an interior quote that must be escaped by #2 has a sequence of backslashes before it then all the backslashes must
211
- # themselves be backslash excaped along with the backslash ecape of the interior quote (double plus one backslashes).
211
+ # themselves be backslash excaped along with the backslash escape of the interior quote (double plus one backslashes).
212
212
  #
213
213
  # And to restate. We are constructing a string which will be parsed by the windows parser into arguments, and we want those
214
214
  # arguments to match the *args array we are passed here. So call the windows parser operation A then we need to apply A^-1 to
@@ -18,6 +18,7 @@
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
@@ -45,6 +46,8 @@ module Process::Constants
45
46
  WIN32_PROFILETYPE_PT_MANDATORY = 0x04
46
47
  WIN32_PROFILETYPE_PT_ROAMING_PREEXISTING = 0x08
47
48
 
49
+ # The environment block list ends with two nulls (\0\0).
50
+ ENVIRONMENT_BLOCK_ENDS = "\0\0".freeze
48
51
  end
49
52
 
50
53
  # Structs required for data handling
@@ -78,6 +81,12 @@ module Process::Functions
78
81
  attach_pfunc :UnloadUserProfile,
79
82
  %i{handle handle}, :bool
80
83
 
84
+ attach_pfunc :CreateEnvironmentBlock,
85
+ %i{pointer ulong bool}, :bool
86
+
87
+ attach_pfunc :DestroyEnvironmentBlock,
88
+ %i{pointer}, :bool
89
+
81
90
  ffi_lib :advapi32
82
91
 
83
92
  attach_pfunc :LogonUserW,
@@ -172,9 +181,25 @@ module Process
172
181
 
173
182
  env = nil
174
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
+
175
200
  # The env string should be passed as a string of ';' separated paths.
176
201
  if hash["environment"]
177
- env = hash["environment"]
202
+ env = env_list.nil? ? hash["environment"] : merge_env_variables(env_list, hash["environment"])
178
203
 
179
204
  unless env.respond_to?(:join)
180
205
  env = hash["environment"].split(File::PATH_SEPARATOR)
@@ -396,6 +421,33 @@ module Process
396
421
  true
397
422
  end
398
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
+
399
451
  def create_process_as_user(token, app, cmd, process_security,
400
452
  thread_security, inherit, creation_flags, env, cwd, startinfo, procinfo)
401
453
 
@@ -530,5 +582,51 @@ module Process
530
582
  [ logon, passwd, domain ]
531
583
  end
532
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
533
631
  end
534
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.1
4
+ version: 3.2.0
5
5
  platform: universal-mingw32
6
6
  authors:
7
7
  - Chef Software Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-17 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
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ffi-win32-extensions
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.3
55
69
  description: Run external commands on Unix or Windows
56
70
  email: info@chef.io
57
71
  executables: []