summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2023-12-04 20:23:56 -0800
committerZac Medico <zmedico@gentoo.org>2023-12-06 08:22:56 -0800
commitdf212738bbb209356472911cda79902f0e25918e (patch)
tree28c3295ffcd510cc7930aee7670c36ef0af1c132
parentcircular_dependency: Handle SonameAtom (diff)
downloadportage-df212738bbb209356472911cda79902f0e25918e.tar.gz
portage-df212738bbb209356472911cda79902f0e25918e.tar.bz2
portage-df212738bbb209356472911cda79902f0e25918e.zip
BuildLogger: Close self._stdin after fork
In order to ensure that we can observe EOF on the read end of the pipe, close self._stdin after fork. Since portage.locks._close_fds() already does something similar for _lock_manager instances which have a close() method, it will also work with these _file_close_wrapper objects. The portage.locks._close_fds() function calls close after fork, in the ForkProcess._bootstrap method. For more general fork coverage, we could move the _close_fds() call to the _ForkWatcher.hook method in portage/__init__.py, but I've reserved that for a later change since _close_fds() has been working fine for us where we call it now. Bug: https://bugs.gentoo.org/919072 Signed-off-by: Zac Medico <zmedico@gentoo.org>
-rw-r--r--lib/portage/util/_async/BuildLogger.py34
1 files changed, 31 insertions, 3 deletions
diff --git a/lib/portage/util/_async/BuildLogger.py b/lib/portage/util/_async/BuildLogger.py
index 502b3390e..9f8a21ab2 100644
--- a/lib/portage/util/_async/BuildLogger.py
+++ b/lib/portage/util/_async/BuildLogger.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2021 Gentoo Authors
+# Copyright 2020-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import functools
@@ -6,13 +6,41 @@ import subprocess
from _emerge.AsynchronousTask import AsynchronousTask
+import portage
from portage import os
+from portage.proxy.objectproxy import ObjectProxy
from portage.util import shlex_split
from portage.util._async.PipeLogger import PipeLogger
from portage.util._async.PopenProcess import PopenProcess
from portage.util.futures import asyncio
+class _file_close_wrapper(ObjectProxy):
+ """
+ Prevent fd inheritance via fork, ensuring that we can observe
+ EOF on the read end of the pipe (bug 919072).
+ """
+
+ __slots__ = ("_file",)
+
+ def __init__(self, file):
+ ObjectProxy.__init__(self)
+ object.__setattr__(self, "_file", file)
+ portage.locks._open_fds[file.fileno()] = self
+
+ def _get_target(self):
+ return object.__getattribute__(self, "_file")
+
+ def close(self):
+ file = object.__getattribute__(self, "_file")
+ if not file.closed:
+ # This must only be called if the file is open,
+ # which ensures that file.fileno() does not
+ # collide with an open lock file descriptor.
+ del portage.locks._open_fds[file.fileno()]
+ file.close()
+
+
class BuildLogger(AsynchronousTask):
"""
Write to a log file, with compression support provided by PipeLogger.
@@ -67,7 +95,7 @@ class BuildLogger(AsynchronousTask):
os.close(log_input)
os.close(filter_output)
else:
- self._stdin = os.fdopen(stdin, "wb", 0)
+ self._stdin = _file_close_wrapper(os.fdopen(stdin, "wb", 0))
os.close(filter_input)
os.close(filter_output)
@@ -76,7 +104,7 @@ class BuildLogger(AsynchronousTask):
# that is missing or broken somehow, create a pipe that
# logs directly to pipe_logger.
log_input, stdin = os.pipe()
- self._stdin = os.fdopen(stdin, "wb", 0)
+ self._stdin = _file_close_wrapper(os.fdopen(stdin, "wb", 0))
# Set background=True so that pipe_logger does not log to stdout.
pipe_logger = PipeLogger(