Skip to content

Commit

Permalink
installer: add a naive systemd service state backup and restore
Browse files Browse the repository at this point in the history
Add support for backing up and restoring explicitly disabled and enabled
services by checking their symlinks and restoring or removing them when
applying the backup.

It does naive enabled/disabled services heuristic:

- if the .preset does not say enabled, and there is a symlink in
  multi-user.target.wants, recreate the symink on restore
- if the .preset says enabled, and there was no symlink in
  multi-user.target.wants, remove the symlink on restore

Additionally, it only checks certain services:
- Skip any oneshot services (these are expected to be disabled with
  enabled preset after first boot)
- Skip any services not targeting multi-user

It does support parameterized services though, as supported by frr (via
frr@something).

This allows e.g. docker to continue to be enabled after an update to a
newer version. It should now also keep the choice of baseboxd vs ryu on
upgrade (untested though).

Signed-off-by: Jonas Gorski <[email protected]>
  • Loading branch information
KanjiMonster committed Jan 28, 2025
1 parent 6944d7f commit 8bc762f
Showing 1 changed file with 105 additions and 2 deletions.
107 changes: 105 additions & 2 deletions scripts/installer/lib/backup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,69 @@ parse_file() {
done < $1
}

backup_systemd_state() {
local enabled_services disabled_services service_files service_links
local service enabled preset oneshot

if [ -d "$1/lib/systemd/system" ]; then
service_files=$(ls $1/lib/systemd/system)
fi
if [ -d "$1/etc/systemd/system/multi-user.target.wants" ]; then
service_links=$(ls $1/etc/systemd/system/multi-user.target.wants)
fi

# collect explicitly disabled services
for service in $service_files; do
case "$service" in
*@.service)
# always disabled by default
;;
*.service)
preset=$(grep "$service" $1/lib/systemd/system-preset/*.preset | cut -d ':' -f 2 | cut -d ' ' -f 1)
# check only services enabled by default
[ "$preset" = "enable" ] || continue

wantedby=$(grep "^WantedBy=" $1/lib/systemd/system/$service || true)

# skip oneshots, these got automatically disabled
grep -q "^Type=oneshot" $1/lib/systemd/system/$service && continue

# for now we only support multi-user services
[ "$wantedby" = "WantedBy=multi-user.target" ] || continue

# check for symlink in multi-user.target.wants
enabled=$(find $1/etc/systemd/system/multi-user.target.wants/ -name $service)

# nothing to do if still enabled
[ -z "$enabled" ] || continue

[ -n "$DEBUG" ] && echo "DEBUG: service disabled by user: $service"
disabled_services="$disabled_services ${service%.service}"
;;
esac
done

# collect explicitly enabled services
for service in $service_links; do
case "$service" in
*.service)
service_file=$(readlink $1/etc/systemd/system/multi-user.target.wants/$service)
base=$(basename $service_file)
preset=$(grep "$base" $1/lib/systemd/system-preset/*.preset | cut -d ':' -f 2 | cut -d ' ' -f 1)

# check only services disabled by default
[ "$preset" != "enable" ] || continue

[ -n "$DEBUG" ] && echo "DEBUG: service enabled by user: $service"
enabled_services="$enabled_services ${service%.service}"
;;
esac
done

echo "$disabled_services" > $2/.SERVICES_DISABLED
echo "$enabled_services" > $2/.SERVICES_ENABLED
}

# $1 src $2 dst
apply_fixups() {
# releases pre 4.7.0 are missing gshadow in the backup list
Expand Down Expand Up @@ -150,20 +213,60 @@ create_backup()
parse_file "$1/$USER_BACKUP_FILE" "-" $1 $2
fi

# step 3 - backup changed systemd service states
backup_systemd_state $1 $2

apply_fixups $1 $2

# step 3 - check if anything is left
# step 4 - check if anything is left
[ -n "$(find $2 -type f)" ] || return 0

# step 4 - remove empty directories
# step 5 - remove empty directories
find $2 -depth -type d -exec rmdir -p --ignore-fail-on-non-empty {} \;

DO_RESTORE=true
}

restore_systemd_state()
{
local enabled_services disabled_service service baseservice

if [ -f "$1/.SERVICES_ENABLED" ]; then
enabled_services=$(cat $1/.SERVICES_ENABLED)
fi
if [ -f "$1/.SERVICES_DISABLED" ]; then
disabled_services=$(cat $1/.SERVICES_DISABLED)
fi

# for any services to enable, create symlinks
for service in $enabled_services; do
# strip foo from service@foo
baseservice=${service/@*/@}
# check that the service exists in the target system
if [ ! -f $2/lib/systemd/system/$baseservice.service ]; then
echo "WARNING: cannot enable service $service.service: $baseservice.service not found" >&2
continue
fi
[ -n "$DEBUG" ] && echo "DEBUG: enabling service: $service.service"
ln -fs /lib/systemd/system/$baseservice.service \
$2/etc/systemd/system/multi-user.target.wants/$service.service
done

# for any services to disable, remove their symlinks
for service in $disabled_services; do
[ -n "$DEBUG" ] && echo "DEBUG: disabling service: $service.service"
rm -f $2/etc/systemd/system/multi-user.target.wants/$service.service
done

rm -f $1/.SERVICES_ENABLED $2/.SERVICES_DISABLED
}

# $1 backup storage dir $2 restore target
restore_backup()
{
# restore systemd services state
restore_systemd_state $1 $2

# rename existing target files if different
for file in $(find $1 -type f); do
basefile="${file#${1}/}"
Expand Down

0 comments on commit 8bc762f

Please sign in to comment.