Permalink
Browse files

net: add (*UnixListener).SetUnlinkOnClose

Let users control whether unix listener socket file is unlinked on close.

Fixes #13877.

Change-Id: I9d1cb47e31418d655f164d15c67e188656a67d1c
Reviewed-on: https://go-review.googlesource.com/32099
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information...
rsc committed Oct 27, 2016
1 parent 13558c4 commit eb88b3eefa113f67e7cf72dfd085f65bbd125179
Showing with 109 additions and 37 deletions.
  1. +12 −0 src/net/unixsock_posix.go
  2. +97 −37 src/net/unixsock_test.go
View
@@ -190,6 +190,18 @@ func (ln *UnixListener) file() (*os.File, error) {
return f, nil
}
+// SetUnlinkOnClose sets whether the underlying socket file should be removed
+// from the file system when the listener is closed.
+//
+// The default behavior is to unlink the socket file only when package net created it.
+// That is, when the listener and the underlying socket file were created by a call to
+// Listen or ListenUnix, then by default closing the listener will remove the socket file.
+// but if the listener was created by a call to FileListener to use an already existing
+// socket file, then by default closing the listener will not remove the socket file.
+func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
+ l.unlink = unlink
+}
+
func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
fd, err := unixSocket(ctx, network, laddr, nil, "listen")
if err != nil {
View
@@ -415,44 +415,104 @@ func TestUnixUnlink(t *testing.T) {
t.Skip("unix test")
}
name := testUnixAddr()
- l, err := Listen("unix", name)
- if err != nil {
- t.Fatal(err)
- }
- if _, err := os.Stat(name); err != nil {
- t.Fatalf("cannot stat unix socket after ListenUnix: %v", err)
- }
- f, _ := l.(*UnixListener).File()
- l1, err := FileListener(f)
- if err != nil {
- t.Fatal(err)
- }
- if _, err := os.Stat(name); err != nil {
- t.Fatalf("cannot stat unix socket after FileListener: %v", err)
- }
- if err := l1.Close(); err != nil {
- t.Fatalf("closing file listener: %v", err)
- }
- if _, err := os.Stat(name); err != nil {
- t.Fatalf("cannot stat unix socket after closing FileListener: %v", err)
- }
- f.Close()
- if _, err := os.Stat(name); err != nil {
- t.Fatalf("cannot stat unix socket after closing FileListener and fd: %v", err)
- }
- l.Close()
- if _, err := os.Stat(name); err == nil {
- t.Fatal("closing unix listener did not remove unix socket")
- }
- if err := ioutil.WriteFile(name, []byte("hello world"), 0666); err != nil {
- t.Fatalf("cannot recreate socket file: %v", err)
+
+ listen := func(t *testing.T) *UnixListener {
+ l, err := Listen("unix", name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return l.(*UnixListener)
}
- if _, err := os.Stat(name); err != nil {
- t.Fatal("recreating unix listener as file failed: %v", err)
+ checkExists := func(t *testing.T, desc string) {
+ if _, err := os.Stat(name); err != nil {
+ t.Fatalf("unix socket does not exist %s: %v", desc, err)
+ }
}
- l.Close()
- if _, err := os.Stat(name); err != nil {
- t.Fatalf("second close of unix socket did second remove: %v", err)
+ checkNotExists := func(t *testing.T, desc string) {
+ if _, err := os.Stat(name); err == nil {
+ t.Fatalf("unix socket does exist %s: %v", desc, err)
+ }
}
- os.Remove(name)
+
+ // Listener should remove on close.
+ t.Run("Listen", func(t *testing.T) {
+ l := listen(t)
+ checkExists(t, "after Listen")
+ l.Close()
+ checkNotExists(t, "after Listener close")
+ })
+
+ // FileListener should not.
+ t.Run("FileListener", func(t *testing.T) {
+ l := listen(t)
+ f, _ := l.File()
+ l1, _ := FileListener(f)
+ checkExists(t, "after FileListener")
+ f.Close()
+ checkExists(t, "after File close")
+ l1.Close()
+ checkExists(t, "after FileListener close")
+ l.Close()
+ checkNotExists(t, "after Listener close")
+ })
+
+ // Only first call to l.Close should remove.
+ t.Run("SecondClose", func(t *testing.T) {
+ l := listen(t)
+ checkExists(t, "after Listen")
+ l.Close()
+ checkNotExists(t, "after Listener close")
+ if err := ioutil.WriteFile(name, []byte("hello world"), 0666); err != nil {
+ t.Fatalf("cannot recreate socket file: %v", err)
+ }
+ checkExists(t, "after writing temp file")
+ l.Close()
+ checkExists(t, "after second Listener close")
+ os.Remove(name)
+ })
+
+ // SetUnlinkOnClose should do what it says.
+
+ t.Run("Listen/SetUnlinkOnClose(true)", func(t *testing.T) {
+ l := listen(t)
+ checkExists(t, "after Listen")
+ l.SetUnlinkOnClose(true)
+ l.Close()
+ checkNotExists(t, "after Listener close")
+ })
+
+ t.Run("Listen/SetUnlinkOnClose(false)", func(t *testing.T) {
+ l := listen(t)
+ checkExists(t, "after Listen")
+ l.SetUnlinkOnClose(false)
+ l.Close()
+ checkExists(t, "after Listener close")
+ os.Remove(name)
+ })
+
+ t.Run("FileListener/SetUnlinkOnClose(true)", func(t *testing.T) {
+ l := listen(t)
+ f, _ := l.File()
+ l1, _ := FileListener(f)
+ checkExists(t, "after FileListener")
+ l1.(*UnixListener).SetUnlinkOnClose(true)
+ f.Close()
+ checkExists(t, "after File close")
+ l1.Close()
+ checkNotExists(t, "after FileListener close")
+ l.Close()
+ })
+
+ t.Run("FileListener/SetUnlinkOnClose(false)", func(t *testing.T) {
+ l := listen(t)
+ f, _ := l.File()
+ l1, _ := FileListener(f)
+ checkExists(t, "after FileListener")
+ l1.(*UnixListener).SetUnlinkOnClose(false)
+ f.Close()
+ checkExists(t, "after File close")
+ l1.Close()
+ checkExists(t, "after FileListener close")
+ l.Close()
+ })
}

0 comments on commit eb88b3e

Please sign in to comment.