From c2a15cf7d19ae45809278c742e9b12c56625922f Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Thu, 2 Apr 2026 17:06:06 -0700 Subject: [PATCH] rpm: fix systemd scriptlet failures in Anaconda/Kickstart installs RPM %post and %preun scriptlets used raw systemctl commands that exit non-zero when systemd is installed but not running, causing Anaconda to abort package installation (libdnf5 treats scriptlet errors as fatal). %post: Replace raw 'systemctl enable [--now]' with 'systemctl enable || :' gated on $1 == 1. For Start: true, use a separate 'systemctl start || :' guarded by /run/systemd/system. We intentionally avoid %systemd_post because it calls 'systemctl preset' which defers to system preset policy and would break the Enable: true contract. %preun: Replace raw 'systemctl disable --now' with %systemd_preun macro, which handles the $1 == 0 check and /run/systemd/system guard internally. Signed-off-by: Brian Goff (cherry picked from commit f89da7370f7c64d3da792496609baf1f6610de40) Signed-off-by: Brian Goff --- packaging/linux/rpm/template.go | 50 +++++++++++++--------------- packaging/linux/rpm/template_test.go | 23 ++++--------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/packaging/linux/rpm/template.go b/packaging/linux/rpm/template.go index 5447e7a53..478fd00d1 100644 --- a/packaging/linux/rpm/template.go +++ b/packaging/linux/rpm/template.go @@ -460,20 +460,11 @@ func (w *specWrapper) BuildSteps() fmt.Stringer { } func systemdPreUnScript(unitName string, cfg dalec.SystemdUnitConfig) string { - // if service isn't explicitly specified as enabled in the spec, - // then we don't need to do anything in the preun script if !cfg.Enable { return "" } - // should be equivalent to the systemd_preun scriptlet in the rpm spec, - // but without the use of a .preset file - return fmt.Sprintf(` -if [ $1 -eq 0 ]; then - # complete uninstallation - systemctl disable --now %s -fi -`, unitName) + return fmt.Sprintf("%%systemd_preun %s\n", unitName) } func (w *specWrapper) PreUn() fmt.Stringer { @@ -497,30 +488,35 @@ func (w *specWrapper) PreUn() fmt.Stringer { } func systemdPostScript(unitName string, cfg dalec.SystemdUnitConfig) string { - // if service isn't explicitly specified as enabled in the spec, - // then we don't need to do anything in the post script if !cfg.Enable { return "" } - // should be equivalent to the systemd_post scriptlet in the rpm spec, - // but without the use of a .preset file - s := ` -if [ $1 -eq 1 ]; then - # initial installation` + // Use systemctl enable directly instead of %systemd_post because + // %systemd_post calls "systemctl preset" which defers to system preset + // policy. All RPM distros have "disable *" as a catch-all in their preset + // files, so third-party services would never be enabled via preset. + // This behavior may change in the future to respect system presets instead. + // See https://github.com/project-dalec/dalec/issues/1017#issuecomment-4181051908 + // + // The "|| :" ensures a non-zero exit (e.g. systemd not running in a + // chroot/Kickstart/container) does not abort the scriptlet. + // Only enable on initial install ($1 == 1), not upgrades. + s := fmt.Sprintf(`if [ $1 -eq 1 ]; then + systemctl enable %s || : +fi +`, unitName) - // Enable/start service when package is installed if cfg.Start { - s = s + fmt.Sprintf(` - systemctl enable --now %s`, unitName) - } else { - s = s + fmt.Sprintf(` - systemctl enable %s`, unitName) - } - - s = s + ` + // Only start on initial install ($1 == 1), not upgrades, to avoid + // restarting a service the user intentionally stopped. + // Guard behind a check for a running systemd so this is safe + // in chroot/Kickstart/container environments. + s += fmt.Sprintf(`if [ $1 -eq 1 ] && [ -d /run/systemd/system ]; then + systemctl start %s || : fi -` +`, unitName) + } return s } diff --git a/packaging/linux/rpm/template_test.go b/packaging/linux/rpm/template_test.go index e0ef05455..b89d7a59a 100644 --- a/packaging/linux/rpm/template_test.go +++ b/packaging/linux/rpm/template_test.go @@ -290,15 +290,14 @@ func TestTemplate_Artifacts(t *testing.T) { assert.Equal(t, w.Post().String(), `%post - if [ $1 -eq 1 ]; then - # initial installation - systemctl enable test2.service + systemctl enable test2.service || : fi - if [ $1 -eq 1 ]; then - # initial installation - systemctl enable --now test3.service + systemctl enable test3.service || : +fi +if [ $1 -eq 1 ] && [ -d /run/systemd/system ]; then + systemctl start test3.service || : fi `) @@ -339,16 +338,8 @@ fi assert.Equal(t, w.PreUn().String(), `%preun - -if [ $1 -eq 0 ]; then - # complete uninstallation - systemctl disable --now test2.service -fi - -if [ $1 -eq 0 ]; then - # complete uninstallation - systemctl disable --now test3.service -fi +%systemd_preun test2.service +%systemd_preun test3.service `) })