[v3,2/8] isar-events: Unhide mounts left behind

Message ID 20241011100050.322686-3-amikan@ilbers.de
State Accepted, archived
Headers show
Series Hanging mount fixes | expand

Commit Message

Anton Mikanovich Oct. 11, 2024, 10 a.m. UTC
Fail the existing build testcases on mounts left behind by calling the
cleanup handler once per build (controlled by a new variable).

Specifically:

1. Execute cleanup handler only once per build.
2. Output error messages in case of umount failure.
3. Unmount /proc/mounts in reverse order to unmount subdirectories first.
4. Introduce ISAR_FAIL_ON_CLEANUP bitbake variable:
   - 0 or unset: Output a warning, unmount, build succeeds (default).
   - 1: Output a warning, keep mounts left behind, build fails.

Signed-off-by: Anton Mikanovich <amikan@ilbers.de>
---
 RECIPE-API-CHANGELOG.md          |  9 +++++++
 meta/classes/isar-events.bbclass | 40 +++++++++++++++++++++++++-------
 2 files changed, 41 insertions(+), 8 deletions(-)

Patch

diff --git a/RECIPE-API-CHANGELOG.md b/RECIPE-API-CHANGELOG.md
index 8eeaf325..608d0cc3 100644
--- a/RECIPE-API-CHANGELOG.md
+++ b/RECIPE-API-CHANGELOG.md
@@ -665,3 +665,12 @@  package on an `amd64` host: you would expect the -native variant to produce
 an `amd64` package and -compat an 'armhf` package: it will however remain
 `arm64` and build of dependent recipes (image or dpkg) may fail because of
 the architecture mismatch.
+
+### Changes in cleanup handler
+
+Bitbake BuildCompleted event handler is now executed only once per build and
+always outputs a warning if mounts are left behind after the build.
+
+Bitbake exit status depends on ISAR_FAIL_ON_CLEANUP bitbake variable:
+ - 0 or unset: Output a warning, unmount, build succeeds (default).
+ - 1: Output a warning, keep mounts left behind, build fails.
diff --git a/meta/classes/isar-events.bbclass b/meta/classes/isar-events.bbclass
index f5061a8b..76b713c7 100644
--- a/meta/classes/isar-events.bbclass
+++ b/meta/classes/isar-events.bbclass
@@ -4,6 +4,10 @@ 
 # Copyright (C) 2015-2017 ilbers GmbH
 # Copyright (c) Siemens AG, 2018
 
+# If set to 1, the build will fail on mounts found during cleanup,
+# keeping those mounts left behind
+ISAR_FAIL_ON_CLEANUP ?= "0"
+
 addhandler build_started
 
 python build_started() {
@@ -48,17 +52,37 @@  python build_completed() {
     if not tmpdir:
         return
 
+    # bitbake calls cleanup for every multiconfig listed in BBMULTICONFIG plus
+    # one for the entire build. E.g., if BBMULTICONFIG="mc1 mc2 mc3", we call
+    # "bitbake mc1 mc2", the following cleanups would be called:
+    # "c1 c2 c3 cdefault".
+    # Skip running cleanup for additional multiconfigs
+    mc = d.getVar('BB_CURRENT_MC')
+    if mc != 'default':
+        return
+
+    fail_on_cleanup = bb.utils.to_boolean(d.getVar('ISAR_FAIL_ON_CLEANUP'))
+
     basepath = tmpdir + '/work/'
 
-    with open('/proc/mounts') as f:
-        for line in f.readlines():
-            if basepath in line:
-                bb.debug(1, '%s left mounted, unmounting...' % line.split()[1])
-                subprocess.call(
-                    ["sudo", "umount", line.split()[1]],
-                    stdout=subprocess.DEVNULL,
-                    stderr=subprocess.DEVNULL,
+    for line in reversed(list(open('/proc/mounts'))):
+        if basepath not in line:
+            continue
+        msg_line = f"{line.split()[1]} left mounted"
+        # If bitbake is started manually, bb.warn and bb.error go to stdout;
+        # with bb.error, bitbake additionally fails the build. Under CI,
+        # bb.warn and bb.error currently go to a file.
+        if fail_on_cleanup:
+            bb.error(msg_line)
+        else:
+            msg_line += ', unmounting...'
+            bb.warn(msg_line)
+            try:
+                subprocess.run(
+                    f"sudo umount {line.split()[1]}", shell=True, check=True
                 )
+            except subprocess.CalledProcessError as e:
+                bb.error(str(e))
 
     # Cleanup build UUID, the next bitbake run will generate new one
     bb.persist_data.persist('BB_ISAR_UUID_DATA', d).clear()