diff --git a/hub/route/restart.go b/hub/route/restart.go index 9539296e..69b4f5b8 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -38,25 +38,29 @@ func restart(w http.ResponseWriter, r *http.Request) { // The background context is used because the underlying functions wrap it // with timeout and shut down the server, which handles current request. It // also should be done in a separate goroutine for the same reason. - go func() { - if runtime.GOOS == "windows" { - cmd := exec.Command(execPath, os.Args[1:]...) - log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - log.Fatalln("restarting: %s", err) - } + go runRestart(execPath) +} - os.Exit(0) - } +func runRestart(execPath string) { + var err error + if runtime.GOOS == "windows" { + cmd := exec.Command(execPath, os.Args[1:]...) log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - err = syscall.Exec(execPath, os.Args, os.Environ()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Start() if err != nil { log.Fatalln("restarting: %s", err) } - }() + + os.Exit(0) + } + + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + err = syscall.Exec(execPath, os.Args, os.Environ()) + if err != nil { + log.Fatalln("restarting: %s", err) + } } diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 5adf79eb..0a7c54f9 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -1,12 +1,15 @@ package route import ( + "fmt" "net/http" + "os" "github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/log" "github.com/go-chi/chi/v5" + "github.com/go-chi/render" ) func upgradeRouter() http.Handler { @@ -18,11 +21,24 @@ func upgradeRouter() http.Handler { func upgrade(w http.ResponseWriter, r *http.Request) { // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 log.Infoln("start update") - err := updater.Update() + execPath, err := os.Executable() if err != nil { - log.Errorln("err:%s", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) return } - restart(w, r) + err = updater.Update(execPath) + if err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("Upgrade: %s", err))) + return + } + + render.JSON(w, r, render.M{"status": "ok"}) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + + go runRestart(execPath) } diff --git a/hub/updater/updater.go b/hub/updater/updater.go index b428ca9c..3d80707a 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -50,12 +50,12 @@ type updateError struct { } func (e *updateError) Error() string { - return fmt.Sprintf("error: %s", e.Message) + return fmt.Sprintf("update error: %s", e.Message) } // Update performs the auto-updater. It returns an error if the updater failed. // If firstRun is true, it assumes the configuration file doesn't exist. -func Update() (err error) { +func Update(execPath string) (err error) { mu.Lock() defer mu.Unlock() @@ -63,7 +63,6 @@ func Update() (err error) { goarch = runtime.GOARCH latestVersion, err = getLatestVersion() if err != nil { - err := &updateError{Message: err.Error()} return err } @@ -84,11 +83,6 @@ func Update() (err error) { } }() - execPath, err := os.Executable() - if err != nil { - return fmt.Errorf("getting executable path: %w", err) - } - workDir = filepath.Dir(execPath) err = prepare(execPath) @@ -110,7 +104,7 @@ func Update() (err error) { err = backup() if err != nil { - return fmt.Errorf("replacing: %w", err) + return fmt.Errorf("backuping: %w", err) } err = replace() @@ -186,9 +180,9 @@ func unpack() error { return nil } -// backup makes a backup of the current configuration and supporting files. +// backup makes a backup of the current executable file func backup() (err error) { - log.Infoln("updater: backing up current ExecFile:%s", currentExeName) + log.Infoln("updater: backing up current ExecFile:%s to %s", currentExeName, backupExeName) _ = os.Mkdir(backupDir, 0o755) err = os.Rename(currentExeName, backupExeName) @@ -199,12 +193,11 @@ func backup() (err error) { return nil } -// replace moves the current executable with the updated one and also copies the -// supporting files. +// replace moves the current executable with the updated one func replace() error { var err error - log.Infoln("copying: %s to %s", updateExeName, currentExeName) + log.Infoln("replacing: %s to %s", updateExeName, currentExeName) if goos == "windows" { // rename fails with "File in use" error err = copyFile(updateExeName, currentExeName) @@ -214,7 +207,9 @@ func replace() error { if err != nil { return err } + log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName) + return nil }