summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Schmaus <flow@gentoo.org>2023-01-15 19:56:10 +0100
committerSam James <sam@gentoo.org>2023-01-15 22:11:19 +0000
commit196caf376c5783c9123277711cdbd8e9711de436 (patch)
treec1426be8cf8288948c2866abe075d9841fb219d2
parentNEWS: add entry for Github #976 (diff)
downloadportage-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--NEWS2
-rw-r--r--lib/portage/process.py29
-rw-r--r--lib/portage/tests/process/test_spawn_fail_e2big.py40
3 files changed, 71 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index b149ecc4d..a173795b3 100644
--- a/NEWS
+++ b/NEWS
@@ -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)