221 lines
8.4 KiB
Bash
Executable File
221 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Overlord management of snapd for package manager actions.
|
|
# Implements actions that would be invoked in %pre(un) actions for snapd.
|
|
# Derived from the snapd.postrm scriptlet used in the Ubuntu packaging for
|
|
# snapd.
|
|
|
|
set -e
|
|
set +x
|
|
|
|
SNAP_MOUNT_DIR="/snap"
|
|
|
|
show_help() {
|
|
exec cat <<'EOF'
|
|
Usage: snap-mgmt.sh [OPTIONS]
|
|
|
|
A simple script to cleanup snap installations.
|
|
|
|
optional arguments:
|
|
--help Show this help message and exit
|
|
--snap-mount-dir=<path> Provide a path to be used as $SNAP_MOUNT_DIR
|
|
--purge Purge all data from $SNAP_MOUNT_DIR
|
|
EOF
|
|
}
|
|
|
|
SNAP_UNIT_PREFIX="$(systemd-escape -p ${SNAP_MOUNT_DIR})"
|
|
|
|
systemctl_stop() {
|
|
unit="$1"
|
|
if systemctl is-active -q "$unit"; then
|
|
echo "Stopping $unit"
|
|
systemctl stop -q "$unit" || true
|
|
fi
|
|
}
|
|
|
|
purge() {
|
|
# shellcheck disable=SC1091
|
|
distribution=$(. /etc/os-release; echo "${ID}-${VERSION_ID}")
|
|
|
|
if [ "$distribution" = "ubuntu-14.04" ]; then
|
|
# snap.mount.service is a trusty thing
|
|
systemctl_stop snap.mount.service
|
|
fi
|
|
|
|
# Undo any bind mounts to ${SNAP_MOUNT_DIR} or /var/snap done by parallel
|
|
# installs or LP:#1668659
|
|
for mp in "$SNAP_MOUNT_DIR" /var/snap; do
|
|
# btrfs bind mounts actually include subvolume in the filesystem-path
|
|
# https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg51810.html
|
|
if grep -q " $mp $mp " /proc/self/mountinfo ||
|
|
grep -q -e "\(/.*\)$mp $mp .* btrfs .*\(subvol=\1\)\(,.*\)\?\$" /proc/self/mountinfo ; then
|
|
umount -l "$mp" || true
|
|
fi
|
|
done
|
|
|
|
units=$(systemctl list-unit-files --no-legend --full | grep -vF snap.mount.service || true)
|
|
# *.snap mount points
|
|
mounts=$(echo "$units" | grep "^${SNAP_UNIT_PREFIX}[-.].*\\.mount" | cut -f1 -d ' ')
|
|
# services from snaps
|
|
services=$(echo "$units" | grep '^snap\..*\.service' | cut -f1 -d ' ')
|
|
# slices from snaps
|
|
slices=$(echo "$units" | grep '^snap\..*\.slice' | cut -f1 -d ' ')
|
|
for unit in $services $mounts $slices; do
|
|
# ensure its really a snap mount unit or systemd unit
|
|
if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then
|
|
echo "Skipping non-snapd systemd unit $unit"
|
|
continue
|
|
fi
|
|
|
|
echo "Stopping $unit"
|
|
systemctl_stop "$unit"
|
|
|
|
if echo "$unit" | grep -q '.*\.mount' ; then
|
|
# Transform ${SNAP_MOUNT_DIR}/core/3440 -> core/3440 removing any
|
|
# extra / preceding snap name, eg:
|
|
# /var/lib/snapd/snap/core/3440 -> core/3440
|
|
# /snap/core/3440 -> core/3440
|
|
# /snap/core//3440 -> core/3440
|
|
# NOTE: we could have used `systemctl show $unit -p Where --value`
|
|
# but systemd 204 shipped with Ubuntu 14.04 does not support this
|
|
snap_rev=$(systemctl show "$unit" -p Where | sed -e 's#Where=##' -e "s#$SNAP_MOUNT_DIR##" -e 's#^/*##')
|
|
snap=$(echo "$snap_rev" |cut -f1 -d/)
|
|
rev=$(echo "$snap_rev" |cut -f2 -d/)
|
|
if [ -n "$snap" ]; then
|
|
echo "Removing snap $snap"
|
|
# aliases
|
|
if [ -d "${SNAP_MOUNT_DIR}/bin" ]; then
|
|
find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap" -delete
|
|
find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap.*" -delete
|
|
fi
|
|
# generated binaries
|
|
rm -f "${SNAP_MOUNT_DIR}/bin/$snap"
|
|
rm -f "${SNAP_MOUNT_DIR}/bin/$snap".*
|
|
# snap mount dir
|
|
umount -l "${SNAP_MOUNT_DIR}/$snap/$rev" 2> /dev/null || true
|
|
rm -rf "${SNAP_MOUNT_DIR:?}/$snap/$rev"
|
|
rm -f "${SNAP_MOUNT_DIR}/$snap/current"
|
|
# snap data dir
|
|
rm -rf "/var/snap/$snap/$rev"
|
|
rm -rf "/var/snap/$snap/common"
|
|
rm -f "/var/snap/$snap/current"
|
|
# opportunistic remove (may fail if there are still revisions left)
|
|
for d in "${SNAP_MOUNT_DIR}/$snap" "/var/snap/$snap"; do
|
|
if [ -d "$d" ]; then
|
|
rmdir --ignore-fail-on-non-empty "$d"
|
|
fi
|
|
done
|
|
# udev rules
|
|
find /etc/udev/rules.d -name "*-snap.${snap}.rules" -execdir rm -f "{}" \;
|
|
# dbus policy files
|
|
if [ -d /etc/dbus-1/system.d ]; then
|
|
find /etc/dbus-1/system.d -name "snap.${snap}.*.conf" -execdir rm -f "{}" \;
|
|
fi
|
|
# modules
|
|
rm -f "/etc/modules-load.d/snap.${snap}.conf"
|
|
rm -f "/etc/modprobe.d/snap.${snap}.conf"
|
|
# timer and socket units
|
|
find /etc/systemd/system -name "snap.${snap}.*.timer" -o -name "snap.${snap}.*.socket" | while read -r f; do
|
|
systemctl_stop "$(basename "$f")"
|
|
rm -f "$f"
|
|
done
|
|
# user services, sockets, and timers - we make no attempt to stop any of them.
|
|
# TODO: ask snapd to ask each snapd.session-agent.service to stop snaps
|
|
# user-session services and stop itself.
|
|
find /etc/systemd/user -name "snap.${snap}.*.timer" -o -name "snap.${snap}.*.socket" -o -name "snap.${snap}.*.service" | while read -r f; do
|
|
rm -f "$f"
|
|
done
|
|
fi
|
|
fi
|
|
|
|
echo "Removing $unit"
|
|
rm -f "/etc/systemd/system/$unit"
|
|
rm -f "/etc/systemd/system/multi-user.target.wants/$unit"
|
|
rm -f "/etc/systemd/system/snapd.mounts.target.wants/${unit}"
|
|
done
|
|
# Remove empty ".wants/" directory created by enabling mount units
|
|
rmdir "/etc/systemd/system/snapd.mounts.target.wants" || true
|
|
# Units may have been removed do a reload
|
|
systemctl -q daemon-reload || true
|
|
|
|
# snapd session-agent
|
|
rm -f /etc/systemd/user/snapd.session-agent.socket
|
|
rm -f /etc/systemd/user/snapd.session-agent.service
|
|
rm -f /etc/systemd/user/sockets.target.wants/snapd.session-agent.socket
|
|
|
|
# dbus activation configuration
|
|
rm -f /etc/dbus-1/session.d/snapd.session-services.conf
|
|
rm -f /etc/dbus-1/system.d/snapd.system-services.conf
|
|
|
|
echo "Discarding preserved snap namespaces"
|
|
# opportunistic as those might not be actually mounted
|
|
if [ -d /run/snapd/ns ]; then
|
|
if [ "$(find /run/snapd/ns/ -name "*.mnt" | wc -l)" -gt 0 ]; then
|
|
for mnt in /run/snapd/ns/*.mnt; do
|
|
umount -l "$mnt" || true
|
|
rm -f "$mnt"
|
|
done
|
|
fi
|
|
find /run/snapd/ns/ \( -name '*.fstab' -o -name '*.user-fstab' -o -name '*.info' \) -delete
|
|
umount -l /run/snapd/ns/ || true
|
|
fi
|
|
|
|
echo "Removing downloaded snaps"
|
|
rm -rf /var/lib/snapd/snaps/*
|
|
|
|
echo "Removing features exported from snapd to helper tools"
|
|
rm -rf /var/lib/snapd/features
|
|
|
|
echo "Final directory cleanup"
|
|
rm -rf "${SNAP_MOUNT_DIR}"
|
|
rm -rf /var/snap
|
|
|
|
echo "Removing leftover snap shared state data"
|
|
rm -rf /var/lib/snapd/dbus-1/services/*
|
|
rm -rf /var/lib/snapd/dbus-1/system-services/*
|
|
rm -rf /var/lib/snapd/desktop/applications/*
|
|
rm -rf /var/lib/snapd/seccomp/bpf/*
|
|
rm -rf /var/lib/snapd/device/*
|
|
rm -rf /var/lib/snapd/assertions/*
|
|
rm -rf /var/lib/snapd/cookie/*
|
|
rm -rf /var/lib/snapd/cache/*
|
|
rm -rf /var/lib/snapd/mount/*
|
|
rm -rf /var/lib/snapd/sequence/*
|
|
rm -rf /var/lib/snapd/apparmor/*
|
|
rm -rf /var/lib/snapd/inhibit/*
|
|
rm -f /var/lib/snapd/state.json
|
|
rm -f /var/lib/snapd/system-key
|
|
|
|
echo "Removing snapd catalog cache"
|
|
rm -rf /var/cache/snapd/*
|
|
|
|
if test -d /etc/apparmor.d; then
|
|
# Remove auto-generated rules for snap-confine from the 'core' snap
|
|
echo "Removing extra snap-confine apparmor rules"
|
|
# shellcheck disable=SC2046
|
|
rm -f /etc/apparmor.d/$(echo "$SNAP_UNIT_PREFIX" | tr '-' '.').core.*.usr.lib.snapd.snap-confine
|
|
fi
|
|
}
|
|
|
|
while [ -n "$1" ]; do
|
|
case "$1" in
|
|
--help)
|
|
show_help
|
|
exit
|
|
;;
|
|
--snap-mount-dir=*)
|
|
SNAP_MOUNT_DIR=${1#*=}
|
|
SNAP_UNIT_PREFIX=$(systemd-escape -p "$SNAP_MOUNT_DIR")
|
|
shift
|
|
;;
|
|
--purge)
|
|
purge
|
|
shift
|
|
;;
|
|
*)
|
|
echo "Unknown command: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|