class WinRM::Shells::Base

Base class for remote shell

Constants

ERROR_OPERATION_ABORTED
FAULTS_FOR_RESET
SHELL_NOT_FOUND
TOO_MANY_COMMANDS

Attributes

connection_opts[R]

@return [ConnectionOpts] connection options of the shell

logger[R]

@return [Logger] logger used for diagnostic messages

shell_id[R]

@return [String] shell id of the currently opn shell or nil if shell is closed

shell_opts[R]

@return [Hash] Options targeted for the created shell

shell_uri[R]

@return [String] uri that SOAP calls use to identify shell type

transport[R]

@return [WinRM::HTTP::HttpTransport] transport used to talk with endpoint

Public Class Methods

new(connection_opts, transport, logger, shell_opts = {}) click to toggle source

Create a new Cmd shell @param connection_opts [ConnectionOpts] The WinRM connection options @param transport [HttpTransport] The WinRM SOAP transport @param logger [Logger] The logger to log diagnostic messages to @param shell_opts [Hash] Options targeted for the created shell

# File lib/winrm/shells/base.rb, line 46
def initialize(connection_opts, transport, logger, shell_opts = {})
  @connection_opts = connection_opts
  @transport = transport
  @logger = logger
  @shell_opts = shell_opts
end

Public Instance Methods

close() click to toggle source

Closes the shell if one is open

# File lib/winrm/shells/base.rb, line 85
def close
  return unless shell_id

  begin
    self.class.close_shell(connection_opts, transport, shell_id)
  rescue WinRMWSManFault => e
    raise unless [ERROR_OPERATION_ABORTED, SHELL_NOT_FOUND].include?(e.fault_code)
  end
  remove_finalizer
  @shell_id = nil
end
run(command, arguments = [], &block) click to toggle source

Runs the specified command with optional arguments @param command [String] The command or executable to run @param arguments [Array] The optional command arguments @param block [&block] The optional callback for any realtime output @yieldparam [string] standard out response text @yieldparam [string] standard error response text @yieldreturn [WinRM::Output] The command output

# File lib/winrm/shells/base.rb, line 78
def run(command, arguments = [], &block)
  with_command_shell(command, arguments) do |shell, cmd|
    response_reader.read_output(command_output_message(shell, cmd), &block)
  end
end

Protected Instance Methods

command_output_message(shell_id, command_id) click to toggle source
# File lib/winrm/shells/base.rb, line 115
def command_output_message(shell_id, command_id)
  cmd_out_opts = {
    shell_id: shell_id,
    command_id: command_id,
    shell_uri: shell_uri,
    out_streams: out_streams
  }
  WinRM::WSMV::CommandOutput.new(connection_opts, cmd_out_opts)
end
open_shell() click to toggle source
# File lib/winrm/shells/base.rb, line 107
def open_shell
  raise NotImplementedError
end
out_streams() click to toggle source
# File lib/winrm/shells/base.rb, line 111
def out_streams
  raise NotImplementedError
end
response_reader() click to toggle source
# File lib/winrm/shells/base.rb, line 103
def response_reader
  raise NotImplementedError
end
send_command(_command, _arguments) click to toggle source
# File lib/winrm/shells/base.rb, line 99
def send_command(_command, _arguments)
  raise NotImplementedError
end
with_command_shell(command, arguments = []) { |shell_id, command_id| ... } click to toggle source
# File lib/winrm/shells/base.rb, line 125
def with_command_shell(command, arguments = [])
  tries ||= 2

  open unless shell_id
  command_id = send_command(command, arguments)
  logger.debug("[WinRM] creating command_id: #{command_id} on shell_id #{shell_id}")
  yield shell_id, command_id
rescue WinRMWSManFault => e
  raise unless FAULTS_FOR_RESET.include?(e.fault_code) && (tries -= 1) > 0

  reset_on_error(e)
  retry
ensure
  cleanup_command(command_id) if command_id
end

Private Instance Methods

add_finalizer() click to toggle source
# File lib/winrm/shells/base.rb, line 175
def add_finalizer
  ObjectSpace.define_finalizer(
    self,
    self.class.finalize(connection_opts, transport, shell_id)
  )
end
cleanup_command(command_id) click to toggle source
# File lib/winrm/shells/base.rb, line 149
def cleanup_command(command_id)
  logger.debug("[WinRM] cleaning up command_id: #{command_id} on shell_id #{shell_id}")
  cleanup_msg = WinRM::WSMV::CleanupCommand.new(
    connection_opts,
    shell_uri: shell_uri,
    shell_id: shell_id,
    command_id: command_id
  )
  transport.send_request(cleanup_msg.build)
rescue WinRMWSManFault => e
  raise unless [ERROR_OPERATION_ABORTED, SHELL_NOT_FOUND].include?(e.fault_code)
rescue WinRMHTTPTransportError => t
  # dont let the cleanup raise so we dont lose any errors from the command
  logger.info("[WinRM] #{t.status_code} returned in cleanup with error: #{t.message}")
end
open() click to toggle source
# File lib/winrm/shells/base.rb, line 165
def open
  close
  retryable(connection_opts[:retry_limit], connection_opts[:retry_delay]) do
    logger.debug("[WinRM] opening remote shell on #{connection_opts[:endpoint]}")
    @shell_id = open_shell
  end
  logger.debug("[WinRM] remote shell created with shell_id: #{shell_id}")
  add_finalizer
end
remove_finalizer() click to toggle source
# File lib/winrm/shells/base.rb, line 182
def remove_finalizer
  ObjectSpace.undefine_finalizer(self)
end
reset_on_error(error) click to toggle source
# File lib/winrm/shells/base.rb, line 143
def reset_on_error(error)
  close if error.fault_code == TOO_MANY_COMMANDS
  logger.debug('[WinRM] opening new shell since the current one was deleted')
  @shell_id = nil
end