internal/system/system.go (75 lines of code) (raw):

package system import ( "bytes" "fmt" "io" "os" "howett.net/plist" ) const ( // versionPath is the path on the root filesystem to the // SystemVersion plist versionPath = "/System/Library/CoreServices/SystemVersion.plist" // dotVersionPath is the path to the symlink that directly // references versionPath and bypasses the compatibility mode // that was introduced with macOS 11.0. dotVersionPath = "/System/Library/CoreServices/.SystemVersionPlatform.plist" // dotVersionSwitch is the product version number returned by // macOS when the system is in compat mode // (SYSTEM_VERSION_COMPAT=1). If this version is returned, // dotVersionPath should be read to bypass compat mode. dotVersionSwitch = "10.16" ) // System correlates VersionInfo with a Product. type System struct { versionInfo *VersionInfo product *Product } func (sys *System) Product() *Product { return sys.product } // Scan reads the VersionInfo and creates a new System struct from // that and the associated Product. func Scan() (*System, error) { version, err := readVersion() if err != nil { return nil, err } product, err := version.Product() if err != nil { return nil, err } system := &System{ versionInfo: version, product: product, } return system, nil } // VersionInfo mirrors the raw data found in the SystemVersion plist file. type VersionInfo struct { ProductBuildVersion string `plist:"ProductBuildVersion"` ProductCopyright string `plist:"ProductCopyright"` ProductName string `plist:"ProductName"` ProductUserVisibleVersion string `plist:"ProductUserVisibleVersion"` ProductVersion string `plist:"ProductVersion"` IOSSupportVersion string `plist:"iOSSupportVersion"` } // Product determines the specific product that the VersionInfo.ProductVersion is associated with. func (v *VersionInfo) Product() (*Product, error) { return newProduct(v.ProductVersion) } // decodeVersionInfo attempts to decode the raw data from the reader // into a new VersionInfo struct. func decodeVersionInfo(reader io.ReadSeeker) (*VersionInfo, error) { // Decode the system version plist into the VersionInfo struct var version VersionInfo decoder := plist.NewDecoder(reader) if err := decoder.Decode(&version); err != nil { return nil, fmt.Errorf("system failed to decode contents of reader: %w", err) } return &version, nil } // readVersion reads the SystemVersion plist data from disk // (versionPath). If "SYSTEM_VERSION_COMPAT" is enabled, it will // instead read from dotVersionPath to bypass macOS's compat mode. func readVersion() (*VersionInfo, error) { // Read the version info from the standard file path version, err := readProductVersionFile(versionPath) if err != nil { return nil, err } // If the returned product version is in compat mode, read the // version info from the dot file to bypass compat mode. if version.ProductVersion == dotVersionSwitch { return readProductVersionFile(dotVersionPath) } return version, nil } // readProductVersion opens the given file and attempts to decode it // as VersionInfo. func readProductVersionFile(path string) (*VersionInfo, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } // Parse loaded plist version data version, err := decodeVersionInfo(bytes.NewReader(data)) if err != nil { return nil, err } return version, nil }