gitee.com/johng/gf@v1.4.7/third/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 "gitee.com/johng/gf/third/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 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 90 passFDChild() 91 return 92 } 93 94 tempDir, err := ioutil.TempDir("", "TestPassFD") 95 if err != nil { 96 t.Fatal(err) 97 } 98 defer os.RemoveAll(tempDir) 99 100 fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) 101 if err != nil { 102 t.Fatalf("Socketpair: %v", err) 103 } 104 defer unix.Close(fds[0]) 105 defer unix.Close(fds[1]) 106 writeFile := os.NewFile(uintptr(fds[0]), "child-writes") 107 readFile := os.NewFile(uintptr(fds[1]), "parent-reads") 108 defer writeFile.Close() 109 defer readFile.Close() 110 111 cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) 112 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 113 if lp := os.Getenv("LD_LIBRARY_PATH"); lp != "" { 114 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+lp) 115 } 116 cmd.ExtraFiles = []*os.File{writeFile} 117 118 out, err := cmd.CombinedOutput() 119 if len(out) > 0 || err != nil { 120 t.Fatalf("child process: %q, %v", out, err) 121 } 122 123 c, err := net.FileConn(readFile) 124 if err != nil { 125 t.Fatalf("FileConn: %v", err) 126 } 127 defer c.Close() 128 129 uc, ok := c.(*net.UnixConn) 130 if !ok { 131 t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) 132 } 133 134 buf := make([]byte, 32) // expect 1 byte 135 oob := make([]byte, 32) // expect 24 bytes 136 closeUnix := time.AfterFunc(5*time.Second, func() { 137 t.Logf("timeout reading from unix socket") 138 uc.Close() 139 }) 140 _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) 141 if err != nil { 142 t.Fatalf("ReadMsgUnix: %v", err) 143 } 144 closeUnix.Stop() 145 146 scms, err := unix.ParseSocketControlMessage(oob[:oobn]) 147 if err != nil { 148 t.Fatalf("ParseSocketControlMessage: %v", err) 149 } 150 if len(scms) != 1 { 151 t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) 152 } 153 scm := scms[0] 154 gotFds, err := unix.ParseUnixRights(&scm) 155 if err != nil { 156 t.Fatalf("unix.ParseUnixRights: %v", err) 157 } 158 if len(gotFds) != 1 { 159 t.Fatalf("wanted 1 fd; got %#v", gotFds) 160 } 161 162 f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") 163 defer f.Close() 164 165 got, err := ioutil.ReadAll(f) 166 want := "Hello from child process!\n" 167 if string(got) != want { 168 t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) 169 } 170 } 171 172 // passFDChild is the child process used by TestPassFD. 173 func passFDChild() { 174 defer os.Exit(0) 175 176 // Look for our fd. It should be fd 3, but we work around an fd leak 177 // bug here (http://golang.org/issue/2603) to let it be elsewhere. 178 var uc *net.UnixConn 179 for fd := uintptr(3); fd <= 10; fd++ { 180 f := os.NewFile(fd, "unix-conn") 181 var ok bool 182 netc, _ := net.FileConn(f) 183 uc, ok = netc.(*net.UnixConn) 184 if ok { 185 break 186 } 187 } 188 if uc == nil { 189 fmt.Println("failed to find unix fd") 190 return 191 } 192 193 // Make a file f to send to our parent process on uc. 194 // We make it in tempDir, which our parent will clean up. 195 flag.Parse() 196 tempDir := flag.Arg(0) 197 f, err := ioutil.TempFile(tempDir, "") 198 if err != nil { 199 fmt.Printf("TempFile: %v", err) 200 return 201 } 202 203 f.Write([]byte("Hello from child process!\n")) 204 f.Seek(0, 0) 205 206 rights := unix.UnixRights(int(f.Fd())) 207 dummyByte := []byte("x") 208 n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) 209 if err != nil { 210 fmt.Printf("WriteMsgUnix: %v", err) 211 return 212 } 213 if n != 1 || oobn != len(rights) { 214 fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) 215 return 216 } 217 } 218 219 // TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage, 220 // and ParseUnixRights are able to successfully round-trip lists of file descriptors. 221 func TestUnixRightsRoundtrip(t *testing.T) { 222 testCases := [...][][]int{ 223 {{42}}, 224 {{1, 2}}, 225 {{3, 4, 5}}, 226 {{}}, 227 {{1, 2}, {3, 4, 5}, {}, {7}}, 228 } 229 for _, testCase := range testCases { 230 b := []byte{} 231 var n int 232 for _, fds := range testCase { 233 // Last assignment to n wins 234 n = len(b) + unix.CmsgLen(4*len(fds)) 235 b = append(b, unix.UnixRights(fds...)...) 236 } 237 // Truncate b 238 b = b[:n] 239 240 scms, err := unix.ParseSocketControlMessage(b) 241 if err != nil { 242 t.Fatalf("ParseSocketControlMessage: %v", err) 243 } 244 if len(scms) != len(testCase) { 245 t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms) 246 } 247 for i, scm := range scms { 248 gotFds, err := unix.ParseUnixRights(&scm) 249 if err != nil { 250 t.Fatalf("ParseUnixRights: %v", err) 251 } 252 wantFds := testCase[i] 253 if len(gotFds) != len(wantFds) { 254 t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds) 255 } 256 for j, fd := range gotFds { 257 if fd != wantFds[j] { 258 t.Fatalf("expected fd %v, got %v", wantFds[j], fd) 259 } 260 } 261 } 262 } 263 } 264 265 func TestRlimit(t *testing.T) { 266 var rlimit, zero unix.Rlimit 267 err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit) 268 if err != nil { 269 t.Fatalf("Getrlimit: save failed: %v", err) 270 } 271 if zero == rlimit { 272 t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit) 273 } 274 set := rlimit 275 set.Cur = set.Max - 1 276 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &set) 277 if err != nil { 278 t.Fatalf("Setrlimit: set failed: %#v %v", set, err) 279 } 280 var get unix.Rlimit 281 err = unix.Getrlimit(unix.RLIMIT_NOFILE, &get) 282 if err != nil { 283 t.Fatalf("Getrlimit: get failed: %v", err) 284 } 285 set = rlimit 286 set.Cur = set.Max - 1 287 if set != get { 288 // Seems like Darwin requires some privilege to 289 // increase the soft limit of rlimit sandbox, though 290 // Setrlimit never reports an error. 291 switch runtime.GOOS { 292 case "darwin": 293 default: 294 t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) 295 } 296 } 297 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit) 298 if err != nil { 299 t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err) 300 } 301 } 302 303 func TestSeekFailure(t *testing.T) { 304 _, err := unix.Seek(-1, 0, 0) 305 if err == nil { 306 t.Fatalf("Seek(-1, 0, 0) did not fail") 307 } 308 str := err.Error() // used to crash on Linux 309 t.Logf("Seek: %v", str) 310 if str == "" { 311 t.Fatalf("Seek(-1, 0, 0) return error with empty message") 312 } 313 } 314 315 func TestDup(t *testing.T) { 316 file, err := ioutil.TempFile("", "TestDup") 317 if err != nil { 318 t.Fatalf("Tempfile failed: %v", err) 319 } 320 defer os.Remove(file.Name()) 321 defer file.Close() 322 f := int(file.Fd()) 323 324 newFd, err := unix.Dup(f) 325 if err != nil { 326 t.Fatalf("Dup: %v", err) 327 } 328 329 err = unix.Dup2(newFd, newFd+1) 330 if err != nil { 331 t.Fatalf("Dup2: %v", err) 332 } 333 334 b1 := []byte("Test123") 335 b2 := make([]byte, 7) 336 _, err = unix.Write(newFd+1, b1) 337 if err != nil { 338 t.Fatalf("Write to dup2 fd failed: %v", err) 339 } 340 _, err = unix.Seek(f, 0, 0) 341 if err != nil { 342 t.Fatalf("Seek failed: %v", err) 343 } 344 _, err = unix.Read(f, b2) 345 if err != nil { 346 t.Fatalf("Read back failed: %v", err) 347 } 348 if string(b1) != string(b2) { 349 t.Errorf("Dup: stdout write not in file, expected %v, got %v", string(b1), string(b2)) 350 } 351 } 352 353 func TestPoll(t *testing.T) { 354 f, cleanup := mktmpfifo(t) 355 defer cleanup() 356 357 const timeout = 100 358 359 ok := make(chan bool, 1) 360 go func() { 361 select { 362 case <-time.After(10 * timeout * time.Millisecond): 363 t.Errorf("Poll: failed to timeout after %d milliseconds", 10*timeout) 364 case <-ok: 365 } 366 }() 367 368 fds := []unix.PollFd{{Fd: int32(f.Fd()), Events: unix.POLLIN}} 369 n, err := unix.Poll(fds, timeout) 370 ok <- true 371 if err != nil { 372 t.Errorf("Poll: unexpected error: %v", err) 373 return 374 } 375 if n != 0 { 376 t.Errorf("Poll: wrong number of events: got %v, expected %v", n, 0) 377 return 378 } 379 } 380 381 func TestGetwd(t *testing.T) { 382 fd, err := os.Open(".") 383 if err != nil { 384 t.Fatalf("Open .: %s", err) 385 } 386 defer fd.Close() 387 // These are chosen carefully not to be symlinks on a Mac 388 // (unlike, say, /var, /etc) 389 dirs := []string{"/", "/usr/bin"} 390 if runtime.GOOS == "darwin" { 391 switch runtime.GOARCH { 392 case "arm", "arm64": 393 d1, err := ioutil.TempDir("", "d1") 394 if err != nil { 395 t.Fatalf("TempDir: %v", err) 396 } 397 d2, err := ioutil.TempDir("", "d2") 398 if err != nil { 399 t.Fatalf("TempDir: %v", err) 400 } 401 dirs = []string{d1, d2} 402 } 403 } 404 oldwd := os.Getenv("PWD") 405 for _, d := range dirs { 406 err = os.Chdir(d) 407 if err != nil { 408 t.Fatalf("Chdir: %v", err) 409 } 410 pwd, err := unix.Getwd() 411 if err != nil { 412 t.Fatalf("Getwd in %s: %s", d, err) 413 } 414 os.Setenv("PWD", oldwd) 415 err = fd.Chdir() 416 if err != nil { 417 // We changed the current directory and cannot go back. 418 // Don't let the tests continue; they'll scribble 419 // all over some other directory. 420 fmt.Fprintf(os.Stderr, "fchdir back to dot failed: %s\n", err) 421 os.Exit(1) 422 } 423 if pwd != d { 424 t.Fatalf("Getwd returned %q want %q", pwd, d) 425 } 426 } 427 } 428 429 func TestFstatat(t *testing.T) { 430 defer chtmpdir(t)() 431 432 touch(t, "file1") 433 434 var st1 unix.Stat_t 435 err := unix.Stat("file1", &st1) 436 if err != nil { 437 t.Fatalf("Stat: %v", err) 438 } 439 440 var st2 unix.Stat_t 441 err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0) 442 if err != nil { 443 t.Fatalf("Fstatat: %v", err) 444 } 445 446 if st1 != st2 { 447 t.Errorf("Fstatat: returned stat does not match Stat") 448 } 449 450 err = os.Symlink("file1", "symlink1") 451 if err != nil { 452 t.Fatal(err) 453 } 454 455 err = unix.Lstat("symlink1", &st1) 456 if err != nil { 457 t.Fatalf("Lstat: %v", err) 458 } 459 460 err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW) 461 if err != nil { 462 t.Fatalf("Fstatat: %v", err) 463 } 464 465 if st1 != st2 { 466 t.Errorf("Fstatat: returned stat does not match Lstat") 467 } 468 } 469 470 func TestFchmodat(t *testing.T) { 471 defer chtmpdir(t)() 472 473 touch(t, "file1") 474 err := os.Symlink("file1", "symlink1") 475 if err != nil { 476 t.Fatal(err) 477 } 478 479 mode := os.FileMode(0444) 480 err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), 0) 481 if err != nil { 482 t.Fatalf("Fchmodat: unexpected error: %v", err) 483 } 484 485 fi, err := os.Stat("file1") 486 if err != nil { 487 t.Fatal(err) 488 } 489 490 if fi.Mode() != mode { 491 t.Errorf("Fchmodat: failed to change file mode: expected %v, got %v", mode, fi.Mode()) 492 } 493 494 mode = os.FileMode(0644) 495 didChmodSymlink := true 496 err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), unix.AT_SYMLINK_NOFOLLOW) 497 if err != nil { 498 if (runtime.GOOS == "linux" || runtime.GOOS == "solaris") && err == unix.EOPNOTSUPP { 499 // Linux and Illumos don't support flags != 0 500 didChmodSymlink = false 501 } else { 502 t.Fatalf("Fchmodat: unexpected error: %v", err) 503 } 504 } 505 506 if !didChmodSymlink { 507 // Didn't change mode of the symlink. On Linux, the permissions 508 // of a symbolic link are always 0777 according to symlink(7) 509 mode = os.FileMode(0777) 510 } 511 512 var st unix.Stat_t 513 err = unix.Lstat("symlink1", &st) 514 if err != nil { 515 t.Fatal(err) 516 } 517 518 got := os.FileMode(st.Mode & 0777) 519 if got != mode { 520 t.Errorf("Fchmodat: failed to change symlink mode: expected %v, got %v", mode, got) 521 } 522 } 523 524 // mktmpfifo creates a temporary FIFO and provides a cleanup function. 525 func mktmpfifo(t *testing.T) (*os.File, func()) { 526 err := unix.Mkfifo("fifo", 0666) 527 if err != nil { 528 t.Fatalf("mktmpfifo: failed to create FIFO: %v", err) 529 } 530 531 f, err := os.OpenFile("fifo", os.O_RDWR, 0666) 532 if err != nil { 533 os.Remove("fifo") 534 t.Fatalf("mktmpfifo: failed to open FIFO: %v", err) 535 } 536 537 return f, func() { 538 f.Close() 539 os.Remove("fifo") 540 } 541 } 542 543 // utilities taken from os/os_test.go 544 545 func touch(t *testing.T, name string) { 546 f, err := os.Create(name) 547 if err != nil { 548 t.Fatal(err) 549 } 550 if err := f.Close(); err != nil { 551 t.Fatal(err) 552 } 553 } 554 555 // chtmpdir changes the working directory to a new temporary directory and 556 // provides a cleanup function. Used when PWD is read-only. 557 func chtmpdir(t *testing.T) func() { 558 oldwd, err := os.Getwd() 559 if err != nil { 560 t.Fatalf("chtmpdir: %v", err) 561 } 562 d, err := ioutil.TempDir("", "test") 563 if err != nil { 564 t.Fatalf("chtmpdir: %v", err) 565 } 566 if err := os.Chdir(d); err != nil { 567 t.Fatalf("chtmpdir: %v", err) 568 } 569 return func() { 570 if err := os.Chdir(oldwd); err != nil { 571 t.Fatalf("chtmpdir: %v", err) 572 } 573 os.RemoveAll(d) 574 } 575 }