From 703bfccdc0145c51519b39381dad3b1cc5479b59 Mon Sep 17 00:00:00 2001 From: Marco Primi Date: Thu, 23 Jan 2025 14:28:47 -0800 Subject: [PATCH] [POC/WIP/DRAFT] Standard exit code when shutdown via signal When receiving , exit with code 128+. Pros: - Implement de-facto convention - Easier integration for any tool/script that expects this convention - Easier investigation of "why did server shutdown?" mystery scenarios Cons: - May break some existing integration/tools/script (Hyrum's Law) Sources: - https://tldp.org/LDP/abs/html/exitcodes.html - https://en.wikipedia.org/wiki/Exit_status#Shell_and_scripts - https://www.baeldung.com/linux/status-codes#fatal-error-signal --- main.go | 8 +++++--- server/server.go | 20 ++++++++++++++------ server/signal.go | 6 ++++-- server/signal_windows.go | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/main.go b/main.go index a85eab97004..b3ec8addef8 100644 --- a/main.go +++ b/main.go @@ -108,7 +108,8 @@ func main() { fs.Usage, server.PrintTLSHelpAndDie) if err != nil { - server.PrintAndDie(fmt.Sprintf("%s: %s", exe, err)) + fmt.Fprintln(os.Stderr, fmt.Sprintf("%s: %s", exe, err)) + os.Exit(1) } else if opts.CheckConfig { fmt.Fprintf(os.Stderr, "%s: configuration file %s is valid (%s)\n", exe, opts.ConfigFile, opts.ConfigDigest()) os.Exit(0) @@ -117,7 +118,8 @@ func main() { // Create the server with appropriate options. s, err := server.NewServer(opts) if err != nil { - server.PrintAndDie(fmt.Sprintf("%s: %s", exe, err)) + fmt.Fprintln(os.Stderr, fmt.Sprintf("%s: %s", exe, err)) + os.Exit(1) } // Configure the logger based on the flags. @@ -125,7 +127,7 @@ func main() { // Start things up. Block here until done. if err := server.Run(s); err != nil { - server.PrintAndDie(err.Error()) + s.PrintAndDie(err.Error()) } // Adjust MAXPROCS if running under linux/cgroups quotas. diff --git a/server/server.go b/server/server.go index ce878d74d36..bd2ecc217e6 100644 --- a/server/server.go +++ b/server/server.go @@ -178,6 +178,7 @@ type Server struct { opts *Options running atomic.Bool shutdown atomic.Bool + shutdownSignal atomic.Int32 listener net.Listener listenerErr error gacc *Account @@ -1544,6 +1545,19 @@ func (s *Server) processTrustedKeys() bool { return true } +// PrintAndDie print error message and exit +func (s *Server) PrintAndDie(msg string) { + + sig := s.shutdownSignal.Load() + if sig == 0 { + fmt.Fprintln(os.Stderr, msg) + os.Exit(1) + } else { + fmt.Fprintln(os.Stderr, fmt.Sprintf("Shutting down due to signal: %d", sig)) + os.Exit(int(sig + 128)) + } +} + // checkTrustedKeyString will check that the string is a valid array // of public operator nkeys. func checkTrustedKeyString(keys string) []string { @@ -1576,12 +1590,6 @@ func (s *Server) initStampedTrustedKeys() bool { return true } -// PrintAndDie is exported for access in other packages. -func PrintAndDie(msg string) { - fmt.Fprintln(os.Stderr, msg) - os.Exit(1) -} - // PrintServerAndExit will print our version and exit. func PrintServerAndExit() { fmt.Printf("nats-server: v%s\n", VERSION) diff --git a/server/signal.go b/server/signal.go index aef50a5b4b9..7527f95120e 100644 --- a/server/signal.go +++ b/server/signal.go @@ -49,10 +49,12 @@ func (s *Server) handleSignals() { s.Noticef("Trapped %q signal", sig) switch sig { case syscall.SIGINT: + s.shutdownSignal.CompareAndSwap(0, int32(syscall.SIGINT)) s.Shutdown() s.WaitForShutdown() - os.Exit(0) + os.Exit(128 + int(syscall.SIGINT)) case syscall.SIGTERM: + s.shutdownSignal.CompareAndSwap(0, int32(syscall.SIGTERM)) // Shutdown unless graceful shutdown already in progress. s.mu.Lock() ldm := s.ldm @@ -61,7 +63,7 @@ func (s *Server) handleSignals() { if !ldm { s.Shutdown() s.WaitForShutdown() - os.Exit(0) + os.Exit(128 + int(syscall.SIGTERM)) } case syscall.SIGUSR1: // File log re-open for rotating file logs. diff --git a/server/signal_windows.go b/server/signal_windows.go index 2f5a27c51d7..1751995bbb7 100644 --- a/server/signal_windows.go +++ b/server/signal_windows.go @@ -37,6 +37,7 @@ func (s *Server) handleSignals() { for { select { case sig := <-c: + s.shutdownSignal.CompareAndSwap(0, int32(syscall.SIGTERM)) s.Debugf("Trapped %q signal", sig) s.Shutdown() os.Exit(0)