fix(linux): keep the Service through released rpm %preun via uninstaller wrapper

Refs #8515

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Tunglies
2026-11-09 18:47:54 +08:00
co-authored by Copilot
parent 33fc9ba9e3
commit ae2b2b6a06
5 changed files with 74 additions and 4 deletions
+14 -2
View File
@@ -3,17 +3,28 @@ set -e
chmod +x /usr/bin/clash-verge-service-install
chmod +x /usr/bin/clash-verge-service-uninstall
chmod +x /usr/lib/clash-verge-rev/clash-verge-service-uninstall
chmod +x /usr/bin/clash-verge-service
SERVICE_UNIT="clash-verge-service.service"
SERVICE_UNIT_FILE="/etc/systemd/system/$SERVICE_UNIT"
# Read by the app, which offers a reinstall when the package could not keep the Service.
UPGRADE_MARKER="/run/clash-verge-service-upgrade"
# Read by /usr/bin/clash-verge-service-uninstall to keep the Service through the previous %preun.
TRANSACTION_RECORD="/run/clash-verge-service-rpm-upgrade"
write_upgrade_marker() {
(umask 022 && printf 'gid=%s\nid=%s\n' "$1" "$(date +%s%N)" >"$UPGRADE_MARKER")
}
# Scriptlets run as children of the process running the rpm transaction; its start time
# keeps a stale record from matching a later process that reuses the PID.
record_transaction() {
local start
start="$(sed 's/.*) //' "/proc/$PPID/stat" | cut -d' ' -f20)"
(umask 022 && printf '%s %s\n' "$PPID" "$start" >"$TRANSACTION_RECORD")
}
existing_service_gid() {
local group
group="$(sed -n 's/^Group=//p' "$SERVICE_UNIT_FILE" | head -n 1)"
@@ -52,9 +63,10 @@ reinstall_existing_service() {
# Upgrades keep an existing Service current; first installs and explicit removals never add one.
# A symlinked unit is masked or managed by the administrator and is left alone.
# The marker is written first: a previous package whose %preun uninstalls unconditionally
# still removes the Service after this script, and the app only acts on it if the Service is not ready.
# The previous %preun runs after this script; the record keeps it from removing the Service.
# The marker stays as a fallback should it still be removed; the app only acts on it if the Service is not ready.
if [ "$1" -ge 2 ] && [ -d /run/systemd/system ] && [ -f "$SERVICE_UNIT_FILE" ] && [ ! -L "$SERVICE_UNIT_FILE" ]; then
record_transaction || true
gid="$(existing_service_gid)"
write_upgrade_marker "$gid"
if [ -z "$gid" ]; then
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
chmod +x /usr/bin/clash-verge-service-install
chmod +x /usr/bin/clash-verge-service-uninstall
chmod +x /usr/lib/clash-verge-rev/clash-verge-service-uninstall
chmod +x /usr/bin/clash-verge-service
SERVICE_UNIT="clash-verge-service.service"
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# Installed as /usr/bin/clash-verge-service-uninstall. During an rpm upgrade the new %post
# reinstalls an existing Service before the previous package's %preun runs, and released
# %preun scripts uninstall it unconditionally through this path. Skip only that call: one
# made by a scriptlet of the transaction that %post recorded.
RECORD=/run/clash-verge-service-rpm-upgrade
UNINSTALLER=/usr/lib/clash-verge-rev/clash-verge-service-uninstall
# Fields after the parenthesised command name, which may itself contain spaces.
stat_field() {
sed 's/.*) //' "/proc/$1/stat" 2>/dev/null | cut -d' ' -f"$2"
}
if [ -f "$RECORD" ] && [ ! -L "$RECORD" ] && read -r txn_pid txn_start <"$RECORD"; then
pid=$PPID
depth=0
while [ "$depth" -lt 4 ] && [ -n "$pid" ] && [ "$pid" -gt 1 ]; do
if [ "$pid" = "$txn_pid" ] && [ "$(stat_field "$pid" 20)" = "$txn_start" ]; then
rm -f "$RECORD"
echo "Keeping the Clash Verge Service reinstalled by this upgrade."
exit 0
fi
pid="$(stat_field "$pid" 2)"
depth=$((depth + 1))
done
fi
exec "$UNINSTALLER" "$@"
+8 -2
View File
@@ -11,7 +11,10 @@
"conflicts": ["clash-verge"],
"replaces": ["clash-verge"],
"postInstallScript": "./packages/linux/post-install.sh",
"preRemoveScript": "./packages/linux/pre-remove.sh"
"preRemoveScript": "./packages/linux/pre-remove.sh",
"files": {
"/usr/lib/clash-verge-rev/clash-verge-service-uninstall": "./sidecar/clash-verge-service-uninstall-bin"
}
},
"rpm": {
"depends": ["openssl", "libayatana-appindicator-gtk3"],
@@ -21,7 +24,10 @@
"conflicts": ["clash-verge"],
"obsoletes": ["clash-verge"],
"postInstallScript": "./packages/linux/post-install-rpm.sh",
"preRemoveScript": "./packages/linux/pre-remove.sh"
"preRemoveScript": "./packages/linux/pre-remove.sh",
"files": {
"/usr/lib/clash-verge-rev/clash-verge-service-uninstall": "./sidecar/clash-verge-service-uninstall-bin"
}
}
},
"externalBin": [