Skip to content

Commit

Permalink
Preserve unix socket file (#20499)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
frankli0324 and zeripath committed Aug 13, 2022
1 parent 99efa02 commit 1f06387
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
38 changes: 35 additions & 3 deletions modules/graceful/net_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
const (
listenFDs = "LISTEN_FDS"
startFD = 3
unlinkFDs = "GITEA_UNLINK_FDS"
)

// In order to keep the working directory the same as when we started we record
Expand All @@ -33,8 +34,10 @@ var (
once = sync.Once{}
mutex = sync.Mutex{}

providedListeners = []net.Listener{}
activeListeners = []net.Listener{}
providedListenersToUnlink = []bool{}
activeListenersToUnlink = []bool{}
providedListeners = []net.Listener{}
activeListeners = []net.Listener{}
)

func getProvidedFDs() (savedErr error) {
Expand All @@ -53,6 +56,16 @@ func getProvidedFDs() (savedErr error) {
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++ {
file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i))

Expand Down Expand Up @@ -136,8 +149,11 @@ func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, err
for i, l := range providedListeners {
if isSameAddr(l.Addr(), address) {
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
needsUnlink := providedListenersToUnlink[i]
providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)

activeListeners = append(activeListeners, l)
activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
return l.(*net.TCPListener), nil
}
}
Expand All @@ -148,6 +164,7 @@ func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, err
return nil, err
}
activeListeners = append(activeListeners, l)
activeListenersToUnlink = append(activeListenersToUnlink, false)
return l, nil
}

Expand All @@ -166,9 +183,15 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener,
for i, l := range providedListeners {
if isSameAddr(l.Addr(), address) {
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)
unixListener := l.(*net.UnixListener)
unixListener.SetUnlinkOnClose(true)
if needsUnlink {
unixListener.SetUnlinkOnClose(true)
}
return unixListener, nil
}
}
Expand All @@ -189,6 +212,7 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener,
}

activeListeners = append(activeListeners, l)
activeListenersToUnlink = append(activeListenersToUnlink, true)
return l, nil
}

Expand Down Expand Up @@ -223,3 +247,11 @@ func getActiveListeners() []net.Listener {
copy(listeners, activeListeners)
return listeners
}

func getActiveListenersToUnlink() []bool {
mutex.Lock()
defer mutex.Unlock()
listenersToUnlink := make([]bool, len(activeListenersToUnlink))
copy(listenersToUnlink, activeListenersToUnlink)
return listenersToUnlink
}
15 changes: 15 additions & 0 deletions modules/graceful/restart_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -75,6 +76,20 @@ func RestartProcess() (int, error) {
}
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...)
process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: originalWD,
Expand Down

0 comments on commit 1f06387

Please sign in to comment.