2004-01-04 04:29:13 -05:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2004-01-04 05:26:00 -05:00
|
|
|
# Copyright (C) 2003-2004 Robey Pointer <robey@lag.net>
|
|
|
|
#
|
|
|
|
# This file is part of paramiko.
|
|
|
|
#
|
|
|
|
# Paramiko is free software; you can redistribute it and/or modify it under the
|
|
|
|
# terms of the GNU Lesser General Public License as published by the Free
|
|
|
|
# Software Foundation; either version 2.1 of the License, or (at your option)
|
|
|
|
# any later version.
|
|
|
|
#
|
|
|
|
# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
|
|
# details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public License
|
2004-06-10 13:55:17 -04:00
|
|
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
2004-01-04 05:26:00 -05:00
|
|
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
|
|
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
|
|
|
Abstraction for an SSH2 channel.
|
|
|
|
"""
|
|
|
|
|
2004-04-06 04:16:02 -04:00
|
|
|
import time, threading, socket, os
|
|
|
|
|
2004-04-05 06:12:59 -04:00
|
|
|
from common import *
|
2004-04-06 04:16:02 -04:00
|
|
|
import util
|
2003-11-04 03:34:24 -05:00
|
|
|
from message import Message
|
2003-12-26 21:03:44 -05:00
|
|
|
from ssh_exception import SSHException
|
2004-03-04 03:21:45 -05:00
|
|
|
from file import BufferedFile
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
|
|
|
|
# this is ugly, and won't work on windows
|
2003-12-30 02:18:20 -05:00
|
|
|
def _set_nonblocking(fd):
|
2003-11-04 03:34:24 -05:00
|
|
|
import fcntl
|
|
|
|
fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
|
|
|
|
|
|
|
|
|
2004-01-04 04:29:13 -05:00
|
|
|
class Channel (object):
|
2003-11-04 03:34:24 -05:00
|
|
|
"""
|
2004-01-04 04:29:13 -05:00
|
|
|
A secure tunnel across an SSH L{Transport}. A Channel is meant to behave
|
|
|
|
like a socket, and has an API that should be indistinguishable from the
|
|
|
|
python socket API.
|
|
|
|
|
|
|
|
Because SSH2 has a windowing kind of flow control, if you stop reading data
|
|
|
|
from a Channel and its buffer fills up, the server will be unable to send
|
|
|
|
you any more data until you read some of it. (This won't affect other
|
|
|
|
channels on the same transport -- all channels on a single transport are
|
|
|
|
flow-controlled independently.) Similarly, if the server isn't reading
|
|
|
|
data you send, calls to L{send} may block, unless you set a timeout. This
|
|
|
|
is exactly like a normal network socket, so it shouldn't be too surprising.
|
2003-11-04 03:34:24 -05:00
|
|
|
"""
|
2003-12-30 02:18:20 -05:00
|
|
|
|
2004-06-10 13:55:17 -04:00
|
|
|
# lower bound on the max packet size we'll accept from the remote host
|
|
|
|
MIN_PACKET_SIZE = 1024
|
|
|
|
|
2003-11-09 16:14:21 -05:00
|
|
|
def __init__(self, chanid):
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
|
|
|
Create a new channel. The channel is not associated with any
|
|
|
|
particular session or L{Transport} until the Transport attaches it.
|
|
|
|
Normally you would only call this method from the constructor of a
|
|
|
|
subclass of L{Channel}.
|
|
|
|
|
|
|
|
@param chanid: the ID of this channel, as passed by an existing
|
|
|
|
L{Transport}.
|
|
|
|
@type chanid: int
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
self.chanid = chanid
|
2003-11-09 16:14:21 -05:00
|
|
|
self.transport = None
|
2003-11-04 03:34:24 -05:00
|
|
|
self.active = 0
|
|
|
|
self.eof_received = 0
|
|
|
|
self.eof_sent = 0
|
|
|
|
self.in_buffer = ''
|
|
|
|
self.timeout = None
|
2004-04-07 02:07:29 -04:00
|
|
|
self.closed = False
|
2004-06-10 13:55:17 -04:00
|
|
|
self.ultra_debug = False
|
2003-11-04 03:34:24 -05:00
|
|
|
self.lock = threading.Lock()
|
|
|
|
self.in_buffer_cv = threading.Condition(self.lock)
|
|
|
|
self.out_buffer_cv = threading.Condition(self.lock)
|
|
|
|
self.name = str(chanid)
|
2003-11-10 03:49:50 -05:00
|
|
|
self.logger = logging.getLogger('paramiko.chan.' + str(chanid))
|
2003-11-04 03:34:24 -05:00
|
|
|
self.pipe_rfd = self.pipe_wfd = None
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event = threading.Event()
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def __repr__(self):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Returns a string representation of this object, for debugging.
|
|
|
|
|
2004-09-03 18:39:20 -04:00
|
|
|
@rtype: str
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
2003-11-10 03:49:50 -05:00
|
|
|
out = '<paramiko.Channel %d' % self.chanid
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed:
|
|
|
|
out += ' (closed)'
|
|
|
|
elif self.active:
|
|
|
|
if self.eof_received:
|
|
|
|
out += ' (EOF received)'
|
|
|
|
if self.eof_sent:
|
|
|
|
out += ' (EOF sent)'
|
|
|
|
out += ' (open) window=%d' % (self.out_window_size)
|
|
|
|
if len(self.in_buffer) > 0:
|
|
|
|
out += ' in-buffer=%d' % (len(self.in_buffer),)
|
|
|
|
out += ' -> ' + repr(self.transport)
|
|
|
|
out += '>'
|
|
|
|
return out
|
|
|
|
|
|
|
|
def get_pty(self, term='vt100', width=80, height=24):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Request a pseudo-terminal from the server. This is usually used right
|
|
|
|
after creating a client channel, to ask the server to provide some
|
|
|
|
basic terminal semantics for the next command you execute.
|
|
|
|
|
|
|
|
@param term: the terminal type to emulate (for example, C{'vt100'}).
|
2004-09-03 18:39:20 -04:00
|
|
|
@type term: str
|
2003-12-30 02:18:20 -05:00
|
|
|
@param width: width (in characters) of the terminal screen
|
|
|
|
@type width: int
|
|
|
|
@param height: height (in characters) of the terminal screen
|
|
|
|
@type height: int
|
2004-08-30 16:22:10 -04:00
|
|
|
@return: C{True} if the operation succeeded; C{False} if not.
|
|
|
|
@rtype: bool
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
2003-11-10 03:49:50 -05:00
|
|
|
raise SSHException('Channel is not open')
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string('pty-req')
|
2004-08-28 00:21:12 -04:00
|
|
|
m.add_boolean(True)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string(term)
|
|
|
|
m.add_int(width)
|
|
|
|
m.add_int(height)
|
|
|
|
# pixel height, width (usually useless)
|
|
|
|
m.add_int(0).add_int(0)
|
|
|
|
m.add_string('')
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event.clear()
|
|
|
|
self.transport._send_user_message(m)
|
|
|
|
while 1:
|
|
|
|
self.event.wait(0.1)
|
|
|
|
if self.closed:
|
|
|
|
return False
|
|
|
|
if self.event.isSet():
|
|
|
|
return True
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def invoke_shell(self):
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
|
|
|
Request an interactive shell session on this channel. If the server
|
|
|
|
allows it, the channel will then be directly connected to the stdin
|
|
|
|
and stdout of the shell.
|
2004-08-30 16:22:10 -04:00
|
|
|
|
|
|
|
@return: C{True} if the operation succeeded; C{False} if not.
|
|
|
|
@rtype: bool
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
2003-11-10 03:49:50 -05:00
|
|
|
raise SSHException('Channel is not open')
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string('shell')
|
|
|
|
m.add_boolean(1)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event.clear()
|
|
|
|
self.transport._send_user_message(m)
|
|
|
|
while 1:
|
|
|
|
self.event.wait(0.1)
|
|
|
|
if self.closed:
|
|
|
|
return False
|
|
|
|
if self.event.isSet():
|
|
|
|
return True
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def exec_command(self, command):
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
|
|
|
Execute a command on the server. If the server allows it, the channel
|
|
|
|
will then be directly connected to the stdin and stdout of the command
|
|
|
|
being executed.
|
|
|
|
|
|
|
|
@param command: a shell command to execute.
|
2004-09-03 18:39:20 -04:00
|
|
|
@type command: str
|
2004-08-30 16:22:10 -04:00
|
|
|
@return: C{True} if the operation succeeded; C{False} if not.
|
|
|
|
@rtype: bool
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
2003-11-10 03:49:50 -05:00
|
|
|
raise SSHException('Channel is not open')
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string('exec')
|
|
|
|
m.add_boolean(1)
|
|
|
|
m.add_string(command)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event.clear()
|
|
|
|
self.transport._send_user_message(m)
|
|
|
|
while 1:
|
|
|
|
self.event.wait(0.1)
|
|
|
|
if self.closed:
|
|
|
|
return False
|
|
|
|
if self.event.isSet():
|
|
|
|
return True
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def invoke_subsystem(self, subsystem):
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
|
|
|
Request a subsystem on the server (for example, C{sftp}). If the
|
|
|
|
server allows it, the channel will then be directly connected to the
|
|
|
|
requested subsystem.
|
|
|
|
|
|
|
|
@param subsystem: name of the subsystem being requested.
|
2004-09-03 18:39:20 -04:00
|
|
|
@type subsystem: str
|
2004-08-30 16:22:10 -04:00
|
|
|
@return: C{True} if the operation succeeded; C{False} if not.
|
|
|
|
@rtype: bool
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
2003-11-10 03:49:50 -05:00
|
|
|
raise SSHException('Channel is not open')
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string('subsystem')
|
|
|
|
m.add_boolean(1)
|
|
|
|
m.add_string(subsystem)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event.clear()
|
|
|
|
self.transport._send_user_message(m)
|
|
|
|
while 1:
|
|
|
|
self.event.wait(0.1)
|
|
|
|
if self.closed:
|
|
|
|
return False
|
|
|
|
if self.event.isSet():
|
|
|
|
return True
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def resize_pty(self, width=80, height=24):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Resize the pseudo-terminal. This can be used to change the width and
|
|
|
|
height of the terminal emulation created in a previous L{get_pty} call.
|
|
|
|
|
|
|
|
@param width: new width (in characters) of the terminal screen
|
|
|
|
@type width: int
|
|
|
|
@param height: new height (in characters) of the terminal screen
|
|
|
|
@type height: int
|
2004-08-30 16:22:10 -04:00
|
|
|
@return: C{True} if the operation succeeded; C{False} if not.
|
|
|
|
@rtype: bool
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
2003-11-10 03:49:50 -05:00
|
|
|
raise SSHException('Channel is not open')
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string('window-change')
|
|
|
|
m.add_boolean(0)
|
|
|
|
m.add_int(width)
|
|
|
|
m.add_int(height)
|
|
|
|
m.add_int(0).add_int(0)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event.clear()
|
|
|
|
self.transport._send_user_message(m)
|
|
|
|
while 1:
|
|
|
|
self.event.wait(0.1)
|
|
|
|
if self.closed:
|
|
|
|
return False
|
|
|
|
if self.event.isSet():
|
|
|
|
return True
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def get_transport(self):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Return the L{Transport} associated with this channel.
|
|
|
|
|
|
|
|
@return: the L{Transport} that was used to create this channel.
|
|
|
|
@rtype: L{Transport}
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
return self.transport
|
|
|
|
|
|
|
|
def set_name(self, name):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Set a name for this channel. Currently it's only used to set the name
|
|
|
|
of the log level used for debugging. The name can be fetched with the
|
|
|
|
L{get_name} method.
|
|
|
|
|
2004-09-03 18:39:20 -04:00
|
|
|
@param name: new channel name.
|
|
|
|
@type name: str
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
self.name = name
|
2003-11-10 03:49:50 -05:00
|
|
|
self.logger = logging.getLogger('paramiko.chan.' + name)
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def get_name(self):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Get the name of this channel that was previously set by L{set_name}.
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2004-09-03 18:39:20 -04:00
|
|
|
@return: the name of this channel.
|
|
|
|
@rtype: str
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
return self.name
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2004-09-03 18:39:20 -04:00
|
|
|
def get_id(self):
|
|
|
|
"""
|
|
|
|
Return the ID # for this channel. The channel ID is unique across
|
|
|
|
a L{Transport} and usually a small number. It's also the number
|
|
|
|
passed to L{ServerInterface.check_channel_request} when determining
|
|
|
|
whether to accept a channel request in server mode.
|
|
|
|
|
|
|
|
@return: the ID of this channel.
|
|
|
|
@rtype: int
|
|
|
|
|
|
|
|
@since: ivysaur
|
|
|
|
"""
|
|
|
|
return self.chanid
|
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
### socket API
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
|
|
|
|
def settimeout(self, timeout):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Set a timeout on blocking read/write operations. The C{timeout}
|
|
|
|
argument can be a nonnegative float expressing seconds, or C{None}. If
|
|
|
|
a float is given, subsequent channel read/write operations will raise
|
|
|
|
a timeout exception if the timeout period value has elapsed before the
|
|
|
|
operation has completed. Setting a timeout of C{None} disables
|
|
|
|
timeouts on socket operations.
|
|
|
|
|
|
|
|
C{chan.settimeout(0.0)} is equivalent to C{chan.setblocking(0)};
|
|
|
|
C{chan.settimeout(None)} is equivalent to C{chan.setblocking(1)}.
|
|
|
|
|
|
|
|
@param timeout: seconds to wait for a pending read/write operation
|
|
|
|
before raising C{socket.timeout}, or C{None} for no timeout.
|
|
|
|
@type timeout: float
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
self.timeout = timeout
|
|
|
|
|
|
|
|
def gettimeout(self):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Returns the timeout in seconds (as a float) associated with socket
|
|
|
|
operations, or C{None} if no timeout is set. This reflects the last
|
|
|
|
call to L{setblocking} or L{settimeout}.
|
|
|
|
|
|
|
|
@return: timeout in seconds, or C{None}.
|
|
|
|
@rtype: float
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
return self.timeout
|
|
|
|
|
|
|
|
def setblocking(self, blocking):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Set blocking or non-blocking mode of the channel: if C{blocking} is 0,
|
|
|
|
the channel is set to non-blocking mode; otherwise it's set to blocking
|
|
|
|
mode. Initially all channels are in blocking mode.
|
|
|
|
|
|
|
|
In non-blocking mode, if a L{recv} call doesn't find any data, or if a
|
|
|
|
L{send} call can't immediately dispose of the data, an error exception
|
|
|
|
is raised. In blocking mode, the calls block until they can proceed.
|
|
|
|
|
|
|
|
C{chan.setblocking(0)} is equivalent to C{chan.settimeout(0)};
|
|
|
|
C{chan.setblocking(1)} is equivalent to C{chan.settimeout(None)}.
|
|
|
|
|
|
|
|
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
|
|
|
|
mode.
|
|
|
|
@type blocking: int
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if blocking:
|
|
|
|
self.settimeout(None)
|
|
|
|
else:
|
|
|
|
self.settimeout(0.0)
|
|
|
|
|
|
|
|
def close(self):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Close the channel. All future read/write operations on the channel
|
|
|
|
will fail. The remote end will receive no more data (after queued data
|
|
|
|
is flushed). Channels are automatically closed when they are garbage-
|
|
|
|
collected, or when their L{Transport} is closed.
|
|
|
|
"""
|
2004-10-31 22:54:01 -05:00
|
|
|
self.lock.acquire()
|
2003-12-24 17:09:43 -05:00
|
|
|
try:
|
2004-10-31 22:54:01 -05:00
|
|
|
if not self.active or self.closed:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
self._send_eof()
|
|
|
|
m = Message()
|
|
|
|
m.add_byte(chr(MSG_CHANNEL_CLOSE))
|
|
|
|
m.add_int(self.remote_chanid)
|
|
|
|
self.transport._send_user_message(m)
|
|
|
|
except EOFError:
|
|
|
|
pass
|
|
|
|
self._set_closed()
|
|
|
|
# can't unlink from the Transport yet -- the remote side may still
|
|
|
|
# try to send meta-data (exit-status, etc)
|
2003-12-24 17:09:43 -05:00
|
|
|
finally:
|
|
|
|
self.lock.release()
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
def recv_ready(self):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
2004-11-26 17:07:31 -05:00
|
|
|
Returns true if data is buffered and ready to be read from this
|
|
|
|
channel. A C{False} result does not mean that the channel has closed;
|
|
|
|
it means you may need to wait before more data arrives.
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
@return: C{True} if a L{recv} call on this channel would immediately
|
|
|
|
return at least one byte; C{False} otherwise.
|
|
|
|
@rtype: boolean
|
|
|
|
|
|
|
|
@note: This method doesn't work if you've called L{fileno}.
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
|
|
|
if len(self.in_buffer) == 0:
|
2003-12-30 02:18:20 -05:00
|
|
|
return False
|
|
|
|
return True
|
2003-11-04 03:34:24 -05:00
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
|
|
|
|
def recv(self, nbytes):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Receive data from the channel. The return value is a string
|
|
|
|
representing the data received. The maximum amount of data to be
|
|
|
|
received at once is specified by C{nbytes}. If a string of length zero
|
|
|
|
is returned, the channel stream has closed.
|
|
|
|
|
|
|
|
@param nbytes: maximum number of bytes to read.
|
|
|
|
@type nbytes: int
|
2004-03-04 03:21:45 -05:00
|
|
|
@return: data.
|
2004-09-03 18:39:20 -04:00
|
|
|
@rtype: str
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
@raise socket.timeout: if no data is ready before the timeout set by
|
|
|
|
L{settimeout}.
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
out = ''
|
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
|
|
|
if self.pipe_rfd != None:
|
|
|
|
# use the pipe
|
2003-12-30 02:18:20 -05:00
|
|
|
return self._read_pipe(nbytes)
|
2003-11-04 03:34:24 -05:00
|
|
|
if len(self.in_buffer) == 0:
|
|
|
|
if self.closed or self.eof_received:
|
|
|
|
return out
|
|
|
|
# should we block?
|
|
|
|
if self.timeout == 0.0:
|
|
|
|
raise socket.timeout()
|
|
|
|
# loop here in case we get woken up but a different thread has grabbed everything in the buffer
|
|
|
|
timeout = self.timeout
|
|
|
|
while (len(self.in_buffer) == 0) and not self.closed and not self.eof_received:
|
|
|
|
then = time.time()
|
|
|
|
self.in_buffer_cv.wait(timeout)
|
|
|
|
if timeout != None:
|
|
|
|
timeout -= time.time() - then
|
|
|
|
if timeout <= 0.0:
|
|
|
|
raise socket.timeout()
|
|
|
|
# something in the buffer and we have the lock
|
|
|
|
if len(self.in_buffer) <= nbytes:
|
|
|
|
out = self.in_buffer
|
|
|
|
self.in_buffer = ''
|
|
|
|
else:
|
|
|
|
out = self.in_buffer[:nbytes]
|
|
|
|
self.in_buffer = self.in_buffer[nbytes:]
|
2003-12-30 02:18:20 -05:00
|
|
|
self._check_add_window(len(out))
|
2003-11-04 03:34:24 -05:00
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
return out
|
|
|
|
|
|
|
|
def send(self, s):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Send data to the channel. Returns the number of bytes sent, or 0 if
|
|
|
|
the channel stream is closed. Applications are responsible for
|
|
|
|
checking that all data has been sent: if only some of the data was
|
|
|
|
transmitted, the application needs to attempt delivery of the remaining
|
|
|
|
data.
|
|
|
|
|
|
|
|
@param s: data to send.
|
2004-09-03 18:39:20 -04:00
|
|
|
@type s: str
|
2003-12-30 02:18:20 -05:00
|
|
|
@return: number of bytes actually sent.
|
|
|
|
@rtype: int
|
|
|
|
|
|
|
|
@raise socket.timeout: if no data could be sent before the timeout set
|
|
|
|
by L{settimeout}.
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
size = 0
|
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.closed or self.eof_sent:
|
|
|
|
return 0
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.out_window_size == 0:
|
|
|
|
# should we block?
|
|
|
|
if self.timeout == 0.0:
|
|
|
|
raise socket.timeout()
|
|
|
|
# loop here in case we get woken up but a different thread has filled the buffer
|
|
|
|
timeout = self.timeout
|
|
|
|
while self.out_window_size == 0:
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.closed or self.eof_sent:
|
|
|
|
return 0
|
2003-11-04 03:34:24 -05:00
|
|
|
then = time.time()
|
|
|
|
self.out_buffer_cv.wait(timeout)
|
|
|
|
if timeout != None:
|
|
|
|
timeout -= time.time() - then
|
|
|
|
if timeout <= 0.0:
|
|
|
|
raise socket.timeout()
|
|
|
|
# we have some window to squeeze into
|
|
|
|
if self.closed:
|
|
|
|
return 0
|
|
|
|
size = len(s)
|
|
|
|
if self.out_window_size < size:
|
|
|
|
size = self.out_window_size
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.out_max_packet_size - 64 < size:
|
|
|
|
size = self.out_max_packet_size - 64
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_DATA))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_string(s[:size])
|
2004-08-28 00:21:12 -04:00
|
|
|
self.transport._send_user_message(m)
|
2003-11-04 03:34:24 -05:00
|
|
|
self.out_window_size -= size
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.ultra_debug:
|
|
|
|
self._log(DEBUG, 'window down to %d' % self.out_window_size)
|
2003-11-04 03:34:24 -05:00
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
return size
|
|
|
|
|
|
|
|
def sendall(self, s):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Send data to the channel, without allowing partial results. Unlike
|
|
|
|
L{send}, this method continues to send data from the given string until
|
|
|
|
either all data has been sent or an error occurs. Nothing is returned.
|
|
|
|
|
|
|
|
@param s: data to send.
|
2004-09-03 18:39:20 -04:00
|
|
|
@type s: str
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
@raise socket.timeout: if sending stalled for longer than the timeout
|
|
|
|
set by L{settimeout}.
|
|
|
|
@raise socket.error: if an error occured before the entire string was
|
|
|
|
sent.
|
|
|
|
|
|
|
|
@note: If the channel is closed while only part of the data hase been
|
|
|
|
sent, there is no way to determine how much data (if any) was sent.
|
|
|
|
This is irritating, but identically follows python's API.
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
while s:
|
|
|
|
if self.closed:
|
|
|
|
# this doesn't seem useful, but it is the documented behavior of Socket
|
|
|
|
raise socket.error('Socket is closed')
|
|
|
|
sent = self.send(s)
|
|
|
|
s = s[sent:]
|
|
|
|
return None
|
|
|
|
|
|
|
|
def makefile(self, *params):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Return a file-like object associated with this channel, without the
|
|
|
|
non-portable side effects of L{fileno}. The optional C{mode} and
|
|
|
|
C{bufsize} arguments are interpreted the same way as by the built-in
|
|
|
|
C{file()} function in python.
|
|
|
|
|
|
|
|
@return: object which can be used for python file I/O.
|
|
|
|
@rtype: L{ChannelFile}
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
return ChannelFile(*([self] + list(params)))
|
|
|
|
|
|
|
|
def fileno(self):
|
|
|
|
"""
|
2003-12-30 02:18:20 -05:00
|
|
|
Returns an OS-level file descriptor which can be used for polling and
|
|
|
|
reading (but I{not} for writing). This is primaily to allow python's
|
|
|
|
C{select} module to work.
|
|
|
|
|
|
|
|
The first time C{fileno} is called on a channel, a pipe is created to
|
|
|
|
simulate real OS-level file descriptor (FD) behavior. Because of this,
|
|
|
|
two actual FDs are created -- this may be inefficient if you plan to
|
|
|
|
use many channels.
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
@return: a small integer file descriptor
|
|
|
|
@rtype: int
|
|
|
|
|
|
|
|
@warning: This method causes several aspects of the channel to change
|
|
|
|
behavior. It is always more efficient to avoid using this method.
|
|
|
|
|
|
|
|
@bug: This does not work on Windows. The problem is that pipes are
|
|
|
|
used to simulate an open FD, but I haven't figured out how to make
|
|
|
|
pipes enter non-blocking mode on Windows yet.
|
2003-11-04 03:34:24 -05:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
|
|
|
if self.pipe_rfd != None:
|
|
|
|
return self.pipe_rfd
|
|
|
|
# create the pipe and feed in any existing data
|
|
|
|
self.pipe_rfd, self.pipe_wfd = os.pipe()
|
2003-12-30 02:18:20 -05:00
|
|
|
_set_nonblocking(self.pipe_wfd)
|
|
|
|
_set_nonblocking(self.pipe_rfd)
|
2003-11-04 03:34:24 -05:00
|
|
|
if len(self.in_buffer) > 0:
|
|
|
|
x = self.in_buffer
|
|
|
|
self.in_buffer = ''
|
2003-12-30 02:18:20 -05:00
|
|
|
self._feed_pipe(x)
|
2003-11-04 03:34:24 -05:00
|
|
|
return self.pipe_rfd
|
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
|
|
|
|
def shutdown(self, how):
|
2003-12-30 02:18:20 -05:00
|
|
|
"""
|
|
|
|
Shut down one or both halves of the connection. If C{how} is 0,
|
|
|
|
further receives are disallowed. If C{how} is 1, further sends
|
|
|
|
are disallowed. If C{how} is 2, further sends and receives are
|
|
|
|
disallowed. This closes the stream in one or both directions.
|
|
|
|
|
|
|
|
@param how: 0 (stop receiving), 1 (stop sending), or 2 (stop
|
|
|
|
receiving and sending).
|
|
|
|
@type how: int
|
|
|
|
"""
|
2003-11-04 03:34:24 -05:00
|
|
|
if (how == 0) or (how == 2):
|
|
|
|
# feign "read" shutdown
|
|
|
|
self.eof_received = 1
|
|
|
|
if (how == 1) or (how == 2):
|
2003-12-30 02:18:20 -05:00
|
|
|
self._send_eof()
|
|
|
|
|
|
|
|
|
|
|
|
### calls from Transport
|
|
|
|
|
|
|
|
|
|
|
|
def _set_transport(self, transport):
|
|
|
|
self.transport = transport
|
|
|
|
|
|
|
|
def _set_window(self, window_size, max_packet_size):
|
|
|
|
self.in_window_size = window_size
|
|
|
|
self.in_max_packet_size = max_packet_size
|
|
|
|
# threshold of bytes we receive before we bother to send a window update
|
|
|
|
self.in_window_threshold = window_size // 10
|
|
|
|
self.in_window_sofar = 0
|
2004-06-10 13:55:17 -04:00
|
|
|
self._log(DEBUG, 'Max packet in: %d bytes' % max_packet_size)
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
def _set_remote_channel(self, chanid, window_size, max_packet_size):
|
|
|
|
self.remote_chanid = chanid
|
|
|
|
self.out_window_size = window_size
|
2004-06-10 13:55:17 -04:00
|
|
|
self.out_max_packet_size = max(max_packet_size, self.MIN_PACKET_SIZE)
|
2003-12-30 02:18:20 -05:00
|
|
|
self.active = 1
|
2004-06-10 13:55:17 -04:00
|
|
|
self._log(DEBUG, 'Max packet out: %d bytes' % max_packet_size)
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
def _request_success(self, m):
|
|
|
|
self._log(DEBUG, 'Sesch channel %d request ok' % self.chanid)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.event.set()
|
2003-12-30 02:18:20 -05:00
|
|
|
return
|
|
|
|
|
|
|
|
def _request_failed(self, m):
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
def _feed(self, m):
|
|
|
|
s = m.get_string()
|
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.ultra_debug:
|
|
|
|
self._log(DEBUG, 'fed %d bytes' % len(s))
|
2003-12-30 02:18:20 -05:00
|
|
|
if self.pipe_wfd != None:
|
|
|
|
self._feed_pipe(s)
|
|
|
|
else:
|
|
|
|
self.in_buffer += s
|
|
|
|
self.in_buffer_cv.notifyAll()
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.ultra_debug:
|
|
|
|
self._log(DEBUG, '(out from feed)')
|
2003-12-30 02:18:20 -05:00
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
|
|
|
|
def _window_adjust(self, m):
|
|
|
|
nbytes = m.get_int()
|
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.ultra_debug or True:
|
|
|
|
self._log(DEBUG, 'window up %d' % nbytes)
|
2003-12-30 02:18:20 -05:00
|
|
|
self.out_window_size += nbytes
|
|
|
|
self.out_buffer_cv.notifyAll()
|
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
|
|
|
|
def _handle_request(self, m):
|
|
|
|
key = m.get_string()
|
|
|
|
want_reply = m.get_boolean()
|
2004-09-03 18:39:20 -04:00
|
|
|
server = self.transport.server_object
|
2003-12-30 02:18:20 -05:00
|
|
|
ok = False
|
|
|
|
if key == 'exit-status':
|
|
|
|
self.exit_status = m.get_int()
|
|
|
|
ok = True
|
|
|
|
elif key == 'xon-xoff':
|
|
|
|
# ignore
|
|
|
|
ok = True
|
|
|
|
elif key == 'pty-req':
|
|
|
|
term = m.get_string()
|
|
|
|
width = m.get_int()
|
|
|
|
height = m.get_int()
|
|
|
|
pixelwidth = m.get_int()
|
|
|
|
pixelheight = m.get_int()
|
|
|
|
modes = m.get_string()
|
2004-09-03 18:39:20 -04:00
|
|
|
if server is None:
|
|
|
|
ok = False
|
|
|
|
else:
|
|
|
|
ok = server.check_channel_pty_request(self, term, width, height, pixelwidth,
|
|
|
|
pixelheight, modes)
|
2003-12-30 02:18:20 -05:00
|
|
|
elif key == 'shell':
|
2004-09-03 18:39:20 -04:00
|
|
|
if server is None:
|
|
|
|
ok = False
|
|
|
|
else:
|
|
|
|
ok = server.check_channel_shell_request(self)
|
2003-12-30 02:18:20 -05:00
|
|
|
elif key == 'subsystem':
|
|
|
|
name = m.get_string()
|
2004-09-03 18:39:20 -04:00
|
|
|
if server is None:
|
|
|
|
ok = False
|
|
|
|
else:
|
|
|
|
ok = server.check_channel_subsystem_request(self, name)
|
2003-12-30 02:18:20 -05:00
|
|
|
elif key == 'window-change':
|
|
|
|
width = m.get_int()
|
|
|
|
height = m.get_int()
|
|
|
|
pixelwidth = m.get_int()
|
|
|
|
pixelheight = m.get_int()
|
2004-09-03 18:39:20 -04:00
|
|
|
if server is None:
|
|
|
|
ok = False
|
|
|
|
else:
|
|
|
|
ok = server.check_channel_window_change_request(self, width, height, pixelwidth,
|
|
|
|
pixelheight)
|
2003-12-30 02:18:20 -05:00
|
|
|
else:
|
|
|
|
self._log(DEBUG, 'Unhandled channel request "%s"' % key)
|
|
|
|
ok = False
|
|
|
|
if want_reply:
|
|
|
|
m = Message()
|
|
|
|
if ok:
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_SUCCESS))
|
2003-12-30 02:18:20 -05:00
|
|
|
else:
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_FAILURE))
|
2003-12-30 02:18:20 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.transport._send_user_message(m)
|
2003-12-30 02:18:20 -05:00
|
|
|
|
|
|
|
def _handle_eof(self, m):
|
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
|
|
|
if not self.eof_received:
|
|
|
|
self.eof_received = 1
|
|
|
|
self.in_buffer_cv.notifyAll()
|
|
|
|
if self.pipe_wfd != None:
|
|
|
|
os.close(self.pipe_wfd)
|
|
|
|
self.pipe_wfd = None
|
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
self._log(DEBUG, 'EOF received')
|
|
|
|
|
|
|
|
def _handle_close(self, m):
|
|
|
|
self.close()
|
2004-10-31 22:54:01 -05:00
|
|
|
self.lock.acquire()
|
2003-12-30 02:18:20 -05:00
|
|
|
try:
|
2004-10-31 22:54:01 -05:00
|
|
|
self.transport._unlink_channel(self.chanid)
|
2003-12-30 02:18:20 -05:00
|
|
|
if self.pipe_wfd != None:
|
|
|
|
os.close(self.pipe_wfd)
|
|
|
|
self.pipe_wfd = None
|
|
|
|
finally:
|
|
|
|
self.lock.release()
|
|
|
|
|
|
|
|
|
|
|
|
### internals...
|
|
|
|
|
|
|
|
|
|
|
|
def _log(self, level, msg):
|
|
|
|
self.logger.log(level, msg)
|
|
|
|
|
2004-04-07 02:07:29 -04:00
|
|
|
def _set_closed(self):
|
2004-05-17 03:41:50 -04:00
|
|
|
# you are holding the lock.
|
2004-04-07 02:07:29 -04:00
|
|
|
self.closed = True
|
2004-05-17 03:41:50 -04:00
|
|
|
self.in_buffer_cv.notifyAll()
|
|
|
|
self.out_buffer_cv.notifyAll()
|
2004-04-07 02:07:29 -04:00
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
def _send_eof(self):
|
|
|
|
if self.eof_sent:
|
|
|
|
return
|
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_EOF))
|
2003-12-30 02:18:20 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.transport._send_user_message(m)
|
2003-12-30 02:18:20 -05:00
|
|
|
self.eof_sent = 1
|
|
|
|
self._log(DEBUG, 'EOF sent')
|
|
|
|
return
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
def _feed_pipe(self, data):
|
2003-11-04 03:34:24 -05:00
|
|
|
"you are already holding the lock"
|
|
|
|
if len(self.in_buffer) > 0:
|
|
|
|
self.in_buffer += data
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
n = os.write(self.pipe_wfd, data)
|
|
|
|
if n < len(data):
|
|
|
|
# at least on linux, this will never happen, as the writes are
|
|
|
|
# considered atomic... but just in case.
|
|
|
|
self.in_buffer = data[n:]
|
2003-12-30 02:18:20 -05:00
|
|
|
self._check_add_window(n)
|
2003-11-04 03:34:24 -05:00
|
|
|
self.in_buffer_cv.notifyAll()
|
|
|
|
return
|
|
|
|
except OSError, e:
|
|
|
|
pass
|
|
|
|
if len(data) > 1:
|
|
|
|
# try writing just one byte then
|
|
|
|
x = data[0]
|
|
|
|
data = data[1:]
|
|
|
|
try:
|
|
|
|
os.write(self.pipe_wfd, x)
|
|
|
|
self.in_buffer = data
|
2003-12-30 02:18:20 -05:00
|
|
|
self._check_add_window(1)
|
2003-11-04 03:34:24 -05:00
|
|
|
self.in_buffer_cv.notifyAll()
|
|
|
|
return
|
|
|
|
except OSError, e:
|
|
|
|
data = x + data
|
|
|
|
# pipe is very full
|
|
|
|
self.in_buffer = data
|
|
|
|
self.in_buffer_cv.notifyAll()
|
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
def _read_pipe(self, nbytes):
|
2003-11-04 03:34:24 -05:00
|
|
|
"you are already holding the lock"
|
|
|
|
try:
|
|
|
|
x = os.read(self.pipe_rfd, nbytes)
|
|
|
|
if len(x) > 0:
|
2003-12-30 02:18:20 -05:00
|
|
|
self._push_pipe(len(x))
|
2003-11-04 03:34:24 -05:00
|
|
|
return x
|
|
|
|
except OSError, e:
|
|
|
|
pass
|
|
|
|
# nothing in the pipe
|
|
|
|
if self.closed or self.eof_received:
|
|
|
|
return ''
|
|
|
|
# should we block?
|
|
|
|
if self.timeout == 0.0:
|
|
|
|
raise socket.timeout()
|
|
|
|
# loop here in case we get woken up but a different thread has grabbed everything in the buffer
|
|
|
|
timeout = self.timeout
|
|
|
|
while not self.closed and not self.eof_received:
|
|
|
|
then = time.time()
|
|
|
|
self.in_buffer_cv.wait(timeout)
|
|
|
|
if timeout != None:
|
|
|
|
timeout -= time.time() - then
|
|
|
|
if timeout <= 0.0:
|
|
|
|
raise socket.timeout()
|
|
|
|
try:
|
|
|
|
x = os.read(self.pipe_rfd, nbytes)
|
|
|
|
if len(x) > 0:
|
2003-12-30 02:18:20 -05:00
|
|
|
self._push_pipe(len(x))
|
2003-11-04 03:34:24 -05:00
|
|
|
return x
|
|
|
|
except OSError, e:
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
def _push_pipe(self, nbytes):
|
2003-11-04 03:34:24 -05:00
|
|
|
# successfully read N bytes from the pipe, now re-feed the pipe if necessary
|
|
|
|
# (assumption: the pipe can hold as many bytes as were read out)
|
|
|
|
if len(self.in_buffer) == 0:
|
|
|
|
return
|
|
|
|
if len(self.in_buffer) <= nbytes:
|
|
|
|
os.write(self.pipe_wfd, self.in_buffer)
|
|
|
|
self.in_buffer = ''
|
|
|
|
return
|
|
|
|
x = self.in_buffer[:nbytes]
|
|
|
|
self.in_buffer = self.in_buffer[nbytes:]
|
2004-11-22 02:40:39 -05:00
|
|
|
os.write(self.pipe_wfd, x)
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2003-12-27 22:20:42 -05:00
|
|
|
def _unlink(self):
|
2003-11-04 03:34:24 -05:00
|
|
|
if self.closed or not self.active:
|
|
|
|
return
|
2004-05-17 03:41:50 -04:00
|
|
|
try:
|
|
|
|
self.lock.acquire()
|
|
|
|
self._set_closed()
|
|
|
|
self.transport._unlink_channel(self.chanid)
|
|
|
|
finally:
|
|
|
|
self.lock.release()
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2003-12-30 02:18:20 -05:00
|
|
|
def _check_add_window(self, n):
|
2003-11-04 03:34:24 -05:00
|
|
|
# already holding the lock!
|
|
|
|
if self.closed or self.eof_received or not self.active:
|
|
|
|
return
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.ultra_debug:
|
|
|
|
self._log(DEBUG, 'addwindow %d' % n)
|
2003-11-04 03:34:24 -05:00
|
|
|
self.in_window_sofar += n
|
|
|
|
if self.in_window_sofar > self.in_window_threshold:
|
2004-06-10 13:55:17 -04:00
|
|
|
if self.ultra_debug:
|
|
|
|
self._log(DEBUG, 'addwindow send %d' % self.in_window_sofar)
|
2003-11-04 03:34:24 -05:00
|
|
|
m = Message()
|
2004-04-05 06:12:59 -04:00
|
|
|
m.add_byte(chr(MSG_CHANNEL_WINDOW_ADJUST))
|
2003-11-09 16:14:21 -05:00
|
|
|
m.add_int(self.remote_chanid)
|
2003-11-04 03:34:24 -05:00
|
|
|
m.add_int(self.in_window_sofar)
|
2004-08-28 00:21:12 -04:00
|
|
|
self.transport._send_user_message(m)
|
2003-11-04 03:34:24 -05:00
|
|
|
self.in_window_sofar = 0
|
|
|
|
|
|
|
|
|
2004-03-04 03:21:45 -05:00
|
|
|
class ChannelFile (BufferedFile):
|
2003-11-04 03:34:24 -05:00
|
|
|
"""
|
2004-01-04 04:29:13 -05:00
|
|
|
A file-like wrapper around L{Channel}. A ChannelFile is created by calling
|
|
|
|
L{Channel.makefile} and doesn't have the non-portable side effect of
|
|
|
|
L{Channel.fileno}.
|
|
|
|
|
|
|
|
@bug: To correctly emulate the file object created from a socket's
|
|
|
|
C{makefile} method, a L{Channel} and its C{ChannelFile} should be able to
|
|
|
|
be closed or garbage-collected independently. Currently, closing the
|
|
|
|
C{ChannelFile} does nothing but flush the buffer.
|
2003-11-04 03:34:24 -05:00
|
|
|
"""
|
|
|
|
|
2004-03-04 03:21:45 -05:00
|
|
|
def __init__(self, channel, mode = 'r', bufsize = -1):
|
2003-11-04 03:34:24 -05:00
|
|
|
self.channel = channel
|
2004-03-04 03:21:45 -05:00
|
|
|
BufferedFile.__init__(self)
|
|
|
|
self._set_mode(mode, bufsize)
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2003-11-09 23:54:02 -05:00
|
|
|
def __repr__(self):
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
|
|
|
Returns a string representation of this object, for debugging.
|
|
|
|
|
2004-09-03 18:39:20 -04:00
|
|
|
@rtype: str
|
2004-01-04 04:29:13 -05:00
|
|
|
"""
|
2003-11-10 03:49:50 -05:00
|
|
|
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
2003-11-09 23:54:02 -05:00
|
|
|
|
2004-03-04 03:21:45 -05:00
|
|
|
def _read(self, size):
|
|
|
|
return self.channel.recv(size)
|
2003-11-04 03:34:24 -05:00
|
|
|
|
2004-03-04 03:21:45 -05:00
|
|
|
def _write(self, data):
|
|
|
|
self.channel.sendall(data)
|
|
|
|
return len(data)
|
2003-11-04 03:34:24 -05:00
|
|
|
|
|
|
|
|
|
|
|
# vim: set shiftwidth=4 expandtab :
|