1
// Copyright 2019 The Gitea Authors. All rights reserved.
2
// SPDX-License-Identifier: MIT
4
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
19
"code.gitea.io/gitea/modules/log"
20
"code.gitea.io/gitea/modules/setting"
21
"code.gitea.io/gitea/modules/util"
25
listenFDsEnv = "LISTEN_FDS"
27
unlinkFDsEnv = "GITEA_UNLINK_FDS"
29
notifySocketEnv = "NOTIFY_SOCKET"
30
watchdogTimeoutEnv = "WATCHDOG_USEC"
33
// In order to keep the working directory the same as when we started we record
35
var originalWD, _ = os.Getwd()
41
providedListenersToUnlink = []bool{}
42
activeListenersToUnlink = []bool{}
43
providedListeners = []net.Listener{}
44
activeListeners = []net.Listener{}
46
notifySocketAddr string
47
watchdogTimeout time.Duration
50
func getProvidedFDs() (savedErr error) {
51
// Only inherit the provided FDS once but we will save the error so that repeated calls to this function will return the same error
55
// now handle some additional systemd provided things
56
notifySocketAddr = os.Getenv(notifySocketEnv)
57
if notifySocketAddr != "" {
58
log.Debug("Systemd Notify Socket provided: %s", notifySocketAddr)
59
savedErr = os.Unsetenv(notifySocketEnv)
61
log.Warn("Unable to Unset the NOTIFY_SOCKET environment variable: %v", savedErr)
64
// FIXME: We don't handle WATCHDOG_PID
65
timeoutStr := os.Getenv(watchdogTimeoutEnv)
67
savedErr = os.Unsetenv(watchdogTimeoutEnv)
69
log.Warn("Unable to Unset the WATCHDOG_USEC environment variable: %v", savedErr)
73
s, err := strconv.ParseInt(timeoutStr, 10, 64)
75
log.Error("Unable to parse the provided WATCHDOG_USEC: %v", err)
76
savedErr = fmt.Errorf("unable to parse the provided WATCHDOG_USEC: %w", err)
80
log.Error("Unable to parse the provided WATCHDOG_USEC: %s should be a positive number", timeoutStr)
81
savedErr = fmt.Errorf("unable to parse the provided WATCHDOG_USEC: %s should be a positive number", timeoutStr)
84
watchdogTimeout = time.Duration(s) * time.Microsecond
87
log.Trace("No Systemd Notify Socket provided")
90
numFDs := os.Getenv(listenFDsEnv)
94
n, err := strconv.Atoi(numFDs)
96
savedErr = fmt.Errorf("%s is not a number: %s. Err: %w", listenFDsEnv, numFDs, err)
100
fdsToUnlinkStr := strings.Split(os.Getenv(unlinkFDsEnv), ",")
101
providedListenersToUnlink = make([]bool, n)
102
for _, fdStr := range fdsToUnlinkStr {
103
i, err := strconv.Atoi(fdStr)
104
if err != nil || i < 0 || i >= n {
107
providedListenersToUnlink[i] = true
110
for i := startFD; i < n+startFD; i++ {
111
file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i))
113
l, err := net.FileListener(file)
115
// Close the inherited file if it's a listener
116
if err = file.Close(); err != nil {
117
savedErr = fmt.Errorf("error closing provided socket fd %d: %w", i, err)
120
providedListeners = append(providedListeners, l)
124
// If needed we can handle packetconns here.
125
savedErr = fmt.Errorf("Error getting provided socket fd %d: %w", i, err)
132
// closeProvidedListeners closes all unused provided listeners.
133
func closeProvidedListeners() {
136
for _, l := range providedListeners {
139
log.Error("Error in closing unused provided listener: %v", err)
142
providedListeners = []net.Listener{}
145
// DefaultGetListener obtains a listener for the stream-oriented local network address:
146
// "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
147
func DefaultGetListener(network, address string) (net.Listener, error) {
148
// Add a deferral to say that we've tried to grab a listener
149
defer GetManager().InformCleanup()
151
case "tcp", "tcp4", "tcp6":
152
tcpAddr, err := net.ResolveTCPAddr(network, address)
156
return GetListenerTCP(network, tcpAddr)
157
case "unix", "unixpacket":
158
unixAddr, err := net.ResolveUnixAddr(network, address)
162
return GetListenerUnix(network, unixAddr)
164
return nil, net.UnknownNetworkError(network)
168
// GetListenerTCP announces on the local network address. The network must be:
169
// "tcp", "tcp4" or "tcp6". It returns a provided net.Listener for the
170
// matching network and address, or creates a new one using net.ListenTCP.
171
func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, error) {
172
if err := getProvidedFDs(); err != nil {
179
// look for a provided listener
180
for i, l := range providedListeners {
181
if isSameAddr(l.Addr(), address) {
182
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
183
needsUnlink := providedListenersToUnlink[i]
184
providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)
186
activeListeners = append(activeListeners, l)
187
activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
188
return l.(*net.TCPListener), nil
192
// no provided listener for this address -> make a fresh listener
193
l, err := net.ListenTCP(network, address)
197
activeListeners = append(activeListeners, l)
198
activeListenersToUnlink = append(activeListenersToUnlink, false)
202
// GetListenerUnix announces on the local network address. The network must be:
203
// "unix" or "unixpacket". It returns a provided net.Listener for the
204
// matching network and address, or creates a new one using net.ListenUnix.
205
func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener, error) {
206
if err := getProvidedFDs(); err != nil {
213
// look for a provided listener
214
for i, l := range providedListeners {
215
if isSameAddr(l.Addr(), address) {
216
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
217
needsUnlink := providedListenersToUnlink[i]
218
providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)
220
activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
221
activeListeners = append(activeListeners, l)
222
unixListener := l.(*net.UnixListener)
224
unixListener.SetUnlinkOnClose(true)
226
return unixListener, nil
230
// make a fresh listener
231
if err := util.Remove(address.Name); err != nil && !os.IsNotExist(err) {
232
return nil, fmt.Errorf("Failed to remove unix socket %s: %w", address.Name, err)
235
l, err := net.ListenUnix(network, address)
240
fileMode := os.FileMode(setting.UnixSocketPermission)
241
if err = os.Chmod(address.Name, fileMode); err != nil {
242
return nil, fmt.Errorf("Failed to set permission of unix socket to %s: %w", fileMode.String(), err)
245
activeListeners = append(activeListeners, l)
246
activeListenersToUnlink = append(activeListenersToUnlink, true)
250
func isSameAddr(a1, a2 net.Addr) bool {
251
// If the addresses are not on the same network fail.
252
if a1.Network() != a2.Network() {
256
// If the two addresses have the same string representation they're equal
263
// This allows for ipv6 vs ipv4 local addresses to compare as equal. This
264
// scenario is common when listening on localhost.
265
const ipv6prefix = "[::]"
266
a1s = strings.TrimPrefix(a1s, ipv6prefix)
267
a2s = strings.TrimPrefix(a2s, ipv6prefix)
268
const ipv4prefix = "0.0.0.0"
269
a1s = strings.TrimPrefix(a1s, ipv4prefix)
270
a2s = strings.TrimPrefix(a2s, ipv4prefix)
274
func getActiveListeners() []net.Listener {
277
listeners := make([]net.Listener, len(activeListeners))
278
copy(listeners, activeListeners)
282
func getActiveListenersToUnlink() []bool {
285
listenersToUnlink := make([]bool, len(activeListenersToUnlink))
286
copy(listenersToUnlink, activeListenersToUnlink)
287
return listenersToUnlink
290
func getNotifySocket() (*net.UnixConn, error) {
291
if err := getProvidedFDs(); err != nil {
292
// This error will be logged elsewhere
296
if notifySocketAddr == "" {
300
socketAddr := &net.UnixAddr{
301
Name: notifySocketAddr,
305
notifySocket, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
307
log.Warn("failed to dial NOTIFY_SOCKET %s: %v", socketAddr, err)
311
return notifySocket, nil
314
func getWatchdogTimeout() time.Duration {
315
if err := getProvidedFDs(); err != nil {
316
// This error will be logged elsewhere
320
return watchdogTimeout