1// Copyright 2024 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 5package unix 6 7import ( 8 "runtime" 9 "sync" 10 "syscall" 11 "unsafe" 12) 13 14//go:linkname procUname libc_uname 15 16var procUname uintptr 17 18// utsname represents the fields of a struct utsname defined in <sys/utsname.h>. 19type utsname struct { 20 Sysname [257]byte 21 Nodename [257]byte 22 Release [257]byte 23 Version [257]byte 24 Machine [257]byte 25} 26 27// KernelVersion returns major and minor kernel version numbers 28// parsed from the syscall.Uname's Version field, or (0, 0) if the 29// version can't be obtained or parsed. 30func KernelVersion() (major int, minor int) { 31 var un utsname 32 _, _, errno := rawSyscall6(uintptr(unsafe.Pointer(&procUname)), 1, uintptr(unsafe.Pointer(&un)), 0, 0, 0, 0, 0) 33 if errno != 0 { 34 return 0, 0 35 } 36 37 // The version string is in the form "<version>.<update>.<sru>.<build>.<reserved>" 38 // on Solaris: https://blogs.oracle.com/solaris/post/whats-in-a-uname- 39 // Therefore, we use the Version field on Solaris when available. 40 ver := un.Version[:] 41 if runtime.GOOS == "illumos" { 42 // Illumos distributions use different formats without a parsable 43 // and unified pattern for the Version field while Release level 44 // string is guaranteed to be in x.y or x.y.z format regardless of 45 // whether the kernel is Solaris or illumos. 46 ver = un.Release[:] 47 } 48 49 parseNext := func() (n int) { 50 for i, c := range ver { 51 if c == '.' { 52 ver = ver[i+1:] 53 return 54 } 55 if '0' <= c && c <= '9' { 56 n = n*10 + int(c-'0') 57 } 58 } 59 ver = nil 60 return 61 } 62 63 major = parseNext() 64 minor = parseNext() 65 66 return 67} 68 69// SupportSockNonblockCloexec tests if SOCK_NONBLOCK and SOCK_CLOEXEC are supported 70// for socket() system call, returns true if affirmative. 71var SupportSockNonblockCloexec = sync.OnceValue(func() bool { 72 // First test if socket() supports SOCK_NONBLOCK and SOCK_CLOEXEC directly. 73 s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0) 74 if err == nil { 75 syscall.Close(s) 76 return true 77 } 78 if err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL { 79 // Something wrong with socket(), fall back to checking the kernel version. 80 major, minor := KernelVersion() 81 if runtime.GOOS == "illumos" { 82 return major > 5 || (major == 5 && minor >= 11) // minimal requirement is SunOS 5.11 83 } 84 return major > 11 || (major == 11 && minor >= 4) 85 } 86 return false 87}) 88 89// SupportAccept4 tests whether accept4 system call is available. 90var SupportAccept4 = sync.OnceValue(func() bool { 91 for { 92 // Test if the accept4() is available. 93 _, _, err := syscall.Accept4(0, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) 94 if err == syscall.EINTR { 95 continue 96 } 97 return err != syscall.ENOSYS 98 } 99}) 100 101// SupportTCPKeepAliveIdleIntvlCNT determines whether the TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT 102// are available by checking the kernel version for Solaris 11.4. 103var SupportTCPKeepAliveIdleIntvlCNT = sync.OnceValue(func() bool { 104 major, minor := KernelVersion() 105 return major > 11 || (major == 11 && minor >= 4) 106}) 107