github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/Godeps/_workspace/src/golang.org/x/sys/unix/syscall_unix_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package unix_test 8 9 import ( 10 "flag" 11 "fmt" 12 "io/ioutil" 13 "net" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "runtime" 18 "testing" 19 "time" 20 21 "golang.org/x/sys/unix" 22 ) 23 24 // Tests that below functions, structures and constants are consistent 25 // on all Unix-like systems. 26 func _() { 27 // program scheduling priority functions and constants 28 var ( 29 _ func(int, int, int) error = unix.Setpriority 30 _ func(int, int) (int, error) = unix.Getpriority 31 ) 32 const ( 33 _ int = unix.PRIO_USER 34 _ int = unix.PRIO_PROCESS 35 _ int = unix.PRIO_PGRP 36 ) 37 38 // termios constants 39 const ( 40 _ int = unix.TCIFLUSH 41 _ int = unix.TCIOFLUSH 42 _ int = unix.TCOFLUSH 43 ) 44 45 // fcntl file locking structure and constants 46 var ( 47 _ = unix.Flock_t{ 48 Type: int16(0), 49 Whence: int16(0), 50 Start: int64(0), 51 Len: int64(0), 52 Pid: int32(0), 53 } 54 ) 55 const ( 56 _ = unix.F_GETLK 57 _ = unix.F_SETLK 58 _ = unix.F_SETLKW 59 ) 60 } 61 62 // TestFcntlFlock tests whether the file locking structure matches 63 // the calling convention of each kernel. 64 func TestFcntlFlock(t *testing.T) { 65 name := filepath.Join(os.TempDir(), "TestFcntlFlock") 66 fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0) 67 if err != nil { 68 t.Fatalf("Open failed: %v", err) 69 } 70 defer unix.Unlink(name) 71 defer unix.Close(fd) 72 flock := unix.Flock_t{ 73 Type: unix.F_RDLCK, 74 Start: 0, Len: 0, Whence: 1, 75 } 76 if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil { 77 t.Fatalf("FcntlFlock failed: %v", err) 78 } 79 } 80 81 // TestPassFD tests passing a file descriptor over a Unix socket. 82 // 83 // This test involved both a parent and child process. The parent 84 // process is invoked as a normal test, with "go test", which then 85 // runs the child process by running the current test binary with args 86 // "-test.run=^TestPassFD$" and an environment variable used to signal 87 // that the test should become the child process instead. 88 func TestPassFD(t *testing.T) { 89 switch runtime.GOOS { 90 case "dragonfly": 91 // TODO(jsing): Figure out why sendmsg is returning EINVAL. 92 t.Skip("skipping test on dragonfly") 93 case "solaris": 94 // TODO(aram): Figure out why ReadMsgUnix is returning empty message. 95 t.Skip("skipping test on solaris, see issue 7402") 96 } 97 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 98 passFDChild() 99 return 100 } 101 102 tempDir, err := ioutil.TempDir("", "TestPassFD") 103 if err != nil { 104 t.Fatal(err) 105 } 106 defer os.RemoveAll(tempDir) 107 108 fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) 109 if err != nil { 110 t.Fatalf("Socketpair: %v", err) 111 } 112 defer unix.Close(fds[0]) 113 defer unix.Close(fds[1]) 114 writeFile := os.NewFile(uintptr(fds[0]), "child-writes") 115 readFile := os.NewFile(uintptr(fds[1]), "parent-reads") 116 defer writeFile.Close() 117 defer readFile.Close() 118 119 cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) 120 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 121 if lp := os.Getenv("LD_LIBRARY_PATH"); lp != "" { 122 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+lp) 123 } 124 cmd.ExtraFiles = []*os.File{writeFile} 125 126 out, err := cmd.CombinedOutput() 127 if len(out) > 0 || err != nil { 128 t.Fatalf("child process: %q, %v", out, err) 129 } 130 131 c, err := net.FileConn(readFile) 132 if err != nil { 133 t.Fatalf("FileConn: %v", err) 134 } 135 defer c.Close() 136 137 uc, ok := c.(*net.UnixConn) 138 if !ok { 139 t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) 140 } 141 142 buf := make([]byte, 32) // expect 1 byte 143 oob := make([]byte, 32) // expect 24 bytes 144 closeUnix := time.AfterFunc(5*time.Second, func() { 145 t.Logf("timeout reading from unix socket") 146 uc.Close() 147 }) 148 _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) 149 closeUnix.Stop() 150 151 scms, err := unix.ParseSocketControlMessage(oob[:oobn]) 152 if err != nil { 153 t.Fatalf("ParseSocketControlMessage: %v", err) 154 } 155 if len(scms) != 1 { 156 t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) 157 } 158 scm := scms[0] 159 gotFds, err := unix.ParseUnixRights(&scm) 160 if err != nil { 161 t.Fatalf("unix.ParseUnixRights: %v", err) 162 } 163 if len(gotFds) != 1 { 164 t.Fatalf("wanted 1 fd; got %#v", gotFds) 165 } 166 167 f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") 168 defer f.Close() 169 170 got, err := ioutil.ReadAll(f) 171 want := "Hello from child process!\n" 172 if string(got) != want { 173 t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) 174 } 175 } 176 177 // passFDChild is the child process used by TestPassFD. 178 func passFDChild() { 179 defer os.Exit(0) 180 181 // Look for our fd. It should be fd 3, but we work around an fd leak 182 // bug here (http://golang.org/issue/2603) to let it be elsewhere. 183 var uc *net.UnixConn 184 for fd := uintptr(3); fd <= 10; fd++ { 185 f := os.NewFile(fd, "unix-conn") 186 var ok bool 187 netc, _ := net.FileConn(f) 188 uc, ok = netc.(*net.UnixConn) 189 if ok { 190 break 191 } 192 } 193 if uc == nil { 194 fmt.Println("failed to find unix fd") 195 return 196 } 197 198 // Make a file f to send to our parent process on uc. 199 // We make it in tempDir, which our parent will clean up. 200 flag.Parse() 201 tempDir := flag.Arg(0) 202 f, err := ioutil.TempFile(tempDir, "") 203 if err != nil { 204 fmt.Printf("TempFile: %v", err) 205 return 206 } 207 208 f.Write([]byte("Hello from child process!\n")) 209 f.Seek(0, 0) 210 211 rights := unix.UnixRights(int(f.Fd())) 212 dummyByte := []byte("x") 213 n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) 214 if err != nil { 215 fmt.Printf("WriteMsgUnix: %v", err) 216 return 217 } 218 if n != 1 || oobn != len(rights) { 219 fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) 220 return 221 } 222 } 223 224 // TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage, 225 // and ParseUnixRights are able to successfully round-trip lists of file descriptors. 226 func TestUnixRightsRoundtrip(t *testing.T) { 227 testCases := [...][][]int{ 228 {{42}}, 229 {{1, 2}}, 230 {{3, 4, 5}}, 231 {{}}, 232 {{1, 2}, {3, 4, 5}, {}, {7}}, 233 } 234 for _, testCase := range testCases { 235 b := []byte{} 236 var n int 237 for _, fds := range testCase { 238 // Last assignment to n wins 239 n = len(b) + unix.CmsgLen(4*len(fds)) 240 b = append(b, unix.UnixRights(fds...)...) 241 } 242 // Truncate b 243 b = b[:n] 244 245 scms, err := unix.ParseSocketControlMessage(b) 246 if err != nil { 247 t.Fatalf("ParseSocketControlMessage: %v", err) 248 } 249 if len(scms) != len(testCase) { 250 t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms) 251 } 252 for i, scm := range scms { 253 gotFds, err := unix.ParseUnixRights(&scm) 254 if err != nil { 255 t.Fatalf("ParseUnixRights: %v", err) 256 } 257 wantFds := testCase[i] 258 if len(gotFds) != len(wantFds) { 259 t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds) 260 } 261 for j, fd := range gotFds { 262 if fd != wantFds[j] { 263 t.Fatalf("expected fd %v, got %v", wantFds[j], fd) 264 } 265 } 266 } 267 } 268 } 269 270 func TestRlimit(t *testing.T) { 271 var rlimit, zero unix.Rlimit 272 err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit) 273 if err != nil { 274 t.Fatalf("Getrlimit: save failed: %v", err) 275 } 276 if zero == rlimit { 277 t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit) 278 } 279 set := rlimit 280 set.Cur = set.Max - 1 281 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &set) 282 if err != nil { 283 t.Fatalf("Setrlimit: set failed: %#v %v", set, err) 284 } 285 var get unix.Rlimit 286 err = unix.Getrlimit(unix.RLIMIT_NOFILE, &get) 287 if err != nil { 288 t.Fatalf("Getrlimit: get failed: %v", err) 289 } 290 set = rlimit 291 set.Cur = set.Max - 1 292 if set != get { 293 // Seems like Darwin requires some privilege to 294 // increase the soft limit of rlimit sandbox, though 295 // Setrlimit never reports an error. 296 switch runtime.GOOS { 297 case "darwin": 298 default: 299 t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) 300 } 301 } 302 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit) 303 if err != nil { 304 t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err) 305 } 306 } 307 308 func TestSeekFailure(t *testing.T) { 309 _, err := unix.Seek(-1, 0, 0) 310 if err == nil { 311 t.Fatalf("Seek(-1, 0, 0) did not fail") 312 } 313 str := err.Error() // used to crash on Linux 314 t.Logf("Seek: %v", str) 315 if str == "" { 316 t.Fatalf("Seek(-1, 0, 0) return error with empty message") 317 } 318 } 319 320 func TestDup(t *testing.T) { 321 file, err := ioutil.TempFile("", "TestDup") 322 if err != nil { 323 t.Fatalf("Tempfile failed: %v", err) 324 } 325 defer os.Remove(file.Name()) 326 defer file.Close() 327 f := int(file.Fd()) 328 329 newFd, err := unix.Dup(f) 330 if err != nil { 331 t.Fatalf("Dup: %v", err) 332 } 333 334 err = unix.Dup2(newFd, newFd+1) 335 if err != nil { 336 t.Fatalf("Dup2: %v", err) 337 } 338 339 b1 := []byte("Test123") 340 b2 := make([]byte, 7) 341 _, err = unix.Write(newFd+1, b1) 342 if err != nil { 343 t.Fatalf("Write to dup2 fd failed: %v", err) 344 } 345 _, err = unix.Seek(f, 0, 0) 346 _, err = unix.Read(f, b2) 347 if err != nil { 348 t.Fatalf("Read back failed: %v", err) 349 } 350 if string(b1) != string(b2) { 351 t.Errorf("Dup: stdout write not in file, expected %v, got %v", string(b1), string(b2)) 352 } 353 }