1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
#--
# =============================================================================
# Copyright (c) 2004,2005 Jamis Buck (jamis@37signals.com)
# All rights reserved.
#
# This source file is distributed as part of the Net::SSH Secure Shell Client
# library for Ruby. This file (and the library as a whole) may be used only as
# allowed by either the BSD license, or the Ruby license (or, by association
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
# distribution for the texts of these licenses.
# -----------------------------------------------------------------------------
# net-ssh website : http://net-ssh.rubyforge.org
# project website: http://rubyforge.org/projects/net-ssh
# =============================================================================
#++
require 'net/ssh/errors'
module Net
module SSH
module Service
module Process
# A delegate class for managing popen3 requests for remote processes.
class POpen3Manager
# Create a new POpen3Manager instance on the given connection.
def initialize( connection, log )
@connection = connection
@log = log
end
# Invokes the given command synchronously on the current connection.
# (This means that parallel commands and operations cannot be
# executed when this method is used.) This will return +nil+ if the
# method could not be executed. If the command is successfully
# invoked, and a block is given, the block is then invoked with the
# input, output, and error streams of the command as parameters, and
# the channel is closed as soon as the block terminates. If a block
# is not given, the input, output, and error channels are returned
# and the process *might* not terminate until the session itself
# terminates.
def popen3( command )
@connection.open_channel( "session" ) do |chan|
chan.on_success do |ch|
input = SSHStdinPipe.new( ch )
output = SSHStdoutPipe.new( ch )
error = SSHStderrPipe.new( ch )
if block_given?
yield input, output, error
chan.close
else
return [ input, output, error ]
end
end
chan.on_failure do |ch|
chan.close
end
chan.exec command, true
end
@connection.loop
return nil
end
# A specialized class for use by the Net::SSH "popen3" service.
# An instance of this class represents a means of writing data to an
# SSH channel. This class should never be instantiated directly; use
# the popen3 method instead.
class SSHStdinPipe
# The channel used by this pipe.
attr_reader :channel
# Create a new +stdin+ pipe on the given channel.
def initialize( channel )
@channel = channel
end
# Write the given data as channel data to the underlying channel.
def write( data )
@channel.send_data data
@channel.connection.process true
end
# Write the given data as channel data to the underlying channel,
# appending a newline character (if one isn't already appended).
def puts( data )
write data.chomp + "\n"
end
end
# An abstract class representing a writable stream on a channel. This
# is subclassed by SSHStdoutPipe and SSHStderrPipe.
class SSHOutputPipe
# The channel used by this pipe.
attr_reader :channel
# Create a new output pipe on the given channel.
def initialize( channel )
@channel = channel
@data = ""
end
# Returns true if there are any bytes available on this pipe. This
# will do a non-blocking read on the connection to determine if
# there
def data_available?
if @data.length == 0
connection = @channel.connection
connection.process while connection.reader_ready?
end
@data.length > 0
end
# Read all available bytes from the pipe. If there are no available
# bytes, then this will block until data becomes available.
def read
if @data.length < 1
@channel.connection.process while @data.length < 1
end
data, @data = @data, ""
return data
end
end
# A specialization of SSHOutputPipe that represents specifically the
# +stdout+ stream of a process. It should only be used by popen3.
class SSHStdoutPipe < SSHOutputPipe
# Create a new +stdout+ stream on the given channel. Only one such
# pipe should ever be associated with a channel.
def initialize( channel )
super( channel )
channel.on_data(&method(:do_data))
end
# Invoked when data is recieved from the channel. It simply
# accumulates all data until a +read+ is invoked.
def do_data( channel, data )
@data << data
end
end
# A specialization of SSHOutputPipe that represents specifically the
# +stderr+ stream of a process. It should only be used by popen3.
class SSHStderrPipe < SSHOutputPipe
# Create a new +stderr+ stream on the given channel. Only one such
# pipe should ever be associated with a channel.
def initialize( channel )
super( channel )
channel.on_extended_data(&method(:do_data))
end
# Invoked when data is recieved from the channel. It simply
# accumulates all data until a +read+ is invoked.
def do_data( channel, type, data )
@data << data if type == 1
end
end
end
end # module Process
end # module Service
end # module SSH
end # module Net
|