summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobey Pointer <robey@lag.net>2005-02-15 15:47:02 +0000
committerRobey Pointer <robey@lag.net>2005-02-15 15:47:02 +0000
commitc7d56a309d8a1e0b9999cc39e24697f3d45eedcf (patch)
treee34696fecffa2c371161bb2c2096fbe595321639
parentf7b0a62e4ba7a2ad0ba3c92aae8ce79ee539f6cf (diff)
[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.
-rw-r--r--README1
-rw-r--r--paramiko/channel.py77
-rw-r--r--paramiko/transport.py2
3 files changed, 73 insertions, 7 deletions
diff --git a/README b/README
index 36618a25..d522dc6f 100644
--- a/README
+++ b/README
@@ -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()
diff --git a/paramiko/channel.py b/paramiko/channel.py
index 978022d8..a09ddf72 100644
--- a/paramiko/channel.py
+++ b/paramiko/channel.py
@@ -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 :
diff --git a/paramiko/transport.py b/paramiko/transport.py
index 25a82dd8..17ac5450 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -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.')