diff options
author | Zac Medico <zmedico@gentoo.org> | 2023-12-04 20:23:56 -0800 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2023-12-06 08:22:56 -0800 |
commit | df212738bbb209356472911cda79902f0e25918e (patch) | |
tree | 28c3295ffcd510cc7930aee7670c36ef0af1c132 | |
parent | circular_dependency: Handle SonameAtom (diff) | |
download | portage-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.py | 34 |
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( |