iotests: Explicitly bequeath FDs in Python

Python 3.4 introduced the inheritable attribute for FDs.  At the same
time, it changed the default so that all FDs are not inheritable by
default, that only inheritable FDs are inherited to subprocesses, and
only if close_fds is explicitly set to False.

Adhere to this by setting close_fds to False when working with
subprocesses that may want to inherit FDs, and by trying to
set_inheritable() on FDs that we do want to bequeath to them.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20181022135307.14398-7-mreitz@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
This commit is contained in:
Max Reitz 2018-10-22 14:53:04 +01:00 committed by Eduardo Habkost
parent 68474776f3
commit bf43b29df4
3 changed files with 31 additions and 7 deletions

View File

@ -148,11 +148,19 @@ class QEMUMachine(object):
if opts: if opts:
options.append(opts) options.append(opts)
# This did not exist before 3.4, but since then it is
# mandatory for our purpose
if hasattr(os, 'set_inheritable'):
os.set_inheritable(fd, True)
self._args.append('-add-fd') self._args.append('-add-fd')
self._args.append(','.join(options)) self._args.append(','.join(options))
return self return self
def send_fd_scm(self, fd_file_path): # Exactly one of fd and file_path must be given.
# (If it is file_path, the helper will open that file and pass its
# own fd)
def send_fd_scm(self, fd=None, file_path=None):
# In iotest.py, the qmp should always use unix socket. # In iotest.py, the qmp should always use unix socket.
assert self._qmp.is_scm_available() assert self._qmp.is_scm_available()
if self._socket_scm_helper is None: if self._socket_scm_helper is None:
@ -160,12 +168,27 @@ class QEMUMachine(object):
if not os.path.exists(self._socket_scm_helper): if not os.path.exists(self._socket_scm_helper):
raise QEMUMachineError("%s does not exist" % raise QEMUMachineError("%s does not exist" %
self._socket_scm_helper) self._socket_scm_helper)
# This did not exist before 3.4, but since then it is
# mandatory for our purpose
if hasattr(os, 'set_inheritable'):
os.set_inheritable(self._qmp.get_sock_fd(), True)
if fd is not None:
os.set_inheritable(fd, True)
fd_param = ["%s" % self._socket_scm_helper, fd_param = ["%s" % self._socket_scm_helper,
"%d" % self._qmp.get_sock_fd(), "%d" % self._qmp.get_sock_fd()]
"%s" % fd_file_path]
if file_path is not None:
assert fd is None
fd_param.append(file_path)
else:
assert fd is not None
fd_param.append(str(fd))
devnull = open(os.path.devnull, 'rb') devnull = open(os.path.devnull, 'rb')
proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT, close_fds=False)
output = proc.communicate()[0] output = proc.communicate()[0]
if output: if output:
LOG.debug(output) LOG.debug(output)
@ -286,7 +309,8 @@ class QEMUMachine(object):
stdin=devnull, stdin=devnull,
stdout=self._qemu_log_file, stdout=self._qemu_log_file,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
shell=False) shell=False,
close_fds=False)
self._post_launch() self._post_launch()
def wait(self): def wait(self):

View File

@ -140,7 +140,7 @@ class TestSCMFd(iotests.QMPTestCase):
os.remove(image0) os.remove(image0)
def _send_fd_by_SCM(self): def _send_fd_by_SCM(self):
ret = self.vm.send_fd_scm(image0) ret = self.vm.send_fd_scm(file_path=image0)
self.assertEqual(ret, 0, 'Failed to send fd with UNIX SCM') self.assertEqual(ret, 0, 'Failed to send fd with UNIX SCM')
def test_add_fd(self): def test_add_fd(self):

View File

@ -229,7 +229,7 @@ class BuiltinNBD(NBDBlockdevAddBase):
sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sockfd.connect(unix_socket) sockfd.connect(unix_socket)
result = self.vm.send_fd_scm(str(sockfd.fileno())) result = self.vm.send_fd_scm(fd=sockfd.fileno())
self.assertEqual(result, 0, 'Failed to send socket FD') self.assertEqual(result, 0, 'Failed to send socket FD')
result = self.vm.qmp('getfd', fdname='nbd-fifo') result = self.vm.qmp('getfd', fdname='nbd-fifo')