Bug 40901: (QA follow-up) Fix the koha-systemd migration, upgrade and removal paths

First install (migration from the SysV layout):

- The postinst ran daemon-reload before stopping koha-common.service, so
  the name already resolved to the new native unit and the stop was a
  no-op; the daemon(1)-supervised starman/zebrasrv/workers survived and
  kept plack.sock and the Zebra sockets. The init script itself is gone
  by then (koha-common's rm_conffile) and the koha-* helpers already
  dispatch to systemd, so neither can be used either. Kill the legacy
  koha-common.service cgroup (the generated unit has KillMode=process,
  a plain stop leaves the supervisors alive), sweep remaining pid files,
  and wait for them to exit, all before daemon-reload.
- Nothing ever started the new units, and koha-common.service was never
  enabled, so a migrated host came back with every instance stopped and
  "systemctl restart koha-common" doing nothing. Enable both koha.target
  and koha-common.service and start them through deb-systemd-invoke.
- koha-plack@ was enabled for every instance including CGI-only ones,
  and koha-indexer@ regardless of USE_INDEXER_DAEMON. Use
  "koha-list --enabled --plack" and /etc/default/koha-common like the
  init script did.
- Drop the "systemctl disable koha-common.service" that targeted our
  own compat unit.

Upgrade: deb-systemd-invoke takes unit names, not glob patterns, so
enumerate the active koha-*@*.service units before try-restarting them.

Removal: the prerm only did a daemon-reload, leaving every daemon
running with its unit file deleted, so swapping to koha-sysv started a
second copy of each. Stop koha-common.service and koha.target on
remove; a new postrm removes the enablement symlinks on purge and
reloads the manager.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Martin Renvoize 2026-09-15 17:35:25 +01:00 committed by Pedro Amorim
parent 1a81cf9bc7
commit 606fc6bcd6
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
3 changed files with 141 additions and 55 deletions

View File

@ -1,66 +1,128 @@
#!/bin/sh
set -e
case "$1" in
configure)
# Reload systemd daemon
systemctl daemon-reload || true
DEFAULTS=/etc/default/koha-common
# On first install (no previous version), enable units for all existing instances
# This handles migration from SysV init to systemd
if [ -z "$2" ]; then
# Stop and disable the legacy SysV koha-common.service if present.
# This service is auto-generated by systemd-sysv-generator from
# /etc/init.d/koha-common and would run in parallel with the native
# systemd units, causing double-starts. Only do this on first
# install: after migration, koha-common.service is our own compat
# unit, and stopping it here would stop every instance on each
# upgrade of this package.
if systemctl is-active --quiet koha-common.service 2>/dev/null; then
systemctl stop koha-common.service || true
fi
if systemctl is-enabled --quiet koha-common.service 2>/dev/null; then
systemctl disable koha-common.service || true
# On a systemd host the daemons started by /etc/init.d/koha-common live in
# the cgroup of the koha-common.service that systemd-sysv-generator created
# for the init script. By the time this postinst runs, the init script has
# already been removed by koha-common's rm_conffile and the koha-* helpers
# already dispatch to systemd, so neither of them can stop those daemons
# any more. Kill the legacy cgroup, then sweep whatever still holds a pid
# file in the instances' run directories (daemon(1) supervisors, starman,
# the Z39.50 responder).
stop_legacy_daemons() {
if systemctl is-active --quiet koha-common.service 2>/dev/null; then
# The generated unit has KillMode=process; a plain stop would leave
# the daemon(1) supervisors and their children running.
systemctl kill --signal=SIGTERM koha-common.service 2>/dev/null || true
systemctl stop koha-common.service 2>/dev/null || true
fi
for pidfile in /run/koha/*/*.pid; do
[ -f "$pidfile" ] || continue
pid=$(cat "$pidfile" 2>/dev/null) || continue
case "$pid" in
'' | *[!0-9]*) continue ;;
esac
kill -TERM "$pid" 2>/dev/null || true
done
# Wait (briefly) for them to release their sockets and pid files.
i=0
while [ "$i" -lt 10 ]; do
alive=0
for pidfile in /run/koha/*/*.pid; do
[ -f "$pidfile" ] || continue
pid=$(cat "$pidfile" 2>/dev/null) || continue
case "$pid" in
'' | *[!0-9]*) continue ;;
esac
if kill -0 "$pid" 2>/dev/null; then
alive=1
fi
done
[ "$alive" -eq 0 ] && break
sleep 1
i=$((i + 1))
done
}
for instance in $(koha-list --enabled 2>/dev/null); do
# Core services: always enable
systemctl enable koha-zebra@${instance}.service 2>/dev/null || true
systemctl enable koha-plack@${instance}.service 2>/dev/null || true
systemctl enable koha-worker@${instance}.service 2>/dev/null || true
systemctl enable koha-worker-long_tasks@${instance}.service 2>/dev/null || true
systemctl enable koha-create-dirs@${instance}.service 2>/dev/null || true
systemctl enable koha@${instance}.target 2>/dev/null || true
# First install (migration from the SysV-managed layout): enable the units
# that correspond to what the init script used to start for each enabled
# instance, then start everything.
enable_existing_instances() {
USE_INDEXER_DAEMON="yes"
if [ -f "$DEFAULTS" ]; then
. "$DEFAULTS"
fi
# Conditional services
# SIP: read the SysV-era flag file DIRECTLY here, not via
# koha-list --sip / is_sip_enabled. The latter now consults
# systemctl is-enabled on systemd hosts (see follow-up 1/3),
# which would return "disabled" for every instance during this
# very first install -- the migration we are about to perform.
# The flag file is the only reliable signal for "did this
# instance have SIP enabled in the SysV-era state we are
# migrating from?".
if [ -e "/var/lib/koha/${instance}/sip.enabled" ]; then
systemctl enable koha-sip@${instance}.service 2>/dev/null || true
fi
if koha-list --enabled --z3950 2>/dev/null | grep -qx "$instance"; then
systemctl enable koha-z3950@${instance}.service 2>/dev/null || true
fi
if koha-list --enabled --elasticsearch 2>/dev/null | grep -qx "$instance"; then
systemctl enable koha-es-indexer@${instance}.service 2>/dev/null || true
else
systemctl enable koha-indexer@${instance}.service 2>/dev/null || true
fi
done
systemctl enable koha.target 2>/dev/null || true
plack_instances=$(koha-list --enabled --plack 2>/dev/null || true)
z3950_instances=$(koha-list --enabled --z3950 2>/dev/null || true)
es_instances=$(koha-list --enabled --elasticsearch 2>/dev/null || true)
for instance in $(koha-list --enabled 2>/dev/null); do
# Always on
systemctl enable "koha-create-dirs@${instance}.service" 2>/dev/null || true
systemctl enable "koha-zebra@${instance}.service" 2>/dev/null || true
systemctl enable "koha-worker@${instance}.service" 2>/dev/null || true
systemctl enable "koha-worker-long_tasks@${instance}.service" 2>/dev/null || true
systemctl enable "koha@${instance}.target" 2>/dev/null || true
# Plack: the init script started it for "koha-list --enabled --plack"
# (Apache configuration), so do the same rather than for everyone.
if echo "$plack_instances" | grep -qx "$instance"; then
systemctl enable "koha-plack@${instance}.service" 2>/dev/null || true
fi
# On upgrade, restart running Koha services so they pick up new code/unit files
if [ -n "$2" ]; then
deb-systemd-invoke try-restart \
'koha-*@*.service' \
>/dev/null || true
# SIP: read the SysV-era flag file DIRECTLY here, not via
# koha-list --sip / is_sip_enabled. The latter consults
# systemctl is-enabled on systemd hosts, which is "disabled" for
# every instance during this very first install. The flag file is
# the only signal for "did this instance have SIP enabled in the
# SysV-era state we are migrating from?".
if [ -e "/var/lib/koha/${instance}/sip.enabled" ]; then
systemctl enable "koha-sip@${instance}.service" 2>/dev/null || true
fi
if echo "$z3950_instances" | grep -qx "$instance"; then
systemctl enable "koha-z3950@${instance}.service" 2>/dev/null || true
fi
# Indexing: the ES indexer for Elasticsearch instances, otherwise
# the Zebra indexer daemon if /etc/default/koha-common asks for it,
# which is what the init script did.
if echo "$es_instances" | grep -qx "$instance"; then
systemctl enable "koha-es-indexer@${instance}.service" 2>/dev/null || true
elif [ "$USE_INDEXER_DAEMON" = "yes" ]; then
systemctl enable "koha-indexer@${instance}.service" 2>/dev/null || true
fi
done
systemctl enable koha.target 2>/dev/null || true
systemctl enable koha-common.service 2>/dev/null || true
}
case "$1" in
configure)
if [ -z "$2" ]; then
stop_legacy_daemons
fi
systemctl daemon-reload || true
if [ -z "$2" ]; then
enable_existing_instances
deb-systemd-invoke start koha.target koha-common.service >/dev/null || true
else
# Upgrade: restart the running per-component units so they pick
# up new code and unit files. deb-systemd-invoke takes unit
# names, not patterns, so enumerate them first.
units=$(systemctl list-units --plain --no-legend --state=active 'koha-*@*.service' 2>/dev/null | awk '{print $1}')
if [ -n "$units" ]; then
# shellcheck disable=SC2086
deb-systemd-invoke try-restart $units >/dev/null || true
fi
fi
;;
esac

21
debian/koha-systemd.postrm vendored Normal file
View File

@ -0,0 +1,21 @@
#!/bin/sh
set -e
case "$1" in
purge)
# Drop the enablement symlinks created by koha-create, koha-* --enable
# and the postinst migration.
rm -rf /etc/systemd/system/koha.target.wants
rm -rf /etc/systemd/system/koha@*.target.wants
rm -f /etc/systemd/system/multi-user.target.wants/koha.target
rm -f /etc/systemd/system/multi-user.target.wants/koha-common.service
;;
esac
if [ -d /run/systemd/system ]; then
systemctl daemon-reload >/dev/null 2>&1 || true
fi
#DEBHELPER#
exit 0

View File

@ -3,8 +3,11 @@ set -e
case "$1" in
remove|deconfigure)
# Stop everything while the unit files are still present. Otherwise
# the daemons keep running with no unit left to manage them, and a
# swap to koha-sysv would start a second copy of each.
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
deb-systemd-invoke stop koha-common.service koha.target >/dev/null || true
fi
;;
esac