google_guest_agent/osinfo/osinfo_unix.go (110 lines of code) (raw):

// Copyright 2023 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // https://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build unix package osinfo import ( "bytes" "errors" "fmt" "os" "strconv" "strings" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "golang.org/x/sys/unix" ) var ( osRelease = "/etc/os-release" systemRelease = "/etc/system-release" ) func parseOSRelease(osRelease string) (OSInfo, error) { var ret OSInfo for _, line := range strings.Split(osRelease, "\n") { var id = line if id = strings.TrimPrefix(line, "ID="); id != line { id = strings.Trim(id, `"`) ret.OS = parseID(id) } if id = strings.TrimPrefix(line, "VERSION_ID="); id != line { id = strings.Trim(id, `"`) ret.VersionID = id version, err := parseVersion(id) if err != nil { return ret, fmt.Errorf("couldn't parse version id: %v", err) } ret.Version = version } if name := strings.TrimPrefix(line, "PRETTY_NAME="); name != line { name = strings.Trim(name, `"`) ret.PrettyName = name } } return ret, nil } func parseSystemRelease(systemRelease string) (OSInfo, error) { var ret OSInfo var key = " release " idx := strings.Index(systemRelease, key) if idx == -1 { return ret, errors.New("SystemRelease does not match expected format") } ret.OS = parseID(systemRelease[:idx]) versionFromRelease := strings.Split(systemRelease[idx+len(key):], " ")[0] version, err := parseVersion(versionFromRelease) if err != nil { return ret, fmt.Errorf("couldn't parse version: %v", err) } ret.Version = version return ret, nil } func parseVersion(version string) (Ver, error) { versionparts := strings.Split(version, ".") ret := Ver{Length: len(versionparts)} // Must have at least major version. var err error ret.Major, err = strconv.Atoi(versionparts[0]) if err != nil { return ret, err } if ret.Length > 1 { ret.Minor, err = strconv.Atoi(versionparts[1]) if err != nil { return ret, err } } if ret.Length > 2 { ret.Patch, err = strconv.Atoi(versionparts[2]) if err != nil { return ret, err } } return ret, nil } func parseID(id string) string { switch id { case "Red Hat Enterprise Linux Server": return "rhel" case "CentOS", "CentOS Linux": return "centos" default: return id } } func parseRelease() (OSInfo, error) { if releaseFile, err := os.ReadFile(osRelease); err == nil { return parseOSRelease(string(releaseFile)) } if releaseFile, err := os.ReadFile(systemRelease); err == nil { return parseSystemRelease(string(releaseFile)) } return OSInfo{}, errors.New("no known release file found") } // Get returns OSInfo on the running system. func Get() OSInfo { osInfo, err := parseRelease() if err != nil { // This is a non critical error, we can still return a partially populated OSInfo. logger.Warningf("Error parsing release info: %v", err) } var uts unix.Utsname if err := unix.Uname(&uts); err != nil { logger.Warningf("unix.Uname error: %v", err) return osInfo } osInfo.KernelVersion = string(bytes.TrimRight(uts.Version[:], "\x00")) osInfo.KernelRelease = string(bytes.TrimRight(uts.Release[:], "\x00")) return osInfo }