[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:
parent
f7b0a62e4b
commit
c7d56a309d
1
README
1
README
|
@ -221,5 +221,4 @@ v0.9 FEAROW
|
||||||
|
|
||||||
* ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr)
|
* ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr)
|
||||||
* server mode needs better documentation
|
* 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()
|
* the error message from this is confusing as hell: DSSKey()
|
||||||
|
|
|
@ -80,11 +80,13 @@ class Channel (object):
|
||||||
self.in_buffer_cv = threading.Condition(self.lock)
|
self.in_buffer_cv = threading.Condition(self.lock)
|
||||||
self.in_stderr_buffer_cv = threading.Condition(self.lock)
|
self.in_stderr_buffer_cv = threading.Condition(self.lock)
|
||||||
self.out_buffer_cv = threading.Condition(self.lock)
|
self.out_buffer_cv = threading.Condition(self.lock)
|
||||||
|
self.status_event = threading.Event()
|
||||||
self.name = str(chanid)
|
self.name = str(chanid)
|
||||||
self.logger = logging.getLogger('paramiko.chan.' + str(chanid))
|
self.logger = logging.getLogger('paramiko.chan.' + str(chanid))
|
||||||
self.pipe_rfd = self.pipe_wfd = None
|
self.pipe_rfd = self.pipe_wfd = None
|
||||||
self.event = threading.Event()
|
self.event = threading.Event()
|
||||||
self.combine_stderr = False
|
self.combine_stderr = False
|
||||||
|
self.exit_status = -1
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -249,19 +251,60 @@ class Channel (object):
|
||||||
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
m.add_string('window-change')
|
m.add_string('window-change')
|
||||||
m.add_boolean(0)
|
m.add_boolean(1)
|
||||||
m.add_int(width)
|
m.add_int(width)
|
||||||
m.add_int(height)
|
m.add_int(height)
|
||||||
m.add_int(0).add_int(0)
|
m.add_int(0).add_int(0)
|
||||||
self.event.clear()
|
self.event.clear()
|
||||||
self.transport._send_user_message(m)
|
self.transport._send_user_message(m)
|
||||||
while 1:
|
while True:
|
||||||
self.event.wait(0.1)
|
self.event.wait(0.1)
|
||||||
if self.closed:
|
if self.closed:
|
||||||
return False
|
return False
|
||||||
if self.event.isSet():
|
if self.event.isSet():
|
||||||
return True
|
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):
|
def get_transport(self):
|
||||||
"""
|
"""
|
||||||
Return the L{Transport} associated with this channel.
|
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.
|
disallowed. This closes the stream in one or both directions.
|
||||||
|
|
||||||
@param how: 0 (stop receiving), 1 (stop sending), or 2 (stop
|
@param how: 0 (stop receiving), 1 (stop sending), or 2 (stop
|
||||||
receiving and sending).
|
receiving and sending).
|
||||||
@type how: int
|
@type how: int
|
||||||
"""
|
"""
|
||||||
if (how == 0) or (how == 2):
|
if (how == 0) or (how == 2):
|
||||||
|
@ -751,6 +794,30 @@ class Channel (object):
|
||||||
self.eof_received = 1
|
self.eof_received = 1
|
||||||
if (how == 1) or (how == 2):
|
if (how == 1) or (how == 2):
|
||||||
self._send_eof()
|
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
|
### calls from Transport
|
||||||
|
@ -820,8 +887,8 @@ class Channel (object):
|
||||||
|
|
||||||
def _window_adjust(self, m):
|
def _window_adjust(self, m):
|
||||||
nbytes = m.get_int()
|
nbytes = m.get_int()
|
||||||
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
|
||||||
if self.ultra_debug:
|
if self.ultra_debug:
|
||||||
self._log(DEBUG, 'window up %d' % nbytes)
|
self._log(DEBUG, 'window up %d' % nbytes)
|
||||||
self.out_window_size += nbytes
|
self.out_window_size += nbytes
|
||||||
|
@ -836,6 +903,7 @@ class Channel (object):
|
||||||
ok = False
|
ok = False
|
||||||
if key == 'exit-status':
|
if key == 'exit-status':
|
||||||
self.exit_status = m.get_int()
|
self.exit_status = m.get_int()
|
||||||
|
self.status_event.set()
|
||||||
ok = True
|
ok = True
|
||||||
elif key == 'xon-xoff':
|
elif key == 'xon-xoff':
|
||||||
# ignore
|
# ignore
|
||||||
|
@ -1130,5 +1198,4 @@ class ChannelStderrFile (ChannelFile):
|
||||||
return len(data)
|
return len(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# vim: set shiftwidth=4 expandtab :
|
# vim: set shiftwidth=4 expandtab :
|
||||||
|
|
|
@ -951,7 +951,7 @@ class BaseTransport (threading.Thread):
|
||||||
send a message, but block if we're in key negotiation. this is used
|
send a message, but block if we're in key negotiation. this is used
|
||||||
for user-initiated requests.
|
for user-initiated requests.
|
||||||
"""
|
"""
|
||||||
while 1:
|
while True:
|
||||||
self.clear_to_send.wait(0.1)
|
self.clear_to_send.wait(0.1)
|
||||||
if not self.active:
|
if not self.active:
|
||||||
self._log(DEBUG, 'Dropping user packet because connection is dead.')
|
self._log(DEBUG, 'Dropping user packet because connection is dead.')
|
||||||
|
|
Loading…
Reference in New Issue