github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/sysinfo/sysinfo_linux.go (about) 1 package sysinfo 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "os/exec" 8 "regexp" 9 "strconv" 10 11 "github.com/patrickmn/go-cache" 12 ) 13 14 // OS returns the system's OS 15 func OS() OsInfo { 16 return Linux 17 } 18 19 var ( 20 kernelVersionRegex = regexp.MustCompile(`^(\d+)\D(\d+)\D(\d+)`) 21 ) 22 23 // OSVersion returns the system's OS version. 24 func OSVersion() (*OSVersionInfo, error) { 25 if cached, found := sysinfoCache.Get(osVersionInfoCacheKey); found { 26 return cached.(*OSVersionInfo), nil 27 } 28 29 if v := os.Getenv(VersionOverrideEnvVar); v != "" { 30 vInfo, err := parseVersionInfo(v) 31 if err != nil { 32 return nil, fmt.Errorf("Could not parse version info: %w", err) 33 } 34 return &OSVersionInfo{vInfo, "spoofed"}, nil 35 } 36 37 // Fetch kernel version. 38 osrelFile := "/proc/sys/kernel/osrelease" 39 osrelData, err := os.ReadFile(osrelFile) 40 if err != nil { 41 return nil, fmt.Errorf("Unable to read %s: %v", osrelFile, err) 42 } 43 version := string(bytes.TrimSpace(osrelData)) 44 45 // Parse kernel version parts. 46 versionParts := kernelVersionRegex.FindStringSubmatch(version) 47 if len(versionParts) != 4 { 48 return nil, fmt.Errorf("Unable to parse version string %q", versionParts) 49 } 50 major, _ := strconv.Atoi(versionParts[1]) 51 minor, _ := strconv.Atoi(versionParts[2]) 52 micro, _ := strconv.Atoi(versionParts[3]) 53 // Fetch distribution name. 54 // lsb_release -d returns output of the form "Description:\t[Name]". 55 name, err := exec.Command("lsb_release", "-d").Output() 56 if err == nil && len(bytes.Split(name, []byte(":"))) > 1 { 57 name = bytes.TrimSpace(bytes.SplitN(name, []byte(":"), 2)[1]) 58 } else { 59 etcFiles := []string{ 60 "/etc/debian_version", // Debians 61 "/etc/redhat-release", // RHELs and Fedoras 62 "/etc/system-release", // Amazon AMIs 63 "/etc/SuSE-release", // SuSEs 64 } 65 for _, etcFile := range etcFiles { 66 name, err = os.ReadFile(etcFile) 67 if err == nil { 68 break 69 } 70 } 71 if bytes.Equal(name, []byte("")) { 72 name = []byte("Unknown") 73 } 74 } 75 info := &OSVersionInfo{&VersionInfo{version, major, minor, micro}, string(name)} 76 sysinfoCache.Set(osVersionInfoCacheKey, info, cache.NoExpiration) 77 return info, nil 78 } 79 80 // Libc returns the system's C library. 81 func Libc() (*LibcInfo, error) { 82 if cached, found := sysinfoCache.Get(libcInfoCacheKey); found { 83 return cached.(*LibcInfo), nil 84 } 85 86 // Assume glibc for now, which exposes a "getconf" command. 87 libc, err := exec.Command("getconf", "GNU_LIBC_VERSION").Output() 88 if err != nil { 89 return nil, fmt.Errorf("Unable to fetch glibc version: %s", err) 90 } 91 regex := regexp.MustCompile(`(\d+)\D(\d+)`) 92 parts := regex.FindStringSubmatch(string(libc)) 93 if len(parts) != 3 { 94 return nil, fmt.Errorf("Unable to parse libc string '%s'", libc) 95 } 96 major, _ := strconv.Atoi(parts[1]) 97 minor, _ := strconv.Atoi(parts[2]) 98 info := &LibcInfo{Glibc, major, minor} 99 sysinfoCache.Set(libcInfoCacheKey, info, cache.NoExpiration) 100 return info, nil 101 } 102 103 // Compilers returns the system's available compilers. 104 func Compilers() ([]*CompilerInfo, error) { 105 if cached, found := sysinfoCache.Get(compilersCacheKey); found { 106 return cached.([]*CompilerInfo), nil 107 } 108 109 compilers := []*CompilerInfo{} 110 111 // Map of compiler commands to CompilerNameInfos. 112 var compilerMap = map[string]CompilerNameInfo{ 113 "gcc": Gcc, 114 "clang": Clang, 115 } 116 for command, nameInfo := range compilerMap { 117 major, minor, err := getCompilerVersion([]string{command, "--version"}) 118 if err != nil { 119 return compilers, err 120 } else if major > 0 { 121 compilers = append(compilers, &CompilerInfo{nameInfo, major, minor}) 122 } 123 } 124 125 sysinfoCache.Set(compilersCacheKey, compilers, cache.NoExpiration) 126 return compilers, nil 127 }