From 28adcf0437e6f051b197655cf7c9ee4fceb3523a Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Mon, 29 Feb 2016 20:21:49 -0800 Subject: [PATCH] Graceful shutdown on error for httpd, smtpd, pop3d --- config/config.go | 12 ++++++------ httpd/server.go | 16 +++++++++++++--- pop3d/listener.go | 27 ++++++++++++++++++++------- smtpd/listener.go | 27 ++++++++++++++++++++------- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/config/config.go b/config/config.go index 2c648ec..9d1bdf1 100644 --- a/config/config.go +++ b/config/config.go @@ -193,11 +193,11 @@ func parseSMTPConfig() error { } addr := net.ParseIP(str) if addr == nil { - return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err) + return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, str) } addr = addr.To4() if addr == nil { - return fmt.Errorf("Failed to parse [%v]%v: '%v' not IPv4!", section, option, err) + return fmt.Errorf("Failed to parse [%v]%v: '%v' not IPv4!", section, option, str) } smtpConfig.IP4address = addr @@ -264,11 +264,11 @@ func parsePOP3Config() error { } addr := net.ParseIP(str) if addr == nil { - return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err) + return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, str) } addr = addr.To4() if addr == nil { - return fmt.Errorf("Failed to parse [%v]%v: '%v' not IPv4!", section, option, err) + return fmt.Errorf("Failed to parse [%v]%v: '%v' not IPv4!", section, option, str) } pop3Config.IP4address = addr @@ -307,11 +307,11 @@ func parseWebConfig() error { } addr := net.ParseIP(str) if addr == nil { - return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err) + return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, str) } addr = addr.To4() if addr == nil { - return fmt.Errorf("Failed to parse [%v]%v: '%v' not IPv4!", section, option, err) + return fmt.Errorf("Failed to parse [%v]%v: '%v' not IPv4!", section, option, str) } webConfig.IP4address = addr diff --git a/httpd/server.go b/httpd/server.go index b262b7f..afaa454 100644 --- a/httpd/server.go +++ b/httpd/server.go @@ -75,8 +75,8 @@ func Start() { listener, err = net.Listen("tcp", addr) if err != nil { log.Errorf("HTTP failed to start TCP4 listener: %v", err) - // TODO More graceful early-shutdown procedure - panic(err) + emergencyShutdown() + return } // Listener go routine @@ -104,7 +104,8 @@ func serve() { // Nop default: log.Errorf("HTTP server failed: %v", err) - // TODO shutdown? + emergencyShutdown() + return } } @@ -141,3 +142,12 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { log.Errorf("HTTP failed to write response: %v", err) } } + +func emergencyShutdown() { + // Shutdown Inbucket + select { + case _ = <-globalShutdown: + default: + close(globalShutdown) + } +} diff --git a/pop3d/listener.go b/pop3d/listener.go index 3419e87..a245b65 100644 --- a/pop3d/listener.go +++ b/pop3d/listener.go @@ -47,16 +47,20 @@ func (s *Server) Start() { cfg.IP4address, cfg.IP4port)) if err != nil { log.Errorf("POP3 Failed to build tcp4 address: %v", err) - // TODO More graceful early-shutdown procedure - panic(err) + // serve() never called, so we do local shutdown here + close(s.localShutdown) + s.emergencyShutdown() + return } log.Infof("POP3 listening on TCP4 %v", addr) s.listener, err = net.ListenTCP("tcp4", addr) if err != nil { log.Errorf("POP3 failed to start tcp4 listener: %v", err) - // TODO More graceful early-shutdown procedure - panic(err) + // serve() never called, so we do local shutdown here + close(s.localShutdown) + s.emergencyShutdown() + return } // Listener go routine @@ -100,9 +104,9 @@ func (s *Server) serve() { close(s.localShutdown) return default: - // TODO Implement a max error counter before shutdown? - // or maybe attempt to restart smtpd - panic(err) + close(s.localShutdown) + s.emergencyShutdown() + return } } } else { @@ -113,6 +117,15 @@ func (s *Server) serve() { } } +func (s *Server) emergencyShutdown() { + // Shutdown Inbucket + select { + case _ = <-s.globalShutdown: + default: + close(s.globalShutdown) + } +} + // Drain causes the caller to block until all active POP3 sessions have finished func (s *Server) Drain() { // Wait for listener to exit diff --git a/smtpd/listener.go b/smtpd/listener.go index 5d63bd7..fe4286d 100644 --- a/smtpd/listener.go +++ b/smtpd/listener.go @@ -78,16 +78,20 @@ func (s *Server) Start() { cfg.IP4address, cfg.IP4port)) if err != nil { log.Errorf("Failed to build tcp4 address: %v", err) - // TODO More graceful early-shutdown procedure - panic(err) + // serve() never called, so we do local shutdown here + close(s.localShutdown) + s.emergencyShutdown() + return } log.Infof("SMTP listening on TCP4 %v", addr) s.listener, err = net.ListenTCP("tcp4", addr) if err != nil { log.Errorf("SMTP failed to start tcp4 listener: %v", err) - // TODO More graceful early-shutdown procedure - panic(err) + // serve() never called, so we do local shutdown here + close(s.localShutdown) + s.emergencyShutdown() + return } if !s.storeMessages { @@ -141,9 +145,9 @@ func (s *Server) serve() { close(s.localShutdown) return default: - // TODO Implement a max error counter before shutdown? - // or maybe attempt to restart smtpd - panic(err) + close(s.localShutdown) + s.emergencyShutdown() + return } } } else { @@ -155,6 +159,15 @@ func (s *Server) serve() { } } +func (s *Server) emergencyShutdown() { + // Shutdown Inbucket + select { + case _ = <-s.globalShutdown: + default: + close(s.globalShutdown) + } +} + // Drain causes the caller to block until all active SMTP sessions have finished func (s *Server) Drain() { // Wait for listener to exit