mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-28 04:49:49 -07:00
7d81342f0f
Background AppImage launches executed directly from a FUSE mount owned by a short-lived helper's AppImage runtime. On quit, the runtime unmounted the squashfs while utility children (network service) were still mid-shutdown, SIGBUSing them and triggering KDE Service Crash notifications on every video close. Detach a POSIX-sh supervisor instead: mount via --appimage-mount, run AppRun from the mount, and release the holder only once no process still executes from the mount. Falls back to the old direct launch on any failure or with SUBMINER_NO_APPIMAGE_MOUNT_KEEPALIVE=1.
76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
// Background AppImage launches must not execute directly from a FUSE mount whose
|
|
// lifetime is owned by a short-lived helper's AppImage runtime: when the app later
|
|
// quits, the runtime unmounts the squashfs while Chromium utility children (network
|
|
// service et al.) are still mid-shutdown, and they die with SIGBUS on their mmapped
|
|
// executable — surfacing as "Service Crash" desktop notifications on every quit.
|
|
//
|
|
// Fix: detach a tiny POSIX-sh supervisor instead of the raw process. It mounts the
|
|
// AppImage via `--appimage-mount` (holder process keeps the mount alive), runs
|
|
// AppRun from that mount, and after the app exits waits until no process is still
|
|
// executing from the mount before releasing the holder.
|
|
|
|
export interface AppImageMountKeepaliveInvocation {
|
|
command: string;
|
|
args: string[];
|
|
}
|
|
|
|
const DISABLE_ENV = 'SUBMINER_NO_APPIMAGE_MOUNT_KEEPALIVE';
|
|
|
|
// $0 is set to this label so the supervisor is identifiable in `ps` output.
|
|
export const APPIMAGE_MOUNT_KEEPALIVE_LABEL = 'subminer-appimage-keepalive';
|
|
|
|
// POSIX sh only; every failure path falls back to executing the AppImage directly,
|
|
// which is exactly the pre-wrapper behavior.
|
|
export const APPIMAGE_MOUNT_KEEPALIVE_SCRIPT = `
|
|
set -u
|
|
appimage=$1
|
|
shift
|
|
run_direct() {
|
|
exec "$appimage" "$@"
|
|
}
|
|
fifo=$(mktemp -u "\${TMPDIR:-/tmp}/subminer-appimage-mount-XXXXXX") || run_direct "$@"
|
|
mkfifo "$fifo" || run_direct "$@"
|
|
"$appimage" --appimage-mount >"$fifo" 2>/dev/null &
|
|
holder=$!
|
|
mount_point=""
|
|
IFS= read -r mount_point <"$fifo" || true
|
|
rm -f "$fifo"
|
|
app_run=""
|
|
if [ -n "$mount_point" ] && [ -x "$mount_point/AppRun" ]; then
|
|
app_run="$mount_point/AppRun"
|
|
fi
|
|
if [ -z "$app_run" ]; then
|
|
kill "$holder" 2>/dev/null
|
|
run_direct "$@"
|
|
fi
|
|
APPDIR="$mount_point" "$app_run" "$@"
|
|
rc=$?
|
|
# Do not release the mount while any process still executes from it; releasing
|
|
# early SIGBUSes Chromium children that are mid-shutdown.
|
|
tries=0
|
|
while [ "$tries" -lt 100 ]; do
|
|
if readlink /proc/[0-9]*/exe 2>/dev/null | grep -qF "$mount_point/"; then
|
|
sleep 0.1
|
|
tries=$((tries + 1))
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
kill "$holder" 2>/dev/null
|
|
exit "$rc"
|
|
`;
|
|
|
|
export function resolveAppImageMountKeepaliveInvocation(
|
|
env: NodeJS.ProcessEnv,
|
|
platform: NodeJS.Platform = process.platform,
|
|
): AppImageMountKeepaliveInvocation | null {
|
|
if (platform !== 'linux') return null;
|
|
if (env[DISABLE_ENV] === '1') return null;
|
|
const appImagePath = env.APPIMAGE?.trim();
|
|
if (!appImagePath) return null;
|
|
return {
|
|
command: '/bin/sh',
|
|
args: ['-c', APPIMAGE_MOUNT_KEEPALIVE_SCRIPT, APPIMAGE_MOUNT_KEEPALIVE_LABEL, appImagePath],
|
|
};
|
|
}
|