github.com/kelleygo/clashcore@v1.0.2/hub/route/restart.go (about) 1 package route 2 3 import ( 4 "fmt" 5 "net/http" 6 "os" 7 "os/exec" 8 "runtime" 9 "syscall" 10 11 "github.com/kelleygo/clashcore/hub/executor" 12 "github.com/kelleygo/clashcore/log" 13 14 "github.com/go-chi/chi/v5" 15 "github.com/go-chi/render" 16 ) 17 18 func restartRouter() http.Handler { 19 r := chi.NewRouter() 20 r.Post("/", restart) 21 return r 22 } 23 24 func restart(w http.ResponseWriter, r *http.Request) { 25 // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 26 execPath, err := os.Executable() 27 if err != nil { 28 render.Status(r, http.StatusInternalServerError) 29 render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) 30 return 31 } 32 33 render.JSON(w, r, render.M{"status": "ok"}) 34 if f, ok := w.(http.Flusher); ok { 35 f.Flush() 36 } 37 38 // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180 39 // The background context is used because the underlying functions wrap it 40 // with timeout and shut down the server, which handles current request. It 41 // also should be done in a separate goroutine for the same reason. 42 go restartExecutable(execPath) 43 } 44 45 func restartExecutable(execPath string) { 46 var err error 47 executor.Shutdown() 48 if runtime.GOOS == "windows" { 49 cmd := exec.Command(execPath, os.Args[1:]...) 50 log.Infoln("restarting: %q %q", execPath, os.Args[1:]) 51 cmd.Stdin = os.Stdin 52 cmd.Stdout = os.Stdout 53 cmd.Stderr = os.Stderr 54 err = cmd.Start() 55 if err != nil { 56 log.Fatalln("restarting: %s", err) 57 } 58 59 os.Exit(0) 60 } 61 62 log.Infoln("restarting: %q %q", execPath, os.Args[1:]) 63 err = syscall.Exec(execPath, os.Args, os.Environ()) 64 if err != nil { 65 log.Fatalln("restarting: %s", err) 66 } 67 }