blob: 3f399411d76bdeed083b5b7e9cc43e4b5b15cd86 [file] [log] [blame]
// Copyright 2024 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.
package unix
import (
"runtime"
"sync"
"syscall"
"unsafe"
)
//go:linkname procUname libc_uname
var procUname uintptr
// utsname represents the fields of a struct utsname defined in <sys/utsname.h>.
type utsname struct {
Sysname [257]byte
Nodename [257]byte
Release [257]byte
Version [257]byte
Machine [257]byte
}
// KernelVersion returns major and minor kernel version numbers
// parsed from the syscall.Uname's Version field, or (0, 0) if the
// version can't be obtained or parsed.
func KernelVersion() (major int, minor int) {
var un utsname
_, _, errno := rawSyscall6(uintptr(unsafe.Pointer(&procUname)), 1, uintptr(unsafe.Pointer(&un)), 0, 0, 0, 0, 0)
if errno != 0 {
return 0, 0
}
// The version string is in the form "<version>.<update>.<sru>.<build>.<reserved>"
// on Solaris: https://blogs.oracle.com/solaris/post/whats-in-a-uname-
// Therefore, we use the Version field on Solaris when available.
ver := un.Version[:]
if runtime.GOOS == "illumos" {
// Illumos distributions use different formats without a parsable
// and unified pattern for the Version field while Release level
// string is guaranteed to be in x.y or x.y.z format regardless of
// whether the kernel is Solaris or illumos.
ver = un.Release[:]
}
parseNext := func() (n int) {
for i, c := range ver {
if c == '.' {
ver = ver[i+1:]
return
}
if '0' <= c && c <= '9' {
n = n*10 + int(c-'0')
}
}
ver = nil
return
}
major = parseNext()
minor = parseNext()
return
}
// SupportSockNonblockCloexec tests if SOCK_NONBLOCK and SOCK_CLOEXEC are supported
// for socket() system call, returns true if affirmative.
var SupportSockNonblockCloexec = sync.OnceValue(func() bool {
// First test if socket() supports SOCK_NONBLOCK and SOCK_CLOEXEC directly.
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
if err == nil {
syscall.Close(s)
return true
}
if err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL {
// Something wrong with socket(), fall back to checking the kernel version.
major, minor := KernelVersion()
if runtime.GOOS == "illumos" {
return major > 5 || (major == 5 && minor >= 11) // minimal requirement is SunOS 5.11
}
return major > 11 || (major == 11 && minor >= 4)
}
return false
})
// SupportAccept4 tests whether accept4 system call is available.
var SupportAccept4 = sync.OnceValue(func() bool {
for {
// Test if the accept4() is available.
_, _, err := syscall.Accept4(0, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
if err == syscall.EINTR {
continue
}
return err != syscall.ENOSYS
}
})
// SupportTCPKeepAliveIdleIntvlCNT determines whether the TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
// are available by checking the kernel version for Solaris 11.4.
var SupportTCPKeepAliveIdleIntvlCNT = sync.OnceValue(func() bool {
major, minor := KernelVersion()
return major > 11 || (major == 11 && minor >= 4)
})