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