Merge branch '1.12'

Conflicts:
	sites/www/changelog.rst
This commit is contained in:
Jeff Forcier 2014-02-14 09:17:04 -08:00
commit 73382933a7
3 changed files with 38 additions and 11 deletions

View File

@ -20,10 +20,13 @@
L{ProxyCommand}. L{ProxyCommand}.
""" """
from datetime import datetime
import os import os
from shlex import split as shlsplit from shlex import split as shlsplit
import signal import signal
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from select import select
import socket
from paramiko.ssh_exception import ProxyCommandFailure from paramiko.ssh_exception import ProxyCommandFailure
@ -48,6 +51,8 @@ class ProxyCommand(object):
""" """
self.cmd = shlsplit(command_line) self.cmd = shlsplit(command_line)
self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
self.timeout = None
self.buffer = []
def send(self, content): def send(self, content):
""" """
@ -64,7 +69,7 @@ class ProxyCommand(object):
# died and we can't proceed. The best option here is to # died and we can't proceed. The best option here is to
# raise an exception informing the user that the informed # raise an exception informing the user that the informed
# ProxyCommand is not working. # ProxyCommand is not working.
raise BadProxyCommand(' '.join(self.cmd), e.strerror) raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
return len(content) return len(content)
def recv(self, size): def recv(self, size):
@ -78,14 +83,30 @@ class ProxyCommand(object):
@rtype: int @rtype: int
""" """
try: try:
return os.read(self.process.stdout.fileno(), size) start = datetime.now()
while len(self.buffer) < size:
if self.timeout is not None:
elapsed = (datetime.now() - start).microseconds
timeout = self.timeout * 1000 * 1000 # to microseconds
if elapsed >= timeout:
raise socket.timeout()
r, w, x = select([self.process.stdout], [], [], 0.0)
if r and r[0] == self.process.stdout:
b = os.read(self.process.stdout.fileno(), 1)
# Store in class-level buffer for persistence across
# timeouts; this makes us act more like a real socket
# (where timeouts don't actually drop data.)
self.buffer.append(b)
result = ''.join(self.buffer)
self.buffer = []
return result
except socket.timeout:
raise # socket.timeout is a subclass of IOError
except IOError, e: except IOError, e:
raise BadProxyCommand(' '.join(self.cmd), e.strerror) raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
def close(self): def close(self):
os.kill(self.process.pid, signal.SIGTERM) os.kill(self.process.pid, signal.SIGTERM)
def settimeout(self, timeout): def settimeout(self, timeout):
# Timeouts are meaningless for this implementation, but are part of the self.timeout = timeout
# spec, so must be present.
pass

View File

@ -1553,10 +1553,6 @@ class Transport (threading.Thread):
# containers. # containers.
Random.atfork() Random.atfork()
# Hold reference to 'sys' so we can test sys.modules to detect
# interpreter shutdown.
self.sys = sys
# active=True occurs before the thread is launched, to avoid a race # active=True occurs before the thread is launched, to avoid a race
_active_threads.append(self) _active_threads.append(self)
if self.server_mode: if self.server_mode:
@ -1626,7 +1622,10 @@ class Transport (threading.Thread):
self.saved_exception = e self.saved_exception = e
except socket.error, e: except socket.error, e:
if type(e.args) is tuple: if type(e.args) is tuple:
emsg = '%s (%d)' % (e.args[1], e.args[0]) if e.args:
emsg = '%s (%d)' % (e.args[1], e.args[0])
else: # empty tuple, e.g. socket.timeout
emsg = str(e) or repr(e)
else: else:
emsg = e.args emsg = e.args
self._log(ERROR, 'Socket exception: ' + emsg) self._log(ERROR, 'Socket exception: ' + emsg)

View File

@ -4,6 +4,13 @@ Changelog
* :feature:`58` Allow client code to access the stored SSH server banner via * :feature:`58` Allow client code to access the stored SSH server banner via
``Transport.get_banner()``. Thanks to ``@Jhoanor`` for the patch. ``Transport.get_banner()``. Thanks to ``@Jhoanor`` for the patch.
* :bug:`252` (`Fabric #1020 <https://github.com/fabric/fabric/issues/1020>`_)
Enhanced the implementation of ``ProxyCommand`` to avoid a deadlock/hang
condition that frequently occurs at ``Transport`` shutdown time. Thanks to
Mateusz Kobos, Matthijs van der Vleuten and Guillaume Zitta for the original
reports and to Marius Gedminas for helping test nontrivial use cases.
* :bug:`268` Fix some missed renames of ``ProxyCommand`` related error classes.
Thanks to Marius Gedminas for catch & patch.
* :bug:`34` (PR :issue:`35`) Fix SFTP prefetching incompatibility with some * :bug:`34` (PR :issue:`35`) Fix SFTP prefetching incompatibility with some
SFTP servers regarding request/response ordering. Thanks to Richard SFTP servers regarding request/response ordering. Thanks to Richard
Kettlewell for catch & patch. Kettlewell for catch & patch.