2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-23 16:24:04 +00:00

fix(packaging): preserve xcatd init state on Debian (#7615)

* refactor(packaging): share precise systemd state detection

* test(packaging): cover precise systemd state detection

* test(packaging): allow explicit Debian init mode

* fix(packaging): honor explicit Debian init targets

* test(packaging): cover Debian init target detection

* fix(packaging): add Debian xcatd init state helper

* test(packaging): cover Debian xcatd init state helper

* test(packaging): allow delegated SysV registration

* fix(packaging): preserve Debian xcatd conffile lifecycle

* test(packaging): mirror explicit Debian init mode

* test(packaging): cover Debian xcatd conffile lifecycle

* fix(packaging): contain init state file umask

* test(packaging): cover init state permissions

* fix(packaging): contain preinstall context umask

* test(packaging): cover preinstall umask containment

* fix(packaging): detect all systemd enablement links

* test(packaging): cover all systemd enablement links

* test(packaging): allow shared purge state path

* refactor(packaging): reuse Debian init state path

* fix(packaging): detect runtime systemd masks

* test(packaging): cover runtime systemd masks

* test(packaging): model Debian SysV registration

* refactor(packaging): reuse shared init state detection

* test(packaging): enforce shared Debian state probes

* test(packaging): allow explicit unregistered masks

* fix(packaging): preserve Debian SysV registration state

* test(packaging): cover Debian SysV registration states

* fix(packaging): clean failed Debian state writes

* test(packaging): cover failed Debian state writes

* fix(packaging): retain unregistered systemd provenance

* test(packaging): cover unregistered systemd upgrades

* fix(packaging): fail closed on shared state errors

* test(packaging): cover shared state detector failures

* test(packaging): mirror native xcatd runlevels

* fix(packaging): recover rejected SysV layouts

* test(packaging): cover rejected SysV layouts

* test(packaging): cover SysV rebuild retries

* refactor(packaging): reuse systemctl readiness guard

* test(packaging): enforce shared systemctl guard
This commit is contained in:
Vinícius Ferrão
2026-07-28 16:31:36 -03:00
committed by GitHub
parent b5510dd017
commit 036bb24c87
13 changed files with 2995 additions and 139 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ Homepage: https://xcat.org/
Package: xcat-server
Architecture: all
Depends: ${perl:Depends}, grub2-xcat (>= 2.02-0.76.el7.1.snap201905160255), perl-xcat (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libsys-syslog-perl, libio-socket-ssl-perl, libxml-simple-perl, make, libdbd-sqlite3-perl, libexpect-perl, libnet-dns-perl, libsoap-lite-perl, libxml-libxml-perl, libsnmp-perl, debootstrap, libdigest-sha-perl,libcrypt-rijndael-perl,libcrypt-cbc-perl,libjson-perl, libnet-https-nb-perl, libhttp-async-perl
Depends: ${perl:Depends}, grub2-xcat (>= 2.02-0.76.el7.1.snap201905160255), perl-xcat (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libsys-syslog-perl, libio-socket-ssl-perl, libxml-simple-perl, make, ucf, libdbd-sqlite3-perl, libexpect-perl, libnet-dns-perl, libsoap-lite-perl, libxml-libxml-perl, libsnmp-perl, debootstrap, libdigest-sha-perl,libcrypt-rijndael-perl,libcrypt-cbc-perl,libjson-perl, libnet-https-nb-perl, libhttp-async-perl
Description: Server and configuration utilities of xCAT
xCAT-server provides the core server and configuration management components
of xCAT.
+2
View File
@@ -29,6 +29,8 @@ lib/xcat/Confluent/* opt/xcat/lib/perl/Confluent/
lib/xcat/shfunctions opt/xcat/lib
etc/init.d/xcatd opt/xcat/share/xcat/scripts
debian/xcatd.md5sum opt/xcat/share/xcat/scripts
debian/xcatd-init-state opt/xcat/share/xcat/scripts
etc/init.d/xcatd.service lib/systemd/system/
xCAT-wsapi/* opt/xcat/ws/
+64 -37
View File
@@ -5,17 +5,48 @@
set -e
xcat_can_use_systemctl()
{
[ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1
}
xcatd_init_compat=/opt/xcat/share/xcat/scripts/xcatd-init-compat
xcatd_init_state=/opt/xcat/share/xcat/scripts/xcatd-init-state
xcatd_state_dir=/var/lib/xcat/xcatd-init-state
xcatd_context_file=$xcatd_state_dir/context
if "$xcatd_init_compat" uses-systemd && \
command -v dpkg-maintscript-helper >/dev/null 2>&1 && \
dpkg-maintscript-helper supports rm_conffile; then
dpkg-maintscript-helper rm_conffile /etc/init.d/xcatd -- "$@"
if [ -L "$xcatd_state_dir" ] || \
{ [ -e "$xcatd_state_dir" ] && [ ! -d "$xcatd_state_dir" ]; }; then
echo "Invalid xcatd init state directory: $xcatd_state_dir" >&2
exit 1
fi
if "$xcatd_init_compat" uses-systemd --explicit-target; then
xcatd_uses_systemd=yes
else
xcatd_uses_systemd=no
fi
if [ -r "$xcatd_context_file" ]; then
xcatd_transition_context=$(cat "$xcatd_context_file")
case "$xcatd_transition_context" in
fresh|upgrade) ;;
*)
echo "Invalid xcatd package transition context: $xcatd_transition_context" >&2
exit 1
;;
esac
elif [ "$1" = configure ] && [ -n "${2:-}" ]; then
# Preserve state when invoked for an already configured package even if a
# prior packaging tool did not provide the explicit preinst context.
xcatd_transition_context=upgrade
else
xcatd_transition_context=fresh
fi
if [ "$xcatd_uses_systemd" = yes ]; then
if [ "$1" = configure ]; then
"$xcatd_init_state" prepare-systemd "$xcatd_transition_context"
fi
if command -v dpkg-maintscript-helper >/dev/null 2>&1 && \
dpkg-maintscript-helper supports rm_conffile; then
dpkg-maintscript-helper rm_conffile /etc/init.d/xcatd -- "$@"
fi
fi
# summary of how this script can be called:
@@ -34,47 +65,37 @@ fi
case "$1" in
configure)
. /etc/profile.d/xcat.sh
xcat_server_upgrade=0
if [ -f /tmp/xCAT-server_upgrade.tmp ]; then
xcat_server_upgrade=1
fi
if "$xcatd_init_compat" uses-systemd; then
legacy_xcatd_enabled=0
if ls /etc/rc?.d/S??xcatd >/dev/null 2>&1; then
legacy_xcatd_enabled=1
fi
if [ "$xcat_server_upgrade" = "1" ] && command -v update-rc.d >/dev/null 2>&1; then
if [ "$xcatd_uses_systemd" = yes ]; then
if command -v update-rc.d >/dev/null 2>&1; then
update-rc.d -f xcatd remove
fi
"$xcatd_init_compat" configure
if xcat_can_use_systemctl; then
"$xcatd_init_compat" configure --explicit-target
if "$xcatd_init_compat" can-use-systemctl; then
systemctl daemon-reload
fi
if command -v systemctl >/dev/null 2>&1 && \
{ [ "$xcat_server_upgrade" = "0" ] || [ "$legacy_xcatd_enabled" = "1" ]; }; then
# Fresh installs are enabled by default. Upgrades preserve the
# legacy enabled state so HA nodes remain disabled.
systemctl enable xcatd.service
desired_enabled=$("$xcatd_init_state" get enabled)
transition_origin=$("$xcatd_init_state" get origin)
if command -v systemctl >/dev/null 2>&1; then
case "$transition_origin:$desired_enabled" in
fresh:yes|legacy:yes) systemctl enable xcatd.service ;;
legacy:no|legacy:unregistered) systemctl disable xcatd.service ;;
esac
fi
"$xcatd_init_state" commit-systemd
else
"$xcatd_init_compat" configure
if [ "$xcat_server_upgrade" = "0" ] && \
[ -x /etc/init.d/xcatd ] && \
command -v update-rc.d >/dev/null 2>&1; then
update-rc.d xcatd defaults
fi
"$xcatd_init_state" configure-legacy "$xcatd_transition_context"
fi
if [ "$xcat_server_upgrade" = "1" ]; then
# No need to reload xcatd here as it will be covered by xCAT/xCATsn now.
rm -f /tmp/xCAT-server_upgrade.tmp
fi
ln -sf /opt/xcat/sbin/xcatd /usr/sbin/xcatd
;;
abort-upgrade|abort-remove|abort-deconfigure)
abort-upgrade)
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
;;
abort-remove|abort-deconfigure)
;;
*)
@@ -88,4 +109,10 @@ esac
#DEBHELPER#
if [ "$1" = configure ]; then
# Keep explicit context across every fallible postinst step. A retry after
# this point is a completed configuration and may safely drop it.
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
fi
exit 0
+114 -30
View File
@@ -5,13 +5,76 @@
set -e
xcat_target_uses_systemd()
xcatd_state_dir=/var/lib/xcat/xcatd-init-state
xcatd_context_file=$xcatd_state_dir/context
clear_xcatd_pending_state()
{
if [ -d /run/systemd/system ]; then
rm -f "$xcatd_state_dir/pending-xcatd" \
"$xcatd_state_dir/pending-deleted" \
"$xcatd_state_dir/pending-enabled" \
"$xcatd_state_dir/pending-xcatd.tmp."*
}
if [ -L "$xcatd_state_dir" ] || \
{ [ -e "$xcatd_state_dir" ] && [ ! -d "$xcatd_state_dir" ]; }; then
echo "Invalid xcatd init state directory: $xcatd_state_dir" >&2
exit 1
fi
xcat_systemd_os_release_status()
{
[ -r /etc/os-release ] || return 2
os_id=
os_version=
while IFS='=' read -r key value; do
value=${value#\"}
value=${value%\"}
value=${value#\'}
value=${value%\'}
case "$key" in
ID) os_id=$value ;;
VERSION_ID) os_version=$value ;;
esac
done < /etc/os-release
if [ "$os_id" = "debian" ] && [ -z "$os_version" ]; then
return 0
fi
if [ -x /usr/lib/systemd/systemd ] || [ -x /lib/systemd/systemd ]; then
os_major=${os_version%%.*}
case "$os_major" in
''|*[!0-9]*) return 2 ;;
esac
case "$os_id" in
rhel|centos|rocky|almalinux|ol|scientific)
[ "$os_major" -ge 7 ] && return 0
return 1
;;
fedora|ubuntu)
[ "$os_major" -ge 15 ] && return 0
return 1
;;
debian)
[ "$os_major" -ge 8 ] && return 0
return 1
;;
sles|sled|opensuse|opensuse-leap|opensuse-tumbleweed)
[ "$os_major" -ge 12 ] && return 0
return 1
;;
*)
return 2
;;
esac
}
xcat_target_uses_systemd()
{
if [ -d /run/systemd/system ]; then
return 0
fi
@@ -19,35 +82,25 @@ xcat_target_uses_systemd()
init_target=$(readlink /sbin/init 2>/dev/null || true)
case "$init_target" in
*systemd*) return 0 ;;
*) return 1 ;;
esac
fi
if [ -r /etc/os-release ]; then
os_id=
os_version=
while IFS='=' read -r key value; do
value=${value#\"}
value=${value%\"}
value=${value#\'}
value=${value%\'}
case "$key" in
ID) os_id=$value ;;
VERSION_ID) os_version=$value ;;
esac
done < /etc/os-release
if [ -e /sbin/init ]; then
return 1
fi
if [ "$os_id" = "debian" ] && [ -z "$os_version" ]; then
return 0
if xcat_systemd_os_release_status; then
return 0
else
os_release_status=$?
if [ "$os_release_status" -eq 1 ]; then
return 1
fi
fi
os_major=${os_version%%.*}
case "$os_major" in
''|*[!0-9]*) return 1 ;;
esac
case "$os_id" in
ubuntu) [ "$os_major" -ge 15 ] && return 0 ;;
debian) [ "$os_major" -ge 8 ] && return 0 ;;
esac
if [ -x /usr/lib/systemd/systemd ] || [ -x /lib/systemd/systemd ]; then
return 0
fi
return 1
@@ -75,16 +128,47 @@ fi
case "$1" in
purge)
rm -f /etc/init.d/xcatd
rm -f /etc/init.d/xcatd \
/etc/init.d/xcatd.ucf-old \
/etc/init.d/xcatd.ucf-new \
/etc/init.d/xcatd.ucf-dist \
/etc/init.d/xcatd.dpkg-bak \
/etc/init.d/xcatd.dpkg-backup \
/etc/init.d/xcatd.dpkg-remove \
/var/lib/xcat/xcatd-systemd-mode
rm -f "$xcatd_state_dir/state" \
"$xcatd_state_dir/xcatd" \
"$xcatd_state_dir/state.tmp."* \
"$xcatd_state_dir/xcatd.tmp."*
clear_xcatd_pending_state
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
rmdir "$xcatd_state_dir" 2>/dev/null || true
if command -v ucf >/dev/null 2>&1; then
ucf --purge /etc/init.d/xcatd
fi
if command -v ucfr >/dev/null 2>&1; then
ucfr --purge xcat-server /etc/init.d/xcatd
fi
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
abort-install)
clear_xcatd_pending_state
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
rmdir "$xcatd_state_dir" 2>/dev/null || true
;;
abort-upgrade|disappear)
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
;;
upgrade|failed-upgrade)
;;
remove)
rm -f /usr/sbin/xcatd #remove the symbolic
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
rm -f /usr/sbin/xcatd #remove the symbolic
rm -f /etc/apache2/conf-enabled/xcat-ws.conf
rm -f /etc/apache2/conf-enabled/xcat-ws.conf
;;
*)
+228 -28
View File
@@ -5,13 +5,59 @@
set -e
xcat_target_uses_systemd()
xcat_systemd_os_release_status()
{
if [ -d /run/systemd/system ]; then
[ -r /etc/os-release ] || return 2
os_id=
os_version=
while IFS='=' read -r key value; do
value=${value#\"}
value=${value%\"}
value=${value#\'}
value=${value%\'}
case "$key" in
ID) os_id=$value ;;
VERSION_ID) os_version=$value ;;
esac
done < /etc/os-release
if [ "$os_id" = "debian" ] && [ -z "$os_version" ]; then
return 0
fi
if [ -x /usr/lib/systemd/systemd ] || [ -x /lib/systemd/systemd ]; then
os_major=${os_version%%.*}
case "$os_major" in
''|*[!0-9]*) return 2 ;;
esac
case "$os_id" in
rhel|centos|rocky|almalinux|ol|scientific)
[ "$os_major" -ge 7 ] && return 0
return 1
;;
fedora|ubuntu)
[ "$os_major" -ge 15 ] && return 0
return 1
;;
debian)
[ "$os_major" -ge 8 ] && return 0
return 1
;;
sles|sled|opensuse|opensuse-leap|opensuse-tumbleweed)
[ "$os_major" -ge 12 ] && return 0
return 1
;;
*)
return 2
;;
esac
}
xcat_target_uses_systemd()
{
if [ -d /run/systemd/system ]; then
return 0
fi
@@ -19,41 +65,193 @@ xcat_target_uses_systemd()
init_target=$(readlink /sbin/init 2>/dev/null || true)
case "$init_target" in
*systemd*) return 0 ;;
*) return 1 ;;
esac
fi
if [ -r /etc/os-release ]; then
os_id=
os_version=
while IFS='=' read -r key value; do
value=${value#\"}
value=${value%\"}
value=${value#\'}
value=${value%\'}
case "$key" in
ID) os_id=$value ;;
VERSION_ID) os_version=$value ;;
esac
done < /etc/os-release
if [ -e /sbin/init ]; then
return 1
fi
if [ "$os_id" = "debian" ] && [ -z "$os_version" ]; then
return 0
if xcat_systemd_os_release_status; then
return 0
else
os_release_status=$?
if [ "$os_release_status" -eq 1 ]; then
return 1
fi
fi
os_major=${os_version%%.*}
case "$os_major" in
''|*[!0-9]*) return 1 ;;
esac
case "$os_id" in
ubuntu) [ "$os_major" -ge 15 ] && return 0 ;;
debian) [ "$os_major" -ge 8 ] && return 0 ;;
esac
if [ -x /usr/lib/systemd/systemd ] || [ -x /lib/systemd/systemd ]; then
return 0
fi
return 1
}
if xcat_target_uses_systemd && \
xcatd_init_state=/opt/xcat/share/xcat/scripts/xcatd-init-state
xcatd_init_compat=/opt/xcat/share/xcat/scripts/xcatd-init-compat
xcatd_legacy_init=/etc/init.d/xcatd
xcatd_state_dir=/var/lib/xcat/xcatd-init-state
xcatd_old_systemd_marker=/var/lib/xcat/xcatd-systemd-mode
xcatd_context_file=$xcatd_state_dir/context
write_xcatd_context()
(
context_value=$1
context_tmp="$xcatd_context_file.tmp.$$"
umask 077
if ! printf '%s\n' "$context_value" > "$context_tmp" ||
! mv -f "$context_tmp" "$xcatd_context_file"; then
rm -f "$context_tmp"
return 1
fi
)
clear_xcatd_pending_state()
{
rm -f "$xcatd_state_dir/pending-xcatd" \
"$xcatd_state_dir/pending-deleted" \
"$xcatd_state_dir/pending-enabled" \
"$xcatd_state_dir/pending-xcatd.tmp."*
}
xcatd_legacy_enable_state()
{
if [ -x "$xcatd_init_compat" ]; then
if detected_legacy_state=$(
"$xcatd_init_compat" debian-legacy-state 2>/dev/null
); then
case "$detected_legacy_state" in
enabled) printf '%s\n' yes ;;
disabled) printf '%s\n' no ;;
unregistered) printf '%s\n' unregistered ;;
*)
echo "Invalid xcatd legacy state: $detected_legacy_state" >&2
return 1
;;
esac
return
else
detector_status=$?
if [ "$detector_status" -ne 2 ]; then
echo "Unable to query xcatd Debian legacy state with $xcatd_init_compat" >&2
return "$detector_status"
fi
fi
fi
# Direct upgrades from a package without the Debian-specific command
# still need to capture registration before dpkg removes the conffile.
for rc_link in /etc/rc?.d/S??xcatd; do
if [ -e "$rc_link" ] || [ -L "$rc_link" ]; then
printf '%s\n' yes
return
fi
done
for rc_link in /etc/rc?.d/K??xcatd; do
if [ -e "$rc_link" ] || [ -L "$rc_link" ]; then
printf '%s\n' no
return
fi
done
printf '%s\n' unregistered
}
if xcat_target_uses_systemd; then
xcatd_uses_systemd=yes
else
xcatd_uses_systemd=no
fi
xcatd_transition_context=
case "$1" in
upgrade) xcatd_transition_context=upgrade ;;
install) xcatd_transition_context=fresh ;;
esac
if [ -n "$xcatd_transition_context" ] || [ "$1" = abort-upgrade ]; then
if [ -L "$xcatd_state_dir" ] || \
{ [ -e "$xcatd_state_dir" ] && [ ! -d "$xcatd_state_dir" ]; }; then
echo "Invalid xcatd init state directory: $xcatd_state_dir" >&2
exit 1
fi
mkdir -p "$xcatd_state_dir"
chmod 700 "$xcatd_state_dir"
fi
if [ -n "$xcatd_transition_context" ] && \
[ "$xcatd_uses_systemd" = yes ] && [ -x "$xcatd_init_state" ]; then
"$xcatd_init_state" prepare-systemd "$xcatd_transition_context"
elif [ -n "$xcatd_transition_context" ] && [ ! -x "$xcatd_init_state" ]; then
# Before the first package carrying the state helper is unpacked, save
# only the evidence that dpkg-maintscript-helper could remove.
recovery_candidate=
for candidate in \
"$xcatd_legacy_init" \
"$xcatd_legacy_init.dpkg-bak" \
"$xcatd_legacy_init.dpkg-backup" \
"$xcatd_legacy_init.dpkg-remove"; do
if [ -e "$candidate" ] || [ -L "$candidate" ]; then
recovery_candidate=$candidate
break
fi
done
known_legacy=no
if [ -e "$xcatd_state_dir/state" ] || \
[ -L "$xcatd_state_dir/state" ]; then
# Durable state from an earlier package is stronger than the
# legacy dpkg metadata left behind after removal.
clear_xcatd_pending_state
elif [ -n "$recovery_candidate" ]; then
clear_xcatd_pending_state
pending_tmp="$xcatd_state_dir/pending-xcatd.tmp.$$"
if ! cp -a "$recovery_candidate" "$pending_tmp" ||
! mv -f "$pending_tmp" "$xcatd_state_dir/pending-xcatd"; then
rm -f "$pending_tmp"
exit 1
fi
rm -f "$xcatd_old_systemd_marker"
known_legacy=yes
else
conffile_record=
if command -v dpkg-query >/dev/null 2>&1; then
conffile_record=$(
dpkg-query -W -f='${Conffiles}\n' xcat-server 2>/dev/null |
sed -n '\|^ /etc/init.d/xcatd |p'
)
fi
if [ -n "$conffile_record" ]; then
case "$conffile_record" in
*" obsolete"*)
# The installed package already removed the conffile.
# Record package omission rather than an admin deletion.
clear_xcatd_pending_state
touch "$xcatd_old_systemd_marker"
;;
*)
# A still-owned conffile that is absent was deleted by
# the administrator and must stay absent.
clear_xcatd_pending_state
rm -f "$xcatd_old_systemd_marker"
touch "$xcatd_state_dir/pending-deleted"
known_legacy=yes
;;
esac
else
# A retry without an installed conffile record must not reuse
# evidence captured by an earlier, interrupted installation.
clear_xcatd_pending_state
fi
fi
if [ "$known_legacy" = yes ]; then
xcatd_legacy_enable_state > "$xcatd_state_dir/pending-enabled"
fi
fi
if [ "$xcatd_uses_systemd" = yes ] && \
command -v dpkg-maintscript-helper >/dev/null 2>&1 && \
dpkg-maintscript-helper supports rm_conffile; then
dpkg-maintscript-helper rm_conffile /etc/init.d/xcatd -- "$@"
@@ -70,12 +268,14 @@ fi
case "$1" in
upgrade)
touch /tmp/xCAT-server_upgrade.tmp
write_xcatd_context upgrade
;;
install)
write_xcatd_context fresh
;;
abort-upgrade)
rm -f "$xcatd_context_file" "$xcatd_context_file.tmp."*
;;
*)
+3 -8
View File
@@ -5,11 +5,6 @@
set -e
xcat_can_use_systemctl()
{
[ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1
}
xcatd_init_compat=/opt/xcat/share/xcat/scripts/xcatd-init-compat
# summary of how this script can be called:
@@ -27,13 +22,13 @@ xcatd_init_compat=/opt/xcat/share/xcat/scripts/xcatd-init-compat
case "$1" in
remove)
if [ -f "/proc/cmdline" ]; then # prevent running it during install into chroot image
if xcat_can_use_systemctl; then
if "$xcatd_init_compat" can-use-systemctl; then
systemctl stop xcatd.service
elif ! "$xcatd_init_compat" uses-systemd && [ -x /etc/init.d/xcatd ]; then
elif ! "$xcatd_init_compat" uses-systemd --explicit-target && [ -x /etc/init.d/xcatd ]; then
/etc/init.d/xcatd stop
fi
fi
if "$xcatd_init_compat" uses-systemd; then
if "$xcatd_init_compat" uses-systemd --explicit-target; then
if command -v systemctl >/dev/null 2>&1; then
systemctl disable xcatd.service
fi
+701
View File
@@ -0,0 +1,701 @@
#!/bin/sh
# Preserve the Debian xcatd SysV configuration while the target uses systemd.
# The state file is parsed as data; it is never sourced.
set -e
compat_root=${XCAT_COMPAT_ROOT:-}
compat_root=${compat_root%/}
xcat_root=${XCATROOT:-/opt/xcat}
case "$xcat_root" in
/*) ;;
*)
echo "XCATROOT must be an absolute path" >&2
exit 2
;;
esac
legacy_init="$compat_root/etc/init.d/xcatd"
legacy_template="$compat_root$xcat_root/share/xcat/scripts/xcatd"
init_compat="$compat_root$xcat_root/share/xcat/scripts/xcatd-init-compat"
state_dir="$compat_root/var/lib/xcat/xcatd-init-state"
state_file="$state_dir/state"
state_stash="$state_dir/xcatd"
pending_stash="$state_dir/pending-xcatd"
pending_deleted="$state_dir/pending-deleted"
pending_enabled="$state_dir/pending-enabled"
old_systemd_marker="$compat_root/var/lib/xcat/xcatd-systemd-mode"
state_mode=unknown
state_content=unknown
state_enabled=unknown
state_origin=unknown
state_format=
read_state()
{
[ -r "$state_file" ] || return 1
state_mode=unknown
state_content=unknown
state_enabled=unknown
state_origin=unknown
state_format=
seen_format=0
seen_mode=0
seen_content=0
seen_enabled=0
seen_origin=0
while IFS='=' read -r state_key state_value; do
case "$state_key" in
format)
[ "$seen_format" -eq 0 ] || return 1
state_format=$state_value
seen_format=1
;;
mode)
[ "$seen_mode" -eq 0 ] || return 1
state_mode=$state_value
seen_mode=1
;;
content)
[ "$seen_content" -eq 0 ] || return 1
state_content=$state_value
seen_content=1
;;
enabled)
[ "$seen_enabled" -eq 0 ] || return 1
state_enabled=$state_value
seen_enabled=1
;;
origin)
[ "$seen_origin" -eq 0 ] || return 1
state_origin=$state_value
seen_origin=1
;;
'') ;;
*)
echo "Invalid xcatd init state key: $state_key" >&2
return 1
;;
esac
done < "$state_file"
if [ "$seen_format" -ne 1 ] || [ "$seen_mode" -ne 1 ] ||
[ "$seen_content" -ne 1 ] || [ "$seen_enabled" -ne 1 ] ||
[ "$seen_origin" -ne 1 ]; then
echo "Invalid xcatd init state format: ${state_format:-missing}" >&2
return 1
fi
case "$state_format" in
1|2) ;;
*)
echo "Invalid xcatd init state format: ${state_format:-missing}" >&2
return 1
;;
esac
case "$state_mode" in
legacy|systemd|transition-systemd|transition-legacy|unknown) ;;
*)
echo "Invalid xcatd init mode state: $state_mode" >&2
return 1
;;
esac
case "$state_content" in
active|stashed|deleted|package-default|unknown) ;;
*)
echo "Invalid xcatd init content state: $state_content" >&2
return 1
;;
esac
case "$state_enabled" in
yes|no|unknown) ;;
unregistered)
if [ "$state_format" = 2 ]; then
:
else
echo "Invalid xcatd init enablement state: $state_enabled" >&2
return 1
fi
;;
*)
echo "Invalid xcatd init enablement state: $state_enabled" >&2
return 1
;;
esac
case "$state_origin" in
fresh|legacy|systemd|unknown) ;;
*)
echo "Invalid xcatd init transition origin: $state_origin" >&2
return 1
;;
esac
# Format 1 collapsed registered-disabled and unregistered into "no",
# then restored that value by removing every SysV link. Preserve that
# behavior when recovering an interrupted pre-format-2 transaction.
if [ "$state_format" = 1 ] && [ "$state_enabled" = no ]; then
state_enabled=unregistered
fi
}
ensure_state_dir()
{
if [ -L "$state_dir" ] || { [ -e "$state_dir" ] && [ ! -d "$state_dir" ]; }; then
echo "Invalid xcatd init state directory: $state_dir" >&2
exit 1
fi
mkdir -p "$state_dir"
chmod 700 "$state_dir"
}
write_state()
{
new_mode=$1
new_content=$2
new_enabled=$3
new_origin=$4
ensure_state_dir
state_tmp="$state_file.tmp.$$"
if ! (
umask 077
printf 'format=2\nmode=%s\ncontent=%s\nenabled=%s\norigin=%s\n' \
"$new_mode" "$new_content" "$new_enabled" "$new_origin" > "$state_tmp" &&
mv -f "$state_tmp" "$state_file"
); then
rm -f "$state_tmp"
return 1
fi
state_mode=$new_mode
state_content=$new_content
state_enabled=$new_enabled
state_origin=$new_origin
}
legacy_init_exists()
{
[ -e "$legacy_init" ] || [ -L "$legacy_init" ]
}
find_recovery_candidate()
{
recovery_candidate=
for candidate in \
"$legacy_init" \
"$legacy_init.dpkg-bak" \
"$legacy_init.dpkg-backup" \
"$legacy_init.dpkg-remove"; do
if [ -e "$candidate" ] || [ -L "$candidate" ]; then
recovery_candidate=$candidate
return 0
fi
done
return 1
}
stash_candidate()
{
stash_source=$1
ensure_state_dir
stash_tmp="$state_stash.tmp.$$"
rm -f "$stash_tmp"
if ! cp -a "$stash_source" "$stash_tmp" ||
! mv -f "$stash_tmp" "$state_stash"; then
rm -f "$stash_tmp"
return 1
fi
}
shared_init_state()
{
if [ ! -x "$init_compat" ]; then
echo "Missing xcatd init compatibility helper: $init_compat" >&2
return 1
fi
"$init_compat" "$@"
}
legacy_enable_state()
{
if ! detected_legacy_state=$(shared_init_state debian-legacy-state); then
return 1
fi
case "$detected_legacy_state" in
enabled) printf '%s\n' yes ;;
disabled) printf '%s\n' no ;;
unregistered) printf '%s\n' unregistered ;;
*)
echo "Invalid legacy state from $init_compat: $detected_legacy_state" >&2
return 1
;;
esac
}
systemd_enable_state()
{
if ! detected_systemd_state=$(shared_init_state systemd-state --allow-unknown); then
return 1
fi
case "$detected_systemd_state" in
enabled) printf '%s\n' yes ;;
disabled) printf '%s\n' no ;;
masked) printf '%s\n' unregistered ;;
unknown) printf '%s\n' unknown ;;
*)
echo "Invalid systemd state from $init_compat: $detected_systemd_state" >&2
return 1
;;
esac
}
validate_context()
{
case "$1" in
fresh|upgrade) ;;
*)
echo "Invalid xcatd init transition context: $1" >&2
exit 2
;;
esac
}
prepare_systemd()
{
transition_context=$1
validate_context "$transition_context"
if [ -e "$state_file" ]; then
read_state
had_state=yes
else
had_state=no
state_mode=unknown
state_content=unknown
state_enabled=unknown
state_origin=unknown
if [ -f "$old_systemd_marker" ]; then
state_mode=systemd
state_content=package-default
fi
fi
previous_mode=$state_mode
previous_origin=$state_origin
known_legacy=no
case "$previous_mode" in
systemd|transition-systemd|transition-legacy) from_systemd=yes ;;
*) from_systemd=no ;;
esac
case "$previous_mode" in
transition-systemd|transition-legacy) transition_origin=$previous_origin ;;
systemd) transition_origin=systemd ;;
legacy) transition_origin=legacy ;;
*)
if [ "$transition_context" = fresh ]; then
transition_origin=fresh
else
transition_origin=systemd
fi
;;
esac
detected_systemd_enabled=$(systemd_enable_state)
if [ "$had_state" = no ] && \
{ [ -e "$pending_stash" ] || [ -L "$pending_stash" ]; }; then
stash_candidate "$pending_stash"
state_content=stashed
known_legacy=yes
transition_origin=legacy
elif [ "$had_state" = no ] && [ -f "$pending_deleted" ]; then
rm -f "$state_stash"
state_content=deleted
known_legacy=yes
transition_origin=legacy
elif [ "$previous_mode" = transition-legacy ]; then
case "$state_content" in
stashed)
if [ ! -e "$state_stash" ] && [ ! -L "$state_stash" ]; then
echo "Stashed xcatd init script is missing: $state_stash" >&2
exit 1
fi
;;
active)
if legacy_init_exists; then
stash_candidate "$legacy_init"
state_content=stashed
else
echo "Active xcatd init transition has no recoverable script" >&2
exit 1
fi
;;
esac
elif legacy_init_exists; then
stash_candidate "$legacy_init"
state_content=stashed
if [ "$from_systemd" = no ]; then
known_legacy=yes
transition_origin=legacy
fi
elif [ "$from_systemd" = no ] && find_recovery_candidate; then
stash_candidate "$recovery_candidate"
state_content=stashed
known_legacy=yes
transition_origin=legacy
elif [ "$previous_mode" = legacy ]; then
rm -f "$state_stash"
state_content=deleted
known_legacy=yes
transition_origin=legacy
elif [ "$from_systemd" = no ] && [ "$transition_context" = fresh ]; then
rm -f "$state_stash"
state_content=package-default
transition_origin=fresh
elif [ "$from_systemd" = no ]; then
rm -f "$state_stash"
if [ "$detected_systemd_enabled" = yes ]; then
state_content=package-default
else
state_content=unknown
fi
fi
if [ "$detected_systemd_enabled" = unregistered ]; then
state_enabled=unregistered
transition_origin=systemd
elif [ "$transition_context" = fresh ]; then
state_enabled=yes
transition_origin=fresh
elif [ "$known_legacy" = yes ]; then
if [ -r "$pending_enabled" ]; then
pending_enabled_value=$(sed -n '1p' "$pending_enabled")
else
pending_enabled_value=unknown
fi
case "$pending_enabled_value" in
yes|no|unregistered) legacy_enabled=$pending_enabled_value ;;
*) legacy_enabled=$(legacy_enable_state) ;;
esac
if [ "$legacy_enabled" = yes ]; then
state_enabled=yes
elif [ "$had_state" = no ]; then
if [ "$detected_systemd_enabled" = yes ]; then
state_enabled=yes
else
state_enabled=$legacy_enabled
fi
else
state_enabled=$legacy_enabled
fi
elif { [ "$previous_mode" = transition-systemd ] ||
[ "$previous_mode" = transition-legacy ]; } && \
[ "$state_enabled" != unknown ]; then
:
else
case "$detected_systemd_enabled" in
yes) state_enabled=yes ;;
no)
if [ "$state_enabled" != unregistered ]; then
state_enabled=no
fi
;;
unregistered) state_enabled=unregistered ;;
esac
fi
write_state transition-systemd "$state_content" "$state_enabled" "$transition_origin"
rm -f "$pending_stash" "$pending_deleted" "$pending_enabled"
}
commit_systemd()
{
read_state
write_state systemd "$state_content" "$state_enabled" unknown
rm -f "$old_systemd_marker"
}
apply_legacy_registration()
{
command -v update-rc.d >/dev/null 2>&1 || return 0
case "$state_content" in
deleted|unknown)
update-rc.d -f xcatd remove
return
;;
esac
if [ -x "$legacy_init" ]; then
case "$state_enabled" in
yes)
update-rc.d xcatd defaults
;;
no)
update-rc.d xcatd defaults
if ! update-rc.d xcatd disable; then
# defaults preserves any existing registration, including
# links outside xcatd's declared start runlevels that the
# native disable operation cannot toggle. Canonicalize
# only after that unusable layout is rejected.
update-rc.d -f xcatd remove
update-rc.d xcatd defaults
update-rc.d xcatd disable
fi
;;
unregistered|unknown)
update-rc.d -f xcatd remove
;;
esac
else
update-rc.d -f xcatd remove
fi
}
configure_legacy()
{
transition_context=$1
validate_context "$transition_context"
returning_from_systemd=no
refresh_systemd_enabled=no
resuming_legacy_transition=no
resuming_stashed_transition=no
stable_legacy_upgrade=no
detected_systemd_enabled=unknown
transition_origin=legacy
if [ -e "$state_file" ]; then
read_state
had_state=yes
if [ "$state_mode" = legacy ] && \
[ "$transition_context" = upgrade ]; then
stable_legacy_upgrade=yes
else
detected_systemd_enabled=$(systemd_enable_state)
fi
if [ "$state_mode" = transition-legacy ]; then
resuming_legacy_transition=yes
if [ "$state_content" = stashed ] &&
{ [ -e "$state_stash" ] || [ -L "$state_stash" ]; }; then
resuming_stashed_transition=yes
fi
fi
case "$state_mode" in
systemd)
returning_from_systemd=yes
refresh_systemd_enabled=yes
transition_origin=systemd
;;
transition-systemd)
returning_from_systemd=yes
transition_origin=$state_origin
;;
transition-legacy)
transition_origin=$state_origin
returning_from_systemd=yes
;;
legacy)
transition_origin=legacy
if legacy_init_exists; then
state_content=active
else
state_content=deleted
fi
if [ "$transition_context" = upgrade ]; then
state_enabled=$(legacy_enable_state)
fi
;;
esac
else
had_state=no
detected_systemd_enabled=$(systemd_enable_state)
state_mode=unknown
state_content=unknown
state_enabled=unknown
state_origin=unknown
if [ -e "$pending_stash" ] || [ -L "$pending_stash" ]; then
stash_candidate "$pending_stash"
state_content=stashed
transition_origin=legacy
elif [ -f "$pending_deleted" ]; then
rm -f "$state_stash"
state_content=deleted
transition_origin=legacy
elif [ -f "$old_systemd_marker" ]; then
returning_from_systemd=yes
refresh_systemd_enabled=yes
transition_origin=systemd
state_content=package-default
elif find_recovery_candidate; then
if [ "$recovery_candidate" = "$legacy_init" ]; then
state_content=active
transition_origin=legacy
else
stash_candidate "$recovery_candidate"
state_content=stashed
returning_from_systemd=yes
refresh_systemd_enabled=yes
transition_origin=systemd
fi
elif [ "$transition_context" = fresh ]; then
state_content=package-default
state_enabled=yes
transition_origin=fresh
else
returning_from_systemd=yes
refresh_systemd_enabled=yes
transition_origin=systemd
state_enabled=$detected_systemd_enabled
if [ "$detected_systemd_enabled" = yes ]; then
state_content=package-default
fi
fi
fi
if legacy_init_exists && [ "$resuming_legacy_transition" = no ]; then
case "$returning_from_systemd:$state_content" in
yes:*|*:active)
stash_candidate "$legacy_init"
state_content=stashed
;;
esac
fi
if [ "$stable_legacy_upgrade" = yes ]; then
:
elif [ "$detected_systemd_enabled" = unregistered ]; then
state_enabled=unregistered
transition_origin=systemd
elif [ "$transition_context" = fresh ]; then
state_enabled=yes
transition_origin=fresh
elif [ "$had_state" = no ]; then
if [ -r "$pending_enabled" ]; then
pending_enabled_value=$(sed -n '1p' "$pending_enabled")
else
pending_enabled_value=unknown
fi
case "$pending_enabled_value" in
yes|no|unregistered) legacy_enabled=$pending_enabled_value ;;
*) legacy_enabled=$(legacy_enable_state) ;;
esac
if [ "$legacy_enabled" = yes ] || \
[ "$detected_systemd_enabled" = yes ]; then
state_enabled=yes
elif [ "$transition_origin" = legacy ] && \
{ [ "$legacy_enabled" = no ] || \
[ "$legacy_enabled" = unregistered ]; }; then
state_enabled=$legacy_enabled
elif [ "$detected_systemd_enabled" = no ]; then
state_enabled=no
elif [ "$detected_systemd_enabled" = unregistered ]; then
state_enabled=unregistered
else
state_enabled=$legacy_enabled
fi
elif [ "$refresh_systemd_enabled" = yes ]; then
case "$detected_systemd_enabled" in
yes) state_enabled=yes ;;
no)
if [ "$state_enabled" != unregistered ]; then
state_enabled=no
fi
;;
unregistered) state_enabled=unregistered ;;
esac
fi
write_state transition-legacy "$state_content" "$state_enabled" "$transition_origin"
case "$state_content" in
stashed)
if [ ! -e "$state_stash" ] && [ ! -L "$state_stash" ]; then
echo "Stashed xcatd init script is missing: $state_stash" >&2
exit 1
fi
if [ "$resuming_stashed_transition" = yes ]; then
rm -f "$legacy_init"
mkdir -p "$compat_root/etc/init.d"
cp -a "$state_stash" "$legacy_init"
elif ! legacy_init_exists; then
mkdir -p "$compat_root/etc/init.d"
cp -a "$state_stash" "$legacy_init"
fi
ucf "$legacy_template" "$legacy_init"
;;
active)
if legacy_init_exists; then
ucf "$legacy_template" "$legacy_init"
fi
;;
package-default)
mkdir -p "$compat_root/etc/init.d"
if [ "$resuming_legacy_transition" = yes ]; then
rm -f "$legacy_init"
fi
UCF_FORCE_CONFFMISS=1 ucf "$legacy_template" "$legacy_init"
;;
deleted)
rm -f "$legacy_init"
;;
unknown)
echo "xcat-server: prior xcatd init state is ambiguous; preserving its absence" >&2
;;
esac
ucfr xcat-server "$legacy_init"
if [ "$transition_context" = fresh ] || \
[ "$returning_from_systemd" = yes ] || [ "$had_state" = no ]; then
apply_legacy_registration
fi
if legacy_init_exists; then
final_content=active
else
final_content=deleted
fi
final_enabled=$(legacy_enable_state)
write_state legacy "$final_content" "$final_enabled" unknown
rm -f "$state_stash" "$old_systemd_marker" \
"$pending_stash" "$pending_deleted" "$pending_enabled" \
"$legacy_init.dpkg-bak" "$legacy_init.dpkg-backup" \
"$legacy_init.dpkg-remove"
}
get_state()
{
requested_field=$1
read_state
case "$requested_field" in
mode) printf '%s\n' "$state_mode" ;;
content) printf '%s\n' "$state_content" ;;
enabled) printf '%s\n' "$state_enabled" ;;
origin) printf '%s\n' "$state_origin" ;;
*)
echo "Usage: $0 get {mode|content|enabled|origin}" >&2
exit 2
;;
esac
}
case "${1:-}" in
prepare-systemd)
prepare_systemd "${2:-}"
;;
commit-systemd)
[ "$#" -eq 1 ] || exit 2
commit_systemd
;;
configure-legacy)
configure_legacy "${2:-}"
;;
get)
get_state "${2:-}"
;;
*)
echo "Usage: $0 {prepare-systemd {fresh|upgrade}|commit-systemd|configure-legacy {fresh|upgrade}|get {mode|content|enabled|origin}}" >&2
exit 2
;;
esac
+9
View File
@@ -0,0 +1,9 @@
be74fa94c7cae192b0522b094ab13f88 2.7.8
106298b7314976e37b61359ac0639f47 2.8.3
22b3d638134b58d249337c7193466f91 2.8.4
ec7700a6317954bd773bb07bc3d352f4 2.9.2_release
7f7b7bf464770c6ed6c6433c340de757 2.12
78730e365b6861c45fedb51f8359bb37 2.12.1
d0f83472c8ad6d9a5aa9a64e50bb5dc5 2.12.2
0b1eea60994ff79faa9a8d0bcd53c558 2.17.0
8797eef6731719e0eacff59612b3e916 2.18.0
+120 -30
View File
@@ -130,22 +130,32 @@ can_use_systemctl()
legacy_state()
{
for legacy_link in \
"$compat_root"/etc/rc.d/rc?.d/S??xcatd \
"$compat_root"/etc/rc?.d/S??xcatd \
"$compat_root"/etc/init.d/rc?.d/S??xcatd
do
detection_mode=${1:-compat}
if [ "$detection_mode" = debian-layout ]; then
set -- "$compat_root"/etc/rc?.d/S??xcatd
else
set -- \
"$compat_root"/etc/rc.d/rc?.d/S??xcatd \
"$compat_root"/etc/rc?.d/S??xcatd \
"$compat_root"/etc/init.d/rc?.d/S??xcatd
fi
for legacy_link in "$@"; do
if [ -e "$legacy_link" ] || [ -L "$legacy_link" ]; then
echo enabled
return
fi
done
for legacy_link in \
"$compat_root"/etc/rc.d/rc?.d/K??xcatd \
"$compat_root"/etc/rc?.d/K??xcatd \
"$compat_root"/etc/init.d/rc?.d/K??xcatd
do
if [ "$detection_mode" = debian-layout ]; then
set -- "$compat_root"/etc/rc?.d/K??xcatd
else
set -- \
"$compat_root"/etc/rc.d/rc?.d/K??xcatd \
"$compat_root"/etc/rc?.d/K??xcatd \
"$compat_root"/etc/init.d/rc?.d/K??xcatd
fi
for legacy_link in "$@"; do
if [ -e "$legacy_link" ] || [ -L "$legacy_link" ]; then
echo disabled
return
@@ -184,7 +194,7 @@ legacy_transition_state()
esac
}
systemd_state()
systemd_is_masked()
{
for systemd_mask in \
"$compat_root/etc/systemd/system/xcatd.service" \
@@ -194,27 +204,17 @@ systemd_state()
systemd_mask_target=$(readlink "$systemd_mask" 2>/dev/null || true)
case "$systemd_mask_target" in
/dev/null|*/dev/null)
echo masked
return
return 0
;;
esac
fi
done
if [ -z "$compat_root" ] && command -v systemctl >/dev/null 2>&1; then
detected_systemd_state=$(systemctl is-enabled xcatd.service 2>/dev/null || true)
case "$detected_systemd_state" in
enabled*)
echo enabled
return
;;
masked*)
echo masked
return
;;
esac
fi
return 1
}
systemd_links_enabled()
{
for systemd_link in \
"$compat_root"/etc/systemd/system/*.wants/xcatd.service \
"$compat_root"/etc/systemd/system/*.requires/xcatd.service \
@@ -222,11 +222,90 @@ systemd_state()
"$compat_root"/run/systemd/system/*.requires/xcatd.service
do
if [ -e "$systemd_link" ] || [ -L "$systemd_link" ]; then
echo enabled
return
return 0
fi
done
return 1
}
systemctl_enable_state()
{
detection_mode=${1:-compat}
command -v systemctl >/dev/null 2>&1 || return 1
if [ -n "$compat_root" ]; then
detected_systemd_state=$(systemctl --root="$compat_root" is-enabled xcatd.service 2>/dev/null || true)
else
detected_systemd_state=$(systemctl is-enabled xcatd.service 2>/dev/null || true)
fi
if [ "$detection_mode" = precise ]; then
case "$detected_systemd_state" in
enabled|enabled-runtime) echo enabled ;;
masked|masked-runtime) echo masked ;;
disabled|linked|linked-runtime|alias|static|indirect|generated|transient)
echo disabled
;;
*) return 1 ;;
esac
else
case "$detected_systemd_state" in
enabled*) echo enabled ;;
masked*) echo masked ;;
disabled|linked|linked-runtime|alias|static|indirect|generated|transient)
echo disabled
;;
*) return 1 ;;
esac
fi
}
systemd_state()
{
detection_mode=${1:-compat}
detected_systemd_state=
if systemd_is_masked; then
echo masked
return
fi
if [ "$detection_mode" = allow-unknown ]; then
detected_systemd_state=$(systemctl_enable_state precise || true)
if [ "$detected_systemd_state" = masked ]; then
echo masked
return
fi
fi
# Preserve the historical host-first order for existing callers.
if [ "$detection_mode" = compat ] && [ -z "$compat_root" ]; then
detected_systemd_state=$(systemctl_enable_state || true)
case "$detected_systemd_state" in
enabled|masked)
echo "$detected_systemd_state"
return
;;
esac
fi
if systemd_links_enabled; then
echo enabled
return
fi
if [ "$detection_mode" = allow-unknown ]; then
case "$detected_systemd_state" in
enabled|disabled)
echo "$detected_systemd_state"
return
;;
esac
echo unknown
return
fi
echo disabled
}
@@ -412,11 +491,22 @@ case "${1:-}" in
legacy-state)
legacy_state
;;
debian-legacy-state)
if [ "$#" -ne 1 ]; then
echo "Usage: $0 debian-legacy-state" >&2
exit 2
fi
legacy_state debian-layout
;;
legacy-transition-state)
legacy_transition_state
;;
systemd-state)
systemd_state
if [ "$#" -eq 2 ] && [ "${2:-}" = --allow-unknown ]; then
systemd_state allow-unknown
else
systemd_state
fi
;;
register-legacy)
case "${2:-}" in
@@ -455,7 +545,7 @@ case "${1:-}" in
remove_managed_legacy_init
;;
*)
echo "Usage: $0 {uses-systemd [--explicit-target]|can-use-systemctl|legacy-state|legacy-transition-state|systemd-state|register-legacy {default|enabled|disabled}|unregister-legacy|disable-systemd [--links-only]|unregister-all|configure [--replace] [--explicit-target] [--track-managed]|remove|remove-managed}" >&2
echo "Usage: $0 {uses-systemd [--explicit-target]|can-use-systemctl|legacy-state|debian-legacy-state|legacy-transition-state|systemd-state [--allow-unknown]|register-legacy {default|enabled|disabled}|unregister-legacy|disable-systemd [--links-only]|unregister-all|configure [--replace] [--explicit-target] [--track-managed]|remove|remove-managed}" >&2
exit 2
;;
esac
@@ -0,0 +1,102 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Digest::MD5 qw(md5_hex);
use File::Spec;
use FindBin;
use Test::More;
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
sub read_file {
my ($path) = @_;
open( my $fh, '<', $path ) or die "Unable to read $path: $!";
binmode($fh);
my $contents = do { local $/; <$fh> };
close($fh);
return $contents;
}
my $deb_install = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'install' )
);
my $dpkg_managed =
$deb_install =~ qr{^etc/init\.d/xcatd etc/init\.d$}m;
my $ucf_managed =
$deb_install =~ qr{^etc/init\.d/xcatd opt/xcat/share/xcat/scripts$}m;
ok( $dpkg_managed || $ucf_managed,
'Debian selects a configuration-aware legacy init backend' );
if ($dpkg_managed) {
pass('dpkg owns the directly packaged legacy init conffile');
done_testing();
exit;
}
my $deb_control = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'control' )
);
like( $deb_control, qr{^Depends:.*(?:,\s+|^)ucf(?:,|$)}m,
'the generated-conffile backend depends on ucf' );
like( $deb_install,
qr{^debian/xcatd\.md5sum opt/xcat/share/xcat/scripts$}m,
'historical template checksums are installed beside the template' );
my $sum_path = File::Spec->catfile(
$repo_root, 'xCAT-server', 'debian', 'xcatd.md5sum'
);
ok( -f $sum_path, 'the Debian package carries historical template checksums' );
my $sums = -f $sum_path ? read_file($sum_path) : '';
like( $sums, qr{^0b1eea60994ff79faa9a8d0bcd53c558\s+2\.17\.0$}m,
'the last pre-transition release is recognized as unmodified' );
my $template = read_file(
File::Spec->catfile(
$repo_root, 'xCAT-server', 'etc', 'init.d', 'xcatd'
)
);
like( $sums, qr{^\Q@{[ md5_hex($template) ]}\E\s+2\.18\.0$}m,
'the current released template is recognized as unmodified' );
my $postinst = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'postinst' )
);
like( $postinst,
qr{"\$xcatd_init_state" configure-legacy "\$xcatd_transition_context"},
'postinst delegates legacy conffile transitions to the state helper' );
my $state_helper = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'xcatd-init-state' )
);
like( $state_helper, qr{ucf "\$legacy_template" "\$legacy_init"},
'legacy configuration delegates updates to ucf' );
like( $state_helper, qr{ucfr xcat-server "\$legacy_init"},
'legacy configuration registers ucf ownership' );
like( $state_helper, qr{state_content=stashed.*?stash_candidate}s,
'a present legacy script is represented by an exact stash' );
like( $state_helper, qr{state_content=deleted},
'administrator deletion has a distinct persistent state' );
like( $state_helper,
qr{package-default\).*?UCF_FORCE_CONFFMISS=1 ucf}s,
'only known package omission forces recreation of a missing script' );
like( $state_helper,
qr{state_content=unknown.*?preserving its absence}s,
'ambiguous markerless upgrades preserve absence instead of guessing' );
my $postrm = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'postrm' )
);
like( $postrm,
qr{xcatd\.ucf-old.*?xcatd\.ucf-new.*?xcatd\.ucf-dist}s,
'purge removes ucf backup artifacts' );
like( $postrm, qr{ucf --purge /etc/init\.d/xcatd},
'purge removes ucf checksum state' );
like( $postrm, qr{ucfr --purge xcat-server /etc/init\.d/xcatd},
'purge removes the ucf ownership registration' );
like( $postrm,
qr{xcatd_state_dir=/var/lib/xcat/xcatd-init-state.*?(?:/var/lib/xcat/xcatd-init-state/state|"\$xcatd_state_dir/state")}s,
'purge removes the persistent init transition state' );
done_testing();
File diff suppressed because it is too large Load Diff
+268 -4
View File
@@ -2,7 +2,9 @@
use strict;
use warnings;
use File::Path qw(make_path);
use File::Spec;
use File::Temp qw(tempdir);
use FindBin;
use Test::More;
@@ -16,6 +18,69 @@ sub read_file {
return $contents;
}
sub init_classifier_block {
my ($script) = @_;
return $1
if $script =~
/(xcat_systemd_os_release_status\(\).*?\n}\n\nxcat_target_uses_systemd\(\).*?\n})\n\n/s;
return '';
}
sub pending_cleanup_block {
my ($script) = @_;
return $1
if $script =~ /(clear_xcatd_pending_state\(\)\n\{.*?\n\})\n/s;
return '';
}
sub legacy_state_block {
my ($script) = @_;
return $1
if $script =~
/(xcatd_legacy_enable_state\(\)\n\{.*?\n\})\n\nif xcat_target_uses_systemd/s;
return '';
}
sub run_legacy_state_block {
my ( $block, $helper_source ) = @_;
my $root = tempdir( CLEANUP => 1 );
my $helper = File::Spec->catfile( $root, 'xcatd-init-compat' );
open( my $helper_fh, '>', $helper )
or die "Unable to stage compatibility helper: $!";
print {$helper_fh} $helper_source;
close($helper_fh);
chmod 0755, $helper;
my $rpm_runlevel =
File::Spec->catdir( $root, 'etc', 'rc.d', 'rc3.d' );
make_path($rpm_runlevel);
symlink( '/etc/init.d/xcatd',
File::Spec->catfile( $rpm_runlevel, 'S85xcatd' ) )
or die "Unable to stage RPM-only runlevel link: $!";
my $isolated_block = $block;
$isolated_block =~ s{/etc/rc}{\$fixture_root/etc/rc}g;
my $runner = File::Spec->catfile( $root, 'run-detector' );
open( my $runner_fh, '>', $runner )
or die "Unable to stage detector runner: $!";
print {$runner_fh} "#!/bin/sh\nset -e\n";
print {$runner_fh} "fixture_root=\${XCAT_TEST_ROOT:?}\n";
print {$runner_fh} "xcatd_init_compat=\${XCAT_TEST_HELPER:?}\n";
print {$runner_fh} "$isolated_block\n\nxcatd_legacy_enable_state\n";
close($runner_fh);
chmod 0755, $runner;
local $ENV{XCAT_TEST_ROOT} = $root;
local $ENV{XCAT_TEST_HELPER} = $helper;
open( my $pipe, '-|', '/bin/sh', $runner )
or die "Unable to run preinst detector: $!";
my $output = do { local $/; <$pipe> };
close($pipe);
my $status = $? >> 8;
$output =~ s/\s+\z//;
return ( $status, $output );
}
my $deb_install = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'install' )
);
@@ -45,31 +110,230 @@ my $deb_postinst = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'postinst' )
);
like( $deb_postinst,
qr{if "\$xcatd_init_compat" uses-systemd; then},
qr{if "\$xcatd_init_compat" uses-systemd --explicit-target; then},
'Debian postinst delegates target init detection to the packaged helper' );
unlike( $deb_postinst,
qr{"\$xcatd_init_compat" (?:uses-systemd|configure)(?! --explicit-target)},
'every Debian postinst helper call uses explicit target detection' );
unlike( $deb_postinst, qr{sub xcat_target_uses_systemd|xcat_target_uses_systemd\(\)},
'Debian postinst does not carry weaker duplicate init detection' );
my $deb_init_state = read_file(
File::Spec->catfile(
$repo_root, 'xCAT-server', 'debian', 'xcatd-init-state'
)
);
like( $deb_init_state,
qr{\[ -x "\$legacy_init" \].*?update-rc\.d xcatd defaults}s,
'Debian registers SysV only after materializing an executable script' );
like( $deb_init_state,
qr{apply_legacy_registration\(\).*?no\).*?update-rc\.d xcatd defaults.*?update-rc\.d xcatd disable.*?unregistered\|unknown\).*?update-rc\.d -f xcatd remove}s,
'Debian distinguishes registered-disabled from unregistered SysV state' );
like( $deb_init_state,
qr{stash_candidate\(\).*?if ! cp -a.*?\|\|\s*! mv -f.*?rm -f "\$stash_tmp".*?return 1}s,
'Debian removes a failed atomic stash temporary' );
like( $deb_init_state, qr{printf 'format=2},
'Debian writes registration-aware state format 2' );
like( $deb_init_state,
qr{state_format" = 1.*?state_enabled" = no.*?state_enabled=unregistered}s,
'Debian preserves format-1 remove-all-links behavior' );
my @unregistered_refresh_guards =
$deb_init_state =~ /state_enabled" != unregistered/g;
is( scalar @unregistered_refresh_guards, 2,
'both systemd refresh paths retain unregistered provenance' );
like( $deb_init_state,
qr{init_compat=.*?xcatd-init-compat.*?shared_init_state\(\).*?"\$init_compat" "\$\@".*?debian-legacy-state.*?systemd-state --allow-unknown}s,
'Debian delegates legacy and precise systemd state detection to the shared helper' );
like( $deb_init_state,
qr{if ! detected_legacy_state=.*?return 1.*?if ! detected_systemd_state=.*?return 1}s,
'Debian propagates failures from both shared state probes' );
unlike( $deb_init_state,
qr{systemctl (?:--root=.*? )?is-enabled|systemd/system/\*\.(?:wants|requires)/xcatd\.service|rc\?\.d/S\?\?xcatd},
'Debian init state does not duplicate shared service-state probes' );
unlike( $deb_init_state, qr{systemd_is_masked},
'Debian does not hide detector failures in a boolean mask probe' );
like( $deb_postinst,
qr{\[ -x /etc/init\.d/xcatd \].*?update-rc\.d xcatd defaults}s,
'Debian postinst registers SysV only after materializing an executable script' );
qr{transition_origin=.*?get origin.*?fresh:yes\|legacy:yes.*?systemctl enable}s,
'Debian changes systemd enablement only for fresh installs and init transitions' );
like( $deb_postinst,
qr{legacy:no\|legacy:unregistered\) systemctl disable xcatd\.service},
'Debian maps both disabled SysV registration states to systemd disablement' );
unlike( $deb_postinst, qr{systemd:yes.*?systemctl enable}s,
'Debian same-systemd upgrades do not normalize existing enablement' );
my %classifier_blocks;
foreach my $fallback_script (qw(preinst postrm)) {
my $script = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', $fallback_script )
);
$classifier_blocks{$fallback_script} = init_classifier_block($script);
ok( $classifier_blocks{$fallback_script} ne '',
"Debian $fallback_script classifier can be extracted" );
like( $script, qr{VERSION_ID.*?ubuntu.*?-ge 15.*?debian.*?-ge 8}s,
"Debian $fallback_script recognizes stripped modern targets" );
like( $script, qr{\[ "\$os_id" = "debian" \].*?\[ -z "\$os_version" \]}s,
"Debian $fallback_script recognizes testing and sid without VERSION_ID" );
like( $script, qr{value=\$\{value#\\'\}\s+value=\$\{value%\\'\}}s,
"Debian $fallback_script strips single-quoted os-release values" );
like( $script,
qr{xcat_target_uses_systemd\(\).*?\[ -d /run/systemd/system \].*?\[ -L /sbin/init \].*?\*\) return 1 ;;.*?\[ -e /sbin/init \].*?return 1.*?if xcat_systemd_os_release_status; then.*?\[ -x /usr/lib/systemd/systemd \]}s,
"Debian $fallback_script prefers active and explicit target evidence" );
like( $script,
qr{xcat_systemd_os_release_status\(\).*?return 2.*?return 1.*?return 2}s,
"Debian $fallback_script distinguishes legacy from unknown releases" );
}
my $deb_preinst = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'preinst' )
);
my $preinst_legacy_state_block = legacy_state_block($deb_preinst);
ok( $preinst_legacy_state_block ne '',
'preinst legacy registration detector can be extracted' );
is_deeply(
[ run_legacy_state_block( $preinst_legacy_state_block, <<'SH' ) ],
#!/bin/sh
root=${XCAT_TEST_ROOT:?}
case "${1:-}" in
legacy-state)
for link in "$root"/etc/rc.d/rc?.d/S??xcatd; do
if [ -e "$link" ] || [ -L "$link" ]; then
printf '%s\n' enabled
exit 0
fi
done
printf '%s\n' unregistered
;;
*) exit 2 ;;
esac
SH
[ 0, 'unregistered' ],
'preinst falls back to Debian links when the installed helper lacks the command'
);
is_deeply(
[ run_legacy_state_block( $preinst_legacy_state_block, <<'SH' ) ],
#!/bin/sh
root=${XCAT_TEST_ROOT:?}
case "${1:-}:$#" in
debian-legacy-state:1)
for link in "$root"/etc/rc?.d/S??xcatd; do
if [ -e "$link" ] || [ -L "$link" ]; then
printf '%s\n' enabled
exit 0
fi
done
printf '%s\n' unregistered
;;
*) exit 2 ;;
esac
SH
[ 0, 'unregistered' ],
'preinst uses Debian-only state from a command-aware installed helper'
);
my ( $malformed_helper_status, $malformed_helper_output ) =
run_legacy_state_block( $preinst_legacy_state_block, <<'SH' );
#!/bin/sh
[ "${1:-}" = debian-legacy-state ] || exit 2
printf '%s\n' malformed
SH
is( $malformed_helper_status, 1,
'preinst rejects malformed successful helper output' );
is( $malformed_helper_output, '',
'preinst does not promote malformed helper output' );
my ( $failed_helper_status, $failed_helper_output ) =
run_legacy_state_block( $preinst_legacy_state_block, <<'SH' );
#!/bin/sh
exit 7
SH
is( $failed_helper_status, 7,
'preinst fails closed on non-capability helper errors' );
is( $failed_helper_output, '',
'preinst does not fall back after a real helper failure' );
like( $deb_preinst,
qr{write_xcatd_context\(\)\s*\(.*?umask 077.*?\n\)}s,
'preinst confines the context-file umask to a subshell' );
like( $deb_preinst,
qr{context_tmp=.*?if ! printf.*?\|\|\s*! mv -f.*?rm -f "\$context_tmp".*?return 1}s,
'preinst removes a failed context-file temporary' );
my $preinst_pending_cleanup = pending_cleanup_block($deb_preinst);
like( $preinst_pending_cleanup,
qr{pending-xcatd.*?pending-deleted.*?pending-enabled.*?pending-xcatd\.tmp\.}s,
'preinst clears every transaction-local pending state artifact' );
like( $deb_preinst,
qr{conffile_record=.*?dpkg-query.*?case "\$conffile_record" in.*?obsolete.*?touch "\$xcatd_old_systemd_marker".*?\*\).*?touch "\$xcatd_state_dir/pending-deleted"}s,
'preinst distinguishes package-obsolete conffiles from administrator deletion' );
like( $deb_preinst,
qr{xcatd_transition_context=\s*case "\$1" in\s*upgrade\) xcatd_transition_context=upgrade ;;\s*install\) xcatd_transition_context=fresh ;;\s*esac\s*if \[ -n "\$xcatd_transition_context" \]}s,
'preinst prepares transition state only for install and upgrade calls' );
like( $deb_preinst,
qr{\$xcatd_legacy_init\.dpkg-bak.*?\$xcatd_legacy_init\.dpkg-backup.*?\$xcatd_legacy_init\.dpkg-remove}s,
'preinst recovers every conffile backup name used during interrupted removal' );
like( $deb_preinst,
qr{pending_tmp=.*?if ! cp -a.*?\|\|\s*! mv -f.*?rm -f "\$pending_tmp".*?exit 1}s,
'preinst removes a failed pending-stash temporary' );
like( $deb_preinst,
qr{if \[ -n "\$conffile_record" \]; then.*?case "\$conffile_record" in.*?esac\s*else\s*# A retry without an installed conffile record.*?clear_xcatd_pending_state\s*fi}s,
'preinst discards stale pending evidence when dpkg has no conffile record' );
like( $deb_preinst,
qr{xcatd_legacy_enable_state\(\).*?"\$xcatd_init_compat" debian-legacy-state 2>/dev/null.*?detector_status=\$\?.*?"\$detector_status" -ne 2.*?/etc/rc\?\.d/S\?\?xcatd.*?/etc/rc\?\.d/K\?\?xcatd.*?unregistered}s,
'preinst reuses shared registration detection with a pre-unpack S/K fallback' );
like( $deb_preinst,
qr{upgrade\)\s*write_xcatd_context upgrade.*?install\)\s*write_xcatd_context fresh.*?abort-upgrade\)\s*rm -f "\$xcatd_context_file"}s,
'preinst records explicit durable package transition context' );
like( $deb_postinst,
qr{ln -sf /opt/xcat/sbin/xcatd /usr/sbin/xcatd.*?#DEBHELPER#.*?rm -f "\$xcatd_context_file"}s,
'postinst retains upgrade context until every fallible installation step succeeds' );
like( $deb_postinst,
qr{if \[ -r "\$xcatd_context_file" \].*?fresh\|upgrade.*?elif \[ "\$1" = configure \] && \[ -n "\$\{2:-\}" \].*?xcatd_transition_context=upgrade}s,
'postinst validates explicit context and preserves state on configured-package fallback' );
unlike( "$deb_preinst\n$deb_postinst", qr{/tmp/xCAT-server_upgrade\.tmp},
'maintainer scripts keep transaction state out of shared temporary storage' );
is( $classifier_blocks{preinst}, $classifier_blocks{postrm},
'Debian preinst and postrm carry the same lifecycle-safe classifier' );
my $deb_postrm = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'postrm' )
);
my $postrm_pending_cleanup = pending_cleanup_block($deb_postrm);
is( $postrm_pending_cleanup, $preinst_pending_cleanup,
'preinst and postrm share the same pending-state cleanup contract' );
unlike( $deb_postrm, qr{/tmp/xCAT-server_upgrade\.tmp},
'postrm keeps transaction state out of shared temporary storage' );
my ($abort_install) = $deb_postrm =~ /\n\s*abort-install\)\n(.*?)\n\s*;;/s;
my ($abort_upgrade) =
$deb_postrm =~ /\n\s*abort-upgrade\|disappear\)\n(.*?)\n\s*;;/s;
ok( defined($abort_install) && defined($abort_upgrade),
'postrm abort recovery arms can be inspected independently' );
like( $abort_install,
qr{clear_xcatd_pending_state.*?rm -f "\$xcatd_context_file".*?rmdir "\$xcatd_state_dir"}s,
'abort-install clears pending context and removes only an empty state directory' );
unlike( $abort_install,
qr{\$xcatd_state_dir/(?:state|xcatd)|xcatd_old_systemd_marker},
'abort-install preserves durable init state and compatibility evidence' );
like( $abort_upgrade, qr{rm -f "\$xcatd_context_file"},
'abort-upgrade clears only the completed-attempt context' );
unlike( $abort_upgrade,
qr{clear_xcatd_pending_state|pending-(?:xcatd|deleted|enabled)},
'abort-upgrade preserves pending recovery evidence for retry' );
like( $deb_postrm,
qr{purge\).*?rm -f "\$xcatd_context_file".*?upgrade\|failed-upgrade\)\s*;;.*?remove\).*?rm -f "\$xcatd_context_file"}s,
'postrm clears upgrade context only when installation cannot continue' );
my $deb_prerm = read_file(
File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'prerm' )
);
like( $deb_prerm,
qr{if "\$xcatd_init_compat" uses-systemd; then.*?update-rc\.d -f xcatd remove}s,
qr{if "\$xcatd_init_compat" uses-systemd --explicit-target; then.*?update-rc\.d -f xcatd remove}s,
'Debian prerm removes registration through the detected init implementation' );
unlike( $deb_prerm,
qr{"\$xcatd_init_compat" uses-systemd(?! --explicit-target)},
'every Debian prerm helper call uses explicit target detection' );
unlike( $deb_postinst, qr{xcat_can_use_systemctl},
'Debian postinst does not duplicate the shared systemctl readiness guard' );
like( $deb_postinst,
qr{if "\$xcatd_init_compat" can-use-systemctl; then\s+systemctl daemon-reload}s,
'Debian postinst delegates its active-systemd guard to the shared helper' );
unlike( $deb_prerm, qr{xcat_can_use_systemctl},
'Debian prerm does not duplicate the shared systemctl readiness guard' );
like( $deb_prerm,
qr{if "\$xcatd_init_compat" can-use-systemctl; then\s+systemctl stop xcatd\.service}s,
'Debian prerm delegates its active-systemd guard to the shared helper' );
done_testing();
+140 -1
View File
@@ -30,9 +30,29 @@ sub stage_root {
my $scripts = File::Spec->catdir(
$root, 'opt', 'xcat', 'share', 'xcat', 'scripts'
);
make_path($scripts);
my $fake_bin = File::Spec->catdir( $root, 'test-bin' );
make_path( $scripts, $fake_bin );
copy( $template, File::Spec->catfile( $scripts, 'xcatd' ) )
or die "Unable to stage legacy init template: $!";
my $systemctl = File::Spec->catfile( $fake_bin, 'systemctl' );
open( my $systemctl_fh, '>', $systemctl )
or die "Unable to stage fake systemctl: $!";
print {$systemctl_fh} <<'SH';
#!/bin/sh
set -eu
root=${XCAT_COMPAT_ROOT:?}
case "${1:-}" in
--root=*) shift ;;
esac
[ "${1:-}" = is-enabled ] || exit 1
if [ -r "$root/systemctl-state" ]; then
sed -n '1p' "$root/systemctl-state"
else
echo unknown
fi
SH
close($systemctl_fh);
chmod 0755, $systemctl;
return $root;
}
@@ -49,10 +69,20 @@ sub stage_systemd_binary {
return $binary;
}
sub set_systemd_state {
my ( $root, $state ) = @_;
my $path = File::Spec->catfile( $root, 'systemctl-state' );
open( my $fh, '>', $path )
or die "Unable to stage systemd state: $!";
print {$fh} "$state\n";
close($fh);
}
sub run_helper {
my ( $root, @arguments ) = @_;
local $ENV{XCAT_COMPAT_ROOT} = $root;
local $ENV{XCATROOT} = '/opt/xcat';
local $ENV{PATH} = File::Spec->catdir( $root, 'test-bin' ) . ':/usr/bin:/bin';
system( '/bin/sh', $helper, @arguments );
return $? >> 8;
}
@@ -61,6 +91,7 @@ sub helper_output {
my ( $root, @arguments ) = @_;
local $ENV{XCAT_COMPAT_ROOT} = $root;
local $ENV{XCATROOT} = '/opt/xcat';
local $ENV{PATH} = File::Spec->catdir( $root, 'test-bin' ) . ':/usr/bin:/bin';
open( my $pipe, '-|', '/bin/sh', $helper, @arguments )
or die "Unable to run compatibility helper: $!";
my $output = do { local $/; <$pipe> };
@@ -255,6 +286,34 @@ is( helper_output( $legacy_state_root, 'legacy-state' ), 'enabled',
is( helper_output( $legacy_state_root, 'legacy-transition-state' ), 'enabled',
'enabled SysV state takes precedence during a legacy transition' );
my $rpm_layout_root = stage_root();
stage_symlink(
$rpm_layout_root, '/etc/init.d/xcatd',
qw(etc rc.d rc3.d S85xcatd)
);
is( helper_output( $rpm_layout_root, 'legacy-state' ), 'enabled',
'default legacy state continues to recognize RPM runlevel layouts' );
is( helper_output( $rpm_layout_root, 'debian-legacy-state' ),
'unregistered', 'Debian legacy state ignores RPM-only runlevel links' );
is( helper_output( $rpm_layout_root, 'legacy-state', '--debian-layout' ),
'enabled', 'legacy state preserves ignored historical extra arguments' );
is( run_helper( $rpm_layout_root, 'debian-legacy-state', 'extra' ), 2,
'Debian legacy state rejects extra arguments' );
my $debian_layout_root = stage_root();
stage_symlink(
$debian_layout_root, '/etc/init.d/xcatd',
qw(etc rc3.d K60xcatd)
);
is( helper_output( $debian_layout_root, 'debian-legacy-state' ),
'disabled', 'Debian legacy state recognizes native kill links' );
stage_symlink(
$debian_layout_root, '/etc/init.d/xcatd',
qw(etc rc3.d S85xcatd)
);
is( helper_output( $debian_layout_root, 'debian-legacy-state' ),
'enabled', 'Debian legacy state gives native start links precedence' );
my $unregistered_legacy_root = stage_root();
make_path( File::Spec->catdir( $unregistered_legacy_root, 'etc', 'init.d' ) );
copy( $template, legacy_init($unregistered_legacy_root) )
@@ -275,6 +334,11 @@ is( helper_output( $deleted_managed_root, 'legacy-transition-state' ),
my $systemd_state_root = stage_root();
is( helper_output( $systemd_state_root, 'systemd-state' ), 'disabled',
'systemd state reports disabled when enablement links are absent' );
is( helper_output( $systemd_state_root, 'systemd-state', '--allow-unknown' ),
'unknown', 'precise systemd state preserves indeterminate evidence' );
set_systemd_state( $systemd_state_root, 'disabled' );
is( helper_output( $systemd_state_root, 'systemd-state', '--allow-unknown' ),
'disabled', 'precise systemd state recognizes an explicit disablement' );
stage_symlink(
$systemd_state_root, '/usr/lib/systemd/system/xcatd.service',
qw(etc systemd system multi-user.target.wants xcatd.service)
@@ -283,6 +347,63 @@ is( helper_output( $systemd_state_root, 'systemd-state' ), 'enabled',
'systemd state recognizes persistent wants links' );
is( helper_output( $systemd_state_root, 'legacy-transition-state' ), 'enabled',
'enabled systemd state transfers to an enabled SysV service' );
is( helper_output( $systemd_state_root, 'systemd-state', '--allow-unknown' ),
'enabled', 'enablement links override stale disabled manager output' );
my $manager_masked_link_root = stage_root();
set_systemd_state( $manager_masked_link_root, 'masked' );
stage_symlink(
$manager_masked_link_root, '/usr/lib/systemd/system/xcatd.service',
qw(etc systemd system multi-user.target.wants xcatd.service)
);
is( helper_output( $manager_masked_link_root, 'systemd-state' ), 'enabled',
'compatibility state keeps historical link precedence for target roots' );
is( helper_output(
$manager_masked_link_root, 'systemd-state', '--allow-unknown'
),
'masked', 'precise systemd state gives a manager mask precedence over links' );
for my $manager_state_case (
[ 'enabled', 'enabled' ],
[ 'enabled-runtime', 'enabled' ],
[ 'masked', 'masked' ],
[ 'masked-runtime', 'masked' ],
[ 'disabled', 'disabled' ],
[ 'linked', 'disabled' ],
[ 'linked-runtime', 'disabled' ],
[ 'alias', 'disabled' ],
[ 'static', 'disabled' ],
[ 'indirect', 'disabled' ],
[ 'generated', 'disabled' ],
[ 'transient', 'disabled' ],
[ 'enabled-garbage', 'unknown' ],
[ 'masked-garbage', 'unknown' ],
) {
my ( $manager_state, $expected_state ) = @{$manager_state_case};
my $manager_state_root = stage_root();
set_systemd_state( $manager_state_root, $manager_state );
is( helper_output(
$manager_state_root, 'systemd-state', '--allow-unknown'
),
$expected_state, "precise systemd state parses $manager_state exactly" );
}
for my $precise_link_case (
[ qw(etc wants) ],
[ qw(etc requires) ],
[ qw(run wants) ],
[ qw(run requires) ],
) {
my ( $scope, $relation ) = @{$precise_link_case};
my $precise_link_root = stage_root();
stage_symlink(
$precise_link_root, '/usr/lib/systemd/system/xcatd.service',
$scope, 'systemd', 'system', "multi-user.target.$relation",
'xcatd.service'
);
is( helper_output( $precise_link_root, 'systemd-state', '--allow-unknown' ),
'enabled', "precise systemd state recognizes $scope $relation links" );
}
my $systemd_requires_root = stage_root();
stage_symlink(
@@ -311,6 +432,24 @@ is( helper_output( $systemd_masked_root, 'systemd-state' ), 'masked',
'systemd state preserves an administrator mask' );
is( helper_output( $systemd_masked_root, 'legacy-transition-state' ), 'masked',
'a systemd mask prevents SysV registration during a legacy transition' );
is( helper_output( $systemd_masked_root, 'systemd-state', '--allow-unknown' ),
'masked', 'precise systemd state recognizes persistent masks' );
my $systemd_runtime_masked_root = stage_root();
stage_symlink(
$systemd_runtime_masked_root, '/dev/null',
qw(run systemd system xcatd.service)
);
is( helper_output( $systemd_runtime_masked_root,
'systemd-state', '--allow-unknown' ),
'masked', 'precise systemd state recognizes runtime masks' );
my $systemd_argv_root = stage_root();
is( helper_output( $systemd_argv_root, 'systemd-state', '--invalid' ),
'disabled', 'systemd state preserves ignored historical extra arguments' );
is( helper_output(
$systemd_argv_root, 'systemd-state', '--allow-unknown', 'extra'
),
'disabled', 'only the exact precision option activates precise state' );
my $cleanup_root = stage_root();
my @cleanup_links = (