diff mbox series

[v16,14/14] lib/oe/package_manager/rpm: handle RPM 6 non-zero exit

Message ID 20260825004555.4111831-15-sumanth.gavini@windriver.com
State New
Headers show
Series rpm: 4.20.1 -> 6.0.2 | expand

Commit Message

Sumanth Gavini Aug. 25, 2026, 12:45 a.m. UTC
RPM 6.0 intentionally changed rpmtsRun() to return -1 whenever any
scriptlet fails, even non-critical ones like %post where warn_only=True
(upstream RPM PR#3819, lib/transaction.cc).

Root cause — two changes in RPM 6 lib/transaction.cc vs RPM 4:

  1. New field set before warn_only resets rc (transaction.cc:1754):
       ts->scriptError = 1;

  2. Final exit now ORs in the new flag (transaction.cc:1910):
       RPM 4: rc = nfailed ? -1 : 0;
       RPM 6: rc = (nfailed || ts->scriptError) ? -1 : 0;

Call chain for a failing %post in RPM 4 (exits 0):
  runScript()        warn_only=1 → rc reset to RPMRC_OK
  psm.c:896          if (rc) break → NOT taken, install continues
  rpmte.c:823        if (failed) → NOT taken, rpmteMarkFailed() skipped
  transaction.c:1652 if (failed) → NOT taken, nfailed stays 0
  transaction.c:1896 rc = nfailed ? -1 : 0 → rc=0
  Python ts.run()    returns None → DNF exits 0

Call chain for a failing %post in RPM 6 (exits 1):
  runScript()         ts->scriptError=1, then rc reset to RPMRC_OK
  psm.c:896           if (rc) break → NOT taken (rc still 0)
  rpmte.c:823         if (failed) → NOT taken (same as RPM 4)
  transaction.cc:1652 if (failed) → NOT taken, nfailed stays 0
  transaction.cc:1910 rc = (nfailed || ts->scriptError) ? -1 : 0 → rc=-1
  Python ts.run()     returns [] → DNF raises "Could not run
transaction."
                      → DNF exits 1

In both RPM versions, RPMCALLBACK_SCRIPT_ERROR fires and DNF's
_scriptError() (dnf/yum/rpmtrans.py:407-418) prints:
  "Error in POSTIN scriptlet in rpm package <name>"

OE's install_pkgs() already scans for this line and calls
failed_postinsts_abort() (package_manager/rpm/__init__.py:210-219).
With RPM 4 this path is always reached because DNF exits 0. With RPM 6
the CalledProcessError raised by DNF's non-zero exit intercepts
execution
in _invoke_dnf() before the scan runs.

DNF 4 (lib/dnf/base.py:1146-1158) was never updated for this RPM 6
behaviour. When ts.run() returns [] with no failed elements it
unconditionally raises "Could not run transaction." — the comment at
base.py:1147 still describes RPM 4 semantics where this case meant a
global error such as a transaction lock failure. There is an open DNF5
issue (#2507) but no fix in DNF 4.

Fix: detect "Error in POSTIN scriptlet in rpm package" in the DNF output
inside _invoke_dnf()'s CalledProcessError handler and return the output
instead of calling bb.fatal(), allowing install_pkgs()'s existing
scriptlet scan to run as it did with RPM 4.

Tested with oe-selftest runtime_test.Postinst.test_failing_postinst:
  rpm 4.20.x: PASSED — DNF exits 0, scan path exercised as expected
  rpm 6.0.x without fix: FAILED — CalledProcessError before scan reached
  rpm 6.0.x with fix: PASSED — scan path restored, bb.fatal() fires

References:
https://github.com/rpm-software-management/rpm/pull/3819

Signed-off-by: Sumanth Gavini <sumanth.gavini@windriver.com>
---
 meta/lib/oe/package_manager/rpm/__init__.py | 7 +++++++
 1 file changed, 7 insertions(+)
diff mbox series

Patch

diff --git a/meta/lib/oe/package_manager/rpm/__init__.py b/meta/lib/oe/package_manager/rpm/__init__.py
index cd6efe3d3b..ccb4316bd0 100644
--- a/meta/lib/oe/package_manager/rpm/__init__.py
+++ b/meta/lib/oe/package_manager/rpm/__init__.py
@@ -333,6 +333,13 @@  class RpmPM(PackageManager):
         except subprocess.CalledProcessError as e:
             if print_output:
                 e_output = e.output.decode("utf-8")
+                # RPM 6 fails the entire transaction when a %post scriptlet
+                # fails, causing dnf to exit non-zero. Let install_pkgs handle
+                # scriptlet failures via its existing "Error in POSTIN scriptlet"
+                # scanning rather than treating this as a fatal dnf invocation error.
+                if "Error in POSTIN scriptlet in rpm package" in e_output:
+                    bb.note(e_output)
+                    return e_output
                 extra_info = ""
                 if "install" in dnf_args:
                     if "Error: Unable to find a match:" in e_output: