github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/kernel/version.go (about) 1 // Copyright 2016-2017 Kinvolk 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kernel 16 17 import ( 18 "fmt" 19 "os" 20 "regexp" 21 "strconv" 22 ) 23 24 var versionRegex = regexp.MustCompile(`^(\d+)\.(\d+).(\d+).*$`) 25 26 type Version struct { 27 Major int 28 Minor int 29 Patch int 30 VersionCode int 31 } 32 33 // KernelVersionFromReleaseString converts a release string with format 34 // 4.4.2[-1] to a kernel version number in LINUX_VERSION_CODE format. 35 // That is, for kernel "a.b.c", the version number will be (a<<16 + b<<8 + c) 36 func KernelVersionFromReleaseString(releaseString string) (Version, error) { 37 versionParts := versionRegex.FindStringSubmatch(releaseString) 38 if len(versionParts) != 4 { 39 return Version{}, fmt.Errorf("got invalid release version %q (expected format '4.3.2-1')", releaseString) //nolint:goerr113 40 } 41 major, err := strconv.Atoi(versionParts[1]) 42 if err != nil { 43 return Version{}, err 44 } 45 46 minor, err := strconv.Atoi(versionParts[2]) 47 if err != nil { 48 return Version{}, err 49 } 50 51 patch, err := strconv.Atoi(versionParts[3]) 52 if err != nil { 53 return Version{}, err 54 } 55 out := major*256*256 + minor*256 + patch 56 return Version{ 57 Major: major, 58 Minor: minor, 59 Patch: patch, 60 VersionCode: out, 61 }, nil 62 } 63 64 func currentVersionUbuntu() (Version, error) { 65 procVersion, err := os.ReadFile("/proc/version_signature") 66 if err != nil { 67 return Version{}, err 68 } 69 var u1, u2, releaseString string 70 _, err = fmt.Sscanf(string(procVersion), "%s %s %s", &u1, &u2, &releaseString) 71 if err != nil { 72 return Version{}, err 73 } 74 return KernelVersionFromReleaseString(releaseString) 75 } 76 77 var debianVersionRegex = regexp.MustCompile(`.* SMP Debian (\d+\.\d+.\d+-\d+)(?:\+[[:alnum:]]*)?.*`) 78 79 func parseDebianVersion(str string) (Version, error) { 80 match := debianVersionRegex.FindStringSubmatch(str) 81 if len(match) != 2 { 82 return Version{}, fmt.Errorf("failed to parse kernel version from /proc/version: %s", str) //nolint:goerr113 83 } 84 return KernelVersionFromReleaseString(match[1]) 85 } 86 87 func currentVersionDebian() (Version, error) { 88 procVersion, err := os.ReadFile("/proc/version") 89 if err != nil { 90 return Version{}, fmt.Errorf("error reading /proc/version: %w", err) 91 } 92 93 return parseDebianVersion(string(procVersion)) 94 } 95 96 var cachedKernelVersion *Version 97 98 func init() { 99 if res, err := CurrentKernelVersion(); err == nil { 100 cachedKernelVersion = res 101 } 102 } 103 104 // CurrentKernelVersion returns the current kernel version. 105 func CurrentKernelVersion() (*Version, error) { 106 if cachedKernelVersion != nil { 107 return cachedKernelVersion, nil 108 } 109 110 // We need extra checks for Debian and Ubuntu as they modify 111 // the kernel version patch number for compatibilty with 112 // out-of-tree modules. Linux perf tools do the same for Ubuntu 113 // systems: https://github.com/torvalds/linux/commit/d18acd15c 114 // 115 // See also: 116 // https://kernel-handbook.alioth.debian.org/ch-versions.html 117 // https://wiki.ubuntu.com/Kernel/FAQ 118 version, err := currentVersionUbuntu() 119 if err == nil { 120 return &version, nil 121 } 122 version, err = currentVersionDebian() 123 if err == nil { 124 return &version, nil 125 } 126 version, err = currentVersionUname() 127 if err != nil { 128 return nil, err 129 } 130 return &version, nil 131 }