feat(client, server): enhance self-update process with fallback directory handling and UAC elevation for Windows

This commit is contained in:
Flik
2026-03-06 21:22:58 +08:00
parent ba9edd3c02
commit d6627a292d
2 changed files with 72 additions and 30 deletions

View File

@@ -910,10 +910,24 @@ func (c *Client) performSelfUpdate(downloadURL string) error {
currentPath, _ = filepath.EvalSymlinks(currentPath) currentPath, _ = filepath.EvalSymlinks(currentPath)
// 预检查:验证是否有写权限(在下载前检查,避免浪费带宽) // 预检查:验证是否有写权限(在下载前检查,避免浪费带宽)
if err := c.checkUpdatePermissions(currentPath); err != nil { // Windows 跳过预检查,因为 Windows 更新通过 batch 脚本以提升权限执行
c.logErrorf("Update failed: %v", err) // 非 Windows原始路径 → DataDir → 临时目录,逐级回退
c.logErrorf("Self-update is not supported in this environment. Please update manually.") fallbackDir := ""
return err if runtime.GOOS != "windows" {
if err := c.checkUpdatePermissions(currentPath); err != nil {
// 尝试 DataDir
fallbackDir = c.DataDir
testFile := filepath.Join(fallbackDir, ".gotunnel_update_test")
if f, err := os.Create(testFile); err != nil {
// DataDir 也不可写,回退到临时目录
fallbackDir = os.TempDir()
c.logf("DataDir not writable, falling back to temp directory: %s", fallbackDir)
} else {
f.Close()
os.Remove(testFile)
c.logf("Original path not writable, falling back to data directory: %s", fallbackDir)
}
}
} }
// 使用共享的下载和解压逻辑 // 使用共享的下载和解压逻辑
@@ -930,42 +944,58 @@ func (c *Client) performSelfUpdate(downloadURL string) error {
return performWindowsClientUpdate(binaryPath, currentPath, c.ServerAddr, c.Token, c.ID) return performWindowsClientUpdate(binaryPath, currentPath, c.ServerAddr, c.Token, c.ID)
} }
// Linux/Mac/Android: 直接替换 // 确定目标路径
backupPath := currentPath + ".bak" targetPath := currentPath
if fallbackDir != "" {
targetPath = filepath.Join(fallbackDir, filepath.Base(currentPath))
c.logf("Will install to fallback path: %s", targetPath)
}
// 停止所有插件 // 停止所有插件
c.stopAllPlugins() c.stopAllPlugins()
// 备份当前文件 if fallbackDir == "" {
c.logf("Backing up current binary...") // 原地替换:备份 → 复制 → 清理
if err := os.Rename(currentPath, backupPath); err != nil { backupPath := currentPath + ".bak"
c.logErrorf("Update failed: cannot backup current binary: %v", err)
c.logErrorf("This may be due to insufficient permissions or read-only filesystem.")
return err
}
// 复制新文件(不能用 rename可能跨文件系统 c.logf("Backing up current binary...")
c.logf("Installing new binary...") if err := os.Rename(currentPath, backupPath); err != nil {
if err := update.CopyFile(binaryPath, currentPath); err != nil { c.logErrorf("Update failed: cannot backup current binary: %v", err)
os.Rename(backupPath, currentPath) return err
c.logErrorf("Update failed: cannot install new binary: %v", err) }
return err
}
// 设置执行权限 c.logf("Installing new binary...")
if err := os.Chmod(currentPath, 0755); err != nil { if err := update.CopyFile(binaryPath, currentPath); err != nil {
os.Rename(backupPath, currentPath) os.Rename(backupPath, currentPath)
c.logErrorf("Update failed: cannot set execute permission: %v", err) c.logErrorf("Update failed: cannot install new binary: %v", err)
return err return err
} }
// 删除备份 if err := os.Chmod(currentPath, 0755); err != nil {
os.Remove(backupPath) os.Rename(backupPath, currentPath)
c.logErrorf("Update failed: cannot set execute permission: %v", err)
return err
}
os.Remove(backupPath)
} else {
// 回退路径:直接复制到回退目录
c.logf("Installing new binary to data directory...")
if err := update.CopyFile(binaryPath, targetPath); err != nil {
c.logErrorf("Update failed: cannot install new binary: %v", err)
return err
}
if err := os.Chmod(targetPath, 0755); err != nil {
c.logErrorf("Update failed: cannot set execute permission: %v", err)
return err
}
}
c.logf("Update completed successfully, restarting...") c.logf("Update completed successfully, restarting...")
// 重启进程 // 重启进程(从新路径启动)
restartClientProcess(currentPath, c.ServerAddr, c.Token, c.ID) restartClientProcess(targetPath, c.ServerAddr, c.Token, c.ID)
return nil return nil
} }
@@ -1014,6 +1044,12 @@ func performWindowsClientUpdate(newFile, currentPath, serverAddr, token, id stri
} }
batchScript := fmt.Sprintf(`@echo off batchScript := fmt.Sprintf(`@echo off
:: Check for admin rights, request UAC elevation if needed
net session >nul 2>&1
if %%errorlevel%% neq 0 (
powershell -Command "Start-Process cmd -ArgumentList '/C \\"\"%%~f0\"\"' -Verb RunAs"
exit /b
)
ping 127.0.0.1 -n 2 > nul ping 127.0.0.1 -n 2 > nul
del "%s" del "%s"
move "%s" "%s" move "%s" "%s"

View File

@@ -165,6 +165,12 @@ func performSelfUpdate(downloadURL string, restart bool) error {
// performWindowsUpdate Windows 平台更新 // performWindowsUpdate Windows 平台更新
func performWindowsUpdate(newFile, currentPath string, restart bool) error { func performWindowsUpdate(newFile, currentPath string, restart bool) error {
batchScript := fmt.Sprintf(`@echo off batchScript := fmt.Sprintf(`@echo off
:: Check for admin rights, request UAC elevation if needed
net session >nul 2>&1
if %%errorlevel%% neq 0 (
powershell -Command "Start-Process cmd -ArgumentList '/C \\"\"%%~f0\"\"' -Verb RunAs"
exit /b
)
ping 127.0.0.1 -n 2 > nul ping 127.0.0.1 -n 2 > nul
del "%s" del "%s"
move "%s" "%s" move "%s" "%s"