Skip to content

Commit

Permalink
[stable-2.6] Fix the local and ssh plugins for a cornercase retrying …
Browse files Browse the repository at this point in the history
…a syscall

The bundled selectors library which is used by the local and ssh
connection plugins had a bug which caused a traceback in a cornercase.
If selectors were in use and a syscall was interrupted, selectors would
attempt to restart the syscall after the interrupt was processed.  if
the attempt determined that the timeout for running the syscall had
already expired, the code attempted to raise OSError.  The raise was
using a Python3-ism and needed to be ported to work on Python2.

Fixes #41630
(cherry picked from commit e2e44f8)

Co-authored-by: Toshio Kuratomi <a.badger@gmail.com>
  • Loading branch information
abadger authored and mattclay committed Aug 13, 2018
1 parent 4cbf048 commit a0061e7
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 8 deletions.
5 changes: 5 additions & 0 deletions changelogs/fragments/fix-selectors-error-condition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bugfixes:
- fix for the bundled selectors module (used in the ssh and local connection
plugins) when a syscall is restarted after being interrupted by a signal
(https://github.com/ansible/ansible/issues/41630)
8 changes: 8 additions & 0 deletions lib/ansible/compat/selectors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
'''
# The following makes it easier for us to script updates of the bundled code
_BUNDLED_METADATA = {"pypi_name": "selectors2", "version": "1.1.0"}
# Added these bugfix commits from 2.1.0:
# * https://github.com/SethMichaelLarson/selectors2/commit/3bd74f2033363b606e1e849528ccaa76f5067590
# Wrap kqueue.control so that timeout is a keyword arg
# * https://github.com/SethMichaelLarson/selectors2/commit/6f6a26f42086d8aab273b30be492beecb373646b
# Fix formatting of the kqueue.control patch for pylint
# * https://github.com/SethMichaelLarson/selectors2/commit/f0c2c6c66cfa7662bc52beaf4e2d65adfa25e189
# Fix use of OSError exception for py3 and use the wrapper of kqueue.control so retries of
# interrupted syscalls work with kqueue

import os.path
import sys
Expand Down
19 changes: 11 additions & 8 deletions lib/ansible/compat/selectors/_selectors2.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def _syscall_wrapper(func, recalc_timeout, *args, **kwargs):
if expires is not None:
current_time = monotonic()
if current_time > expires:
raise OSError(errno=errno.ETIMEDOUT)
raise OSError(errno.ETIMEDOUT)
if recalc_timeout:
if "timeout" in kwargs:
kwargs["timeout"] = expires - current_time
Expand Down Expand Up @@ -340,7 +340,7 @@ def select(self, timeout=None):
timeout = None if timeout is None else max(timeout, 0.0)
ready = []
r, w, _ = _syscall_wrapper(self._select, True, self._readers,
self._writers, timeout)
self._writers, timeout=timeout)
r = set(r)
w = set(w)
for fd in r | w:
Expand Down Expand Up @@ -561,14 +561,14 @@ def register(self, fileobj, events, data=None):
select.KQ_FILTER_READ,
select.KQ_EV_ADD)

_syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
_syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)

if events & EVENT_WRITE:
kevent = select.kevent(key.fd,
select.KQ_FILTER_WRITE,
select.KQ_EV_ADD)

_syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
_syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)

return key

Expand All @@ -579,15 +579,15 @@ def unregister(self, fileobj):
select.KQ_FILTER_READ,
select.KQ_EV_DELETE)
try:
_syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
_syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
except SelectorError:
pass
if key.events & EVENT_WRITE:
kevent = select.kevent(key.fd,
select.KQ_FILTER_WRITE,
select.KQ_EV_DELETE)
try:
_syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
_syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
except SelectorError:
pass

Expand All @@ -600,8 +600,8 @@ def select(self, timeout=None):
max_events = len(self._fd_to_key) * 2
ready_fds = {}

kevent_list = _syscall_wrapper(self._kqueue.control, True,
None, max_events, timeout)
kevent_list = _syscall_wrapper(self._wrap_control, True,
None, max_events, timeout=timeout)

for kevent in kevent_list:
fd = kevent.ident
Expand All @@ -626,6 +626,9 @@ def close(self):
self._kqueue.close()
super(KqueueSelector, self).close()

def _wrap_control(self, changelist, max_events, timeout):
return self._kqueue.control(changelist, max_events, timeout)

__all__.append('KqueueSelector')


Expand Down

0 comments on commit a0061e7

Please sign in to comment.