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 5 package unix
6 7 import (
8 "runtime"
9 "sync"
10 "syscall"
11 "unsafe"
12 )
13 14 //go:linkname procUname libc_uname
15 16 var procUname uintptr
17 18 // utsname represents the fields of a struct utsname defined in <sys/utsname.h>.
19 type 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.
30 func 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.
71 var 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.
90 var 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.
103 var SupportTCPKeepAliveIdleIntvlCNT = sync.OnceValue(func() bool {
104 major, minor := KernelVersion()
105 return major > 11 || (major == 11 && minor >= 4)
106 })
107