From ea4bb57b9460b7ad555ecfc021789d603834b516 Mon Sep 17 00:00:00 2001 From: Jay Pipes Date: Mon, 4 Mar 2024 12:31:26 -0500 Subject: [PATCH] uplift github.com/jaypipes/ghw to v0.12.0 This commit uplifts the github.com/jaypipes/ghw dependency to the v0.12.0 release (released July 2023). Included in the v0.11.0 release was a fix for detecting memory areas in hardware with NUMA architecture and Linux kernels > 5.19.0 as well as ARM platforms. We are running resource-topology-exporter in our environments and ran into the aforementioned bug in ghw 0.9.0, thus this commit to uplift to a more modern ghw release. Related https://github.com/jaypipes/ghw/issues/341 Signed-off-by: Jay Pipes --- go.mod | 2 +- go.sum | 8 +- vendor/github.com/jaypipes/ghw/README.md | 39 +- vendor/github.com/jaypipes/ghw/doc.go | 448 +++++++++--------- .../jaypipes/ghw/pkg/block/block.go | 23 +- .../jaypipes/ghw/pkg/block/block_linux.go | 76 ++- .../jaypipes/ghw/pkg/block/block_windows.go | 66 ++- vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go | 19 +- .../jaypipes/ghw/pkg/cpu/cpu_linux.go | 312 ++++++++---- .../jaypipes/ghw/pkg/gpu/gpu_windows.go | 4 +- .../jaypipes/ghw/pkg/linuxpath/path_linux.go | 2 + .../jaypipes/ghw/pkg/memory/memory_linux.go | 72 ++- vendor/github.com/jaypipes/ghw/pkg/net/net.go | 26 +- .../jaypipes/ghw/pkg/net/net_linux.go | 206 ++++++-- .../jaypipes/ghw/pkg/pci/pci_linux.go | 6 +- .../github.com/jaypipes/ghw/pkg/util/util.go | 26 + vendor/modules.txt | 4 +- 17 files changed, 878 insertions(+), 461 deletions(-) diff --git a/go.mod b/go.mod index 8de8fe544..f58aadea2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 github.com/fsnotify/fsnotify v1.7.0 github.com/google/go-cmp v0.6.0 - github.com/jaypipes/ghw v0.9.0 + github.com/jaypipes/ghw v0.12.0 github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4 github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.1 github.com/k8stopologyawareschedwg/podfingerprint v0.2.2 diff --git a/go.sum b/go.sum index beca8588d..d27f6e4ce 100644 --- a/go.sum +++ b/go.sum @@ -863,11 +863,10 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jaypipes/ghw v0.9.0 h1:TWF4wNIGtZcgDJaiNcFgby5BR8s2ixcUe0ydxNO2McY= -github.com/jaypipes/ghw v0.9.0/go.mod h1:dXMo19735vXOjpIBDyDYSp31sB2u4hrtRCMxInqQ64k= +github.com/jaypipes/ghw v0.12.0 h1:xU2/MDJfWmBhJnujHY9qwXQLs3DBsf0/Xa9vECY0Tho= +github.com/jaypipes/ghw v0.12.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g= github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8= github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLRCuNDfk= github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4 h1:eA9wi6ZzpIRobvXkn/S2Lyw1hr2pc71zxzOPl7Xjs4w= @@ -918,7 +917,6 @@ github.com/mdomke/git-semver v1.0.0 h1:cg/a+bI/D2EtPWlx4pKSUKz9G9bTOHEBdF2EjbV0b github.com/mdomke/git-semver v1.0.0/go.mod h1:fNw8giSaJDzhF/Gvxe7JSZJVDlkRR+/a8y1b3g6SGZ8= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -1021,10 +1019,8 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/vendor/github.com/jaypipes/ghw/README.md b/vendor/github.com/jaypipes/ghw/README.md index b0c742d27..1f3f04a74 100644 --- a/vendor/github.com/jaypipes/ghw/README.md +++ b/vendor/github.com/jaypipes/ghw/README.md @@ -408,12 +408,13 @@ A `ghw.ProcessorCore` has the following fields: core. Note that this does *not* necessarily equate to a zero-based index of the core within a physical package. For example, the core IDs for an Intel Core i7 are 0, 1, 2, 8, 9, and 10 -* `ghw.ProcessorCore.Index` is the zero-based index of the core on the physical - processor package * `ghw.ProcessorCore.NumThreads` is the number of hardware threads associated with the core -* `ghw.ProcessorCore.LogicalProcessors` is an array of logical processor IDs - assigned to any processing unit for the core +* `ghw.ProcessorCore.LogicalProcessors` is an array of ints representing the + logical processor IDs assigned to any processing unit for the core. These are + sometimes called the "thread siblings". Logical processor IDs are the + *zero-based* index of the processor on the host and are *not* related to the + core ID. ```go package main @@ -530,6 +531,12 @@ Each `ghw.Partition` struct contains these fields: * `ghw.Partition.Name` contains a string with the short name of the partition, e.g. "sda1" +* `ghw.Partition.Label` contains the label for the partition itself. On Linux + systems, this is derived from the `ID_PART_ENTRY_NAME` udev entry for the + partition. +* `ghw.Partition.FilesystemLabel` contains the label for the filesystem housed + on the partition. On Linux systems, this is derived from the `ID_FS_NAME` + udev entry for the partition. * `ghw.Partition.SizeBytes` contains the amount of storage the partition provides * `ghw.Partition.MountPoint` contains a string with the partition's mount @@ -540,8 +547,10 @@ Each `ghw.Partition` struct contains these fields: * `ghw.Partition.Disk` is a pointer to the `ghw.Disk` object associated with the partition. This will be `nil` if the `ghw.Partition` struct was returned by the `ghw.DiskPartitions()` library function. -* `ghw.Partition.UUID` is a string containing the volume UUID on Linux, the - partition UUID on MacOS and nothing on Windows. +* `ghw.Partition.UUID` is a string containing the partition UUID on Linux, the + partition UUID on MacOS and nothing on Windows. On Linux + systems, this is derived from the `ID_PART_ENTRY_UUID` udev entry for the + partition. ```go package main @@ -703,10 +712,25 @@ Each `ghw.NIC` struct contains the following fields: device * `ghw.NIC.Capabilities` is an array of pointers to `ghw.NICCapability` structs that can describe the things the NIC supports. These capabilities match the - returned values from the `ethtool -k ` call on Linux + returned values from the `ethtool -k ` call on Linux as well as the + AutoNegotiation and PauseFrameUse capabilities from `ethtool`. * `ghw.NIC.PCIAddress` is the PCI device address of the device backing the NIC. this is not-nil only if the backing device is indeed a PCI device; more backing devices (e.g. USB) will be added in future versions. +* `ghw.NIC.Speed` is a string showing the current link speed. On Linux, this + field will be present even if `ethtool` is not available. +* `ghw.NIC.Duplex` is a string showing the current link duplex. On Linux, this + field will be present even if `ethtool` is not available. +* `ghw.NIC.SupportedLinkModes` is a string slice containing a list of + supported link modes +* `ghw.NIC.SupportedPorts` is a string slice containing the list of + supported port types (MII, TP, FIBRE) +* `ghw.NIC.SupportedFECModes` is a string slice containing a list of + supported FEC Modes. +* `ghw.NIC.AdvertisedLinkModes` is a string slice containing the + link modes being advertised during auto negotiation. +* `ghw.NIC.AdvertisedFECModes` is a string slice containing the FEC + modes advertised during auto negotiation. The `ghw.NICCapability` struct contains the following fields: @@ -795,6 +819,7 @@ net (3 NICs) - rx-vlan-offload - tx-vlan-offload - highdma + - auto-negotiation wlp59s0 enabled capabilities: - scatter-gather diff --git a/vendor/github.com/jaypipes/ghw/doc.go b/vendor/github.com/jaypipes/ghw/doc.go index 9ae0c30ae..6722cda7d 100644 --- a/vendor/github.com/jaypipes/ghw/doc.go +++ b/vendor/github.com/jaypipes/ghw/doc.go @@ -5,310 +5,310 @@ // /* - package ghw can determine various hardware-related - information about the host computer: +package ghw can determine various hardware-related +information about the host computer: - * Memory - * CPU - * Block storage - * Topology - * Network - * PCI - * GPU +* Memory +* CPU +* Block storage +* Topology +* Network +* PCI +* GPU - Memory +# Memory - Information about the host computer's memory can be retrieved using the - Memory function which returns a pointer to a MemoryInfo struct. +Information about the host computer's memory can be retrieved using the +Memory function which returns a pointer to a MemoryInfo struct. - package main + package main - import ( - "fmt" + import ( + "fmt" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - memory, err := ghw.Memory() - if err != nil { - fmt.Printf("Error getting memory info: %v", err) - } - - fmt.Println(memory.String()) + func main() { + memory, err := ghw.Memory() + if err != nil { + fmt.Printf("Error getting memory info: %v", err) } - CPU + fmt.Println(memory.String()) + } - The CPU function returns a CPUInfo struct that contains information about - the CPUs on the host system. +# CPU - package main +The CPU function returns a CPUInfo struct that contains information about +the CPUs on the host system. - import ( - "fmt" - "math" - "strings" + package main - "github.com/jaypipes/ghw" - ) + import ( + "fmt" + "math" + "strings" - func main() { - cpu, err := ghw.CPU() - if err != nil { - fmt.Printf("Error getting CPU info: %v", err) - } + "github.com/jaypipes/ghw" + ) - fmt.Printf("%v\n", cpu) + func main() { + cpu, err := ghw.CPU() + if err != nil { + fmt.Printf("Error getting CPU info: %v", err) + } - for _, proc := range cpu.Processors { - fmt.Printf(" %v\n", proc) - for _, core := range proc.Cores { - fmt.Printf(" %v\n", core) - } - if len(proc.Capabilities) > 0 { - // pretty-print the (large) block of capability strings into rows - // of 6 capability strings - rows := int(math.Ceil(float64(len(proc.Capabilities)) / float64(6))) - for row := 1; row < rows; row = row + 1 { - rowStart := (row * 6) - 1 - rowEnd := int(math.Min(float64(rowStart+6), float64(len(proc.Capabilities)))) - rowElems := proc.Capabilities[rowStart:rowEnd] - capStr := strings.Join(rowElems, " ") - if row == 1 { - fmt.Printf(" capabilities: [%s\n", capStr) - } else if rowEnd < len(proc.Capabilities) { - fmt.Printf(" %s\n", capStr) - } else { - fmt.Printf(" %s]\n", capStr) - } + fmt.Printf("%v\n", cpu) + + for _, proc := range cpu.Processors { + fmt.Printf(" %v\n", proc) + for _, core := range proc.Cores { + fmt.Printf(" %v\n", core) + } + if len(proc.Capabilities) > 0 { + // pretty-print the (large) block of capability strings into rows + // of 6 capability strings + rows := int(math.Ceil(float64(len(proc.Capabilities)) / float64(6))) + for row := 1; row < rows; row = row + 1 { + rowStart := (row * 6) - 1 + rowEnd := int(math.Min(float64(rowStart+6), float64(len(proc.Capabilities)))) + rowElems := proc.Capabilities[rowStart:rowEnd] + capStr := strings.Join(rowElems, " ") + if row == 1 { + fmt.Printf(" capabilities: [%s\n", capStr) + } else if rowEnd < len(proc.Capabilities) { + fmt.Printf(" %s\n", capStr) + } else { + fmt.Printf(" %s]\n", capStr) } } } } + } - Block storage +# Block storage - Information about the host computer's local block storage is returned from - the Block function. This function returns a pointer to a BlockInfo struct. +Information about the host computer's local block storage is returned from +the Block function. This function returns a pointer to a BlockInfo struct. - package main + package main - import ( - "fmt" + import ( + "fmt" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - block, err := ghw.Block() - if err != nil { - fmt.Printf("Error getting block storage info: %v", err) - } + func main() { + block, err := ghw.Block() + if err != nil { + fmt.Printf("Error getting block storage info: %v", err) + } - fmt.Printf("%v\n", block) + fmt.Printf("%v\n", block) - for _, disk := range block.Disks { - fmt.Printf(" %v\n", disk) - for _, part := range disk.Partitions { - fmt.Printf(" %v\n", part) - } + for _, disk := range block.Disks { + fmt.Printf(" %v\n", disk) + for _, part := range disk.Partitions { + fmt.Printf(" %v\n", part) } } + } - Topology +# Topology - Information about the host computer's architecture (NUMA vs. SMP), the - host's node layout and processor caches can be retrieved from the Topology - function. This function returns a pointer to a TopologyInfo struct. +Information about the host computer's architecture (NUMA vs. SMP), the +host's node layout and processor caches can be retrieved from the Topology +function. This function returns a pointer to a TopologyInfo struct. - package main + package main - import ( - "fmt" + import ( + "fmt" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - topology, err := ghw.Topology() - if err != nil { - fmt.Printf("Error getting topology info: %v", err) - } + func main() { + topology, err := ghw.Topology() + if err != nil { + fmt.Printf("Error getting topology info: %v", err) + } - fmt.Printf("%v\n", topology) + fmt.Printf("%v\n", topology) - for _, node := range topology.Nodes { - fmt.Printf(" %v\n", node) - for _, cache := range node.Caches { - fmt.Printf(" %v\n", cache) - } + for _, node := range topology.Nodes { + fmt.Printf(" %v\n", node) + for _, cache := range node.Caches { + fmt.Printf(" %v\n", cache) } } + } - Network +# Network - Information about the host computer's networking hardware is returned from - the Network function. This function returns a pointer to a NetworkInfo - struct. +Information about the host computer's networking hardware is returned from +the Network function. This function returns a pointer to a NetworkInfo +struct. - package main + package main - import ( - "fmt" + import ( + "fmt" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - net, err := ghw.Network() - if err != nil { - fmt.Printf("Error getting network info: %v", err) - } + func main() { + net, err := ghw.Network() + if err != nil { + fmt.Printf("Error getting network info: %v", err) + } - fmt.Printf("%v\n", net) + fmt.Printf("%v\n", net) - for _, nic := range net.NICs { - fmt.Printf(" %v\n", nic) + for _, nic := range net.NICs { + fmt.Printf(" %v\n", nic) - enabledCaps := make([]int, 0) - for x, cap := range nic.Capabilities { - if cap.IsEnabled { - enabledCaps = append(enabledCaps, x) - } + enabledCaps := make([]int, 0) + for x, cap := range nic.Capabilities { + if cap.IsEnabled { + enabledCaps = append(enabledCaps, x) } - if len(enabledCaps) > 0 { - fmt.Printf(" enabled capabilities:\n") - for _, x := range enabledCaps { - fmt.Printf(" - %s\n", nic.Capabilities[x].Name) - } + } + if len(enabledCaps) > 0 { + fmt.Printf(" enabled capabilities:\n") + for _, x := range enabledCaps { + fmt.Printf(" - %s\n", nic.Capabilities[x].Name) } } } + } - PCI +# PCI - ghw contains a PCI database inspection and querying facility that allows - developers to not only gather information about devices on a local PCI bus - but also query for information about hardware device classes, vendor and - product information. +ghw contains a PCI database inspection and querying facility that allows +developers to not only gather information about devices on a local PCI bus +but also query for information about hardware device classes, vendor and +product information. - **NOTE**: Parsing of the PCI-IDS file database is provided by the separate - http://github.com/jaypipes/pcidb library. You can read that library's - README for more information about the various structs that are exposed on - the PCIInfo struct. +**NOTE**: Parsing of the PCI-IDS file database is provided by the separate +http://github.com/jaypipes/pcidb library. You can read that library's +README for more information about the various structs that are exposed on +the PCIInfo struct. - PCIInfo.ListDevices is used to iterate over a host's PCI devices: +PCIInfo.ListDevices is used to iterate over a host's PCI devices: - package main + package main - import ( - "fmt" + import ( + "fmt" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - pci, err := ghw.PCI() - if err != nil { - fmt.Printf("Error getting PCI info: %v", err) - } - fmt.Printf("host PCI devices:\n") - fmt.Println("====================================================") - devices := pci.ListDevices() - if len(devices) == 0 { - fmt.Printf("error: could not retrieve PCI devices\n") - return - } + func main() { + pci, err := ghw.PCI() + if err != nil { + fmt.Printf("Error getting PCI info: %v", err) + } + fmt.Printf("host PCI devices:\n") + fmt.Println("====================================================") + devices := pci.ListDevices() + if len(devices) == 0 { + fmt.Printf("error: could not retrieve PCI devices\n") + return + } - for _, device := range devices { - vendor := device.Vendor - vendorName := vendor.Name - if len(vendor.Name) > 20 { - vendorName = string([]byte(vendorName)[0:17]) + "..." - } - product := device.Product - productName := product.Name - if len(product.Name) > 40 { - productName = string([]byte(productName)[0:37]) + "..." - } - fmt.Printf("%-12s\t%-20s\t%-40s\n", device.Address, vendorName, productName) + for _, device := range devices { + vendor := device.Vendor + vendorName := vendor.Name + if len(vendor.Name) > 20 { + vendorName = string([]byte(vendorName)[0:17]) + "..." + } + product := device.Product + productName := product.Name + if len(product.Name) > 40 { + productName = string([]byte(productName)[0:37]) + "..." } + fmt.Printf("%-12s\t%-20s\t%-40s\n", device.Address, vendorName, productName) } + } - The following code snippet shows how to call the PCIInfo.GetDevice method - and use its returned PCIDevice struct pointer: +The following code snippet shows how to call the PCIInfo.GetDevice method +and use its returned PCIDevice struct pointer: - package main + package main - import ( - "fmt" - "os" + import ( + "fmt" + "os" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - pci, err := ghw.PCI() - if err != nil { - fmt.Printf("Error getting PCI info: %v", err) - } + func main() { + pci, err := ghw.PCI() + if err != nil { + fmt.Printf("Error getting PCI info: %v", err) + } - addr := "0000:00:00.0" - if len(os.Args) == 2 { - addr = os.Args[1] - } - fmt.Printf("PCI device information for %s\n", addr) - fmt.Println("====================================================") - deviceInfo := pci.GetDevice(addr) - if deviceInfo == nil { - fmt.Printf("could not retrieve PCI device information for %s\n", addr) - return - } + addr := "0000:00:00.0" + if len(os.Args) == 2 { + addr = os.Args[1] + } + fmt.Printf("PCI device information for %s\n", addr) + fmt.Println("====================================================") + deviceInfo := pci.GetDevice(addr) + if deviceInfo == nil { + fmt.Printf("could not retrieve PCI device information for %s\n", addr) + return + } - vendor := deviceInfo.Vendor - fmt.Printf("Vendor: %s [%s]\n", vendor.Name, vendor.ID) - product := deviceInfo.Product - fmt.Printf("Product: %s [%s]\n", product.Name, product.ID) - subsystem := deviceInfo.Subsystem - subvendor := pci.Vendors[subsystem.VendorID] - subvendorName := "UNKNOWN" - if subvendor != nil { - subvendorName = subvendor.Name - } - fmt.Printf("Subsystem: %s [%s] (Subvendor: %s)\n", subsystem.Name, subsystem.ID, subvendorName) - class := deviceInfo.Class - fmt.Printf("Class: %s [%s]\n", class.Name, class.ID) - subclass := deviceInfo.Subclass - fmt.Printf("Subclass: %s [%s]\n", subclass.Name, subclass.ID) - progIface := deviceInfo.ProgrammingInterface - fmt.Printf("Programming Interface: %s [%s]\n", progIface.Name, progIface.ID) + vendor := deviceInfo.Vendor + fmt.Printf("Vendor: %s [%s]\n", vendor.Name, vendor.ID) + product := deviceInfo.Product + fmt.Printf("Product: %s [%s]\n", product.Name, product.ID) + subsystem := deviceInfo.Subsystem + subvendor := pci.Vendors[subsystem.VendorID] + subvendorName := "UNKNOWN" + if subvendor != nil { + subvendorName = subvendor.Name } + fmt.Printf("Subsystem: %s [%s] (Subvendor: %s)\n", subsystem.Name, subsystem.ID, subvendorName) + class := deviceInfo.Class + fmt.Printf("Class: %s [%s]\n", class.Name, class.ID) + subclass := deviceInfo.Subclass + fmt.Printf("Subclass: %s [%s]\n", subclass.Name, subclass.ID) + progIface := deviceInfo.ProgrammingInterface + fmt.Printf("Programming Interface: %s [%s]\n", progIface.Name, progIface.ID) + } - GPU +# GPU - Information about the host computer's graphics hardware is returned from - the GPU function. This function returns a pointer to a GPUInfo struct. +Information about the host computer's graphics hardware is returned from +the GPU function. This function returns a pointer to a GPUInfo struct. - package main + package main - import ( - "fmt" + import ( + "fmt" - "github.com/jaypipes/ghw" - ) + "github.com/jaypipes/ghw" + ) - func main() { - gpu, err := ghw.GPU() - if err != nil { - fmt.Printf("Error getting GPU info: %v", err) - } + func main() { + gpu, err := ghw.GPU() + if err != nil { + fmt.Printf("Error getting GPU info: %v", err) + } - fmt.Printf("%v\n", gpu) + fmt.Printf("%v\n", gpu) - for _, card := range gpu.GraphicsCards { - fmt.Printf(" %v\n", card) - } + for _, card := range gpu.GraphicsCards { + fmt.Printf(" %v\n", card) } + } */ package ghw diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block.go b/vendor/github.com/jaypipes/ghw/pkg/block/block.go index 38830ccf7..a495f69c5 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block.go @@ -29,6 +29,7 @@ const ( DRIVE_TYPE_FDD // Floppy disk drive DRIVE_TYPE_ODD // Optical disk drive DRIVE_TYPE_SSD // Solid-state drive + DRIVE_TYPE_VIRTUAL // virtual drive i.e. loop devices ) var ( @@ -38,6 +39,7 @@ var ( DRIVE_TYPE_FDD: "FDD", DRIVE_TYPE_ODD: "ODD", DRIVE_TYPE_SSD: "SSD", + DRIVE_TYPE_VIRTUAL: "virtual", } // NOTE(fromani): the keys are all lowercase and do not match @@ -51,6 +53,7 @@ var ( "fdd": DRIVE_TYPE_FDD, "odd": DRIVE_TYPE_ODD, "ssd": DRIVE_TYPE_SSD, + "virtual": DRIVE_TYPE_VIRTUAL, } ) @@ -93,6 +96,7 @@ const ( STORAGE_CONTROLLER_NVME // Non-volatile Memory Express STORAGE_CONTROLLER_VIRTIO // Virtualized storage controller/driver STORAGE_CONTROLLER_MMC // Multi-media controller (used for mobile phone storage devices) + STORAGE_CONTROLLER_LOOP // loop device ) var ( @@ -103,6 +107,7 @@ var ( STORAGE_CONTROLLER_NVME: "NVMe", STORAGE_CONTROLLER_VIRTIO: "virtio", STORAGE_CONTROLLER_MMC: "MMC", + STORAGE_CONTROLLER_LOOP: "loop", } // NOTE(fromani): the keys are all lowercase and do not match @@ -117,6 +122,7 @@ var ( "nvme": STORAGE_CONTROLLER_NVME, "virtio": STORAGE_CONTROLLER_VIRTIO, "mmc": STORAGE_CONTROLLER_MMC, + "loop": STORAGE_CONTROLLER_LOOP, } ) @@ -169,14 +175,15 @@ type Disk struct { // Partition describes a logical division of a Disk. type Partition struct { - Disk *Disk `json:"-"` - Name string `json:"name"` - Label string `json:"label"` - MountPoint string `json:"mount_point"` - SizeBytes uint64 `json:"size_bytes"` - Type string `json:"type"` - IsReadOnly bool `json:"read_only"` - UUID string `json:"uuid"` // This would be volume UUID on macOS, PartUUID on linux, empty on Windows + Disk *Disk `json:"-"` + Name string `json:"name"` + Label string `json:"label"` + MountPoint string `json:"mount_point"` + SizeBytes uint64 `json:"size_bytes"` + Type string `json:"type"` + IsReadOnly bool `json:"read_only"` + UUID string `json:"uuid"` // This would be volume UUID on macOS, PartUUID on linux, empty on Windows + FilesystemLabel string `json:"filesystem_label"` } // Info describes all disk drives and partitions in the host system. diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go b/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go index a3c40c418..ce1641321 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go @@ -10,7 +10,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path/filepath" "strconv" "strings" @@ -209,26 +208,28 @@ func diskPartitions(ctx *context.Context, paths *linuxpath.Paths, disk string) [ } size := partitionSizeBytes(paths, disk, fname) mp, pt, ro := partitionInfo(paths, fname) - du := diskPartUUID(ctx, fname) + du := diskPartUUID(paths, disk, fname) label := diskPartLabel(paths, disk, fname) if pt == "" { pt = diskPartTypeUdev(paths, disk, fname) } + fsLabel := diskFSLabel(paths, disk, fname) p := &Partition{ - Name: fname, - SizeBytes: size, - MountPoint: mp, - Type: pt, - IsReadOnly: ro, - UUID: du, - Label: label, + Name: fname, + SizeBytes: size, + MountPoint: mp, + Type: pt, + IsReadOnly: ro, + UUID: du, + Label: label, + FilesystemLabel: fsLabel, } out = append(out, p) } return out } -func diskPartLabel(paths *linuxpath.Paths, disk string, partition string) string { +func diskFSLabel(paths *linuxpath.Paths, disk string, partition string) string { info, err := udevInfoPartition(paths, disk, partition) if err != nil { return util.UNKNOWN @@ -240,6 +241,18 @@ func diskPartLabel(paths *linuxpath.Paths, disk string, partition string) string return util.UNKNOWN } +func diskPartLabel(paths *linuxpath.Paths, disk string, partition string) string { + info, err := udevInfoPartition(paths, disk, partition) + if err != nil { + return util.UNKNOWN + } + + if label, ok := info["ID_PART_ENTRY_NAME"]; ok { + return label + } + return util.UNKNOWN +} + // diskPartTypeUdev gets the partition type from the udev database directly and its only used as fallback when // the partition is not mounted, so we cannot get the type from paths.ProcMounts from the partitionInfo function func diskPartTypeUdev(paths *linuxpath.Paths, disk string, partition string) string { @@ -254,37 +267,16 @@ func diskPartTypeUdev(paths *linuxpath.Paths, disk string, partition string) str return util.UNKNOWN } -func diskPartUUID(ctx *context.Context, part string) string { - if !ctx.EnableTools { - ctx.Warn("EnableTools=false disables partition UUID detection.") - return "" - } - if !strings.HasPrefix(part, "/dev") { - part = "/dev/" + part - } - args := []string{ - "blkid", - "-s", - "PARTUUID", - part, - } - out, err := exec.Command(args[0], args[1:]...).Output() +func diskPartUUID(paths *linuxpath.Paths, disk string, partition string) string { + info, err := udevInfoPartition(paths, disk, partition) if err != nil { - ctx.Warn("failed to read disk partuuid of %s : %s\n", part, err.Error()) - return "" - } - - if len(out) == 0 { - return "" + return util.UNKNOWN } - parts := strings.Split(string(out), "PARTUUID=") - if len(parts) != 2 { - ctx.Warn("failed to parse the partuuid of %s\n", part) - return "" + if pType, ok := info["ID_PART_ENTRY_UUID"]; ok { + return pType } - - return strings.ReplaceAll(strings.TrimSpace(parts[1]), `"`, "") + return util.UNKNOWN } func diskIsRemovable(paths *linuxpath.Paths, disk string) bool { @@ -309,9 +301,6 @@ func disks(ctx *context.Context, paths *linuxpath.Paths) []*Disk { } for _, file := range files { dname := file.Name() - if strings.HasPrefix(dname, "loop") { - continue - } driveType, storageController := diskTypes(dname) // TODO(jaypipes): Move this into diskTypes() once abstracting @@ -329,6 +318,10 @@ func disks(ctx *context.Context, paths *linuxpath.Paths) []*Disk { wwn := diskWWN(paths, dname) removable := diskIsRemovable(paths, dname) + if storageController == STORAGE_CONTROLLER_LOOP && size == 0 { + // We don't care about unused loop devices... + continue + } d := &Disk{ Name: dname, SizeBytes: size, @@ -390,6 +383,9 @@ func diskTypes(dname string) ( } else if strings.HasPrefix(dname, "mmc") { driveType = DRIVE_TYPE_SSD storageController = STORAGE_CONTROLLER_MMC + } else if strings.HasPrefix(dname, "loop") { + driveType = DRIVE_TYPE_VIRTUAL + storageController = STORAGE_CONTROLLER_LOOP } return driveType, storageController diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go b/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go index 75c6f04c7..574f5612e 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go @@ -6,6 +6,7 @@ package block import ( + "strconv" "strings" "github.com/StackExchange/wmi" @@ -13,6 +14,29 @@ import ( "github.com/jaypipes/ghw/pkg/util" ) +type physicalDiskMediaType int + +const ( + PHYSICAL_DISK_MEDIA_TYPE_UNSPECIFIED physicalDiskMediaType = 0 + PHYSICAL_DISK_MEDIA_TYPE_HDD physicalDiskMediaType = 3 + PHYSICAL_DISK_MEDIA_TYPE_SSD physicalDiskMediaType = 4 + PHYSICAL_DISK_MEDIA_TYPE_SCM physicalDiskMediaType = 5 +) + +func (dt physicalDiskMediaType) ToDriveType() DriveType { + switch dt { + case PHYSICAL_DISK_MEDIA_TYPE_UNSPECIFIED: + return DRIVE_TYPE_UNKNOWN + case PHYSICAL_DISK_MEDIA_TYPE_HDD: + return DRIVE_TYPE_HDD + case PHYSICAL_DISK_MEDIA_TYPE_SSD: + return DRIVE_TYPE_SSD + case PHYSICAL_DISK_MEDIA_TYPE_SCM: + return DRIVE_TYPE_UNKNOWN + } + return DRIVE_TYPE_UNKNOWN +} + const wqlDiskDrive = "SELECT Caption, CreationClassName, DefaultBlockSize, Description, DeviceID, Index, InterfaceType, Manufacturer, MediaType, Model, Name, Partitions, SerialNumber, Size, TotalCylinders, TotalHeads, TotalSectors, TotalTracks, TracksPerCylinder FROM Win32_DiskDrive" type win32DiskDrive struct { @@ -75,6 +99,13 @@ type win32LogicalDisk struct { SystemName *string } +const wqlPhysicalDisk = "SELECT DeviceId, MediaType FROM MSFT_PhysicalDisk" + +type win32PhysicalDisk struct { + DeviceId string + MediaType physicalDiskMediaType +} + func (i *Info) load() error { win32DiskDriveDescriptions, err := getDiskDrives() if err != nil { @@ -96,14 +127,27 @@ func (i *Info) load() error { return err } + win32PhysicalDisks, err := getPhysicalDisks() + if err != nil { + return err + } + // Converting into standard structures disks := make([]*Disk, 0) for _, diskdrive := range win32DiskDriveDescriptions { + var physicalDiskMediaType physicalDiskMediaType + for _, physicalDisk := range win32PhysicalDisks { + if id, err := strconv.Atoi(physicalDisk.DeviceId); err != nil { + return err + } else if uint32(id) == *diskdrive.Index { + physicalDiskMediaType = physicalDisk.MediaType + } + } disk := &Disk{ Name: strings.TrimSpace(*diskdrive.DeviceID), SizeBytes: *diskdrive.Size, PhysicalBlockSizeBytes: *diskdrive.DefaultBlockSize, - DriveType: toDriveType(*diskdrive.MediaType, *diskdrive.Caption), + DriveType: toDriveType(physicalDiskMediaType, *diskdrive.MediaType, *diskdrive.Caption), StorageController: toStorageController(*diskdrive.InterfaceType), BusPath: util.UNKNOWN, // TODO: add information NUMANodeID: -1, @@ -115,7 +159,10 @@ func (i *Info) load() error { } for _, diskpartition := range win32DiskPartitionDescriptions { // Finding disk partition linked to current disk drive - if diskdrive.Index == diskpartition.DiskIndex { + if diskdrive.Index == nil || diskpartition.DiskIndex == nil { + continue + } + if *diskdrive.Index == *diskpartition.DiskIndex { disk.PhysicalBlockSizeBytes = *diskpartition.BlockSize // Finding logical partition linked to current disk partition for _, logicaldisk := range win32LogicalDiskDescriptions { @@ -188,7 +235,20 @@ func getLogicalDisks() ([]win32LogicalDisk, error) { return win32LogicalDiskDescriptions, nil } -func toDriveType(mediaType string, caption string) DriveType { +func getPhysicalDisks() ([]win32PhysicalDisk, error) { + // Getting physical disks from WMI + var win32PhysicalDisks []win32PhysicalDisk + if err := wmi.QueryNamespace(wqlPhysicalDisk, &win32PhysicalDisks, "root\\Microsoft\\Windows\\Storage"); err != nil { + return nil, err + } + return win32PhysicalDisks, nil +} + +func toDriveType(physicalDiskMediaType physicalDiskMediaType, mediaType string, caption string) DriveType { + if driveType := physicalDiskMediaType.ToDriveType(); driveType != DRIVE_TYPE_UNKNOWN { + return driveType + } + mediaType = strings.ToLower(mediaType) caption = strings.ToLower(caption) if strings.Contains(mediaType, "fixed") || strings.Contains(mediaType, "ssd") || strings.Contains(caption, "ssd") { diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go index 2fa0cd2d0..4865b46dd 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go @@ -23,13 +23,12 @@ type ProcessorCore struct { // within a physical package. For example, the core IDs for an Intel Core // i7 are 0, 1, 2, 8, 9, and 10 ID int `json:"id"` - // Index is the zero-based index of the core on the physical processor - // package - Index int `json:"index"` // NumThreads is the number of hardware threads associated with the core NumThreads uint32 `json:"total_threads"` // LogicalProcessors is a slice of ints representing the logical processor - // IDs assigned to any processing unit for the core + // IDs assigned to any processing unit for the core. These are sometimes + // called the "thread siblings". Logical processor IDs are the *zero-based* + // index of the processor on the host and are *not* related to the core ID. LogicalProcessors []int `json:"logical_processors"` } @@ -38,7 +37,7 @@ type ProcessorCore struct { func (c *ProcessorCore) String() string { return fmt.Sprintf( "processor core #%d (%d threads), logical processors %v", - c.Index, + c.ID, c.NumThreads, c.LogicalProcessors, ) @@ -64,6 +63,16 @@ type Processor struct { Cores []*ProcessorCore `json:"cores"` } +// CoreByID returns the ProcessorCore having the supplied ID. +func (p *Processor) CoreByID(coreID int) *ProcessorCore { + for _, core := range p.Cores { + if core.ID == coreID { + return core + } + } + return nil +} + // HasCapability returns true if the Processor has the supplied cpuid // capability, false otherwise. Example of cpuid capabilities would be 'vmx' or // 'sse4_2'. To see a list of potential cpuid capabilitiies, see the section on diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go index 44e4ced74..00e701940 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "strconv" "strings" @@ -19,6 +20,10 @@ import ( "github.com/jaypipes/ghw/pkg/util" ) +var ( + regexForCpulCore = regexp.MustCompile("^cpu([0-9]+)$") +) + func (i *Info) load() error { i.Processors = processorsGet(i.ctx) var totCores uint32 @@ -33,118 +38,110 @@ func (i *Info) load() error { } func processorsGet(ctx *context.Context) []*Processor { - procs := make([]*Processor, 0) paths := linuxpath.New(ctx) - r, err := os.Open(paths.ProcCpuinfo) + lps := logicalProcessorsFromProcCPUInfo(ctx) + // keyed by processor ID (physical_package_id) + procs := map[int]*Processor{} + + // /sys/devices/system/cpu pseudodir contains N number of pseudodirs with + // information about the logical processors on the host. These logical + // processor pseudodirs are of the pattern /sys/devices/system/cpu/cpu{N} + fnames, err := ioutil.ReadDir(paths.SysDevicesSystemCPU) if err != nil { - return nil + ctx.Warn("failed to read /sys/devices/system/cpu: %s", err) + return []*Processor{} } - defer util.SafeClose(r) - - // An array of maps of attributes describing the logical processor - procAttrs := make([]map[string]string, 0) - curProcAttrs := make(map[string]string) - - scanner := bufio.NewScanner(r) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" { - // Output of /proc/cpuinfo has a blank newline to separate logical - // processors, so here we collect up all the attributes we've - // collected for this logical processor block - procAttrs = append(procAttrs, curProcAttrs) - // Reset the current set of processor attributes... - curProcAttrs = make(map[string]string) + for _, fname := range fnames { + matches := regexForCpulCore.FindStringSubmatch(fname.Name()) + if len(matches) < 2 { continue } - parts := strings.Split(line, ":") - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - curProcAttrs[key] = value - } - // Build a set of physical processor IDs which represent the physical - // package of the CPU - setPhysicalIDs := make(map[int]bool) - for _, attrs := range procAttrs { - pid, err := strconv.Atoi(attrs["physical id"]) + lpID, err := strconv.Atoi(matches[1]) if err != nil { + ctx.Warn("failed to find numeric logical processor ID: %s", err) continue } - setPhysicalIDs[pid] = true - } - for pid := range setPhysicalIDs { - p := &Processor{ - ID: pid, - } - // The indexes into the array of attribute maps for each logical - // processor within the physical processor - lps := make([]int, 0) - for x := range procAttrs { - lppid, err := strconv.Atoi(procAttrs[x]["physical id"]) - if err != nil { + procID := processorIDFromLogicalProcessorID(ctx, lpID) + proc, found := procs[procID] + if !found { + proc = &Processor{ID: procID} + lp, ok := lps[lpID] + if !ok { + ctx.Warn( + "failed to find attributes for logical processor %d", + lpID, + ) continue } - if pid == lppid { - lps = append(lps, x) - } - } - first := procAttrs[lps[0]] - p.Model = first["model name"] - p.Vendor = first["vendor_id"] - numCores, err := strconv.Atoi(first["cpu cores"]) - if err != nil { - continue - } - p.NumCores = uint32(numCores) - numThreads, err := strconv.Atoi(first["siblings"]) - if err != nil { - continue - } - p.NumThreads = uint32(numThreads) - - // The flags field is a space-separated list of CPU capabilities - p.Capabilities = strings.Split(first["flags"], " ") - cores := make([]*ProcessorCore, 0) - for _, lpidx := range lps { - lpid, err := strconv.Atoi(procAttrs[lpidx]["processor"]) - if err != nil { - continue - } - coreID, err := strconv.Atoi(procAttrs[lpidx]["core id"]) - if err != nil { - continue + // Assumes /proc/cpuinfo is in order of logical processor id, then + // lps[lpID] describes logical processor `lpID`. + // Once got a more robust way of fetching the following info, + // can we drop /proc/cpuinfo. + if len(lp.Attrs["flags"]) != 0 { // x86 + proc.Capabilities = strings.Split(lp.Attrs["flags"], " ") + } else if len(lp.Attrs["Features"]) != 0 { // ARM64 + proc.Capabilities = strings.Split(lp.Attrs["Features"], " ") } - var core *ProcessorCore - for _, c := range cores { - if c.ID == coreID { - c.LogicalProcessors = append( - c.LogicalProcessors, - lpid, - ) - c.NumThreads = uint32(len(c.LogicalProcessors)) - core = c - } + if len(lp.Attrs["model name"]) != 0 { + proc.Model = lp.Attrs["model name"] + } else if len(lp.Attrs["uarch"]) != 0 { // SiFive + proc.Model = lp.Attrs["uarch"] } - if core == nil { - coreLps := make([]int, 1) - coreLps[0] = lpid - core = &ProcessorCore{ - ID: coreID, - Index: len(cores), - NumThreads: 1, - LogicalProcessors: coreLps, - } - cores = append(cores, core) + if len(lp.Attrs["vendor_id"]) != 0 { + proc.Vendor = lp.Attrs["vendor_id"] + } else if len(lp.Attrs["isa"]) != 0 { // RISCV64 + proc.Vendor = lp.Attrs["isa"] } + procs[procID] = proc + } + + coreID := coreIDFromLogicalProcessorID(ctx, lpID) + core := proc.CoreByID(coreID) + if core == nil { + core = &ProcessorCore{ID: coreID, NumThreads: 1} + proc.Cores = append(proc.Cores, core) + proc.NumCores += 1 + } else { + core.NumThreads += 1 } - p.Cores = cores - procs = append(procs, p) + proc.NumThreads += 1 + core.LogicalProcessors = append(core.LogicalProcessors, lpID) } - return procs + res := []*Processor{} + for _, p := range procs { + res = append(res, p) + } + return res +} + +// processorIDFromLogicalProcessorID returns the processor physical package ID +// for the supplied logical processor ID +func processorIDFromLogicalProcessorID(ctx *context.Context, lpID int) int { + paths := linuxpath.New(ctx) + // Fetch CPU ID + path := filepath.Join( + paths.SysDevicesSystemCPU, + fmt.Sprintf("cpu%d", lpID), + "topology", "physical_package_id", + ) + return util.SafeIntFromFile(ctx, path) +} + +// coreIDFromLogicalProcessorID returns the core ID for the supplied logical +// processor ID +func coreIDFromLogicalProcessorID(ctx *context.Context, lpID int) int { + paths := linuxpath.New(ctx) + // Fetch CPU ID + path := filepath.Join( + paths.SysDevicesSystemCPU, + fmt.Sprintf("cpu%d", lpID), + "topology", "core_id", + ) + return util.SafeIntFromFile(ctx, path) } func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) { @@ -169,8 +166,7 @@ func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) { c := &ProcessorCore{ ID: coreID, - Index: len(cores), - LogicalProcessors: make([]int, 0), + LogicalProcessors: []int{}, } cores = append(cores, c) return c @@ -196,8 +192,7 @@ func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) { cpuPath := filepath.Join(path, filename) procID, err := strconv.Atoi(filename[3:]) if err != nil { - _, _ = fmt.Fprintf( - os.Stderr, + ctx.Warn( "failed to determine procID from %s. Expected integer after 3rd char.", filename, ) @@ -218,3 +213,126 @@ func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) { return cores, nil } + +// logicalProcessor contains information about a single logical processor +// on the host. +type logicalProcessor struct { + // This is the logical processor ID assigned by the host. In /proc/cpuinfo, + // this is the zero-based index of the logical processor as it appears in + // the /proc/cpuinfo file and matches the "processor" attribute. In + // /sys/devices/system/cpu/cpu{N} pseudodir entries, this is the N value. + ID int + // The entire collection of string attribute name/value pairs for the + // logical processor. + Attrs map[string]string +} + +// logicalProcessorsFromProcCPUInfo reads the `/proc/cpuinfo` pseudofile and +// returns a map, keyed by logical processor ID, of logical processor structs. +// +// `/proc/cpuinfo` files look like the following: +// +// ``` +// processor : 0 +// vendor_id : AuthenticAMD +// cpu family : 23 +// model : 8 +// model name : AMD Ryzen 7 2700X Eight-Core Processor +// stepping : 2 +// microcode : 0x800820d +// cpu MHz : 2200.000 +// cache size : 512 KB +// physical id : 0 +// siblings : 16 +// core id : 0 +// cpu cores : 8 +// apicid : 0 +// initial apicid : 0 +// fpu : yes +// fpu_exception : yes +// cpuid level : 13 +// wp : yes +// flags : fpu vme de pse tsc msr pae mce +// bugs : sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb +// bogomips : 7386.41 +// TLB size : 2560 4K pages +// clflush size : 64 +// cache_alignment : 64 +// address sizes : 43 bits physical, 48 bits virtual +// power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] +// +// processor : 1 +// vendor_id : AuthenticAMD +// cpu family : 23 +// model : 8 +// model name : AMD Ryzen 7 2700X Eight-Core Processor +// stepping : 2 +// microcode : 0x800820d +// cpu MHz : 1885.364 +// cache size : 512 KB +// physical id : 0 +// siblings : 16 +// core id : 1 +// cpu cores : 8 +// apicid : 2 +// initial apicid : 2 +// fpu : yes +// fpu_exception : yes +// cpuid level : 13 +// wp : yes +// flags : fpu vme de pse tsc msr pae mce +// bugs : sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb +// bogomips : 7386.41 +// TLB size : 2560 4K pages +// clflush size : 64 +// cache_alignment : 64 +// address sizes : 43 bits physical, 48 bits virtual +// power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] +// ``` +// +// with blank line-separated blocks of colon-delimited attribute name/value +// pairs for a specific logical processor on the host. +func logicalProcessorsFromProcCPUInfo( + ctx *context.Context, +) map[int]*logicalProcessor { + paths := linuxpath.New(ctx) + r, err := os.Open(paths.ProcCpuinfo) + if err != nil { + return nil + } + defer util.SafeClose(r) + + lps := map[int]*logicalProcessor{} + + // A map of attributes describing the logical processor + lpAttrs := map[string]string{} + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + // Output of /proc/cpuinfo has a blank newline to separate logical + // processors, so here we collect up all the attributes we've + // collected for this logical processor block + lpIDstr, ok := lpAttrs["processor"] + if !ok { + ctx.Warn("expected to find 'processor' key in /proc/cpuinfo attributes") + continue + } + lpID, _ := strconv.Atoi(lpIDstr) + lp := &logicalProcessor{ + ID: lpID, + Attrs: lpAttrs, + } + lps[lpID] = lp + // Reset the current set of processor attributes... + lpAttrs = map[string]string{} + continue + } + parts := strings.Split(line, ":") + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + lpAttrs[key] = value + } + return lps +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go index 5fb542814..70e19918c 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go @@ -15,13 +15,14 @@ import ( "github.com/jaypipes/ghw/pkg/util" ) -const wqlVideoController = "SELECT Caption, CreationClassName, Description, DeviceID, Name, PNPDeviceID, SystemCreationClassName, SystemName, VideoArchitecture, VideoMemoryType, VideoModeDescription, VideoProcessor FROM Win32_VideoController" +const wqlVideoController = "SELECT Caption, CreationClassName, Description, DeviceID, DriverVersion, Name, PNPDeviceID, SystemCreationClassName, SystemName, VideoArchitecture, VideoMemoryType, VideoModeDescription, VideoProcessor FROM Win32_VideoController" type win32VideoController struct { Caption string CreationClassName string Description string DeviceID string + DriverVersion string Name string PNPDeviceID string SystemCreationClassName string @@ -75,6 +76,7 @@ func (i *Info) load() error { Index: 0, DeviceInfo: GetDevice(description.PNPDeviceID, win32PnPDescriptions), } + card.DeviceInfo.Driver = description.DriverVersion cards = append(cards, card) } i.GraphicsCards = cards diff --git a/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go b/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go index c5967d619..bbe81b64e 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go @@ -64,6 +64,7 @@ type Paths struct { SysBlock string SysDevicesSystemNode string SysDevicesSystemMemory string + SysDevicesSystemCPU string SysBusPciDevices string SysClassDRM string SysClassDMI string @@ -84,6 +85,7 @@ func New(ctx *context.Context) *Paths { SysBlock: filepath.Join(ctx.Chroot, roots.Sys, "block"), SysDevicesSystemNode: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "node"), SysDevicesSystemMemory: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "memory"), + SysDevicesSystemCPU: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "cpu"), SysBusPciDevices: filepath.Join(ctx.Chroot, roots.Sys, "bus", "pci", "devices"), SysClassDRM: filepath.Join(ctx.Chroot, roots.Sys, "class", "drm"), SysClassDMI: filepath.Join(ctx.Chroot, roots.Sys, "class", "dmi"), diff --git a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go index 4b7631a19..21d10f2fc 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go @@ -38,6 +38,10 @@ var ( // System log lines will look similar to the following: // ... kernel: [0.000000] Memory: 24633272K/25155024K ... _REGEX_SYSLOG_MEMLINE = regexp.MustCompile(`Memory:\s+\d+K\/(\d+)K`) + // regexMemoryBlockDirname matches a subdirectory in either + // /sys/devices/system/memory or /sys/devices/system/node/nodeX that + // represents information on a specific memory cell/block + regexMemoryBlockDirname = regexp.MustCompile(`memory\d+$`) ) func (i *Info) load() error { @@ -64,19 +68,31 @@ func AreaForNode(ctx *context.Context, nodeID int) (*Area, error) { fmt.Sprintf("node%d", nodeID), ) - blockSizeBytes, err := memoryBlockSizeBytes(paths.SysDevicesSystemMemory) - if err != nil { - return nil, err - } + var err error + var blockSizeBytes uint64 + var totPhys int64 + var totUsable int64 - totPhys, err := memoryTotalPhysicalBytesFromPath(path, blockSizeBytes) + totUsable, err = memoryTotalUsableBytesFromPath(filepath.Join(path, "meminfo")) if err != nil { return nil, err } - totUsable, err := memoryTotalUsableBytesFromPath(filepath.Join(path, "meminfo")) - if err != nil { - return nil, err + blockSizeBytes, err = memoryBlockSizeBytes(paths.SysDevicesSystemMemory) + if err == nil { + totPhys, err = memoryTotalPhysicalBytesFromPath(path, blockSizeBytes) + if err != nil { + return nil, err + } + } else { + // NOTE(jaypipes): Some platforms (e.g. ARM) will not have a + // /sys/device/system/memory/block_size_bytes file. If this is the + // case, we set physical bytes equal to either the physical memory + // determined from syslog or the usable bytes + // + // see: https://bugzilla.redhat.com/show_bug.cgi?id=1794160 + // see: https://github.com/jaypipes/ghw/issues/336 + totPhys = memTotalPhysicalBytesFromSyslog(paths) } supportedHP, err := memorySupportedPageSizes(filepath.Join(path, "hugepages")) @@ -125,29 +141,37 @@ func memTotalPhysicalBytes(paths *linuxpath.Paths) (total int64) { return total } +// memoryTotalPhysicalBytesFromPath accepts a directory -- either +// /sys/devices/system/memory (for the entire system) or +// /sys/devices/system/node/nodeX (for a specific NUMA node) -- and a block +// size in bytes and iterates over the sysfs memory block subdirectories, +// accumulating blocks that are "online" to determine a total physical memory +// size in bytes func memoryTotalPhysicalBytesFromPath(dir string, blockSizeBytes uint64) (int64, error) { - // iterate over memory's block /sys/.../memory*, - // if the memory block state is 'online' we increment the total - // with the memory block size to determine the amount of physical - // memory available on this system. - // This works for both system-wide: - // /sys/devices/system/memory/memory* - // and for per-numa-node report: - // /sys/devices/system/node/node*/memory* - - sysMemory, err := filepath.Glob(filepath.Join(dir, "memory*")) + var total int64 + files, err := ioutil.ReadDir(dir) if err != nil { return -1, err - } else if sysMemory == nil { - return -1, fmt.Errorf("cannot find memory entries in %q", dir) } - - var total int64 - for _, path := range sysMemory { - s, err := ioutil.ReadFile(filepath.Join(path, "state")) + // There are many subdirectories of /sys/devices/system/memory or + // /sys/devices/system/node/nodeX that are named memory{cell} where {cell} + // is a 0-based index of the memory block. These subdirectories contain a + // state file (e.g. /sys/devices/system/memory/memory64/state that will + // contain the string "online" if that block is active. + for _, file := range files { + fname := file.Name() + // NOTE(jaypipes): we cannot rely on file.IsDir() here because the + // memory{cell} sysfs directories are not actual directories. + if !regexMemoryBlockDirname.MatchString(fname) { + continue + } + s, err := ioutil.ReadFile(filepath.Join(dir, fname, "state")) if err != nil { return -1, err } + // if the memory block state is 'online' we increment the total with + // the memory block size to determine the amount of physical + // memory available on this system. if strings.TrimSpace(string(s)) != "online" { continue } diff --git a/vendor/github.com/jaypipes/ghw/pkg/net/net.go b/vendor/github.com/jaypipes/ghw/pkg/net/net.go index 8994d112e..82d3226a1 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/net/net.go +++ b/vendor/github.com/jaypipes/ghw/pkg/net/net.go @@ -21,14 +21,30 @@ type NICCapability struct { } type NIC struct { - Name string `json:"name"` - MacAddress string `json:"mac_address"` - IsVirtual bool `json:"is_virtual"` - Capabilities []*NICCapability `json:"capabilities"` - PCIAddress *string `json:"pci_address,omitempty"` + Name string `json:"name"` + MacAddress string `json:"mac_address"` + IsVirtual bool `json:"is_virtual"` + Capabilities []*NICCapability `json:"capabilities"` + PCIAddress *string `json:"pci_address,omitempty"` + Speed string `json:"speed"` + Duplex string `json:"duplex"` + SupportedLinkModes []string `json:"supported_link_modes,omitempty"` + SupportedPorts []string `json:"supported_ports,omitempty"` + SupportedFECModes []string `json:"supported_fec_modes,omitempty"` + AdvertisedLinkModes []string `json:"advertised_link_modes,omitempty"` + AdvertisedFECModes []string `json:"advertised_fec_modes,omitempty"` // TODO(fromani): add other hw addresses (USB) when we support them } +func (nc *NICCapability) String() string { + return fmt.Sprintf( + "{Name:%s IsEnabled:%t CanEnable:%t}", + nc.Name, + nc.IsEnabled, + nc.CanEnable, + ) +} + func (n *NIC) String() string { isVirtualStr := "" if n.IsVirtual { diff --git a/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go b/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go index 1b338dfaf..cbdea3040 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go @@ -17,6 +17,7 @@ import ( "github.com/jaypipes/ghw/pkg/context" "github.com/jaypipes/ghw/pkg/linuxpath" + "github.com/jaypipes/ghw/pkg/util" ) const ( @@ -67,9 +68,11 @@ func nics(ctx *context.Context) []*NIC { mac := netDeviceMacAddress(paths, filename) nic.MacAddress = mac if etAvailable { - nic.Capabilities = netDeviceCapabilities(ctx, filename) + nic.netDeviceParseEthtool(ctx, filename) } else { nic.Capabilities = []*NICCapability{} + // Sets NIC struct fields from data in SysFs + nic.setNicAttrSysFs(paths, filename) } nic.PCIAddress = netDevicePCIAddress(paths.SysClassNet, filename) @@ -105,47 +108,72 @@ func ethtoolInstalled() bool { return err == nil } -func netDeviceCapabilities(ctx *context.Context, dev string) []*NICCapability { - caps := make([]*NICCapability, 0) - path, _ := exec.LookPath("ethtool") - cmd := exec.Command(path, "-k", dev) +func (n *NIC) netDeviceParseEthtool(ctx *context.Context, dev string) { var out bytes.Buffer + path, _ := exec.LookPath("ethtool") + + // Get auto-negotiation and pause-frame-use capabilities from "ethtool" (with no options) + // Populate Speed, Duplex, SupportedLinkModes, SupportedPorts, SupportedFECModes, + // AdvertisedLinkModes, and AdvertisedFECModes attributes from "ethtool" output. + cmd := exec.Command(path, dev) cmd.Stdout = &out err := cmd.Run() - if err != nil { - msg := fmt.Sprintf("could not grab NIC capabilities for %s: %s", dev, err) + if err == nil { + m := parseNicAttrEthtool(&out) + n.Capabilities = append(n.Capabilities, autoNegCap(m)) + n.Capabilities = append(n.Capabilities, pauseFrameUseCap(m)) + + // Update NIC Attributes with ethtool output + n.Speed = strings.Join(m["Speed"], "") + n.Duplex = strings.Join(m["Duplex"], "") + n.SupportedLinkModes = m["Supported link modes"] + n.SupportedPorts = m["Supported ports"] + n.SupportedFECModes = m["Supported FEC modes"] + n.AdvertisedLinkModes = m["Advertised link modes"] + n.AdvertisedFECModes = m["Advertised FEC modes"] + } else { + msg := fmt.Sprintf("could not grab NIC link info for %s: %s", dev, err) ctx.Warn(msg) - return caps } - // The out variable will now contain something that looks like the - // following. - // - // Features for enp58s0f1: - // rx-checksumming: on - // tx-checksumming: off - // tx-checksum-ipv4: off - // tx-checksum-ip-generic: off [fixed] - // tx-checksum-ipv6: off - // tx-checksum-fcoe-crc: off [fixed] - // tx-checksum-sctp: off [fixed] - // scatter-gather: off - // tx-scatter-gather: off - // tx-scatter-gather-fraglist: off [fixed] - // tcp-segmentation-offload: off - // tx-tcp-segmentation: off - // tx-tcp-ecn-segmentation: off [fixed] - // tx-tcp-mangleid-segmentation: off - // tx-tcp6-segmentation: off - // < snipped > - scanner := bufio.NewScanner(&out) - // Skip the first line... - scanner.Scan() - for scanner.Scan() { - line := strings.TrimPrefix(scanner.Text(), "\t") - caps = append(caps, netParseEthtoolFeature(line)) + // Get all other capabilities from "ethtool -k" + cmd = exec.Command(path, "-k", dev) + cmd.Stdout = &out + err = cmd.Run() + if err == nil { + // The out variable will now contain something that looks like the + // following. + // + // Features for enp58s0f1: + // rx-checksumming: on + // tx-checksumming: off + // tx-checksum-ipv4: off + // tx-checksum-ip-generic: off [fixed] + // tx-checksum-ipv6: off + // tx-checksum-fcoe-crc: off [fixed] + // tx-checksum-sctp: off [fixed] + // scatter-gather: off + // tx-scatter-gather: off + // tx-scatter-gather-fraglist: off [fixed] + // tcp-segmentation-offload: off + // tx-tcp-segmentation: off + // tx-tcp-ecn-segmentation: off [fixed] + // tx-tcp-mangleid-segmentation: off + // tx-tcp6-segmentation: off + // < snipped > + scanner := bufio.NewScanner(&out) + // Skip the first line... + scanner.Scan() + for scanner.Scan() { + line := strings.TrimPrefix(scanner.Text(), "\t") + n.Capabilities = append(n.Capabilities, netParseEthtoolFeature(line)) + } + + } else { + msg := fmt.Sprintf("could not grab NIC capabilities for %s: %s", dev, err) + ctx.Warn(msg) } - return caps + } // netParseEthtoolFeature parses a line from the ethtool -k output and returns @@ -220,3 +248,111 @@ func netDevicePCIAddress(netDevDir, netDevName string) *string { pciAddr := filepath.Base(devPath) return &pciAddr } + +func (nic *NIC) setNicAttrSysFs(paths *linuxpath.Paths, dev string) { + // Get speed and duplex from /sys/class/net/$DEVICE/ directory + nic.Speed = readFile(filepath.Join(paths.SysClassNet, dev, "speed")) + nic.Duplex = readFile(filepath.Join(paths.SysClassNet, dev, "duplex")) +} + +func readFile(path string) string { + contents, err := ioutil.ReadFile(path) + if err != nil { + return "" + } + return strings.TrimSpace(string(contents)) +} + +func autoNegCap(m map[string][]string) *NICCapability { + autoNegotiation := NICCapability{Name: "auto-negotiation", IsEnabled: false, CanEnable: false} + + an, anErr := util.ParseBool(strings.Join(m["Auto-negotiation"], "")) + aan, aanErr := util.ParseBool(strings.Join(m["Advertised auto-negotiation"], "")) + if an && aan && aanErr == nil && anErr == nil { + autoNegotiation.IsEnabled = true + } + + san, err := util.ParseBool(strings.Join(m["Supports auto-negotiation"], "")) + if san && err == nil { + autoNegotiation.CanEnable = true + } + + return &autoNegotiation +} + +func pauseFrameUseCap(m map[string][]string) *NICCapability { + pauseFrameUse := NICCapability{Name: "pause-frame-use", IsEnabled: false, CanEnable: false} + + apfu, err := util.ParseBool(strings.Join(m["Advertised pause frame use"], "")) + if apfu && err == nil { + pauseFrameUse.IsEnabled = true + } + + spfu, err := util.ParseBool(strings.Join(m["Supports pause frame use"], "")) + if spfu && err == nil { + pauseFrameUse.CanEnable = true + } + + return &pauseFrameUse +} + +func parseNicAttrEthtool(out *bytes.Buffer) map[string][]string { + // The out variable will now contain something that looks like the + // following. + // + //Settings for eth0: + // Supported ports: [ TP ] + // Supported link modes: 10baseT/Half 10baseT/Full + // 100baseT/Half 100baseT/Full + // 1000baseT/Full + // Supported pause frame use: No + // Supports auto-negotiation: Yes + // Supported FEC modes: Not reported + // Advertised link modes: 10baseT/Half 10baseT/Full + // 100baseT/Half 100baseT/Full + // 1000baseT/Full + // Advertised pause frame use: No + // Advertised auto-negotiation: Yes + // Advertised FEC modes: Not reported + // Speed: 1000Mb/s + // Duplex: Full + // Auto-negotiation: on + // Port: Twisted Pair + // PHYAD: 1 + // Transceiver: internal + // MDI-X: off (auto) + // Supports Wake-on: pumbg + // Wake-on: d + // Current message level: 0x00000007 (7) + // drv probe link + // Link detected: yes + + scanner := bufio.NewScanner(out) + // Skip the first line + scanner.Scan() + m := make(map[string][]string) + var name string + for scanner.Scan() { + var fields []string + if strings.Contains(scanner.Text(), ":") { + line := strings.Split(scanner.Text(), ":") + name = strings.TrimSpace(line[0]) + str := strings.Trim(strings.TrimSpace(line[1]), "[]") + switch str { + case + "Not reported", + "Unknown": + continue + } + fields = strings.Fields(str) + } else { + fields = strings.Fields(strings.Trim(strings.TrimSpace(scanner.Text()), "[]")) + } + + for _, f := range fields { + m[name] = append(m[name], strings.TrimSpace(f)) + } + } + + return m +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go b/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go index 485ac9bb0..087da33d2 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go @@ -160,9 +160,9 @@ func parseModaliasData(data string) *deviceModaliasInfo { productID := strings.ToLower(data[18:22]) subvendorID := strings.ToLower(data[28:32]) subproductID := strings.ToLower(data[38:42]) - classID := data[44:46] - subclassID := data[48:50] - progIfaceID := data[51:53] + classID := strings.ToLower(data[44:46]) + subclassID := strings.ToLower(data[48:50]) + progIfaceID := strings.ToLower(data[51:53]) return &deviceModaliasInfo{ vendorID: vendorID, productID: productID, diff --git a/vendor/github.com/jaypipes/ghw/pkg/util/util.go b/vendor/github.com/jaypipes/ghw/pkg/util/util.go index b72430e2c..5d57bda23 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/util/util.go +++ b/vendor/github.com/jaypipes/ghw/pkg/util/util.go @@ -57,3 +57,29 @@ func SafeIntFromFile(ctx *context.Context, path string) int { func ConcatStrings(items ...string) string { return strings.Join(items, "") } + +// Convert strings to bool using strconv.ParseBool() when recognized, otherwise +// use map lookup to convert strings like "Yes" "No" "On" "Off" to bool +// `ethtool` uses on, off, yes, no (upper and lower case) rather than true and +// false. +func ParseBool(str string) (bool, error) { + if b, err := strconv.ParseBool(str); err == nil { + return b, err + } else { + ExtraBools := map[string]bool{ + "on": true, + "off": false, + "yes": true, + "no": false, + // Return false instead of an error on empty strings + // For example from empty files in SysClassNet/Device + "": false, + } + if b, ok := ExtraBools[strings.ToLower(str)]; ok { + return b, nil + } else { + // Return strconv.ParseBool's error here + return b, err + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 966a96951..068431862 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -113,8 +113,8 @@ github.com/imdario/mergo # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap -# github.com/jaypipes/ghw v0.9.0 -## explicit; go 1.15 +# github.com/jaypipes/ghw v0.12.0 +## explicit; go 1.18 github.com/jaypipes/ghw github.com/jaypipes/ghw/pkg/baseboard github.com/jaypipes/ghw/pkg/bios