From: "Zac Medico" <zmedico@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/
Date: Mon, 29 Jan 2024 16:09:02 +0000 (UTC) [thread overview]
Message-ID: <1706517832.7ec7a647ef6e2d94e6f2b387d0a012e78cafbaff.zmedico@gentoo> (raw)
commit: 7ec7a647ef6e2d94e6f2b387d0a012e78cafbaff
Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Mon Jan 29 08:20:55 2024 +0000
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Mon Jan 29 08:43:52 2024 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=7ec7a647
process.spawn: add abstraction layer for os.fork()
Since os.fork() is unsafe in threaded processes, add a basic
abstraction layer that will ultimately allow support of other process
cloning implementations.
Usage of os.fork() is now wrapped in a _start_fork function which can
easily be replaced with an alternative implementation. This function
takes target, args, and kwargs arguments which are equivalent to
the corresponding multiprocessing.Process parameters. It also has
fd_pipes and close_fds parameters, since implementation of these is
dependent on the process cloning implementation.
The process cloning implementation does not need to be bothered with
the details of the _exec function, since spawn uses an _exec_wrapper
function as a target function which calls _exec and handles any
exceptions appropriately (special exception handling is required for
success of test_spawnE2big related to bug 830187).
Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>
lib/portage/process.py | 203 +++++++++++++++++++++++++++++++++----------------
1 file changed, 139 insertions(+), 64 deletions(-)
diff --git a/lib/portage/process.py b/lib/portage/process.py
index 0d58adecad..be1c5d350a 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -18,6 +18,7 @@ import os as _os
from dataclasses import dataclass
from functools import lru_cache
+from typing import Any, Optional, Callable
from portage import os
from portage import _encodings
@@ -450,68 +451,31 @@ def spawn(
# fork, so that the result is cached in the main process.
bool(groups)
- parent_pid = portage.getpid()
- pid = None
- try:
- pid = os.fork()
-
- if pid == 0:
- try:
- _exec(
- binary,
- mycommand,
- opt_name,
- fd_pipes,
- env,
- gid,
- groups,
- uid,
- umask,
- cwd,
- pre_exec,
- close_fds,
- unshare_net,
- unshare_ipc,
- unshare_mount,
- unshare_pid,
- unshare_flags,
- )
- except SystemExit:
- raise
- except Exception as e:
- if isinstance(e, OSError) and e.errno == errno.E2BIG:
- # If exec() failed with E2BIG, then this is
- # potentially because the environment variables
- # grew to large. The following will gather some
- # stats about the environment and print a
- # diagnostic message to help identifying the
- # culprit. See also
- # - https://bugs.gentoo.org/721088
- # - https://bugs.gentoo.org/830187
- if not env_stats:
- env_stats = calc_env_stats(env)
-
- writemsg(
- f"ERROR: Executing {mycommand} failed with E2BIG. Child process environment size: {env_stats.env_size} bytes. Largest environment variable: {env_stats.env_largest_name} ({env_stats.env_largest_size} bytes)\n"
- )
-
- # We need to catch _any_ exception so that it doesn't
- # propagate out of this function and cause exiting
- # with anything other than os._exit()
- writemsg(f"{e}:\n {' '.join(mycommand)}\n", noiselevel=-1)
- traceback.print_exc()
- sys.stderr.flush()
-
- finally:
- # Don't used portage.getpid() here, in case there is a race
- # with getpid cache invalidation via _ForkWatcher hook.
- if pid == 0 or (pid is None and _os.getpid() != parent_pid):
- # Call os._exit() from a finally block in order
- # to suppress any finally blocks from earlier
- # in the call stack (see bug #345289). This
- # finally block has to be setup before the fork
- # in order to avoid a race condition.
- os._exit(1)
+ pid = _start_fork(
+ _exec_wrapper,
+ args=(
+ binary,
+ mycommand,
+ opt_name,
+ fd_pipes,
+ env,
+ gid,
+ groups,
+ uid,
+ umask,
+ cwd,
+ pre_exec,
+ close_fds,
+ unshare_net,
+ unshare_ipc,
+ unshare_mount,
+ unshare_pid,
+ unshare_flags,
+ env_stats,
+ ),
+ fd_pipes=fd_pipes,
+ close_fds=close_fds,
+ )
if not isinstance(pid, int):
raise AssertionError(f"fork returned non-integer: {repr(pid)}")
@@ -633,6 +597,76 @@ def _configure_loopback_interface():
)
+def _exec_wrapper(
+ binary,
+ mycommand,
+ opt_name,
+ fd_pipes,
+ env,
+ gid,
+ groups,
+ uid,
+ umask,
+ cwd,
+ pre_exec,
+ close_fds,
+ unshare_net,
+ unshare_ipc,
+ unshare_mount,
+ unshare_pid,
+ unshare_flags,
+ env_stats,
+):
+ """
+ Calls _exec with the given args and handles any raised Exception.
+ The intention is for _exec_wrapper and _exec to be reusable with
+ other process cloning implementations besides _start_fork.
+ """
+ try:
+ _exec(
+ binary,
+ mycommand,
+ opt_name,
+ fd_pipes,
+ env,
+ gid,
+ groups,
+ uid,
+ umask,
+ cwd,
+ pre_exec,
+ close_fds,
+ unshare_net,
+ unshare_ipc,
+ unshare_mount,
+ unshare_pid,
+ unshare_flags,
+ )
+ except Exception as e:
+ if isinstance(e, OSError) and e.errno == errno.E2BIG:
+ # If exec() failed with E2BIG, then this is
+ # potentially because the environment variables
+ # grew to large. The following will gather some
+ # stats about the environment and print a
+ # diagnostic message to help identifying the
+ # culprit. See also
+ # - https://bugs.gentoo.org/721088
+ # - https://bugs.gentoo.org/830187
+ if not env_stats:
+ env_stats = calc_env_stats(env)
+
+ writemsg(
+ f"ERROR: Executing {mycommand} failed with E2BIG. Child process environment size: {env_stats.env_size} bytes. Largest environment variable: {env_stats.env_largest_name} ({env_stats.env_largest_size} bytes)\n"
+ )
+
+ # We need to catch _any_ exception so that it doesn't
+ # propagate out of this function and cause exiting
+ # with anything other than os._exit()
+ writemsg(f"{e}:\n {' '.join(mycommand)}\n", noiselevel=-1)
+ traceback.print_exc()
+ sys.stderr.flush()
+
+
def _exec(
binary,
mycommand,
@@ -733,8 +767,6 @@ def _exec(
# the parent process (see bug #289486).
signal.signal(signal.SIGQUIT, signal.SIG_DFL)
- _setup_pipes(fd_pipes, close_fds=close_fds, inheritable=True)
-
# Unshare (while still uid==0)
if unshare_net or unshare_ipc or unshare_mount or unshare_pid:
filename = find_library("c")
@@ -1114,6 +1146,49 @@ def _setup_pipes(fd_pipes, close_fds=True, inheritable=None):
pass
+def _start_fork(
+ target: Callable[..., None],
+ args: Optional[tuple[Any, ...]] = (),
+ kwargs: Optional[dict[str, Any]] = {},
+ fd_pipes: Optional[dict[int, int]] = None,
+ close_fds: Optional[bool] = True,
+) -> int:
+ """
+ Execute the target function in a fork. The fd_pipes and
+ close_fds parameters are handled in the fork, before the target
+ function is called. The args and kwargs parameters are passed
+ as positional and keyword arguments for the target function.
+
+ The target, args, and kwargs parameters are intended to
+ be equivalent to the corresponding multiprocessing.Process
+ constructor parameters.
+
+ Ultimately, the intention is for spawn to support other
+ process cloning implementations besides _start_fork, since
+ fork is unsafe for threaded processes as discussed in
+ https://github.com/python/cpython/issues/84559.
+ """
+ parent_pid = portage.getpid()
+ pid = None
+ try:
+ pid = os.fork()
+
+ if pid == 0:
+ _setup_pipes(fd_pipes, close_fds=close_fds, inheritable=True)
+ target(*args, **kwargs)
+ finally:
+ # Don't used portage.getpid() here, in case there is a race
+ # with getpid cache invalidation via _ForkWatcher hook.
+ if pid == 0 or (pid is None and _os.getpid() != parent_pid):
+ # Call os._exit() from a finally block in order
+ # to suppress any finally blocks from earlier
+ # in the call stack (see bug #345289). This
+ # finally block has to be setup before the fork
+ # in order to avoid a race condition.
+ os._exit(1)
+ return pid
+
+
def find_binary(binary):
"""
Given a binary name, find the binary in PATH
next reply other threads:[~2024-01-29 16:09 UTC|newest]
Thread overview: 148+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-29 16:09 Zac Medico [this message]
-- strict thread matches above, loose matches on Subject: below --
2025-01-21 21:14 [gentoo-commits] proj/portage:master commit in: lib/portage/ Sam James
2025-01-11 16:01 Mike Gilbert
2025-01-11 16:01 Mike Gilbert
2025-01-01 0:33 Zac Medico
2024-11-02 22:12 Zac Medico
2024-11-02 15:48 Zac Medico
2024-11-02 15:48 Zac Medico
2024-09-09 18:08 Ulrich Müller
2024-09-09 18:08 Ulrich Müller
2024-08-14 15:22 Zac Medico
2024-06-09 17:54 Zac Medico
2024-06-02 18:28 Zac Medico
2024-04-26 22:06 Sam James
2024-04-26 22:06 Sam James
2024-02-28 16:01 Sam James
2024-02-28 15:52 Sam James
2024-02-28 15:49 Sam James
2024-02-25 8:25 Sam James
2024-02-24 20:10 Zac Medico
2024-02-21 2:08 Sam James
2024-02-21 2:08 Sam James
2024-02-12 7:58 Zac Medico
2024-02-11 19:57 Zac Medico
2024-02-10 6:09 Zac Medico
2024-02-10 6:06 Zac Medico
2024-02-09 8:51 Sam James
2024-02-09 7:08 Sam James
2024-02-07 2:35 Zac Medico
2024-02-07 2:35 Zac Medico
2024-02-05 1:03 Zac Medico
2024-02-05 1:03 Zac Medico
2024-01-29 17:49 Zac Medico
2023-12-26 23:15 Zac Medico
2023-11-02 14:58 Zac Medico
2023-10-24 21:26 Zac Medico
2023-10-24 1:48 Zac Medico
2023-10-03 15:07 Zac Medico
2023-10-02 2:10 Zac Medico
2023-09-26 5:53 Zac Medico
2023-09-08 20:36 Sam James
2023-09-08 19:49 Sam James
2023-08-24 18:23 Mike Gilbert
2023-08-02 6:31 Sam James
2023-07-29 3:57 Sam James
2023-06-29 8:22 Sam James
2023-03-21 2:30 Sam James
2023-03-21 2:30 Sam James
2023-03-21 2:30 Sam James
2023-03-21 2:30 Sam James
2023-03-21 2:30 Sam James
2023-03-21 2:30 Sam James
2023-02-27 6:15 Sam James
2023-02-17 1:23 Sam James
2023-01-02 5:25 Sam James
2022-11-02 22:58 Sam James
2022-11-02 22:58 Sam James
2022-09-29 21:37 Sam James
2022-09-29 20:45 Sam James
2022-09-28 23:56 Sam James
2022-09-26 17:52 Zac Medico
2022-09-20 19:45 Sam James
2022-09-20 3:39 Sam James
2022-09-18 18:30 Mike Gilbert
2022-08-01 22:39 Sam James
2022-08-01 17:34 Mike Gilbert
2022-07-19 21:39 Sam James
2022-07-18 18:47 Sam James
2022-07-11 23:02 Sam James
2022-07-10 15:07 Mike Gilbert
2022-07-05 22:56 Sam James
2022-06-05 20:25 Zac Medico
2022-04-11 12:11 Mike Gilbert
2022-04-11 12:11 Mike Gilbert
2022-04-09 4:32 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-04 19:04 Sam James
2022-04-01 20:30 Matt Turner
2022-03-30 23:11 Sam James
2022-03-28 1:10 Sam James
2022-03-27 23:07 Sam James
2022-03-27 23:07 Sam James
2022-03-27 23:07 Sam James
2022-03-27 23:07 Sam James
2022-03-27 23:07 Sam James
2022-03-15 2:52 Matt Turner
2022-02-09 11:13 Sam James
2021-09-20 20:06 Zac Medico
2021-09-20 19:55 Mike Gilbert
2021-09-07 7:04 Michał Górny
2021-09-04 11:53 Michał Górny
2021-05-24 6:08 Zac Medico
2021-05-24 4:55 Zac Medico
2021-03-28 3:33 Zac Medico
2021-03-11 12:32 Zac Medico
2021-03-07 14:03 Zac Medico
2021-03-06 9:18 Zac Medico
2021-03-06 9:05 Zac Medico
2021-03-06 9:05 Zac Medico
2021-03-06 8:20 Zac Medico
2021-03-06 6:16 Zac Medico
2021-02-08 4:55 Zac Medico
2020-09-11 19:02 Zac Medico
2020-08-04 1:39 Zac Medico
2020-08-03 23:28 Zac Medico
2020-08-03 23:28 Zac Medico
2020-08-03 19:30 Zac Medico
2020-08-03 19:30 Zac Medico
2020-08-03 19:30 Zac Medico
2020-08-03 19:30 Zac Medico
2020-06-27 19:46 Zac Medico
2020-06-09 0:58 Zac Medico
2020-05-17 9:37 Michał Górny
2020-05-07 20:35 Zac Medico
2020-04-20 21:16 Mike Gilbert
2020-03-28 18:57 Michał Górny
2020-03-25 19:18 Zac Medico
2020-03-25 7:57 Zac Medico
2020-03-25 7:57 Zac Medico
2020-02-04 6:43 Zac Medico
2020-02-02 9:00 Zac Medico
2019-12-15 23:04 Zac Medico
2019-11-12 22:25 Zac Medico
2019-09-17 2:59 Zac Medico
2019-09-07 6:40 Zac Medico
2019-08-18 22:15 Zac Medico
2019-08-04 18:03 Zac Medico
2019-08-02 20:03 Mike Gilbert
2019-08-01 19:02 Mike Gilbert
2019-05-28 1:49 Zac Medico
2019-04-27 19:20 Zac Medico
2019-02-20 0:58 Zac Medico
2019-02-20 0:58 Zac Medico
2019-02-20 0:58 Zac Medico
2019-02-18 1:01 Zac Medico
2019-02-11 19:46 Zac Medico
2019-01-04 3:49 Zac Medico
2018-12-31 5:27 Zac Medico
2018-12-04 1:35 Zac Medico
2018-11-25 0:03 Zac Medico
2018-11-24 21:34 Zac Medico
2018-08-07 18:36 Zac Medico
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1706517832.7ec7a647ef6e2d94e6f2b387d0a012e78cafbaff.zmedico@gentoo \
--to=zmedico@gentoo.org \
--cc=gentoo-commits@lists.gentoo.org \
--cc=gentoo-dev@lists.gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox