Preserve unix socket file (#20499)

By default Gitea will always unlink any sockets that are provided using the `LISTEN_FDS` environment variable. This is because it uses this variable to handle passing when it is doing a graceful restart. However, this same mechanism is used by systemd - which explicitly expects that passed in sockets should not be unlinked by the receiving process. 

This PR adjusts Gitea's graceful restart mechanism to use an additional environment variable which tracks if a listening socket was opened by Gitea - and therefore should be unlinked on shutdown by Gitea.

Fix #20490

Co-authored-by: zeripath <art27@cantab.net>
tokarchuk/v1.18
Frank 2 years ago committed by GitHub
parent 99efa02edf
commit 1f0638747b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      modules/graceful/net_unix.go
  2. 15
      modules/graceful/restart_unix.go

@ -23,6 +23,7 @@ import (
const ( const (
listenFDs = "LISTEN_FDS" listenFDs = "LISTEN_FDS"
startFD = 3 startFD = 3
unlinkFDs = "GITEA_UNLINK_FDS"
) )
// In order to keep the working directory the same as when we started we record // In order to keep the working directory the same as when we started we record
@ -33,8 +34,10 @@ var (
once = sync.Once{} once = sync.Once{}
mutex = sync.Mutex{} mutex = sync.Mutex{}
providedListeners = []net.Listener{} providedListenersToUnlink = []bool{}
activeListeners = []net.Listener{} activeListenersToUnlink = []bool{}
providedListeners = []net.Listener{}
activeListeners = []net.Listener{}
) )
func getProvidedFDs() (savedErr error) { func getProvidedFDs() (savedErr error) {
@ -53,6 +56,16 @@ func getProvidedFDs() (savedErr error) {
return return
} }
fdsToUnlinkStr := strings.Split(os.Getenv(unlinkFDs), ",")
providedListenersToUnlink = make([]bool, n)
for _, fdStr := range fdsToUnlinkStr {
i, err := strconv.Atoi(fdStr)
if err != nil || i < 0 || i >= n {
continue
}
providedListenersToUnlink[i] = true
}
for i := startFD; i < n+startFD; i++ { for i := startFD; i < n+startFD; i++ {
file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i)) file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i))
@ -136,8 +149,11 @@ func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, err
for i, l := range providedListeners { for i, l := range providedListeners {
if isSameAddr(l.Addr(), address) { if isSameAddr(l.Addr(), address) {
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...) providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
needsUnlink := providedListenersToUnlink[i]
providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)
activeListeners = append(activeListeners, l) activeListeners = append(activeListeners, l)
activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
return l.(*net.TCPListener), nil return l.(*net.TCPListener), nil
} }
} }
@ -148,6 +164,7 @@ func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, err
return nil, err return nil, err
} }
activeListeners = append(activeListeners, l) activeListeners = append(activeListeners, l)
activeListenersToUnlink = append(activeListenersToUnlink, false)
return l, nil return l, nil
} }
@ -166,9 +183,15 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener,
for i, l := range providedListeners { for i, l := range providedListeners {
if isSameAddr(l.Addr(), address) { if isSameAddr(l.Addr(), address) {
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...) providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
needsUnlink := providedListenersToUnlink[i]
providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)
activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
activeListeners = append(activeListeners, l) activeListeners = append(activeListeners, l)
unixListener := l.(*net.UnixListener) unixListener := l.(*net.UnixListener)
unixListener.SetUnlinkOnClose(true) if needsUnlink {
unixListener.SetUnlinkOnClose(true)
}
return unixListener, nil return unixListener, nil
} }
} }
@ -189,6 +212,7 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener,
} }
activeListeners = append(activeListeners, l) activeListeners = append(activeListeners, l)
activeListenersToUnlink = append(activeListenersToUnlink, true)
return l, nil return l, nil
} }
@ -223,3 +247,11 @@ func getActiveListeners() []net.Listener {
copy(listeners, activeListeners) copy(listeners, activeListeners)
return listeners return listeners
} }
func getActiveListenersToUnlink() []bool {
mutex.Lock()
defer mutex.Unlock()
listenersToUnlink := make([]bool, len(activeListenersToUnlink))
copy(listenersToUnlink, activeListenersToUnlink)
return listenersToUnlink
}

@ -12,6 +12,7 @@ import (
"net" "net"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
@ -75,6 +76,20 @@ func RestartProcess() (int, error) {
} }
env = append(env, fmt.Sprintf("%s=%d", listenFDs, len(listeners))) env = append(env, fmt.Sprintf("%s=%d", listenFDs, len(listeners)))
sb := &strings.Builder{}
for i, unlink := range getActiveListenersToUnlink() {
if !unlink {
continue
}
_, _ = sb.WriteString(strconv.Itoa(i))
_, _ = sb.WriteString(",")
}
unlinkStr := sb.String()
if len(unlinkStr) > 0 {
unlinkStr = unlinkStr[:len(unlinkStr)-1]
env = append(env, fmt.Sprintf("%s=%s", unlinkFDs, unlinkStr))
}
allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...) allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{ process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: originalWD, Dir: originalWD,

Loading…
Cancel
Save