#!/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