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  }