summaryrefslogtreecommitdiff
path: root/listen_unix.go
diff options
context:
space:
mode:
authorTom Barrett <tom@tombarrett.xyz>2023-11-01 17:57:48 +0100
committerTom Barrett <tom@tombarrett.xyz>2023-11-01 18:11:33 +0100
commit240c3d1338415e5d82ef7ca0e52c4284be6441bd (patch)
tree4b0ee5d208c2cdffa78d65f1b0abe0ec85f15652 /listen_unix.go
parent73e78ab226f21e6c6c68961af88c4ab9c746f4f4 (diff)
parent0e204b730aa2b1fa0835336b1117eff8c420f713 (diff)
vbump to v2.7.5HEADcaddy-cgi
Diffstat (limited to 'listen_unix.go')
-rw-r--r--listen_unix.go67
1 files changed, 61 insertions, 6 deletions
diff --git a/listen_unix.go b/listen_unix.go
index 7ea6745..8870da5 100644
--- a/listen_unix.go
+++ b/listen_unix.go
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// TODO: Go 1.19 introduced the "unix" build tag. We have to support Go 1.18 until Go 1.20 is released.
-// When Go 1.19 is our minimum, remove this build tag, since "_unix" in the filename will do this.
-// (see also change needed in listen.go)
-//go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris
+// Even though the filename ends in _unix.go, we still have to specify the
+// build constraint here, because the filename convention only works for
+// literal GOOS values, and "unix" is a shortcut unique to build tags.
+//go:build unix
package caddy
@@ -98,7 +98,28 @@ func listenTCPOrUnix(ctx context.Context, lnKey string, network, address string,
}
return reusePort(network, address, c)
}
- return config.Listen(ctx, network, address)
+
+ // even though SO_REUSEPORT lets us bind the socket multiple times,
+ // we still put it in the listenerPool so we can count how many
+ // configs are using this socket; necessary to ensure we can know
+ // whether to enforce shutdown delays, for example (see #5393).
+ ln, err := config.Listen(ctx, network, address)
+ if err == nil {
+ listenerPool.LoadOrStore(lnKey, nil)
+ }
+
+ // if new listener is a unix socket, make sure we can reuse it later
+ // (we do our own "unlink on close" -- not required, but more tidy)
+ one := int32(1)
+ if unix, ok := ln.(*net.UnixListener); ok {
+ unix.SetUnlinkOnClose(false)
+ ln = &unixListener{unix, lnKey, &one}
+ unixSockets[lnKey] = ln.(*unixListener)
+ }
+
+ // lightly wrap the listener so that when it is closed,
+ // we can decrement the usage pool counter
+ return deleteListener{ln, lnKey}, err
}
// reusePort sets SO_REUSEPORT. Ineffective for unix sockets.
@@ -107,7 +128,7 @@ func reusePort(network, address string, conn syscall.RawConn) error {
return nil
}
return conn.Control(func(descriptor uintptr) {
- if err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
+ if err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unixSOREUSEPORT, 1); err != nil {
Log().Error("setting SO_REUSEPORT",
zap.String("network", network),
zap.String("address", address),
@@ -116,3 +137,37 @@ func reusePort(network, address string, conn syscall.RawConn) error {
}
})
}
+
+type unixListener struct {
+ *net.UnixListener
+ mapKey string
+ count *int32 // accessed atomically
+}
+
+func (uln *unixListener) Close() error {
+ newCount := atomic.AddInt32(uln.count, -1)
+ if newCount == 0 {
+ defer func() {
+ addr := uln.Addr().String()
+ unixSocketsMu.Lock()
+ delete(unixSockets, uln.mapKey)
+ unixSocketsMu.Unlock()
+ _ = syscall.Unlink(addr)
+ }()
+ }
+ return uln.UnixListener.Close()
+}
+
+// deleteListener is a type that simply deletes itself
+// from the listenerPool when it closes. It is used
+// solely for the purpose of reference counting (i.e.
+// counting how many configs are using a given socket).
+type deleteListener struct {
+ net.Listener
+ lnKey string
+}
+
+func (dl deleteListener) Close() error {
+ _, _ = listenerPool.Delete(dl.lnKey)
+ return dl.Listener.Close()
+}