From e3f22ad2cca094cba057683f35f8536e3f71a582 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 24 Nov 2019 02:11:24 +0000 Subject: [PATCH] Graceful: Allow graceful restart for unix sockets (#9113) Previously we could not handle graceful restarts for http over unix sockets. These can now be handled. --- cmd/web.go | 29 ++++++----------------------- cmd/web_graceful.go | 12 ++++++------ modules/graceful/net_unix.go | 15 ++++++++++++++- modules/graceful/restart_unix.go | 5 +++++ 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/cmd/web.go b/cmd/web.go index 22a7f9082..e45e52be3 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -60,7 +60,7 @@ func runHTTPRedirector() { http.Redirect(w, r, target, http.StatusTemporaryRedirect) }) - var err = runHTTP(source, context2.ClearHandler(handler)) + var err = runHTTP("tcp", source, context2.ClearHandler(handler)) if err != nil { log.Fatal("Failed to start port redirection: %v", err) @@ -77,12 +77,12 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) go func() { log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here) - var err = runHTTP(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) + var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) if err != nil { log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err) } }() - return runHTTPSWithTLSConfig(listenAddr, certManager.TLSConfig(), context2.ClearHandler(m)) + return runHTTPSWithTLSConfig("tcp", listenAddr, certManager.TLSConfig(), context2.ClearHandler(m)) } func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) { @@ -171,7 +171,7 @@ func runWeb(ctx *cli.Context) error { switch setting.Protocol { case setting.HTTP: NoHTTPRedirector() - err = runHTTP(listenAddr, context2.ClearHandler(m)) + err = runHTTP("tcp", listenAddr, context2.ClearHandler(m)) case setting.HTTPS: if setting.EnableLetsEncrypt { err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m)) @@ -182,7 +182,7 @@ func runWeb(ctx *cli.Context) error { } else { NoHTTPRedirector() } - err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m)) + err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m)) case setting.FCGI: NoHTTPRedirector() // FCGI listeners are provided as stdin - this is orthogonal to the LISTEN_FDS approach @@ -200,25 +200,8 @@ func runWeb(ctx *cli.Context) error { }() err = fcgi.Serve(listener, context2.ClearHandler(m)) case setting.UnixSocket: - // This could potentially be inherited using LISTEN_FDS but currently - // these cannot be inherited NoHTTPRedirector() - NoMainListener() - if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) { - log.Fatal("Failed to remove unix socket directory %s: %v", listenAddr, err) - } - var listener *net.UnixListener - listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"}) - if err != nil { - break // Handle error after switch - } - - // FIXME: add proper implementation of signal capture on all protocols - // execute this on SIGTERM or SIGINT: listener.Close() - if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil { - log.Fatal("Failed to set permission of unix socket: %v", err) - } - err = http.Serve(listener, context2.ClearHandler(m)) + err = runHTTP("unix", listenAddr, context2.ClearHandler(m)) default: log.Fatal("Invalid protocol: %s", setting.Protocol) } diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go index a37f669d0..3907e843a 100644 --- a/cmd/web_graceful.go +++ b/cmd/web_graceful.go @@ -11,16 +11,16 @@ import ( "code.gitea.io/gitea/modules/graceful" ) -func runHTTP(listenAddr string, m http.Handler) error { - return graceful.HTTPListenAndServe("tcp", listenAddr, m) +func runHTTP(network, listenAddr string, m http.Handler) error { + return graceful.HTTPListenAndServe(network, listenAddr, m) } -func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error { - return graceful.HTTPListenAndServeTLS("tcp", listenAddr, certFile, keyFile, m) +func runHTTPS(network, listenAddr, certFile, keyFile string, m http.Handler) error { + return graceful.HTTPListenAndServeTLS(network, listenAddr, certFile, keyFile, m) } -func runHTTPSWithTLSConfig(listenAddr string, tlsConfig *tls.Config, m http.Handler) error { - return graceful.HTTPListenAndServeTLSConfig("tcp", listenAddr, tlsConfig, m) +func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m http.Handler) error { + return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, tlsConfig, m) } // NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go index 2b8efe035..5550c09f4 100644 --- a/modules/graceful/net_unix.go +++ b/modules/graceful/net_unix.go @@ -16,6 +16,7 @@ import ( "sync" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) const ( @@ -165,15 +166,27 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener, if isSameAddr(l.Addr(), address) { providedListeners = append(providedListeners[:i], providedListeners[i+1:]...) activeListeners = append(activeListeners, l) - return l.(*net.UnixListener), nil + unixListener := l.(*net.UnixListener) + unixListener.SetUnlinkOnClose(true) + return unixListener, nil } } // make a fresh listener + if err := os.Remove(address.Name); err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("Failed to remove unix socket %s: %v", address.Name, err) + } + l, err := net.ListenUnix(network, address) if err != nil { return nil, err } + + fileMode := os.FileMode(setting.UnixSocketPermission) + if err = os.Chmod(address.Name, fileMode); err != nil { + return nil, fmt.Errorf("Failed to set permission of unix socket to %s: %v", fileMode.String(), err) + } + activeListeners = append(activeListeners, l) return l, nil } diff --git a/modules/graceful/restart_unix.go b/modules/graceful/restart_unix.go index 8c68965f5..3fc4f0511 100644 --- a/modules/graceful/restart_unix.go +++ b/modules/graceful/restart_unix.go @@ -9,6 +9,7 @@ package graceful import ( "fmt" + "net" "os" "os/exec" "strings" @@ -48,6 +49,10 @@ func RestartProcess() (int, error) { if err != nil { return 0, err } + + if unixListener, ok := l.(*net.UnixListener); ok { + unixListener.SetUnlinkOnClose(false) + } // Remember to close these at the end. defer files[i].Close() }