diff options
author | Florian Schmaus <flow@gentoo.org> | 2023-01-15 19:56:10 +0100 |
---|---|---|
committer | Sam James <sam@gentoo.org> | 2023-01-15 22:11:19 +0000 |
commit | 196caf376c5783c9123277711cdbd8e9711de436 (patch) | |
tree | c1426be8cf8288948c2866abe075d9841fb219d2 | |
parent | NEWS: add entry for Github #976 (diff) | |
download | portage-196caf376c5783c9123277711cdbd8e9711de436.tar.gz portage-196caf376c5783c9123277711cdbd8e9711de436.tar.bz2 portage-196caf376c5783c9123277711cdbd8e9711de436.zip |
lib/portage/process.py: show diag message if exec failed with E2BIG
Signed-off-by: Florian Schmaus <flow@gentoo.org>
Closes: https://github.com/gentoo/portage/pull/978
Signed-off-by: Sam James <sam@gentoo.org>
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | lib/portage/process.py | 29 | ||||
-rw-r--r-- | lib/portage/tests/process/test_spawn_fail_e2big.py | 40 |
3 files changed, 71 insertions, 0 deletions
@@ -7,6 +7,8 @@ Features: * cleanups: Use flynt on the codebase to upgrade to Python f-strings everywhere. +* process: Show diagnostic message if exec failed with E2BIG + Bug fixes: * ebuild: the PATH variable exported to ebuilds has been changed: The PATH setting from /etc/profile.env is appended to portage-internal diff --git a/lib/portage/process.py b/lib/portage/process.py index 5ba6c7867..6760f617c 100644 --- a/lib/portage/process.py +++ b/lib/portage/process.py @@ -437,6 +437,35 @@ def spawn( 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 + def encoded_length(s): + return len(os.fsencode(s)) + + env_size = 0 + env_largest_name = None + env_largest_size = 0 + for env_name, env_value in env.items(): + env_name_size = encoded_length(env_name) + env_value_size = encoded_length(env_value) + # Add two for '=' and the terminating null byte. + total_size = env_name_size + env_value_size + 2 + if total_size > env_largest_size: + env_largest_name = env_name + env_largest_size = total_size + env_size += total_size + + writemsg( + f"ERROR: Executing {mycommand} failed with E2BIG. Child process environment size: {env_size} bytes. Largest environment variable: {env_largest_name} ({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() diff --git a/lib/portage/tests/process/test_spawn_fail_e2big.py b/lib/portage/tests/process/test_spawn_fail_e2big.py new file mode 100644 index 000000000..d3be51670 --- /dev/null +++ b/lib/portage/tests/process/test_spawn_fail_e2big.py @@ -0,0 +1,40 @@ +# Copyright 2023 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +import platform +import tempfile + +from pathlib import Path + +import portage.process + +from portage import shutil +from portage.const import BASH_BINARY +from portage.tests import TestCase + + +class SpawnE2bigTestCase(TestCase): + def testSpawnE2big(self): + if platform.system() != "Linux": + self.skipTest("not Linux") + + env = dict() + env["VERY_LARGE_ENV_VAR"] = "X" * 1024 * 256 + + tmpdir = tempfile.mkdtemp() + try: + logfile = tmpdir / Path("logfile") + echo_output = "Should never appear" + retval = portage.process.spawn( + [BASH_BINARY, "-c", "echo", echo_output], env=env, logfile=logfile + ) + + with open(logfile) as f: + logfile_content = f.read() + self.assertIn( + "Largest environment variable: VERY_LARGE_ENV_VAR (262164 bytes)", + logfile_content, + ) + self.assertEqual(retval, 1) + finally: + shutil.rmtree(tmpdir) |