Module: Process

### Home Classes Methods

### In Files

- lib/mixlib/shellout/windows/core_ext.rb

### Namespace

- MODULE Process::Constants
- MODULE Process::Functions

### Methods

- ::create

### Included Modules

- Process::Constants
- Process::Structs

### Files

- LICENSE

### Class/Module Index

Quicksearch

- Mixlib
- Mixlib::ShellOut
- Mixlib::ShellOut::CommandTimeout
- Mixlib::ShellOut::InvalidCommandOption
- Mixlib::ShellOut::ShellCommandFailed
- Mixlib::ShellOut::Unix
- Mixlib::ShellOut::Windows
- Mixlib::ShellOut::Windows::ThingThatLooksSortOfLikeAProcessStatus
- Mixlib::ShellOut::Windows::Utils
- Process
- Process::Constants
- Process::Functions
- Process::Functions::FFI
- Process::Functions::FFI::Library

No matching classes.

# Process

Override Process.create to check for running in the Service window station and doing a full logon with LogonUser, instead of a CreateProcessWithLogon

### Public Class Methods

create(args) click to toggle source

    # File lib/mixlib/shellout/windows/core_ext.rb, line 65
    def create(args)
      unless args.kind_of?(Hash)
        raise TypeError, 'hash keyword arguments expected'
      end

      valid_keys = ]
        app_name command_line inherit creation_flags cwd environment
        startup_info thread_inherit process_inherit close_handles with_logon
        domain password
      ]

      valid_si_keys = ]
        startf_flags desktop title x y x_size y_size x_count_chars
        y_count_chars fill_attribute sw_flags stdin stdout stderr
      ]

      # Set default values
      hash = {
        'app_name'       => nil,
        'creation_flags' => 0,
        'close_handles'  => true
      }

      # Validate the keys, and convert symbols and case to lowercase strings.
      args.each{ |key, val|
        key = key.to_s.downcase
        unless valid_keys.include?(key)
          raise ArgumentError, "invalid key '#{key}'"
        end
        hash[key] = val
      }

      si_hash = {}

      # If the startup_info key is present, validate its subkeys
      if hash['startup_info']
        hash['startup_info'].each{ |key, val|
          key = key.to_s.downcase
          unless valid_si_keys.include?(key)
            raise ArgumentError, "invalid startup_info key '#{key}'"
          end
          si_hash[key] = val
        }
      end

      # The +command_line+ key is mandatory unless the +app_name+ key
      # is specified.
      unless hash['command_line']
        if hash['app_name']
          hash['command_line'] = hash['app_name']
          hash['app_name'] = nil
        else
          raise ArgumentError, 'command_line or app_name must be specified'
        end
      end

      env = nil

      # The env string should be passed as a string of ';' separated paths.
      if hash['environment']
        env = hash['environment']

        unless env.respond_to?(:join)
          env = hash['environment'].split(File::PATH_SEPARATOR)
        end

        env = env.map{ |e| e + 0.chr }.join('') + 0.chr
        env.to_wide_string! if hash['with_logon']
      end

      # Process SECURITY_ATTRIBUTE structure
      process_security = nil

      if hash['process_inherit']
        process_security = SECURITY_ATTRIBUTES.new
        process_security[:nLength] = 12
        process_security[:bInheritHandle] = true
      end

      # Thread SECURITY_ATTRIBUTE structure
      thread_security = nil

      if hash['thread_inherit']
        thread_security = SECURITY_ATTRIBUTES.new
        thread_security[:nLength] = 12
        thread_security[:bInheritHandle] = true
      end

      # Automatically handle stdin, stdout and stderr as either IO objects
      # or file descriptors. This won't work for StringIO, however. It also
      # will not work on JRuby because of the way it handles internal file
      # descriptors.
      #
      ['stdin', 'stdout', 'stderr'].each{ |io|
        if si_hash[io]
          if si_hash[io].respond_to?(:fileno)
            handle = get_osfhandle(si_hash[io].fileno)
          else
            handle = get_osfhandle(si_hash[io])
          end

          if handle == INVALID_HANDLE_VALUE
            ptr = FFI::MemoryPointer.new(:int)

            if windows_version >= 6 && get_errno(ptr) == 0
              errno = ptr.read_int
            else
              errno = FFI.errno
            end

            raise SystemCallError.new("get_osfhandle", errno)
          end

          # Most implementations of Ruby on Windows create inheritable
          # handles by default, but some do not. RF bug #26988.
          bool = SetHandleInformation(
            handle,
            HANDLE_FLAG_INHERIT,
            HANDLE_FLAG_INHERIT
          )

          raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool

          si_hash[io] = handle
          si_hash['startf_flags'] ||= 0
          si_hash['startf_flags'] |= STARTF_USESTDHANDLES
          hash['inherit'] = true
        end
      }

      procinfo  = PROCESS_INFORMATION.new
      startinfo = STARTUPINFO.new

      unless si_hash.empty?
        startinfo[:cb]              = startinfo.size
        startinfo[:lpDesktop]       = si_hash['desktop'] if si_hash['desktop']
        startinfo[:lpTitle]         = si_hash['title'] if si_hash['title']
        startinfo[:dwX]             = si_hash['x'] if si_hash['x']
        startinfo[:dwY]             = si_hash['y'] if si_hash['y']
        startinfo[:dwXSize]         = si_hash['x_size'] if si_hash['x_size']
        startinfo[:dwYSize]         = si_hash['y_size'] if si_hash['y_size']
        startinfo[:dwXCountChars]   = si_hash['x_count_chars'] if si_hash['x_count_chars']
        startinfo[:dwYCountChars]   = si_hash['y_count_chars'] if si_hash['y_count_chars']
        startinfo[:dwFillAttribute] = si_hash['fill_attribute'] if si_hash['fill_attribute']
        startinfo[:dwFlags]         = si_hash['startf_flags'] if si_hash['startf_flags']
        startinfo[:wShowWindow]     = si_hash['sw_flags'] if si_hash['sw_flags']
        startinfo[:cbReserved2]     = 0
        startinfo[:hStdInput]       = si_hash['stdin'] if si_hash['stdin']
        startinfo[:hStdOutput]      = si_hash['stdout'] if si_hash['stdout']
        startinfo[:hStdError]       = si_hash['stderr'] if si_hash['stderr']
      end

      app = nil
      cmd = nil

      # Convert strings to wide character strings if present
      if hash['app_name']
        app = hash['app_name'].to_wide_string
      end

      if hash['command_line']
        cmd = hash['command_line'].to_wide_string
      end

      if hash['cwd']
        cwd = hash['cwd'].to_wide_string
      end

      inherit  = hash['inherit'] || false

      if hash['with_logon']
        logon = hash['with_logon'].to_wide_string

        if hash['password']
          passwd = hash['password'].to_wide_string
        else
          raise ArgumentError, 'password must be specified if with_logon is used'
        end

        if hash['domain']
          domain = hash['domain'].to_wide_string
        end

        hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT

        winsta_name = FFI::MemoryPointer.new(:char, 256)
        return_size = FFI::MemoryPointer.new(:ulong)

        bool = GetUserObjectInformationA(
          GetProcessWindowStation(),  # Window station handle
          UOI_NAME,                   # Information to get
          winsta_name,                # Buffer to receive information
          winsta_name.size,           # Size of buffer
          return_size                 # Size filled into buffer
        )

        unless bool
          raise SystemCallError.new("GetUserObjectInformationA", FFI.errno)
        end

        winsta_name = winsta_name.read_string(return_size.read_ulong)

        # If running in the service windows station must do a log on to get
        # to the interactive desktop.  Running process user account must have
        # the 'Replace a process level token' permission.  This is necessary as
        # the logon (which happens with CreateProcessWithLogon) must have an
        # interactive windows station to attach to, which is created with the
        # LogonUser cann with the LOGON32_LOGON_INTERACTIVE flag.
        if winsta_name =~ /^Service-0x0-.*$/
          token = FFI::MemoryPointer.new(:ulong)

          bool = LogonUserW(
            logon,                      # User
            domain,                     # Domain
            passwd,                     # Password
            LOGON32_LOGON_INTERACTIVE,  # Logon Type
            LOGON32_PROVIDER_DEFAULT,   # Logon Provider
            token                       # User token handle
          )

          unless bool
            raise SystemCallError.new("LogonUserW", FFI.errno)
          end

          token = token.read_ulong

          begin
            bool = CreateProcessAsUserW(
              token,                  # User token handle
              app,                    # App name
              cmd,                    # Command line
              process_security,       # Process attributes
              thread_security,        # Thread attributes
              inherit,                # Inherit handles
              hash['creation_flags'], # Creation Flags
              env,                    # Environment
              cwd,                    # Working directory
              startinfo,              # Startup Info
              procinfo                # Process Info
            )
          ensure
            CloseHandle(token)
          end

          unless bool
            raise SystemCallError.new("CreateProcessAsUserW (You must hold the 'Replace a process level token' permission)", FFI.errno)
          end
        else
          bool = CreateProcessWithLogonW(
            logon,                  # User
            domain,                 # Domain
            passwd,                 # Password
            LOGON_WITH_PROFILE,     # Logon flags
            app,                    # App name
            cmd,                    # Command line
            hash['creation_flags'], # Creation flags
            env,                    # Environment
            cwd,                    # Working directory
            startinfo,              # Startup Info
            procinfo                # Process Info
          )
        end

        unless bool
          raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
        end
      else
        bool = CreateProcessW(
          app,                    # App name
          cmd,                    # Command line
          process_security,       # Process attributes
          thread_security,        # Thread attributes
          inherit,                # Inherit handles?
          hash['creation_flags'], # Creation flags
          env,                    # Environment
          cwd,                    # Working directory
          startinfo,              # Startup Info
          procinfo                # Process Info
        )

        unless bool
          raise SystemCallError.new("CreateProcessW", FFI.errno)
        end
      end

      # Automatically close the process and thread handles in the
      # PROCESS_INFORMATION struct unless explicitly told not to.
      if hash['close_handles']
        CloseHandle(procinfo[:hProcess]) if procinfo[:hProcess]
        CloseHandle(procinfo[:hThread]) if procinfo[:hThread]

        # Set fields to nil so callers don't attempt to close the handle
        # which can result in the wrong handle being closed or an
        # exception in some circumstances
        procinfo[:hProcess] = nil
        procinfo[:hThread] = nil
      end

      ProcessInfo.new(
        procinfo[:hProcess],
        procinfo[:hThread],
        procinfo[:dwProcessId],
        procinfo[:dwThreadId]
      )
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.