[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-145]

add methods for sending/receiving a channel's exit status
track a channel's exit status and provide a method (recv_exit_status) to
block waiting for it to arrive.  also provide a convenience method for
servers to send it (send_exit_status).  add shutdown_read and shutdown_write.
fix a bug in sending window change requests.
This commit is contained in:
Robey Pointer 2005-02-15 15:47:02 +00:00
parent f7b0a62e4b
commit c7d56a309d
3 changed files with 73 additions and 7 deletions

1
README
View File

@ -221,5 +221,4 @@ v0.9 FEAROW
* ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr)
* server mode needs better documentation
* add method to block until a channel's "exit-status" is set
* the error message from this is confusing as hell: DSSKey()

View File

@ -80,11 +80,13 @@ class Channel (object):
self.in_buffer_cv = threading.Condition(self.lock)
self.in_stderr_buffer_cv = threading.Condition(self.lock)
self.out_buffer_cv = threading.Condition(self.lock)
self.status_event = threading.Event()
self.name = str(chanid)
self.logger = logging.getLogger('paramiko.chan.' + str(chanid))
self.pipe_rfd = self.pipe_wfd = None
self.event = threading.Event()
self.combine_stderr = False
self.exit_status = -1
def __repr__(self):
"""
@ -249,19 +251,60 @@ class Channel (object):
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
m.add_string('window-change')
m.add_boolean(0)
m.add_boolean(1)
m.add_int(width)
m.add_int(height)
m.add_int(0).add_int(0)
self.event.clear()
self.transport._send_user_message(m)
while 1:
while True:
self.event.wait(0.1)
if self.closed:
return False
if self.event.isSet():
return True
def recv_exit_status(self):
"""
Return the exit status from the process on the server. This is
mostly useful for retrieving the reults of an L{exec_command}.
If the command hasn't finished yet, this method will wait until
it does, or until the channel is closed. If no exit status is
provided by the server, -1 is returned.
@return: the exit code of the process on the server.
@rtype: int
@since: 1.2
"""
while True:
if self.closed or self.status_event.isSet():
return self.exit_status
self.status_event.wait(0.1)
def send_exit_status(self, status):
"""
Send the exit status of an executed command to the client. (This
really only makes sense in server mode.) Many clients expect to
get some sort of status code back from an executed command after
it completes.
@param status: the exit code of the process
@type status: int
@since: 1.2
"""
# in many cases, the channel will not still be open here.
# that's fine.
m = Message()
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
m.add_string('exit-status')
m.add_boolean(0)
m.add_int(status)
self.transport._send_user_message(m)
self._log(DEBUG, 'EXIT-STATUS')
def get_transport(self):
"""
Return the L{Transport} associated with this channel.
@ -743,7 +786,7 @@ class Channel (object):
disallowed. This closes the stream in one or both directions.
@param how: 0 (stop receiving), 1 (stop sending), or 2 (stop
receiving and sending).
receiving and sending).
@type how: int
"""
if (how == 0) or (how == 2):
@ -751,6 +794,30 @@ class Channel (object):
self.eof_received = 1
if (how == 1) or (how == 2):
self._send_eof()
def shutdown_read(self):
"""
Shutdown the receiving side of this socket, closing the stream in
the incoming direction. After this call, future reads on this
channel will fail instantly. This is a convenience method, equivalent
to C{shutdown(0)}, for people who don't make it a habit to
memorize unix constants from the 1970s.
@since: 1.2
"""
self.shutdown(0)
def shutdown_write(self):
"""
Shutdown the sending side of this socket, closing the stream in
the outgoing direction. After this call, future writes on this
channel will fail instantly. This is a convenience method, equivalent
to C{shutdown(1)}, for people who don't make it a habit to
memorize unix constants from the 1970s.
@since: 1.2
"""
self.shutdown(1)
### calls from Transport
@ -820,8 +887,8 @@ class Channel (object):
def _window_adjust(self, m):
nbytes = m.get_int()
self.lock.acquire()
try:
self.lock.acquire()
if self.ultra_debug:
self._log(DEBUG, 'window up %d' % nbytes)
self.out_window_size += nbytes
@ -836,6 +903,7 @@ class Channel (object):
ok = False
if key == 'exit-status':
self.exit_status = m.get_int()
self.status_event.set()
ok = True
elif key == 'xon-xoff':
# ignore
@ -1130,5 +1198,4 @@ class ChannelStderrFile (ChannelFile):
return len(data)
# vim: set shiftwidth=4 expandtab :

View File

@ -951,7 +951,7 @@ class BaseTransport (threading.Thread):
send a message, but block if we're in key negotiation. this is used
for user-initiated requests.
"""
while 1:
while True:
self.clear_to_send.wait(0.1)
if not self.active:
self._log(DEBUG, 'Dropping user packet because connection is dead.')