mixlib-shellout 3.0.4-universal-mingw32 → 3.1.2-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 +4 -4
- data/lib/mixlib/shellout.rb +10 -6
- data/lib/mixlib/shellout/helper.rb +197 -0
- data/lib/mixlib/shellout/unix.rb +6 -2
- data/lib/mixlib/shellout/version.rb +1 -1
- data/lib/mixlib/shellout/windows.rb +10 -3
- data/lib/mixlib/shellout/windows/core_ext.rb +15 -7
- metadata +18 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ff0d68faea99e58c39edacd851b596bfd5a39d53fb778b9e12da8abb3bab8a46
         | 
| 4 | 
            +
              data.tar.gz: b9e6f5efb5f63c8767dda3f47b46fac20829dc7bfd6de35c3317d5f02fe98652
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ba31783f95a5db34698906f43591e649eb1615e1eee536f94e61f27e87622aed9e1b7714c9c00602d64e529ccd8b9d7db2061525d7142ded0580b6760cbbca28
         | 
| 7 | 
            +
              data.tar.gz: bdf404d7c0ab0ce145170428388c5a02994cea8088a880fc2a71865896fdc75c9b562bc65eb9131e855dd0bf8190e30a6553c246fb5eb2863723e0f786560ae8
         | 
    
        data/lib/mixlib/shellout.rb
    CHANGED
    
    | @@ -19,7 +19,7 @@ | |
| 19 19 | 
             
            require "etc"
         | 
| 20 20 | 
             
            require "tmpdir"
         | 
| 21 21 | 
             
            require "fcntl"
         | 
| 22 | 
            -
             | 
| 22 | 
            +
            require_relative "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 | 
            -
                   | 
| 32 | 
            +
                  require_relative "shellout/windows"
         | 
| 33 33 | 
             
                  include ShellOut::Windows
         | 
| 34 34 | 
             
                else
         | 
| 35 | 
            -
                   | 
| 35 | 
            +
                  require_relative "shellout/unix"
         | 
| 36 36 | 
             
                  include ShellOut::Unix
         | 
| 37 37 | 
             
                end
         | 
| 38 38 |  | 
| @@ -65,7 +65,7 @@ module Mixlib | |
| 65 65 | 
             
                # as the subprocess is running.
         | 
| 66 66 | 
             
                attr_accessor :live_stderr
         | 
| 67 67 |  | 
| 68 | 
            -
                # ShellOut will push data from :input down the stdin of the  | 
| 68 | 
            +
                # ShellOut will push data from :input down the stdin of the subprocess.
         | 
| 69 69 | 
             
                # Normally set via options passed to new.
         | 
| 70 70 | 
             
                # Default: nil
         | 
| 71 71 | 
             
                attr_accessor :input
         | 
| @@ -210,15 +210,17 @@ module Mixlib | |
| 210 210 | 
             
                # TODO migrate to shellout/unix.rb
         | 
| 211 211 | 
             
                def uid
         | 
| 212 212 | 
             
                  return nil unless user
         | 
| 213 | 
            -
             | 
| 213 | 
            +
             | 
| 214 | 
            +
                  user.is_a?(Integer) ? user : Etc.getpwnam(user.to_s).uid
         | 
| 214 215 | 
             
                end
         | 
| 215 216 |  | 
| 216 217 | 
             
                # The gid that the subprocess will switch to. If the group attribute is
         | 
| 217 218 | 
             
                # given as a group name, it is converted to a gid by Etc.getgrnam
         | 
| 218 219 | 
             
                # TODO migrate to shellout/unix.rb
         | 
| 219 220 | 
             
                def gid
         | 
| 220 | 
            -
                  return group. | 
| 221 | 
            +
                  return group.is_a?(Integer) ? group : Etc.getgrnam(group.to_s).gid if group
         | 
| 221 222 | 
             
                  return Etc.getpwuid(uid).gid if using_login?
         | 
| 223 | 
            +
             | 
| 222 224 | 
             
                  nil
         | 
| 223 225 | 
             
                end
         | 
| 224 226 |  | 
| @@ -231,6 +233,7 @@ module Mixlib | |
| 231 233 | 
             
                # results when the command exited with an unexpected status.
         | 
| 232 234 | 
             
                def format_for_exception
         | 
| 233 235 | 
             
                  return "Command execution failed. STDOUT/STDERR suppressed for sensitive resource" if sensitive
         | 
| 236 | 
            +
             | 
| 234 237 | 
             
                  msg = ""
         | 
| 235 238 | 
             
                  msg << "#{@terminate_reason}\n" if @terminate_reason
         | 
| 236 239 | 
             
                  msg << "---- Begin output of #{command} ----\n"
         | 
| @@ -363,6 +366,7 @@ module Mixlib | |
| 363 366 | 
             
                  if login && !user
         | 
| 364 367 | 
             
                    raise InvalidCommandOption, "cannot set login without specifying a user"
         | 
| 365 368 | 
             
                  end
         | 
| 369 | 
            +
             | 
| 366 370 | 
             
                  super
         | 
| 367 371 | 
             
                end
         | 
| 368 372 | 
             
              end
         | 
| @@ -0,0 +1,197 @@ | |
| 1 | 
            +
            #--
         | 
| 2 | 
            +
            # Author:: Daniel DeLeo (<dan@chef.io>)
         | 
| 3 | 
            +
            # Copyright:: Copyright (c) Chef Software Inc.
         | 
| 4 | 
            +
            # License:: Apache License, Version 2.0
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 7 | 
            +
            # you may not use this file except in compliance with the License.
         | 
| 8 | 
            +
            # You may obtain a copy of the License at
         | 
| 9 | 
            +
            #
         | 
| 10 | 
            +
            #     http://www.apache.org/licenses/LICENSE-2.0
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            # Unless required by applicable law or agreed to in writing, software
         | 
| 13 | 
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 14 | 
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 15 | 
            +
            # See the License for the specific language governing permissions and
         | 
| 16 | 
            +
            # limitations under the License.
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            require_relative "../shellout"
         | 
| 19 | 
            +
            require "chef-utils"
         | 
| 20 | 
            +
            require "chef-utils/dsl/default_paths"
         | 
| 21 | 
            +
            require "chef-utils/internal"
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            module Mixlib
         | 
| 24 | 
            +
              class ShellOut
         | 
| 25 | 
            +
                module Helper
         | 
| 26 | 
            +
                  include ChefUtils::Internal
         | 
| 27 | 
            +
                  include ChefUtils::DSL::DefaultPaths
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  #
         | 
| 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 | 
            +
                  #
         | 
| 33 | 
            +
                  # You can see an example of how to handle the "dependenecy 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).
         | 
| 36 | 
            +
                  #
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def shell_out(*args, **options)
         | 
| 39 | 
            +
                    options = options.dup
         | 
| 40 | 
            +
                    options = __maybe_add_timeout(self, options)
         | 
| 41 | 
            +
                    if options.empty?
         | 
| 42 | 
            +
                      shell_out_compacted(*__clean_array(*args))
         | 
| 43 | 
            +
                    else
         | 
| 44 | 
            +
                      shell_out_compacted(*__clean_array(*args), **options)
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def shell_out!(*args, **options)
         | 
| 49 | 
            +
                    options = options.dup
         | 
| 50 | 
            +
                    options = __maybe_add_timeout(self, options)
         | 
| 51 | 
            +
                    if options.empty?
         | 
| 52 | 
            +
                      shell_out_compacted!(*__clean_array(*args))
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      shell_out_compacted!(*__clean_array(*args), **options)
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  private
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # helper sugar for resources that support passing timeouts to shell_out
         | 
| 61 | 
            +
                  #
         | 
| 62 | 
            +
                  # module method to not pollute namespaces, but that means we need self injected as an arg
         | 
| 63 | 
            +
                  # @api private
         | 
| 64 | 
            +
                  def __maybe_add_timeout(obj, options)
         | 
| 65 | 
            +
                    options = options.dup
         | 
| 66 | 
            +
                    # historically resources have not properly declared defaults on their timeouts, so a default default of 900s was enforced here
         | 
| 67 | 
            +
                    default_val = 900
         | 
| 68 | 
            +
                    return options if options.key?(:timeout)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    # FIXME: need to nuke descendent tracker out of Chef::Provider so we can just define that class here without requiring the
         | 
| 71 | 
            +
                    # world, and then just use symbol lookup
         | 
| 72 | 
            +
                    if obj.class.ancestors.map(&:name).include?("Chef::Provider") && obj.respond_to?(:new_resource) && obj.new_resource.respond_to?(:timeout) && !options.key?(:timeout)
         | 
| 73 | 
            +
                      options[:timeout] = obj.new_resource.timeout ? obj.new_resource.timeout.to_f : default_val
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                    options
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  # helper function to mangle options when `default_env` is true
         | 
| 79 | 
            +
                  #
         | 
| 80 | 
            +
                  # @api private
         | 
| 81 | 
            +
                  def __apply_default_env(options)
         | 
| 82 | 
            +
                    options = options.dup
         | 
| 83 | 
            +
                    default_env = options.delete(:default_env)
         | 
| 84 | 
            +
                    default_env = true if default_env.nil?
         | 
| 85 | 
            +
                    if default_env
         | 
| 86 | 
            +
                      env_key = options.key?(:env) ? :env : :environment
         | 
| 87 | 
            +
                      options[env_key] = {
         | 
| 88 | 
            +
                        "LC_ALL" => __config[:internal_locale],
         | 
| 89 | 
            +
                        "LANGUAGE" => __config[:internal_locale],
         | 
| 90 | 
            +
                        "LANG" => __config[:internal_locale],
         | 
| 91 | 
            +
                        __env_path_name => default_paths,
         | 
| 92 | 
            +
                      }.update(options[env_key] || {})
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                    options
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 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.
         | 
| 100 | 
            +
                  #
         | 
| 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.
         | 
| 113 | 
            +
                  #
         | 
| 114 | 
            +
                  def shell_out_compacted(*args, **options)
         | 
| 115 | 
            +
                    options = __apply_default_env(options)
         | 
| 116 | 
            +
                    if options.empty?
         | 
| 117 | 
            +
                      __shell_out_command(*args)
         | 
| 118 | 
            +
                    else
         | 
| 119 | 
            +
                      __shell_out_command(*args, **options)
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  def shell_out_compacted!(*args, **options)
         | 
| 124 | 
            +
                    options = __apply_default_env(options)
         | 
| 125 | 
            +
                    cmd = if options.empty?
         | 
| 126 | 
            +
                            __shell_out_command(*args)
         | 
| 127 | 
            +
                          else
         | 
| 128 | 
            +
                            __shell_out_command(*args, **options)
         | 
| 129 | 
            +
                          end
         | 
| 130 | 
            +
                    cmd.error!
         | 
| 131 | 
            +
                    cmd
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
             | 
| 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).
         | 
| 136 | 
            +
                  #
         | 
| 137 | 
            +
                  # @param args [String] variable number of string arguments
         | 
| 138 | 
            +
                  # @return [Array] array of strings with nil and null string rejection
         | 
| 139 | 
            +
                  #
         | 
| 140 | 
            +
                  def __clean_array(*args)
         | 
| 141 | 
            +
                    args.flatten.compact.map(&:to_s)
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  def __shell_out_command(*args, **options)
         | 
| 145 | 
            +
                    if __transport_connection
         | 
| 146 | 
            +
                      FakeShellOut.new(args, options, __transport_connection.run_command(args.join(" "))) # FIXME: train should accept run_command(*args)
         | 
| 147 | 
            +
                    else
         | 
| 148 | 
            +
                      cmd = if options.empty?
         | 
| 149 | 
            +
                              Mixlib::ShellOut.new(*args)
         | 
| 150 | 
            +
                            else
         | 
| 151 | 
            +
                              Mixlib::ShellOut.new(*args, **options)
         | 
| 152 | 
            +
                            end
         | 
| 153 | 
            +
                      cmd.live_stream ||= __io_for_live_stream
         | 
| 154 | 
            +
                      cmd.run_command
         | 
| 155 | 
            +
                      cmd
         | 
| 156 | 
            +
                    end
         | 
| 157 | 
            +
                  end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  def __io_for_live_stream
         | 
| 160 | 
            +
                    if STDOUT.tty? && !__config[:daemon] && __log.debug?
         | 
| 161 | 
            +
                      STDOUT
         | 
| 162 | 
            +
                    else
         | 
| 163 | 
            +
                      nil
         | 
| 164 | 
            +
                    end
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  def __env_path_name
         | 
| 168 | 
            +
                    if ChefUtils.windows?
         | 
| 169 | 
            +
                      "Path"
         | 
| 170 | 
            +
                    else
         | 
| 171 | 
            +
                      "PATH"
         | 
| 172 | 
            +
                    end
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  class FakeShellOut
         | 
| 176 | 
            +
                    attr_reader :stdout, :stderr, :exitstatus, :status
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                    def initialize(args, options, result)
         | 
| 179 | 
            +
                      @args = args
         | 
| 180 | 
            +
                      @options = options
         | 
| 181 | 
            +
                      @stdout = result.stdout
         | 
| 182 | 
            +
                      @stderr = result.stderr
         | 
| 183 | 
            +
                      @exitstatus = result.exit_status
         | 
| 184 | 
            +
                      @status = OpenStruct.new(success?: ( exitstatus == 0 ))
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    def error?
         | 
| 188 | 
            +
                      exitstatus != 0
         | 
| 189 | 
            +
                    end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                    def error!
         | 
| 192 | 
            +
                      raise Mixlib::ShellOut::ShellCommandFailed, "Unexpected exit status of #{exitstatus} running #{@args}" if error?
         | 
| 193 | 
            +
                    end
         | 
| 194 | 
            +
                  end
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
            end
         | 
    
        data/lib/mixlib/shellout/unix.rb
    CHANGED
    
    | @@ -53,14 +53,16 @@ module Mixlib | |
| 53 53 | 
             
                  # to the user's secondary groups
         | 
| 54 54 | 
             
                  def sgids
         | 
| 55 55 | 
             
                    return nil unless using_login?
         | 
| 56 | 
            +
             | 
| 56 57 | 
             
                    user_name = Etc.getpwuid(uid).name
         | 
| 57 | 
            -
                    all_seconderies.select { |g| g.mem.include?(user_name) }.map | 
| 58 | 
            +
                    all_seconderies.select { |g| g.mem.include?(user_name) }.map(&:gid)
         | 
| 58 59 | 
             
                  end
         | 
| 59 60 |  | 
| 60 61 | 
             
                  # The environment variables that are deduced from simulating logon
         | 
| 61 62 | 
             
                  # Only valid if login is used
         | 
| 62 63 | 
             
                  def logon_environment
         | 
| 63 64 | 
             
                    return {} unless using_login?
         | 
| 65 | 
            +
             | 
| 64 66 | 
             
                    entry = Etc.getpwuid(uid)
         | 
| 65 67 | 
             
                    # According to `man su`, the set fields are:
         | 
| 66 68 | 
             
                    #  $HOME, $SHELL, $USER, $LOGNAME, $PATH, and $IFS
         | 
| @@ -269,6 +271,7 @@ module Mixlib | |
| 269 271 | 
             
                  # Keep this unbuffered for now
         | 
| 270 272 | 
             
                  def write_to_child_stdin
         | 
| 271 273 | 
             
                    return unless input
         | 
| 274 | 
            +
             | 
| 272 275 | 
             
                    child_stdin << input
         | 
| 273 276 | 
             
                    child_stdin.close # Kick things off
         | 
| 274 277 | 
             
                  end
         | 
| @@ -337,7 +340,7 @@ module Mixlib | |
| 337 340 | 
             
                      set_cwd
         | 
| 338 341 |  | 
| 339 342 | 
             
                      begin
         | 
| 340 | 
            -
                        command. | 
| 343 | 
            +
                        command.is_a?(Array) ? exec(*command, close_others: true) : exec(command, close_others: true)
         | 
| 341 344 |  | 
| 342 345 | 
             
                        raise "forty-two" # Should never get here
         | 
| 343 346 | 
             
                      rescue Exception => e
         | 
| @@ -365,6 +368,7 @@ module Mixlib | |
| 365 368 |  | 
| 366 369 | 
             
                  def reap_errant_child
         | 
| 367 370 | 
             
                    return if attempt_reap
         | 
| 371 | 
            +
             | 
| 368 372 | 
             
                    @terminate_reason = "Command exceeded allowed execution time, process terminated"
         | 
| 369 373 | 
             
                    logger.error("Command exceeded allowed execution time, sending TERM") if logger
         | 
| 370 374 | 
             
                    Process.kill(:TERM, child_pgid)
         | 
| @@ -19,7 +19,7 @@ | |
| 19 19 | 
             
            #
         | 
| 20 20 |  | 
| 21 21 | 
             
            require "win32/process"
         | 
| 22 | 
            -
             | 
| 22 | 
            +
            require_relative "windows/core_ext"
         | 
| 23 23 |  | 
| 24 24 | 
             
            module Mixlib
         | 
| 25 25 | 
             
              class ShellOut
         | 
| @@ -88,7 +88,7 @@ module Mixlib | |
| 88 88 | 
             
                      #
         | 
| 89 89 | 
             
                      # Start the process
         | 
| 90 90 | 
             
                      #
         | 
| 91 | 
            -
                      process, profile, token = Process. | 
| 91 | 
            +
                      process, profile, token = Process.create3(create_process_args)
         | 
| 92 92 | 
             
                      logger.debug(format_process(process, app_name, command_line, timeout)) if logger
         | 
| 93 93 | 
             
                      begin
         | 
| 94 94 | 
             
                        # Start pushing data into input
         | 
| @@ -110,6 +110,7 @@ module Mixlib | |
| 110 110 | 
             
                            unless GetExitCodeProcess(process.process_handle, exit_code)
         | 
| 111 111 | 
             
                              raise get_last_error
         | 
| 112 112 | 
             
                            end
         | 
| 113 | 
            +
             | 
| 113 114 | 
             
                            @status = ThingThatLooksSortOfLikeAProcessStatus.new
         | 
| 114 115 | 
             
                            @status.exitstatus = exit_code.unpack("l").first
         | 
| 115 116 |  | 
| @@ -170,8 +171,9 @@ module Mixlib | |
| 170 171 |  | 
| 171 172 | 
             
                  def consume_output(open_streams, stdout_read, stderr_read)
         | 
| 172 173 | 
             
                    return false if open_streams.length == 0
         | 
| 174 | 
            +
             | 
| 173 175 | 
             
                    ready = IO.select(open_streams, nil, nil, READ_WAIT_TIME)
         | 
| 174 | 
            -
                    return true  | 
| 176 | 
            +
                    return true unless ready
         | 
| 175 177 |  | 
| 176 178 | 
             
                    if ready.first.include?(stdout_read)
         | 
| 177 179 | 
             
                      begin
         | 
| @@ -227,6 +229,7 @@ module Mixlib | |
| 227 229 | 
             
                  # @return String
         | 
| 228 230 | 
             
                  def combine_args(*args)
         | 
| 229 231 | 
             
                    return args[0] if args.length == 1
         | 
| 232 | 
            +
             | 
| 230 233 | 
             
                    args.map do |arg|
         | 
| 231 234 | 
             
                      if arg =~ /[ \t\n\v"]/
         | 
| 232 235 | 
             
                        arg = arg.gsub(/(\\*)"/, '\1\1\"') # interior quotes with N preceeding backslashes need 2N+1 backslashes
         | 
| @@ -321,10 +324,12 @@ module Mixlib | |
| 321 324 | 
             
                        return true unless quote
         | 
| 322 325 | 
             
                      when "%"
         | 
| 323 326 | 
             
                        return true if env
         | 
| 327 | 
            +
             | 
| 324 328 | 
             
                        env = env_first_char = true
         | 
| 325 329 | 
             
                        next
         | 
| 326 330 | 
             
                      else
         | 
| 327 331 | 
             
                        next unless env
         | 
| 332 | 
            +
             | 
| 328 333 | 
             
                        if env_first_char
         | 
| 329 334 | 
             
                          env_first_char = false
         | 
| 330 335 | 
             
                          (env = false) && next if c !~ /[A-Za-z_]/
         | 
| @@ -370,6 +375,7 @@ module Mixlib | |
| 370 375 |  | 
| 371 376 | 
             
                  def unsafe_process?(name, logger)
         | 
| 372 377 | 
             
                    return false unless system_required_processes.include? name
         | 
| 378 | 
            +
             | 
| 373 379 | 
             
                    logger.debug(
         | 
| 374 380 | 
             
                      "A request to kill a critical system process - #{name} - was received and skipped."
         | 
| 375 381 | 
             
                    )
         | 
| @@ -383,6 +389,7 @@ module Mixlib | |
| 383 389 | 
             
                  def kill_process_tree(pid, wmi, logger)
         | 
| 384 390 | 
             
                    wmi.query("select * from Win32_Process where ParentProcessID=#{pid}").each do |instance|
         | 
| 385 391 | 
             
                      next if unsafe_process?(instance.wmi_ole_object.name, logger)
         | 
| 392 | 
            +
             | 
| 386 393 | 
             
                      child_pid = instance.wmi_ole_object.processid
         | 
| 387 394 | 
             
                      kill_process_tree(child_pid, wmi, logger)
         | 
| 388 395 | 
             
                      kill_process(instance, logger)
         | 
| @@ -73,19 +73,19 @@ module Process::Functions | |
| 73 73 | 
             
                [:pointer], :bool
         | 
| 74 74 |  | 
| 75 75 | 
             
              attach_pfunc :LoadUserProfileW,
         | 
| 76 | 
            -
                 | 
| 76 | 
            +
                %i{handle pointer}, :bool
         | 
| 77 77 |  | 
| 78 78 | 
             
              attach_pfunc :UnloadUserProfile,
         | 
| 79 | 
            -
                 | 
| 79 | 
            +
                %i{handle handle}, :bool
         | 
| 80 80 |  | 
| 81 81 | 
             
              ffi_lib :advapi32
         | 
| 82 82 |  | 
| 83 83 | 
             
              attach_pfunc :LogonUserW,
         | 
| 84 | 
            -
                 | 
| 84 | 
            +
                %i{buffer_in buffer_in buffer_in ulong ulong pointer}, :bool
         | 
| 85 85 |  | 
| 86 86 | 
             
              attach_pfunc :CreateProcessAsUserW,
         | 
| 87 | 
            -
                 | 
| 88 | 
            -
                   | 
| 87 | 
            +
                %i{ulong buffer_in buffer_inout pointer pointer int
         | 
| 88 | 
            +
                  ulong buffer_in buffer_in pointer pointer}, :bool
         | 
| 89 89 |  | 
| 90 90 | 
             
              ffi_lib :user32
         | 
| 91 91 |  | 
| @@ -93,7 +93,7 @@ module Process::Functions | |
| 93 93 | 
             
                [], :ulong
         | 
| 94 94 |  | 
| 95 95 | 
             
              attach_pfunc :GetUserObjectInformationA,
         | 
| 96 | 
            -
                 | 
| 96 | 
            +
                %i{ulong uint buffer_out ulong pointer}, :bool
         | 
| 97 97 | 
             
            end
         | 
| 98 98 |  | 
| 99 99 | 
             
            # Override Process.create to check for running in the Service window station and doing
         | 
| @@ -109,7 +109,11 @@ module Process | |
| 109 109 | 
             
              class << self
         | 
| 110 110 |  | 
| 111 111 | 
             
                def create(args)
         | 
| 112 | 
            -
                   | 
| 112 | 
            +
                  create3(args).first
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def create3(args)
         | 
| 116 | 
            +
                  unless args.is_a?(Hash)
         | 
| 113 117 | 
             
                    raise TypeError, "hash keyword arguments expected"
         | 
| 114 118 | 
             
                  end
         | 
| 115 119 |  | 
| @@ -137,6 +141,7 @@ module Process | |
| 137 141 | 
             
                    unless valid_keys.include?(key)
         | 
| 138 142 | 
             
                      raise ArgumentError, "invalid key '#{key}'"
         | 
| 139 143 | 
             
                    end
         | 
| 144 | 
            +
             | 
| 140 145 | 
             
                    hash[key] = val
         | 
| 141 146 | 
             
                  end
         | 
| 142 147 |  | 
| @@ -149,6 +154,7 @@ module Process | |
| 149 154 | 
             
                      unless valid_si_keys.include?(key)
         | 
| 150 155 | 
             
                        raise ArgumentError, "invalid startup_info key '#{key}'"
         | 
| 151 156 | 
             
                      end
         | 
| 157 | 
            +
             | 
| 152 158 | 
             
                      si_hash[key] = val
         | 
| 153 159 | 
             
                    end
         | 
| 154 160 | 
             
                  end
         | 
| @@ -367,6 +373,7 @@ module Process | |
| 367 373 | 
             
                  unless GetProfileType(ptr)
         | 
| 368 374 | 
             
                    raise SystemCallError.new("GetProfileType", FFI.errno)
         | 
| 369 375 | 
             
                  end
         | 
| 376 | 
            +
             | 
| 370 377 | 
             
                  ptr.read_uint
         | 
| 371 378 | 
             
                end
         | 
| 372 379 |  | 
| @@ -374,6 +381,7 @@ module Process | |
| 374 381 | 
             
                  unless LoadUserProfileW(token, profile_ptr)
         | 
| 375 382 | 
             
                    raise SystemCallError.new("LoadUserProfileW", FFI.errno)
         | 
| 376 383 | 
             
                  end
         | 
| 384 | 
            +
             | 
| 377 385 | 
             
                  true
         | 
| 378 386 | 
             
                end
         | 
| 379 387 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,29 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mixlib-shellout
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3. | 
| 4 | 
            +
              version: 3.1.2
         | 
| 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:  | 
| 11 | 
            +
            date: 2020-07-23 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: chef-utils
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ">="
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 13 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 28 | 
             
              name: win32-process
         | 
| 15 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -47,6 +61,7 @@ files: | |
| 47 61 | 
             
            - LICENSE
         | 
| 48 62 | 
             
            - lib/mixlib/shellout.rb
         | 
| 49 63 | 
             
            - lib/mixlib/shellout/exceptions.rb
         | 
| 64 | 
            +
            - lib/mixlib/shellout/helper.rb
         | 
| 50 65 | 
             
            - lib/mixlib/shellout/unix.rb
         | 
| 51 66 | 
             
            - lib/mixlib/shellout/version.rb
         | 
| 52 67 | 
             
            - lib/mixlib/shellout/windows.rb
         | 
| @@ -62,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 62 77 | 
             
              requirements:
         | 
| 63 78 | 
             
              - - ">="
         | 
| 64 79 | 
             
                - !ruby/object:Gem::Version
         | 
| 65 | 
            -
                  version: '2. | 
| 80 | 
            +
                  version: '2.4'
         | 
| 66 81 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 67 82 | 
             
              requirements:
         | 
| 68 83 | 
             
              - - ">="
         |