From c97cf055d91f25e25c3d576ab483586eff224e0b Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 17 Mar 2012 22:19:57 -0700 Subject: [PATCH] syscall: add a test for passing an fd over a unix socket Updates #1101 R=golang-dev, iant CC=golang-dev https://golang.org/cl/5849057 --- src/pkg/syscall/passfd_test.go | 150 +++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 src/pkg/syscall/passfd_test.go diff --git a/src/pkg/syscall/passfd_test.go b/src/pkg/syscall/passfd_test.go new file mode 100644 index 0000000000..96a41316b5 --- /dev/null +++ b/src/pkg/syscall/passfd_test.go @@ -0,0 +1,150 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin probablyfreebsd probablyopenbsd + +package syscall_test + +import ( + "flag" + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "syscall" + "testing" + "time" +) + +// TestPassFD tests passing a file descriptor over a Unix socket. +func TestPassFD(t *testing.T) { + tempDir, err := ioutil.TempDir("", "TestPassFD") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("Socketpair: %v", err) + } + defer syscall.Close(fds[0]) + defer syscall.Close(fds[1]) + writeFile := os.NewFile(uintptr(fds[0]), "child-writes") + readFile := os.NewFile(uintptr(fds[1]), "parent-reads") + defer writeFile.Close() + defer readFile.Close() + + cmd := exec.Command(os.Args[0], "-test.run=TestPassFDChild", "--", tempDir) + cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) + cmd.ExtraFiles = []*os.File{writeFile} + + out, err := cmd.CombinedOutput() + if len(out) > 0 || err != nil { + t.Errorf("child process: %q, %v", out, err) + return // not fatalf, so defers above run. + } + + c, err := net.FileConn(readFile) + if err != nil { + t.Errorf("FileConn: %v", err) + return + } + defer c.Close() + + uc, ok := c.(*net.UnixConn) + if !ok { + t.Errorf("unexpected FileConn type; expected UnixConn, got %T", c) + return + } + + buf := make([]byte, 32) // expect 1 byte + oob := make([]byte, 32) // expect 24 bytes + closeUnix := time.AfterFunc(5*time.Second, func() { + t.Logf("timeout reading from unix socket") + uc.Close() + }) + _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) + closeUnix.Stop() + + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + t.Errorf("ParseSocketControlMessage: %v", err) + return + } + if len(scms) != 1 { + t.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) + return + } + scm := scms[0] + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + t.Errorf("syscall.ParseUnixRights: %v", err) + return + } + if len(gotFds) != 1 { + t.Errorf("wanted 1 fd; got %#v", gotFds) + return + } + + f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") + defer f.Close() + + got, err := ioutil.ReadAll(f) + want := "Hello from child process!\n" + if string(got) != want { + t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) + } +} + +// Not a real test. This is the helper child process for TestPassFD. +func TestPassFDChild(*testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + defer os.Exit(0) + + // Look for our fd. I