class Net::SSH::Shell

Constants

VERSION

Attributes

channel[R]
default_process_class[RW]
processes[R]
session[R]
shell[R]
state[R]

Public Class Methods

new(session, shell = :default) click to toggle source
# File lib/net/ssh/shell.rb, line 14
def initialize(session, shell = :default)
  @session = session
  @shell = shell
  @state = :closed
  @processes = []
  @when_open = []
  @on_process_run = nil
  @default_process_class = Net::SSH::Shell::Process
  open
end

Public Instance Methods

busy?() click to toggle source
# File lib/net/ssh/shell.rb, line 94
def busy?
  opening? || processes.any?
end
child_finished(child) click to toggle source
# File lib/net/ssh/shell.rb, line 106
def child_finished(child)
  channel.on_close(&method(:on_channel_close)) unless channel.nil?
  processes.delete(child)
  run_next_process
end
close!() click to toggle source
# File lib/net/ssh/shell.rb, line 102
def close!
  channel.close if channel
end
closed?() click to toggle source
# File lib/net/ssh/shell.rb, line 57
def closed?
  state == :closed
end
execute(command, *args, &callback) click to toggle source
# File lib/net/ssh/shell.rb, line 69
def execute(command, *args, &callback)
  # The class is an optional second argument.
  klass = default_process_class
  klass = args.shift if args.first.is_a?(Class)

  # The properties are expected to be the next argument.
  props = {}
  props = args.shift if args.first.is_a?(Hash)

  process = klass.new(self, command, props, callback)
  processes << process
  run_next_process if processes.length == 1
  process
end
execute!(command, &callback) click to toggle source
# File lib/net/ssh/shell.rb, line 88
def execute!(command, &callback)
  process = execute(command, &callback)
  wait!
  process
end
on_channel_close(_channel) click to toggle source
# File lib/net/ssh/shell.rb, line 122
def on_channel_close(_channel)
  @state = :closed
  @channel = nil
end
on_process_run(&callback) click to toggle source
# File lib/net/ssh/shell.rb, line 65
def on_process_run(&callback)
  @on_process_run = callback
end
open(&callback) click to toggle source
# File lib/net/ssh/shell.rb, line 25
def open(&callback)
  if closed?
    @state = :opening
    @channel = session.open_channel(&method(:open_succeeded))
    @channel.on_open_failed(&method(:open_failed))
    @channel.on_request('exit-status', &method(:on_exit_status))
  end
  when_open(&callback) if callback
  self
end
open!() click to toggle source
# File lib/net/ssh/shell.rb, line 36
def open!
  unless open?
    open if closed?
    session.loop { opening? }
  end
  self
end
open?() click to toggle source
# File lib/net/ssh/shell.rb, line 53
def open?
  state == :open
end
opening?() click to toggle source
# File lib/net/ssh/shell.rb, line 61
def opening?
  !open? && !closed?
end
separator() click to toggle source
# File lib/net/ssh/shell.rb, line 112
def separator
  @separator ||= begin
    s = Digest::SHA1.hexdigest(
      [session.object_id, object_id, Time.now.to_i, Time.now.usec, rand(0xFFFFFFFF)].join(':')
    )

    s << Digest::SHA1.hexdigest(s)
  end
end
subshell(command, &callback) click to toggle source
# File lib/net/ssh/shell.rb, line 84
def subshell(command, &callback)
  execute(command, Net::SSH::Shell::Subshell, &callback)
end
wait!() click to toggle source
# File lib/net/ssh/shell.rb, line 98
def wait!
  session.loop { busy? }
end
when_open() { |self| ... } click to toggle source
# File lib/net/ssh/shell.rb, line 44
def when_open(&callback)
  if open?
    yield self
  else
    @when_open << callback
  end
  self
end

Private Instance Methods

look_for_initialization_done(_channel, data) click to toggle source
# File lib/net/ssh/shell.rb, line 168
def look_for_initialization_done(_channel, data)
  return unless data.include?(separator)
  @state = :open
  @when_open.each { |callback| callback.call(self) }
  @when_open.clear
end
on_exit_status(_channel, data) click to toggle source
# File lib/net/ssh/shell.rb, line 147
def on_exit_status(_channel, data)
  fail 'the shell exited unexpectedly' unless data.read_long == 0
end
open_failed(_channel, code, description) click to toggle source
# File lib/net/ssh/shell.rb, line 142
def open_failed(_channel, code, description)
  @state = :closed
  fail "could not open channel for process manager (#{description}, ##{code})"
end
open_succeeded(channel) click to toggle source
# File lib/net/ssh/shell.rb, line 136
def open_succeeded(channel)
  @state = :pty
  channel.on_close(&method(:on_channel_close))
  channel.request_pty(modes: { Net::SSH::Connection::Term::ECHO => 0 }, &method(:pty_requested))
end
pty_requested(channel, success) click to toggle source
# File lib/net/ssh/shell.rb, line 151
def pty_requested(channel, success)
  @state = :shell
  fail 'could not request pty for process manager' unless success
  if shell == :default
    channel.send_channel_request('shell', &method(:shell_requested))
  else
    channel.exec(shell, &method(:shell_requested))
  end
end
run_next_process() click to toggle source
# File lib/net/ssh/shell.rb, line 129
def run_next_process
  return unless processes.any?
  process = processes.first
  @on_process_run.call(self, process) if @on_process_run
  process.run
end
shell_requested(channel, success) click to toggle source
# File lib/net/ssh/shell.rb, line 161
def shell_requested(channel, success)
  @state = :initializing
  fail 'could not request shell for process manager' unless success
  channel.on_data(&method(:look_for_initialization_done))
  channel.send_data "export PS1=; echo #{separator} $?\n"
end