mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/mihomo-party-org/clash-party.git
synced 2026-09-20 08:03:39 +08:00
fix: preserve and recover macOS helper service (#2052)
This commit is contained in:
20
build/pkg-scripts/party.mihomo.helper.plist
Normal file
20
build/pkg-scripts/party.mihomo.helper.plist
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>party.mihomo.helper</string>
|
||||
<key>AssociatedBundleIdentifiers</key>
|
||||
<array>
|
||||
<string>party.mihomo.app</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>Program</key>
|
||||
<string>/Library/PrivilegedHelperTools/party.mihomo.helper</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/party.mihomo.helper.err</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/party.mihomo.helper.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -24,6 +24,7 @@ fi
|
||||
|
||||
HELPER_PATH="/Library/PrivilegedHelperTools/party.mihomo.helper"
|
||||
LAUNCH_DAEMON="/Library/LaunchDaemons/party.mihomo.helper.plist"
|
||||
LAUNCH_DAEMON_SOURCE="$APP_PATH/Contents/Resources/files/party.mihomo.helper.plist"
|
||||
|
||||
log "Starting installation..."
|
||||
|
||||
@@ -74,32 +75,13 @@ fi
|
||||
# 创建并配置 LaunchDaemon
|
||||
log "Configuring LaunchDaemon..."
|
||||
mkdir -p "/Library/LaunchDaemons"
|
||||
cat << EOF > "$LAUNCH_DAEMON"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>party.mihomo.helper</string>
|
||||
<key>AssociatedBundleIdentifiers</key>
|
||||
<array>
|
||||
<string>party.mihomo.app</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>Program</key>
|
||||
<string>${HELPER_PATH}</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/party.mihomo.helper.err</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/party.mihomo.helper.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
chown root:wheel "$LAUNCH_DAEMON"
|
||||
chmod 644 "$LAUNCH_DAEMON"
|
||||
if [ -f "$LAUNCH_DAEMON_SOURCE" ]; then
|
||||
/usr/bin/install -o root -g wheel -m 644 "$LAUNCH_DAEMON_SOURCE" "$LAUNCH_DAEMON"
|
||||
log "LaunchDaemon configured"
|
||||
else
|
||||
log "Error: LaunchDaemon file not found at $LAUNCH_DAEMON_SOURCE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证关键文件
|
||||
log "Verifying installation..."
|
||||
|
||||
@@ -7,18 +7,6 @@ if [ "$EUID" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HELPER_PATH="/Library/PrivilegedHelperTools/party.mihomo.helper"
|
||||
LAUNCH_DAEMON="/Library/LaunchDaemons/party.mihomo.helper.plist"
|
||||
|
||||
# 停止并卸载现有的 LaunchDaemon
|
||||
if [ -f "$LAUNCH_DAEMON" ]; then
|
||||
launchctl unload "$LAUNCH_DAEMON" 2>/dev/null || true
|
||||
rm -f "$LAUNCH_DAEMON"
|
||||
fi
|
||||
|
||||
# 移除 helper 工具
|
||||
rm -f "$HELPER_PATH"
|
||||
|
||||
# 清理可能存在的旧版本文件
|
||||
rm -rf "/Applications/Clash Party.app"
|
||||
rm -rf "/Applications/Clash\\ Party.app"
|
||||
|
||||
@@ -46,6 +46,9 @@ nsis:
|
||||
mac:
|
||||
target:
|
||||
- pkg
|
||||
extraResources:
|
||||
- from: './build/pkg-scripts/party.mihomo.helper.plist'
|
||||
to: 'files/party.mihomo.helper.plist'
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
hardenedRuntime: true
|
||||
gatekeeperAssess: false
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { promisify } from 'util'
|
||||
import { exec } from 'child_process'
|
||||
import { exec, execFile } from 'child_process'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { triggerAutoProxy, triggerManualProxy } from 'sysproxy-rs'
|
||||
import { net } from 'electron'
|
||||
import axios from 'axios'
|
||||
@@ -8,9 +9,13 @@ import { getAppConfig, getControledMihomoConfig } from '../config'
|
||||
import { DEFAULT_MIHOMO_PORTS } from '../../shared/appConfig'
|
||||
import { pacPort, startPacServer, stopPacServer } from '../resolve/server'
|
||||
import { proxyLogger } from '../utils/logger'
|
||||
import { resourcesFilesDir } from '../utils/dirs'
|
||||
|
||||
let triggerSysProxyTimer: NodeJS.Timeout | null = null
|
||||
const helperSocketPath = '/tmp/mihomo-party-helper.sock'
|
||||
const helperPath = '/Library/PrivilegedHelperTools/party.mihomo.helper'
|
||||
const helperPlistPath = '/Library/LaunchDaemons/party.mihomo.helper.plist'
|
||||
const helperService = 'system/party.mihomo.helper'
|
||||
|
||||
const defaultBypass: string[] = (() => {
|
||||
switch (process.platform) {
|
||||
@@ -180,11 +185,39 @@ async function isHelperRunning(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
async function startHelperService(): Promise<void> {
|
||||
async function isHelperServiceRegistered(): Promise<boolean> {
|
||||
try {
|
||||
const execPromise = promisify(exec)
|
||||
const shell = `launchctl kickstart -k system/party.mihomo.helper`
|
||||
const command = `do shell script "${shell}" with administrator privileges`
|
||||
await execPromise(`osascript -e '${command}'`)
|
||||
await execPromise(`/bin/launchctl print ${helperService}`)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function shellQuote(value: string): string {
|
||||
return `'${value.replace(/'/g, `'"'"'`)}'`
|
||||
}
|
||||
|
||||
async function startHelperService(forceRepair = false): Promise<void> {
|
||||
const sourceHelper = shellQuote(path.join(resourcesFilesDir(), 'party.mihomo.helper'))
|
||||
const sourcePlist = shellQuote(path.join(resourcesFilesDir(), 'party.mihomo.helper.plist'))
|
||||
const installedHelper = shellQuote(helperPath)
|
||||
const installedPlist = shellQuote(helperPlistPath)
|
||||
const shell =
|
||||
`if ${forceRepair ? 'true' : 'false'} || [ ! -x ${installedHelper} ] || [ ! -f ${installedPlist} ] || ! /bin/launchctl print ${helperService} >/dev/null 2>&1; then ` +
|
||||
`[ -f ${sourceHelper} ] && [ -f ${sourcePlist} ] || exit 1; ` +
|
||||
`/bin/launchctl bootout ${helperService} >/dev/null 2>&1 || true; ` +
|
||||
`/bin/mkdir -p /Library/PrivilegedHelperTools /Library/LaunchDaemons; ` +
|
||||
`/bin/rm -f ${shellQuote(helperSocketPath)}; ` +
|
||||
`/usr/bin/install -o root -g wheel -m 544 ${sourceHelper} ${installedHelper}; ` +
|
||||
`/usr/bin/install -o root -g wheel -m 644 ${sourcePlist} ${installedPlist}; ` +
|
||||
`/bin/launchctl enable ${helperService} >/dev/null 2>&1 || true; ` +
|
||||
`/bin/launchctl bootstrap system ${installedPlist}; ` +
|
||||
`else /bin/launchctl kickstart -k ${helperService}; fi`
|
||||
const command = `do shell script ${JSON.stringify(shell)} with administrator privileges`
|
||||
const execFilePromise = promisify(execFile)
|
||||
await execFilePromise('/usr/bin/osascript', ['-e', command])
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500))
|
||||
}
|
||||
|
||||
@@ -224,12 +257,13 @@ async function helperRequest(requestFn: () => Promise<unknown>, maxRetries = 2):
|
||||
)
|
||||
|
||||
const helperRunning = await isHelperRunning()
|
||||
const helperRegistered = await isHelperServiceRegistered()
|
||||
const socketExists = isSocketFileExists()
|
||||
|
||||
if (!helperRunning) {
|
||||
await proxyLogger.info('Helper process not running, starting service...')
|
||||
if (!helperRunning || !helperRegistered || !fs.existsSync(helperPlistPath)) {
|
||||
await proxyLogger.info('Helper service unavailable, repairing...')
|
||||
try {
|
||||
await startHelperService()
|
||||
await startHelperService(!helperRunning)
|
||||
await proxyLogger.info('Helper service started, retrying...')
|
||||
continue
|
||||
} catch (startError) {
|
||||
@@ -244,6 +278,15 @@ async function helperRequest(requestFn: () => Promise<unknown>, maxRetries = 2):
|
||||
} catch (signalError) {
|
||||
await proxyLogger.warn('Failed to request socket recreation', signalError)
|
||||
}
|
||||
} else {
|
||||
await proxyLogger.info('Helper socket is unresponsive, restarting service...')
|
||||
try {
|
||||
await startHelperService()
|
||||
await proxyLogger.info('Helper service restarted, retrying...')
|
||||
continue
|
||||
} catch (restartError) {
|
||||
await proxyLogger.warn('Failed to restart helper service', restartError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user