github.com/minio/madmin-go/v2@v2.2.1/kernel/kernel.go (about)

     1  //
     2  // Copyright (c) 2015-2022 MinIO, Inc.
     3  //
     4  // This file is part of MinIO Object Storage stack
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU Affero General Public License as
     8  // published by the Free Software Foundation, either version 3 of the
     9  // License, or (at your option) any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU Affero General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU Affero General Public License
    17  // along with this program. If not, see <http://www.gnu.org/licenses/>.
    18  //
    19  
    20  //go:build linux
    21  // +build linux
    22  
    23  package kernel
    24  
    25  import (
    26  	"fmt"
    27  	"io/ioutil"
    28  	"regexp"
    29  	"strconv"
    30  	"strings"
    31  	"syscall"
    32  )
    33  
    34  var versionRegex = regexp.MustCompile(`^(\d+)\.(\d+).(\d+).*$`)
    35  
    36  // VersionFromRelease converts a release string with format
    37  // 4.4.2[-1] to a kernel version number in LINUX_VERSION_CODE format.
    38  // That is, for kernel "a.b.c", the version number will be (a<<16 + b<<8 + c)
    39  func VersionFromRelease(releaseString string) (uint32, error) {
    40  	versionParts := versionRegex.FindStringSubmatch(releaseString)
    41  	if len(versionParts) != 4 {
    42  		return 0, fmt.Errorf("got invalid release version %q (expected format '4.3.2-1')", releaseString)
    43  	}
    44  	major, err := strconv.Atoi(versionParts[1])
    45  	if err != nil {
    46  		return 0, err
    47  	}
    48  
    49  	minor, err := strconv.Atoi(versionParts[2])
    50  	if err != nil {
    51  		return 0, err
    52  	}
    53  
    54  	patch, err := strconv.Atoi(versionParts[3])
    55  	if err != nil {
    56  		return 0, err
    57  	}
    58  	return Version(major, minor, patch), nil
    59  }
    60  
    61  // Version implements KERNEL_VERSION equivalent macro
    62  // #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
    63  func Version(major, minor, patch int) uint32 {
    64  	if patch > 255 {
    65  		patch = 255
    66  	}
    67  	out := major<<16 + minor<<8 + patch
    68  	return uint32(out)
    69  }
    70  
    71  func currentReleaseUname() (string, error) {
    72  	var buf syscall.Utsname
    73  	if err := syscall.Uname(&buf); err != nil {
    74  		return "", err
    75  	}
    76  	releaseString := strings.Trim(utsnameStr(buf.Release[:]), "\x00")
    77  	return releaseString, nil
    78  }
    79  
    80  func currentReleaseUbuntu() (string, error) {
    81  	procVersion, err := ioutil.ReadFile("/proc/version_signature")
    82  	if err != nil {
    83  		return "", err
    84  	}
    85  	var u1, u2, releaseString string
    86  	_, err = fmt.Sscanf(string(procVersion), "%s %s %s", &u1, &u2, &releaseString)
    87  	if err != nil {
    88  		return "", err
    89  	}
    90  	return releaseString, nil
    91  }
    92  
    93  var debianVersionRegex = regexp.MustCompile(`.* SMP Debian (\d+\.\d+.\d+-\d+)(?:\+[[:alnum:]]*)?.*`)
    94  
    95  func parseDebianRelease(str string) (string, error) {
    96  	match := debianVersionRegex.FindStringSubmatch(str)
    97  	if len(match) != 2 {
    98  		return "", fmt.Errorf("failed to parse kernel version from /proc/version: %s", str)
    99  	}
   100  	return match[1], nil
   101  }
   102  
   103  func currentReleaseDebian() (string, error) {
   104  	procVersion, err := ioutil.ReadFile("/proc/version")
   105  	if err != nil {
   106  		return "", fmt.Errorf("error reading /proc/version: %s", err)
   107  	}
   108  
   109  	return parseDebianRelease(string(procVersion))
   110  }
   111  
   112  // CurrentRelease returns the current kernel release ensuring that
   113  // ubuntu and debian release numbers are accurate.
   114  func CurrentRelease() (string, error) {
   115  	// We need extra checks for Debian and Ubuntu as they modify
   116  	// the kernel version patch number for compatibility with
   117  	// out-of-tree modules. Linux perf tools do the same for Ubuntu
   118  	// systems: https://github.com/torvalds/linux/commit/d18acd15c
   119  	//
   120  	// See also:
   121  	// https://kernel-team.pages.debian.net/kernel-handbook/ch-versions.html
   122  	// https://wiki.ubuntu.com/Kernel/FAQ
   123  	version, err := currentReleaseUbuntu()
   124  	if err == nil {
   125  		return version, nil
   126  	}
   127  	version, err = currentReleaseDebian()
   128  	if err == nil {
   129  		return version, nil
   130  	}
   131  	return currentReleaseUname()
   132  }
   133  
   134  // CurrentVersion returns the current kernel version in
   135  // LINUX_VERSION_CODE format (see VersionFromRelease())
   136  func CurrentVersion() (uint32, error) {
   137  	release, err := CurrentRelease()
   138  	if err == nil {
   139  		return VersionFromRelease(release)
   140  	}
   141  	return 0, err
   142  }