From ad40d7d8a972c641106bc2e63a1dce4b0abd3cfb Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sun, 11 Jun 2023 19:48:03 +0100 Subject: [PATCH] fix bugs with restarts/interrupts The password reset daemon wasn't being closed on restarts, so an extra pwr would be sent w/ every restart. Restarts & Interrupts (Ctrl-C) rarely worked, as there were multiple listeners to the "RESTART" channel, and I didn't know the message was consumed by whoever got it first, meaning if the main thread didn't get it first, the app wouldn't quit. Listeners are now registered, and the restart message is re-broadcasted until everyone's got it. Fixes #264 --- api.go | 4 +++ discord.go | 2 +- main.go | 84 ++++++++++++++++++++++++++++++------------------------ pwreset.go | 2 +- restart.go | 7 +++-- tray.go | 3 ++ 6 files changed, 60 insertions(+), 42 deletions(-) diff --git a/api.go b/api.go index 92579b6..7fc4740 100644 --- a/api.go +++ b/api.go @@ -359,6 +359,8 @@ func (app *appContext) ModifyConfig(gc *gin.Context) { } else { RESTART <- true } + // Safety Sleep (Ensure shutdown tasks get done) + time.Sleep(time.Second) } app.loadConfig() // Reinitialize password validator on config change, as opposed to every applicable request like in python. @@ -527,5 +529,7 @@ func (app *appContext) Restart() error { } else { RESTART <- true } + // Safety Sleep (Ensure shutdown tasks get done) + time.Sleep(time.Second) return nil } diff --git a/discord.go b/discord.go index 14fc59c..7362fa3 100644 --- a/discord.go +++ b/discord.go @@ -120,7 +120,7 @@ func (d *DiscordDaemon) run() { defer d.deregisterCommands() defer d.bot.Close() - d.registerCommands() + go d.registerCommands() <-d.ShutdownChannel d.ShutdownChannel <- "Down" diff --git a/main.go b/main.go index c07f6f8..fb6c725 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "time" "github.com/fatih/color" @@ -43,12 +44,14 @@ var ( SWAGGER *bool QUIT = false RUNNING = false - warning = color.New(color.FgYellow).SprintfFunc() - info = color.New(color.FgMagenta).SprintfFunc() - hiwhite = color.New(color.FgHiWhite).SprintfFunc() - white = color.New(color.FgWhite).SprintfFunc() - version string - commit string + // Used to know how many times to re-broadcast restart signal. + RESTARTLISTENERCOUNT = 0 + warning = color.New(color.FgYellow).SprintfFunc() + info = color.New(color.FgMagenta).SprintfFunc() + hiwhite = color.New(color.FgHiWhite).SprintfFunc() + white = color.New(color.FgWhite).SprintfFunc() + version string + commit string ) var temp = func() string { @@ -102,7 +105,6 @@ type appContext struct { host string port int version string - quit chan os.Signal URLBase string updater *Updater newUpdate bool // Whether whatever's in update is new. @@ -152,6 +154,7 @@ func test(app *appContext) { } func start(asDaemon, firstCall bool) { + RESTARTLISTENERCOUNT = 0 RUNNING = true defer func() { RUNNING = false }() @@ -250,7 +253,7 @@ func start(asDaemon, firstCall bool) { app.err.Fatalf("Couldn't establish socket connection at %s\n", SOCK) } c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c os.Remove(SOCK) @@ -578,40 +581,38 @@ func start(asDaemon, firstCall bool) { } else { app.info.Printf("Loaded @ %s", address) } - app.quit = make(chan os.Signal) - signal.Notify(app.quit, os.Interrupt) - go func() { - for range app.quit { - app.shutdown() - } - }() - for range RESTART { - println("got it too!") - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - if err := SRV.Shutdown(ctx); err != nil { - app.err.Fatalf("Server shutdown error: %s", err) - } - app.info.Println("Server shut down.") - return + + waitForRestart() + + app.info.Printf("Restart/Quit signal received, give me a second!") + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + if err := SRV.Shutdown(ctx); err != nil { + app.err.Fatalf("Server shutdown error: %s", err) } + app.info.Println("Server shut down.") + return } -func (app *appContext) shutdown() { - app.info.Println("Shutting down...") +func shutdown() { QUIT = true RESTART <- true - for { - if RUNNING { - continue - } - cntx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - if err := SRV.Shutdown(cntx); err != nil { - app.err.Fatalf("Server shutdown error: %s", err) - } + // Safety Sleep (Ensure shutdown tasks get done) + time.Sleep(time.Second) +} - os.Exit(1) +func (app *appContext) shutdown() { + app.info.Println("Shutting down...") + shutdown() +} + +// Receives a restart signal and re-broadcasts it for other components. +func waitForRestart() { + RESTARTLISTENERCOUNT++ + <-RESTART + RESTARTLISTENERCOUNT-- + if RESTARTLISTENERCOUNT > 0 { + RESTART <- true } } @@ -686,6 +687,15 @@ func main() { TEST = true } loadFilesystems() + + quit := make(chan os.Signal, 0) + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + // defer close(quit) + go func() { + <-quit + shutdown() + }() + if flagPassed("start") { args := []string{} for i, f := range os.Args { @@ -760,7 +770,7 @@ You can then run: start(false, true) for { if QUIT { - continue + break } printVersion() start(false, false) diff --git a/pwreset.go b/pwreset.go index 12e26ac..22e9a74 100644 --- a/pwreset.go +++ b/pwreset.go @@ -46,7 +46,7 @@ func (app *appContext) StartPWR() { app.err.Printf("Failed to start password reset daemon: %s", err) } - <-RESTART + waitForRestart() } // PasswordReset represents a passwordreset-xyz.json file generated by Jellyfin. diff --git a/restart.go b/restart.go index 1713619..9c3f782 100644 --- a/restart.go +++ b/restart.go @@ -1,4 +1,4 @@ -// +build !windows +//go:build !windows package main @@ -11,9 +11,10 @@ import ( func (app *appContext) HardRestart() error { defer func() { + quit := make(chan os.Signal, 0) if r := recover(); r != nil { - signal.Notify(app.quit, os.Interrupt) - <-app.quit + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + <-quit } }() args := os.Args diff --git a/tray.go b/tray.go index a89f108..135a06b 100644 --- a/tray.go +++ b/tray.go @@ -8,6 +8,7 @@ import ( "os" "os/signal" "syscall" + "time" "github.com/getlantern/systray" ) @@ -26,6 +27,8 @@ func onExit() { if RUNNING { QUIT = true RESTART <- true + // Safety Sleep (Ensure shutdown tasks get done) + time.Sleep(time.Second) } os.Remove(SOCK) }