ghw - Golang HardWare discovery/inspection library

ghw - Golang HardWare discovery/inspection library Build Status

ghw mascot

ghw is a small Golang library providing hardware inspection and discovery for Linux and Windows. There currently exists partial support for MacOSX.

Design Principles

  • No root privileges needed for discovery

    ghw goes the extra mile to be useful without root priveleges. We query for host hardware information as directly as possible without relying on shellouts to programs like dmidecode that require root privileges to execute.

    Elevated privileges are indeed required to query for some information, but ghw will never error out if blocked from reading that information. Instead, ghw will print a warning message about the information that could not be retrieved. You may disable these warning messages with GHW_DISABLE_WARNINGS environment variable.

  • Well-documented code and plenty of example code

    The code itself should be well-documented with lots of usage examples.

  • Interfaces should be consistent across modules

    Each module in the library should be structured in a consistent fashion, and the structs returned by various library functions should have consistent attribute and method names.

Inspecting != Monitoring

ghw is a tool for gathering information about your hardware's capacity and capabilities.

It is important to point out that ghw does NOT report information that is temporary or variable. It is NOT a system monitor nor is it an appropriate tool for gathering data points for metrics that change over time. If you are looking for a system that tracks usage of CPU, memory, network I/O or disk I/O, there are plenty of great open source tools that do this! Check out the Prometheus project for a great example.

Usage

You can use the functions in ghw to determine various hardware-related information about the host computer:

Overriding the root mountpoint ghw uses

The default root mountpoint that ghw uses when looking for information about the host system is /. So, for example, when looking up CPU information on a Linux system, ghw.CPU() will use the path /proc/cpuinfo.

If you are calling ghw from a system that has an alternate root mountpoint, you can either set the GHW_CHROOT environment variable to that alternate path, or call the module constructor function with the ghw.WithChroot() modifier.

For example, if you are executing from within an application container that has bind-mounted the root host filesystem to the mount point /host, you would set GHW_CHROOT to /host so that ghw can find /proc/cpuinfo at /host/proc/cpuinfo.

Alternately, you can use the ghw.WithChroot() function like so:

cpu, err := ghw.CPU(ghw.WithChroot("/host"))

Consuming snapshots

You can make ghw read from snapshots (created with ghw-snapshot) using environment variables or programmatically. Please check SNAPSHOT.md to learn more about how ghw creates and consumes snapshots.

The environment variable GHW_SNAPSHOT_PATH let users specify a snapshot that ghw will automatically consume. All the needed chroot changes will be automatically performed. By default, the snapshot is unpacked on a temporary directory managed by ghw, and cleaned up when no longer needed, avoiding leftovers.

The rest of the environment variables are relevant iff GHW_SNAPSHOT_PATH is given. GHW_SNAPSHOT_ROOT let users specify the directory on which the snapshot should be unpacked. This moves the ownership of that directory from ghw to users. For this reason, ghw will not clean up automatically the content unpacked in GHW_SNAPSHOT_ROOT.

GHW_SNAPSHOT_EXCLUSIVE is relevant iff GHW_SNAPSHOT_ROOT is given. Set it to any value to toggle it on. This tells ghw that the directory is meant only to contain the given snapshot, thus ghw will not attempt to unpack it (and will go ahead silently!) unless the directory is empty. You can use both GHW_SNAPSHOT_ROOT and GHW_SNAPSHOT_EXCLUSIVE to make sure ghw unpacks the snapshot only once regardless of how many ghw packages (e.g. cpu, memory) access it.

Set GHW_SNAPSHOT_PRESERVE to any value to enable it. If set, ghw will not clean up the unpacked snapshot once done, leaving it into the system.

cpu, err := ghw.CPU(ghw.WithSnapshot(ghw.SnapshotOptions{
	Path: "/path/to/linux-amd64-d4771ed3300339bc75f856be09fc6430.tar.gz",
}))


myRoot := "/my/safe/directory"
cpu, err := ghw.CPU(ghw.WithSnapshot(ghw.SnapshotOptions{
	Path: "/path/to/linux-amd64-d4771ed3300339bc75f856be09fc6430.tar.gz",
	Root: &myRoot,
}))

myOtherRoot := "/my/other/safe/directory"
cpu, err := ghw.CPU(ghw.WithSnapshot(ghw.SnapshotOptions{
	Path:      "/path/to/linux-amd64-d4771ed3300339bc75f856be09fc6430.tar.gz",
	Root:      &myOtherRoot,
	Exclusive: true,
}))

Creating snapshots

You can create ghw snapshots in two ways. You can just consume the ghw-snapshot tool, or you can create them programmatically from your golang code. We explore now the latter case.

Snapshotting takes two phases:

  1. clone the relevant pseudofiles/pseudodirectories into a temporary tree This tree is usually deleted once the packing is successful.
  2. pack the cloned tree into a tar.gz
import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/jaypipes/ghw/pkg/snapshot"
)

// ...

scratchDir, err := ioutil.TempDir("", "ghw-snapshot-*")
if err != nil {
	fmt.Printf("Error creating clone directory: %v", err)
}
defer os.RemoveAll(scratchDir)

// this step clones all the files and directories ghw cares about
if err := snapshot.CloneTreeInto(scratchDir); err != nil {
	fmt.Printf("error cloning into %q: %v", scratchDir, err)
}

// optionally, you may add extra content into your snapshot.
// ghw will ignore the extra content.
// Glob patterns like `filepath.Glob` are supported.
fileSpecs := []string{
	"/proc/cmdline",
}

// options allows the client code to optionally deference symlinks, or copy
// them into the cloned tree as symlinks
var opts *snapshot.CopyFileOptions
if err := snapshot.CopyFilesInto(fileSpecs, scratchDir, opts); err != nil {
	fmt.Printf("error cloning extra files into %q: %v", scratchDir, err)
}

// automates the creation of the gzipped tarball out of the given tree.
if err := snapshot.PackFrom("my-snapshot.tgz", scratchDir); err != nil {
	fmt.Printf("error packing %q into %q: %v", scratchDir, *output, err)
}

Disabling warning messages

When ghw isn't able to retrieve some information, it may print certain warning messages to stderr. To disable these warnings, simply set the GHW_DISABLE_WARNINGS environs variable:

$ ghwc memory
WARNING:
Could not determine total physical bytes of memory. This may
be due to the host being a virtual machine or container with no
/var/log/syslog file, or the current user may not have necessary
privileges to read the syslog. We are falling back to setting the
total physical amount of memory to the total usable amount of memory
memory (24GB physical, 24GB usable)
$ GHW_DISABLE_WARNINGS=1 ghwc memory
memory (24GB physical, 24GB usable)

You can disable warning programmatically using the WithDisableWarnings option:

import (
	"github.com/jaypipes/ghw"
)

mem, err := ghw.Memory(ghw.WithDisableWarnings())

WithDisableWarnings is a alias for the WithNullAlerter option, which in turn leverages the more general Alerter feature of ghw.

You may supply a Alerter to ghw to redirect all the warnings there, like logger objects (see for example golang's stdlib log.Logger). Alerter is in fact the minimal logging interface ghw needs. To learn more, please check the option.Alerterinterface and theghw.WithAlerter()` function.

Memory

Information about the host computer's memory can be retrieved using the ghw.Memory() function which returns a pointer to a ghw.MemoryInfo struct.

The ghw.MemoryInfo struct contains three fields:

  • ghw.MemoryInfo.TotalPhysicalBytes contains the amount of physical memory on the host
  • ghw.MemoryInfo.TotalUsableBytes contains the amount of memory the system can actually use. Usable memory accounts for things like the kernel's resident memory size and some reserved system bits
  • ghw.MemoryInfo.SupportedPageSizes is an array of integers representing the size, in bytes, of memory pages the system supports
  • ghw.MemoryInfo.Modules is an array of pointers to ghw.MemoryModule structs, one for each physical DIMM. Currently, this information is only included on Windows, with Linux support planned.
package main

import (
	"fmt"

	"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())
}

Example output from my personal workstation:

memory (24GB physical, 24GB usable)

Physical versus Usable Memory

There has been some confusion regarding the difference between the total physical bytes versus total usable bytes of memory.

Some of this confusion has been due to a misunderstanding of the term "usable". As mentioned above, ghw does inspection of the system's capacity.

A host computer has two capacities when it comes to RAM. The first capacity is the amount of RAM that is contained in all memory banks (DIMMs) that are attached to the motherboard. ghw.MemoryInfo.TotalPhysicalBytes refers to this first capacity.

There is a (usually small) amount of RAM that is consumed by the bootloader before the operating system is started (booted). Once the bootloader has booted the operating system, the amount of RAM that may be used by the operating system and its applications is fixed. ghw.MemoryInfo.TotalUsableBytes refers to this second capacity.

You can determine the amount of RAM that the bootloader used (that is not made available to the operating system) by subtracting ghw.MemoryInfo.TotalUsableBytes from ghw.MemoryInfo.TotalPhysicalBytes:

package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	memory, err := ghw.Memory()
	if err != nil {
		fmt.Printf("Error getting memory info: %v", err)
	}

        phys := memory.TotalPhysicalBytes
        usable := memory.TotalUsableBytes

	fmt.Printf("The bootloader consumes %d bytes of RAM\n", phys - usable)
}

Example output from my personal workstation booted into a Windows10 operating system with a Linux GRUB bootloader:

The bootloader consumes 3832720 bytes of RAM

CPU

The ghw.CPU() function returns a ghw.CPUInfo struct that contains information about the CPUs on the host system.

ghw.CPUInfo contains the following fields:

  • ghw.CPUInfo.TotalCores has the total number of physical cores the host system contains
  • ghw.CPUInfo.TotalThreads has the total number of hardware threads the host system contains
  • ghw.CPUInfo.Processors is an array of ghw.Processor structs, one for each physical processor package contained in the host

Each ghw.Processor struct contains a number of fields:

  • ghw.Processor.ID is the physical processor uint32 ID according to the system
  • ghw.Processor.NumCores is the number of physical cores in the processor package
  • ghw.Processor.NumThreads is the number of hardware threads in the processor package
  • ghw.Processor.Vendor is a string containing the vendor name
  • ghw.Processor.Model is a string containing the vendor's model name
  • ghw.Processor.Capabilities is an array of strings indicating the features the processor has enabled
  • ghw.Processor.Cores is an array of ghw.ProcessorCore structs that are packed onto this physical processor

A ghw.ProcessorCore has the following fields:

  • ghw.ProcessorCore.ID is the uint32 identifier that the host gave this 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
package main

import (
	"fmt"
	"math"
	"strings"

	"github.com/jaypipes/ghw"
)

func main() {
	cpu, err := ghw.CPU()
	if err != nil {
		fmt.Printf("Error getting CPU info: %v", err)
	}

	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)
				}
			}
		}
	}
}

Example output from my personal workstation:

cpu (1 physical package, 6 cores, 12 hardware threads)
 physical package #0 (6 cores, 12 hardware threads)
  processor core #0 (2 threads), logical processors [0 6]
  processor core #1 (2 threads), logical processors [1 7]
  processor core #2 (2 threads), logical processors [2 8]
  processor core #3 (2 threads), logical processors [3 9]
  processor core #4 (2 threads), logical processors [4 10]
  processor core #5 (2 threads), logical processors [5 11]
  capabilities: [msr pae mce cx8 apic sep
                 mtrr pge mca cmov pat pse36
                 clflush dts acpi mmx fxsr sse
                 sse2 ss ht tm pbe syscall
                 nx pdpe1gb rdtscp lm constant_tsc arch_perfmon
                 pebs bts rep_good nopl xtopology nonstop_tsc
                 cpuid aperfmperf pni pclmulqdq dtes64 monitor
                 ds_cpl vmx est tm2 ssse3 cx16
                 xtpr pdcm pcid sse4_1 sse4_2 popcnt
                 aes lahf_lm pti retpoline tpr_shadow vnmi
                 flexpriority ept vpid dtherm ida arat]

Block storage

Information about the host computer's local block storage is returned from the ghw.Block() function. This function returns a pointer to a ghw.BlockInfo struct.

The ghw.BlockInfo struct contains two fields:

  • ghw.BlockInfo.TotalPhysicalBytes contains the amount of physical block storage on the host
  • ghw.BlockInfo.Disks is an array of pointers to ghw.Disk structs, one for each disk drive found by the system

Each ghw.Disk struct contains the following fields:

  • ghw.Disk.Name contains a string with the short name of the disk, e.g. "sda"
  • ghw.Disk.SizeBytes contains the amount of storage the disk provides
  • ghw.Disk.PhysicalBlockSizeBytes contains the size of the physical blocks used on the disk, in bytes
  • ghw.Disk.IsRemovable contains a boolean indicating if the disk drive is removable
  • ghw.Disk.DriveType is the type of drive. It is of type ghw.DriveType which has a ghw.DriveType.String() method that can be called to return a string representation of the bus. This string will be "HDD", "FDD", "ODD", or "SSD", which correspond to a hard disk drive (rotational), floppy drive, optical (CD/DVD) drive and solid-state drive.
  • ghw.Disk.StorageController is the type of storage controller/drive. It is of type ghw.StorageController which has a ghw.StorageController.String() method that can be called to return a string representation of the bus. This string will be "SCSI", "IDE", "virtio", "MMC", or "NVMe"
  • ghw.Disk.NUMANodeID is the numeric index of the NUMA node this disk is local to, or -1
  • ghw.Disk.Vendor contains a string with the name of the hardware vendor for the disk drive
  • ghw.Disk.Model contains a string with the vendor-assigned disk model name
  • ghw.Disk.SerialNumber contains a string with the disk's serial number
  • ghw.Disk.WWN contains a string with the disk's World Wide Name
  • ghw.Disk.Partitions contains an array of pointers to ghw.Partition structs, one for each partition on the disk

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.SizeBytes contains the amount of storage the partition provides
  • ghw.Partition.MountPoint contains a string with the partition's mount point, or "" if no mount point was discovered
  • ghw.Partition.Type contains a string indicated the filesystem type for the partition, or "" if the system could not determine the type
  • ghw.Partition.IsReadOnly is a bool indicating the partition is read-only
  • 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.
package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	block, err := ghw.Block()
	if err != nil {
		fmt.Printf("Error getting block storage info: %v", err)
	}

	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)
		}
	}
}

Example output from my personal workstation:

block storage (1 disk, 2TB physical storage)
 sda HDD (2TB) SCSI [@pci-0000:04:00.0-scsi-0:1:0:0 (node #0)] vendor=LSI model=Logical_Volume serial=600508e000000000f8253aac9a1abd0c WWN=0x600508e000000000f8253aac9a1abd0c
  /dev/sda1 (100MB)
  /dev/sda2 (187GB)
  /dev/sda3 (449MB)
  /dev/sda4 (1KB)
  /dev/sda5 (15GB)
  /dev/sda6 (2TB) [ext4] mounted@/

Note that ghw looks in the udev runtime database for some information. If you are using ghw in a container, remember to bind mount /dev/disk and /run into your container, otherwise ghw won't be able to query the udev DB or sysfs paths for information.

Topology

NOTE: Topology support is currently Linux-only. Windows support is planned.

Information about the host computer's architecture (NUMA vs. SMP), the host's node layout and processor caches can be retrieved from the ghw.Topology() function. This function returns a pointer to a ghw.TopologyInfo struct.

The ghw.TopologyInfo struct contains two fields:

  • ghw.TopologyInfo.Architecture contains an enum with the value ghw.NUMA or ghw.SMP depending on what the topology of the system is
  • ghw.TopologyInfo.Nodes is an array of pointers to ghw.TopologyNode structs, one for each topology node (typically physical processor package) found by the system

Each ghw.TopologyNode struct contains the following fields:

  • ghw.TopologyNode.ID is the system's uint32 identifier for the node
  • ghw.TopologyNode.Cores is an array of pointers to ghw.ProcessorCore structs that are contained in this node
  • ghw.TopologyNode.Caches is an array of pointers to ghw.MemoryCache structs that represent the low-level caches associated with processors and cores on the system
  • ghw.TopologyNode.Distance is an array of distances between NUMA nodes as reported by the system.

See above in the CPU section for information about the ghw.ProcessorCore struct and how to use and query it.

Each ghw.MemoryCache struct contains the following fields:

  • ghw.MemoryCache.Type is an enum that contains one of ghw.DATA, ghw.INSTRUCTION or ghw.UNIFIED depending on whether the cache stores CPU instructions, program data, or both
  • ghw.MemoryCache.Level is a positive integer indicating how close the cache is to the processor
  • ghw.MemoryCache.SizeBytes is an integer containing the number of bytes the cache can contain
  • ghw.MemoryCache.LogicalProcessors is an array of integers representing the logical processors that use the cache
package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	topology, err := ghw.Topology()
	if err != nil {
		fmt.Printf("Error getting topology info: %v", err)
	}

	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)
		}
	}
}

Example output from my personal workstation:

topology SMP (1 nodes)
 node #0 (6 cores)
  L1i cache (32 KB) shared with logical processors: 3,9
  L1i cache (32 KB) shared with logical processors: 2,8
  L1i cache (32 KB) shared with logical processors: 11,5
  L1i cache (32 KB) shared with logical processors: 10,4
  L1i cache (32 KB) shared with logical processors: 0,6
  L1i cache (32 KB) shared with logical processors: 1,7
  L1d cache (32 KB) shared with logical processors: 11,5
  L1d cache (32 KB) shared with logical processors: 10,4
  L1d cache (32 KB) shared with logical processors: 3,9
  L1d cache (32 KB) shared with logical processors: 1,7
  L1d cache (32 KB) shared with logical processors: 0,6
  L1d cache (32 KB) shared with logical processors: 2,8
  L2 cache (256 KB) shared with logical processors: 2,8
  L2 cache (256 KB) shared with logical processors: 3,9
  L2 cache (256 KB) shared with logical processors: 0,6
  L2 cache (256 KB) shared with logical processors: 10,4
  L2 cache (256 KB) shared with logical processors: 1,7
  L2 cache (256 KB) shared with logical processors: 11,5
  L3 cache (12288 KB) shared with logical processors: 0,1,10,11,2,3,4,5,6,7,8,9

Network

Information about the host computer's networking hardware is returned from the ghw.Network() function. This function returns a pointer to a ghw.NetworkInfo struct.

The ghw.NetworkInfo struct contains one field:

  • ghw.NetworkInfo.NICs is an array of pointers to ghw.NIC structs, one for each network interface controller found for the systen

Each ghw.NIC struct contains the following fields:

  • ghw.NIC.Name is the system's identifier for the NIC
  • ghw.NIC.MacAddress is the MAC address for the NIC, if any
  • ghw.NIC.IsVirtual is a boolean indicating if the NIC is a virtualized 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
  • 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.

The ghw.NICCapability struct contains the following fields:

  • ghw.NICCapability.Name is the string name of the capability (e.g. "tcp-segmentation-offload")
  • ghw.NICCapability.IsEnabled is a boolean indicating whether the capability is currently enabled/active on the NIC
  • ghw.NICCapability.CanEnable is a boolean indicating whether the capability may be enabled
package main

import (
    "fmt"

    "github.com/jaypipes/ghw"
)

func main() {
    net, err := ghw.Network()
    if err != nil {
        fmt.Printf("Error getting network info: %v", err)
    }

    fmt.Printf("%v\n", net)

    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)
            }
        }
        if len(enabledCaps) > 0 {
            fmt.Printf("  enabled capabilities:\n")
            for _, x := range enabledCaps {
                fmt.Printf("   - %s\n", nic.Capabilities[x].Name)
            }
        }
    }
}

Example output from my personal laptop:

net (3 NICs)
 docker0
  enabled capabilities:
   - tx-checksumming
   - tx-checksum-ip-generic
   - scatter-gather
   - tx-scatter-gather
   - tx-scatter-gather-fraglist
   - tcp-segmentation-offload
   - tx-tcp-segmentation
   - tx-tcp-ecn-segmentation
   - tx-tcp-mangleid-segmentation
   - tx-tcp6-segmentation
   - udp-fragmentation-offload
   - generic-segmentation-offload
   - generic-receive-offload
   - tx-vlan-offload
   - highdma
   - tx-lockless
   - netns-local
   - tx-gso-robust
   - tx-fcoe-segmentation
   - tx-gre-segmentation
   - tx-gre-csum-segmentation
   - tx-ipxip4-segmentation
   - tx-ipxip6-segmentation
   - tx-udp_tnl-segmentation
   - tx-udp_tnl-csum-segmentation
   - tx-gso-partial
   - tx-sctp-segmentation
   - tx-esp-segmentation
   - tx-vlan-stag-hw-insert
 enp58s0f1
  enabled capabilities:
   - rx-checksumming
   - generic-receive-offload
   - rx-vlan-offload
   - tx-vlan-offload
   - highdma
 wlp59s0
  enabled capabilities:
   - scatter-gather
   - tx-scatter-gather
   - generic-segmentation-offload
   - generic-receive-offload
   - highdma
   - netns-local

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.

NOTE: Parsing of the PCI-IDS file database is provided by the separate github.com/jaypipes/pcidb library. You can read that library's README for more information about the various structs that are exposed on the ghw.PCIInfo struct.

The ghw.PCI() function returns a ghw.PCIInfo struct. The ghw.PCIInfo struct contains a number of fields that may be queried for PCI information:

  • ghw.PCIInfo.Devices is a slice of pointers to ghw.PCIDevice structs that describe the PCI devices on the host system
  • ghw.PCIInfo.Classes is a map, keyed by the PCI class ID (a hex-encoded string) of pointers to pcidb.Class structs, one for each class of PCI device known to ghw (DEPRECATED, will be removed in ghw v1.0. Use the github.com/jaypipes/pcidb library for exploring PCI database information)
  • ghw.PCIInfo.Vendors is a map, keyed by the PCI vendor ID (a hex-encoded string) of pointers to pcidb.Vendor structs, one for each PCI vendor known to ghw (DEPRECATED, will be removed in ghw v1.0. Use the github.com/jaypipes/pcidb library for exploring PCI database information)
  • ghw.PCIInfo.Products is a map, keyed by the PCI product ID (a hex-encoded string) of pointers to pcidb.Product structs, one for each PCI product known to ghw (DEPRECATED, will be removed in ghw v1.0. Use the github.com/jaypipes/pcidb library for exploring PCI database information)

NOTE: PCI products are often referred to by their "device ID". We use the term "product ID" in ghw because it more accurately reflects what the identifier is for: a specific product line produced by the vendor.

The ghw.PCIDevice struct has the following fields:

  • ghw.PCIDevice.Vendor is a pointer to a pcidb.Vendor struct that describes the device's primary vendor. This will always be non-nil.
  • ghw.PCIDevice.Product is a pointer to a pcidb.Product struct that describes the device's primary product. This will always be non-nil.
  • ghw.PCIDevice.Subsystem is a pointer to a pcidb.Product struct that describes the device's secondary/sub-product. This will always be non-nil.
  • ghw.PCIDevice.Class is a pointer to a pcidb.Class struct that describes the device's class. This will always be non-nil.
  • ghw.PCIDevice.Subclass is a pointer to a pcidb.Subclass struct that describes the device's subclass. This will always be non-nil.
  • ghw.PCIDevice.ProgrammingInterface is a pointer to a pcidb.ProgrammingInterface struct that describes the device subclass' programming interface. This will always be non-nil.

Finding a PCI device by PCI address

In addition to the above information, the ghw.PCIInfo struct has the following method:

  • ghw.PCIInfo.GetDevice(address string)

The following code snippet shows how to call the ghw.PCIInfo.ListDevices() method and output a simple list of PCI address and vendor/product information:

package main

import (
	"fmt"

	"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("====================================================")

	for _, device := range pci.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)
	}
}

on my local workstation the output of the above looks like the following:

host PCI devices:
====================================================
0000:00:00.0	Intel Corporation   	5520/5500/X58 I/O Hub to ESI Port
0000:00:01.0	Intel Corporation   	5520/5500/X58 I/O Hub PCI Express Roo...
0000:00:02.0	Intel Corporation   	5520/5500/X58 I/O Hub PCI Express Roo...
0000:00:03.0	Intel Corporation   	5520/5500/X58 I/O Hub PCI Express Roo...
0000:00:07.0	Intel Corporation   	5520/5500/X58 I/O Hub PCI Express Roo...
0000:00:10.0	Intel Corporation   	7500/5520/5500/X58 Physical and Link ...
0000:00:10.1	Intel Corporation   	7500/5520/5500/X58 Routing and Protoc...
0000:00:14.0	Intel Corporation   	7500/5520/5500/X58 I/O Hub System Man...
0000:00:14.1	Intel Corporation   	7500/5520/5500/X58 I/O Hub GPIO and S...
0000:00:14.2	Intel Corporation   	7500/5520/5500/X58 I/O Hub Control St...
0000:00:14.3	Intel Corporation   	7500/5520/5500/X58 I/O Hub Throttle R...
0000:00:19.0	Intel Corporation   	82567LF-2 Gigabit Network Connection
0000:00:1a.0	Intel Corporation   	82801JI (ICH10 Family) USB UHCI Contr...
0000:00:1a.1	Intel Corporation   	82801JI (ICH10 Family) USB UHCI Contr...
0000:00:1a.2	Intel Corporation   	82801JI (ICH10 Family) USB UHCI Contr...
0000:00:1a.7	Intel Corporation   	82801JI (ICH10 Family) USB2 EHCI Cont...
0000:00:1b.0	Intel Corporation   	82801JI (ICH10 Family) HD Audio Contr...
0000:00:1c.0	Intel Corporation   	82801JI (ICH10 Family) PCI Express Ro...
0000:00:1c.1	Intel Corporation   	82801JI (ICH10 Family) PCI Express Po...
0000:00:1c.4	Intel Corporation   	82801JI (ICH10 Family) PCI Express Ro...
0000:00:1d.0	Intel Corporation   	82801JI (ICH10 Family) USB UHCI Contr...
0000:00:1d.1	Intel Corporation   	82801JI (ICH10 Family) USB UHCI Contr...
0000:00:1d.2	Intel Corporation   	82801JI (ICH10 Family) USB UHCI Contr...
0000:00:1d.7	Intel Corporation   	82801JI (ICH10 Family) USB2 EHCI Cont...
0000:00:1e.0	Intel Corporation   	82801 PCI Bridge
0000:00:1f.0	Intel Corporation   	82801JIR (ICH10R) LPC Interface Contr...
0000:00:1f.2	Intel Corporation   	82801JI (ICH10 Family) SATA AHCI Cont...
0000:00:1f.3	Intel Corporation   	82801JI (ICH10 Family) SMBus Controller
0000:01:00.0	NEC Corporation     	uPD720200 USB 3.0 Host Controller
0000:02:00.0	Marvell Technolog...	88SE9123 PCIe SATA 6.0 Gb/s controller
0000:02:00.1	Marvell Technolog...	88SE912x IDE Controller
0000:03:00.0	NVIDIA Corporation  	GP107 [GeForce GTX 1050 Ti]
0000:03:00.1	NVIDIA Corporation  	UNKNOWN
0000:04:00.0	LSI Logic / Symbi...	SAS2004 PCI-Express Fusion-MPT SAS-2 ...
0000:06:00.0	Qualcomm Atheros    	AR5418 Wireless Network Adapter [AR50...
0000:08:03.0	LSI Corporation     	FW322/323 [TrueFire] 1394a Controller
0000:3f:00.0	Intel Corporation   	UNKNOWN
0000:3f:00.1	Intel Corporation   	Xeon 5600 Series QuickPath Architectu...
0000:3f:02.0	Intel Corporation   	Xeon 5600 Series QPI Link 0
0000:3f:02.1	Intel Corporation   	Xeon 5600 Series QPI Physical 0
0000:3f:02.2	Intel Corporation   	Xeon 5600 Series Mirror Port Link 0
0000:3f:02.3	Intel Corporation   	Xeon 5600 Series Mirror Port Link 1
0000:3f:03.0	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:03.1	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:03.4	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:04.0	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:04.1	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:04.2	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:04.3	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:05.0	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:05.1	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:05.2	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:05.3	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:06.0	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:06.1	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:06.2	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...
0000:3f:06.3	Intel Corporation   	Xeon 5600 Series Integrated Memory Co...

The following code snippet shows how to call the ghw.PCIInfo.GetDevice() method and use its returned ghw.PCIDevice struct pointer:

package main

import (
	"fmt"
	"os"

	"github.com/jaypipes/ghw"
)

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
	}

	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)
}

Here's a sample output from my local workstation:

PCI device information for 0000:03:00.0
====================================================
Vendor: NVIDIA Corporation [10de]
Product: GP107 [GeForce GTX 1050 Ti] [1c82]
Subsystem: UNKNOWN [8613] (Subvendor: ASUSTeK Computer Inc.)
Class: Display controller [03]
Subclass: VGA compatible controller [00]
Programming Interface: VGA controller [00]

GPU

Information about the host computer's graphics hardware is returned from the ghw.GPU() function. This function returns a pointer to a ghw.GPUInfo struct.

The ghw.GPUInfo struct contains one field:

  • ghw.GPUInfo.GraphicCards is an array of pointers to ghw.GraphicsCard structs, one for each graphics card found for the systen

Each ghw.GraphicsCard struct contains the following fields:

  • ghw.GraphicsCard.Index is the system's numeric zero-based index for the card on the bus
  • ghw.GraphicsCard.Address is the PCI address for the graphics card
  • ghw.GraphicsCard.DeviceInfo is a pointer to a ghw.PCIDevice struct describing the graphics card. This may be nil if no PCI device information could be determined for the card.
  • ghw.GraphicsCard.Node is an pointer to a ghw.TopologyNode struct that the GPU/graphics card is affined to. On non-NUMA systems, this will always be nil.
package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	gpu, err := ghw.GPU()
	if err != nil {
		fmt.Printf("Error getting GPU info: %v", err)
	}

	fmt.Printf("%v\n", gpu)

	for _, card := range gpu.GraphicsCards {
		fmt.Printf(" %v\n", card)
	}
}

Example output from my personal workstation:

gpu (1 graphics card)
 card #0 @0000:03:00.0 -> class: 'Display controller' vendor: 'NVIDIA Corporation' product: 'GP107 [GeForce GTX 1050 Ti]'

NOTE: You can read more about the fields of the ghw.PCIDevice struct if you'd like to dig deeper into PCI subsystem and programming interface information

NOTE: You can read more about the fields of the ghw.TopologyNode struct if you'd like to dig deeper into the NUMA/topology subsystem

Chassis

The host's chassis information is accessible with the ghw.Chassis() function. This function returns a pointer to a ghw.ChassisInfo struct.

The ghw.ChassisInfo struct contains multiple fields:

  • ghw.ChassisInfo.AssetTag is a string with the chassis asset tag
  • ghw.ChassisInfo.SerialNumber is a string with the chassis serial number
  • ghw.ChassisInfo.Type is a string with the chassis type code
  • ghw.ChassisInfo.TypeDescription is a string with a description of the chassis type
  • ghw.ChassisInfo.Vendor is a string with the chassis vendor
  • ghw.ChassisInfo.Version is a string with the chassis version

NOTE: These fields are often missing for non-server hardware. Don't be surprised to see empty string or "None" values.

package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	chassis, err := ghw.Chassis()
	if err != nil {
		fmt.Printf("Error getting chassis info: %v", err)
	}

	fmt.Printf("%v\n", chassis)
}

Example output from my personal workstation:

chassis type=Desktop vendor=System76 version=thelio-r1

NOTE: Some of the values such as serial numbers are shown as unknown because the Linux kernel by default disallows access to those fields if you're not running as root. They will be populated if it runs as root or otherwise you may see warnings like the following:

WARNING: Unable to read chassis_serial: open /sys/class/dmi/id/chassis_serial: permission denied

You can ignore them or use the Disabling warning messages feature to quiet things down.

BIOS

The host's basis input/output system (BIOS) information is accessible with the ghw.BIOS() function. This function returns a pointer to a ghw.BIOSInfo struct.

The ghw.BIOSInfo struct contains multiple fields:

  • ghw.BIOSInfo.Vendor is a string with the BIOS vendor
  • ghw.BIOSInfo.Version is a string with the BIOS version
  • ghw.BIOSInfo.Date is a string with the date the BIOS was flashed/created
package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	bios, err := ghw.BIOS()
	if err != nil {
		fmt.Printf("Error getting BIOS info: %v", err)
	}

	fmt.Printf("%v\n", bios)
}

Example output from my personal workstation:

bios vendor=System76 version=F2 Z5 date=11/14/2018

Baseboard

The host's baseboard information is accessible with the ghw.Baseboard() function. This function returns a pointer to a ghw.BaseboardInfo struct.

The ghw.BaseboardInfo struct contains multiple fields:

  • ghw.BaseboardInfo.AssetTag is a string with the baseboard asset tag
  • ghw.BaseboardInfo.SerialNumber is a string with the baseboard serial number
  • ghw.BaseboardInfo.Vendor is a string with the baseboard vendor
  • ghw.BaseboardInfo.Version is a string with the baseboard version

NOTE: These fields are often missing for non-server hardware. Don't be surprised to see empty string or "None" values.

package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	baseboard, err := ghw.Baseboard()
	if err != nil {
		fmt.Printf("Error getting baseboard info: %v", err)
	}

	fmt.Printf("%v\n", baseboard)
}

Example output from my personal workstation:

baseboard vendor=System76 version=thelio-r1

NOTE: Some of the values such as serial numbers are shown as unknown because the Linux kernel by default disallows access to those fields if you're not running as root. They will be populated if it runs as root or otherwise you may see warnings like the following:

WARNING: Unable to read board_serial: open /sys/class/dmi/id/board_serial: permission denied

You can ignore them or use the Disabling warning messages feature to quiet things down.

Product

The host's product information is accessible with the ghw.Product() function. This function returns a pointer to a ghw.ProductInfo struct.

The ghw.ProductInfo struct contains multiple fields:

  • ghw.ProductInfo.Family is a string describing the product family
  • ghw.ProductInfo.Name is a string with the product name
  • ghw.ProductInfo.SerialNumber is a string with the product serial number
  • ghw.ProductInfo.UUID is a string with the product UUID
  • ghw.ProductInfo.SKU is a string with the product stock unit identifier (SKU)
  • ghw.ProductInfo.Vendor is a string with the product vendor
  • ghw.ProductInfo.Version is a string with the product version

NOTE: These fields are often missing for non-server hardware. Don't be surprised to see empty string, "Default string" or "None" values.

package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	product, err := ghw.Product()
	if err != nil {
		fmt.Printf("Error getting product info: %v", err)
	}

	fmt.Printf("%v\n", product)
}

Example output from my personal workstation:

product family=Default string name=Thelio vendor=System76 sku=Default string version=thelio-r1

NOTE: Some of the values such as serial numbers are shown as unknown because the Linux kernel by default disallows access to those fields if you're not running as root. They will be populated if it runs as root or otherwise you may see warnings like the following:

WARNING: Unable to read product_serial: open /sys/class/dmi/id/product_serial: permission denied

You can ignore them or use the Disabling warning messages feature to quiet things down.

Serialization

All of the ghw XXXInfo structs -- e.g. ghw.CPUInfo -- have two methods for producing a serialized JSON or YAML string representation of the contained information:

  • JSONString() returns a string containing the information serialized into JSON. It accepts a single boolean parameter indicating whether to use indentation when outputting the string
  • YAMLString() returns a string containing the information serialized into YAML
package main

import (
	"fmt"

	"github.com/jaypipes/ghw"
)

func main() {
	mem, err := ghw.Memory()
	if err != nil {
		fmt.Printf("Error getting memory info: %v", err)
	}

	fmt.Printf("%s", mem.YAMLString())
}

the above example code prints the following out on my local workstation:

memory:
  supported_page_sizes:
  - 1073741824
  - 2097152
  total_physical_bytes: 25263415296
  total_usable_bytes: 25263415296

Calling external programs

By default ghw may call external programs, for example ethtool, to learn about hardware capabilities. In some rare circumstances it may be useful to opt out from this behaviour and rely only on the data provided by pseudo-filesystems, like sysfs. The most common use case is when we want to consume a snapshot from ghw. In these cases the information provided by tools will be most likely inconsistent with the data from the snapshot - they will run on a different host! To prevent ghw from calling external tools, set the environs variable GHW_DISABLE_TOOLS to any value, or, programmatically, check the WithDisableTools function. The default behaviour of ghw is to call external tools when available.

Developers

Contributions to ghw are welcomed! Fork the repo on GitHub and submit a pull request with your proposed changes. Or, feel free to log an issue for a feature request or bug report.

Running tests

You can run unit tests easily using the make test command, like so:

[jaypipes@uberbox ghw]$ make test
go test github.com/jaypipes/ghw github.com/jaypipes/ghw/cmd/ghwc
ok      github.com/jaypipes/ghw 0.084s
?       github.com/jaypipes/ghw/cmd/ghwc    [no test files]
Owner
Jay Pipes
Principal Engineer at AWS working on open source projects in the Kubernetes and containers ecosystem.
Jay Pipes
Comments
  • Issue while fetching bios info in windows 10.

    Issue while fetching bios info in windows 10.

    Metricbeat failing to fetch bios info. Getting the following error while tried to fetch those.

    020-05-18T20:53:53.604+0530 DEBUG [module] module/wrapper.go:154 Starting metricSetWrapper[module=system, name=asset, host=] Error getting BIOS info: wmi: cannot load field "InstallDate" into a "string": unsupported type ()2020-05-18T20:53:53.946+0530 DEBUG [module] runtime/panic.go:969 Stopped metricSetWrapper[module=system, name=asset, host=] 2020-05-18T20:53:53.946+0530 ERROR runtime/signal_windows.go:246 recovered from panic while fetching 'system/asset' for host ''. Recovering, but please report this: runtime error: invalid memory address or nil pointer dereference. {"stack": "github.com/elastic/beats/libbeat/logp.Recover\n\tG:/go/src/github.com/elastic/beats/libbeat/logp/global.go:88\nruntime.gopanic\n\tc:/go/src/runtime/panic.go:969\nruntime.panicmem\n\tc:/go/src/runtime/panic.go:212\nruntime.sigpanic\n\tc:/go/src/runtime/signal_windows.go:246\ngithub.com/elastic/beats/metricbeat/module/system/asset.ProdInfo\n\tG:/go/src/github.com/elastic/beats/metricbeat/module/system/asset/asset_func.go:160\ngithub.com/elastic/beats/metricbeat/module/system/asset.(*MetricSet).Fetch\n\tG:/go/src/github.com/elastic/beats/metricbeat/module/system/asset/asset.go:62\ngithub.com/elastic/beats/metricbeat/mb/module.(*metricSetWrapper).multiEventFetch\n\tG:/go/src/github.com/elastic/beats/metricbeat/mb/module/wrapper.go:227\ngithub.com/elastic/beats/metricbeat/mb/module.(*metricSetWrapper).fetch\n\tG:/go/src/github.com/elastic/beats/metricbeat/mb/module/wrapper.go:207\ngithub.com/elastic/beats/metricbeat/mb/module.(*metricSetWrapper).startPeriodicFetching\n\tG:/go/src/github.com/elastic/beats/metricbeat/mb/module/wrapper.go:184\ngithub.com/elastic/beats/metricbeat/mb/module.(*metricSetWrapper).run\n\tG:/go/src/github.com/elastic/beats/metricbeat/mb/module/wrapper.go:171\ngithub.com/elastic/beats/metricbeat/mb/module.(*Wrapper).Start.func1\n\tG:/go/src/github.com/elastic/beats/metricbeat/mb/module/wrapper.go:112"} 2020-05-18T20:53:53.947+0530 DEBUG [module] module/wrapper.go:120 Stopped Wrapper[name=system, len(metricSetWrappers)=1] 2020-05-18T20:53:58.527+0530 DEBUG [service] service/service.go:33 Received sigterm/sigint, stopping 2020-05-18T20:53:58.527+0530 DEBUG [service] service/service.go:39 Received svc stop/shutdown request 2020-05-18T20:53:58.528+0530 INFO instance/beat.go:308 metricbeat stopped. 2020-05-18T20:53:58.667+0530 INFO [monitoring] log/log.go:132 Total non-zero metrics {"monitoring": {"metrics": {"beat":{"cpu":{"system":{"ticks":218,"time":218},"total":{"ticks":358,"time":358,"value":358},"user":{"ticks":140,"time":140}},"info":{"ephemeral_id":"30e22451-2f46-4833-9d41-64017cad3489","uptime":{"ms":8160}},"memstats":{"gc_next":4194304,"memory_alloc":2410544,"memory_total":4124872,"rss":24899584}},"libbeat":{"config":{"module":{"running":0}},"output":{"type":"file"},"pipeline":{"clients":0,"events":{"active":0}}},"system":{"cpu":{"cores":4}}}}} 2020-05-18T20:53:58.668+0530 INFO [monitoring] log/log.go:133 Uptime: 8.2992701s 2020-05-18T20:53:58.668+0530 INFO [monitoring] log/log.go:110 Stopping metrics logging.

  • Unable to use ghw on Windows

    Unable to use ghw on Windows

    The package sounds good, but I am unable to use it (Win10 x64). There are no instructions, so I simply ran go get github.com/jaypipes/ghw, which leads (during install & execution) to

    go\src\github.com\jaypipes\ghw\block.go:36: undefined: blockFillInfo go\src\github.com\jaypipes\ghw\cpu.go:71: undefined: cpuFillInfo go\src\github.com\jaypipes\ghw\memory.go:17: undefined: memFillInfo go\src\github.com\jaypipes\ghw\net.go:47: undefined: netFillInfo go\src\github.com\jaypipes\ghw\topology.go:70: undefined: topologyFillInfo

  • net_linux : Add detection to know if nic is on PCI BUS

    net_linux : Add detection to know if nic is on PCI BUS

    I need it to find physical interface on my server; especially ones on PCI BUS If nic is on pci bus, print nic according this info Update documentation according nic pci detection

  • Provide `ghw.WithDisableWarnings()` function

    Provide `ghw.WithDisableWarnings()` function

    https://github.com/jaypipes/ghw#disabling-warning-messages

    Now, there is to use an environment variable to disable the warnings, but it is not an option when the package is going to be compiled into a main program.

    The best would be to add a global variable at 'ghw' to disable them.

  • Allow configuration options to be passed to module constructors

    Allow configuration options to be passed to module constructors

    It seems a bit weird to set GHW_CHROOT as env var. we might have a few containers that each of them has a different CHROOT, so would like to have the option to set it for each invocation. an env var is seems too global. Maybe you can have a options object that is passed to each of the root methods (like ghw.CPU(), and then check if it's missing the chroot then check for the env var and if it's missing set it to the default root.

    What do you say?

  • Fix class name lookups

    Fix class name lookups

    All class IDs in https://pci-ids.ucw.cz/v2.2/pci.ids are stored as lowercase hex. The modalias file is used to extract the class IDs for a given device. The class IDs in that file can be uppercase HEX, e.g. pci:v00008086d00009D23sv00001028sd000007EAbc0Csc05i00. PCI DB lookup fails in that case.

    - What I did

    • Made sure that class-related IDs (class, subclass, programming interface) are parsed as lowercase hex.

    - How I did it

    • Using the strings.ToLower function on raw data
  • Add WithLoopDevices to report loop devices

    Add WithLoopDevices to report loop devices

    Currently we skipp over all loop devices in the system and with no possibility of reporting those.

    This patch introduces a new option to Block that allows us to report loop devices as disk and their partitions like if they were normal disks.

    This patch does not modify any existing functionality or alters anything with the default values.

    With WithLoopDevices enabled this reports the same info for loop devices as with other disks, including size, fs, mountpoints and so on.

    Signed-off-by: Itxaka [email protected]

  • GHW crashes when /numa_node file does not exist

    GHW crashes when /numa_node file does not exist

    When running ghw on an AMD Ryzen 7 5800X running proxmox the following error causes not only ghw to crash but the whole application to panic. I suspect this may be an issue with the VM but it would be worth error handling events like this to minimize risk. The following error causes the full panic WARNING: failed to read int from file: open /sys/class/drm/card0/device/numa_node: no such file or directory

    System Info

           _,met$$$$$gg.          root@xxxxxxxx
        ,g$$$$$$$$$$$$$$$P.       --------------
      ,g$$P"     """Y$$.".        OS: Debian GNU/Linux 10 (buster) x86_64
     ,$$P'              `$$$.     Host: X570 AORUS PRO -CF
    ',$$P       ,ggs.     `$$b:   Kernel: 5.4.78-2-pve
    `d$$'     ,$P"'   .    $$$    Uptime: 12 hours, 54 mins
     $$P      d$'     ,    $$P    Packages: 782 (dpkg)
     $$:      $$.   -    ,d$$'    Shell: bash 5.0.3
     $$;      Y$b._   _,d$P'      Terminal: /dev/pts/11
     Y$$.    `.`"Y$$$$P"'         CPU: AMD Ryzen 7 5800X 8- (16) @ 3.800GHz
     `$$b      "-.__              GPU: NVIDIA GeForce GTX 1060 6GB
      `Y$$                        GPU: NVIDIA GeForce GT 610
       `Y$$.                      Memory: 10036MiB / 32054MiB
         `$$b.
           `Y$$b.
              `"Y$b._
                  `"""
                  ```
  • unknown disk bus path on Alpine

    unknown disk bus path on Alpine

    For writing a CSI driver I'm using this library to list the disks on a node in a kubernetes cluster to be able to mount it, for finding the disks I'm using the disk bus path.

    On the node itself running Ubuntu 16.04 and there I can see the bus paths of the disks clearly

    Code

    import (
    	"fmt"
    	"log"
    
    	"github.com/jaypipes/ghw"
    )
    
    func main() {
    	blockInfo, err := ghw.Block()
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	for _, d := range blockInfo.Disks {
    		fmt.Println(d.Name)
    		fmt.Println(d.BusPath)
    	}
    }
    

    output directly on node

    vda
    virtio-pci-0000:00:04.0
    vdb
    virtio-pci-0000:00:06.0
    vdc
    virtio-pci-0000:00:08.0
    

    Executing the same code on the Alpine (3.8.4) deamonset container

    vda
    unknown
    vdb
    unknown
    vdc
    unknown
    

    Listing disks by path inside the container

    / # ls -la /dev/disk/by-path/
    total 0
    drwxr-xr-x    2 root     root           160 Jun 19 14:45 .
    drwxr-xr-x    6 root     root           120 Jun 18 00:46 ..
    lrwxrwxrwx    1 root     root             9 Jun 18 00:46 virtio-pci-0000:00:04.0 -> ../../vda
    lrwxrwxrwx    1 root     root            10 Jun 18 00:46 virtio-pci-0000:00:04.0-part1 -> ../../vda1
    lrwxrwxrwx    1 root     root            11 Jun 18 00:46 virtio-pci-0000:00:04.0-part14 -> ../../vda14
    lrwxrwxrwx    1 root     root            11 Jun 18 00:46 virtio-pci-0000:00:04.0-part15 -> ../../vda15
    lrwxrwxrwx    1 root     root             9 Jun 18 00:46 virtio-pci-0000:00:06.0 -> ../../vdb
    lrwxrwxrwx    1 root     root             9 Jun 19 14:45 virtio-pci-0000:00:09.0 -> ../../vdc
    

    Expected the same output in the Alpine container as the Ubuntu host

  • Support alternate root mountpoints

    Support alternate root mountpoints

    Hi,

    First, thanks for this robust solution, I'm hoping to use it, I have a specific use case where I want to run this library inside a docker container to get info about the host, and in that case, if understand correctly how your library works, the paths should be changed to where I bind the host folders to (e.g /host/proc). Do you think it's a doable change - to make the default paths changeable?

    Thanks

  • pkg: add support to consume snapshots

    pkg: add support to consume snapshots

    Needed for https://github.com/jaypipes/ghw/issues/66 (see https://github.com/jaypipes/ghw/issues/66#issuecomment-738618640 and following)

    Add support to allow ghw read from snapshots (created with ghw-snapshot) using environment variables or programmatically. This is meant to be used mostly in the testsuite, but can be useful for users in general.

    Added environment variables (and counterpart in golang pkgs):

    • GHW_SNAPSHOT_PATH let users specify a snapshot that ghw will automatically consume.
    • GHW_SNAPSHOT_ROOT let users specify the directory on which the snapshot should be unpacked.
    • GHW_SNAPSHOT_EXCLUSIVE is relevant iff GHW_SNAPSHOT_ROOT is given. Tells ghw that the directory is meant only to contain the given snapshot, thus ghw will not attempt to unpack it (and will go ahead silently!) unless the directory is empty.
    • GHW_SNAPSHOT_PRESERVE if set, ghw will not clean up the unpacked snapshot once done, leaving it into the system.

    Signed-off-by: Francesco Romani [email protected]

  • Panic when collecting host information

    Panic when collecting host information

    During the execution of this line of code in a VirtualBox VM running CentOS 8, go panicked with the following message:

    host, err := ghw.Host(ghw.WithDisableWarnings())
    
    panic: runtime error: slice bounds out of range [6:3]
    
    goroutine 16 [running]:
    github.com/jaypipes/pcidb.parseDBFile(0xc0008ca240, 0xc0005457a8, 0x3, 0xc000000000)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/pcidb/parse.go:72 +0x17cc
    github.com/jaypipes/pcidb.(*PCIDB).load(0xc0008ca240, 0xc000309ce0, 0x0, 0x0)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/pcidb/discover.go:61 +0x213
    github.com/jaypipes/pcidb.New(0xc000545960, 0x1, 0x1, 0xd, 0xc0008ca228, 0x0)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/pcidb/main.go:192 +0x8a
    github.com/jaypipes/ghw/pkg/pci.(*Info).load(0xc00041dd00, 0x1, 0x1)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go:43 +0xec
    github.com/jaypipes/ghw/pkg/pci.New.func1(0xc0002b5680, 0x0)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/ghw/pkg/pci/pci.go:170 +0x1b3
    github.com/jaypipes/ghw/pkg/context.(*Context).Do(0xc0002b5680, 0xc000545ad8, 0x0, 0x0)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/ghw/pkg/context/context.go:125 +0x90
    github.com/jaypipes/ghw/pkg/pci.New(0xc000545bf8, 0x1, 0x1, 0xc0002b5500, 0x0, 0x0)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/ghw/pkg/pci/pci.go:177 +0x105
    github.com/jaypipes/ghw.Host(0xc000545bf8, 0x1, 0x1, 0x1, 0x1, 0xc0000b6030)
            /Volumes/data/work/zaleos/git/softin-shared/licensing/vendor/github.com/jaypipes/ghw/host.go:90 +0x3a5
    ... (non-ghw code)
    

    Go script was executed with root privileges.

    Any insights into what might be causing this?

  • inaccurate disk size on windows

    inaccurate disk size on windows

    wmi report inaccurate disk size, refer : https://stackoverflow.com/questions/9901792/wmi-win32-diskdrive-to-get-total-sector-on-the-physical-disk-drive
    replace Win32_DiskDrive with DeviceIOControl.

  • WIP: net: replace

    WIP: net: replace "ethtool" with a package

    DEMO PR (please not merge!) to illustrate how it could like to consume https://github.com/safchain/ethtool

    Depends on unmerged feature (FeaturesWithState function) atm only available on my fork (PR pending)

    Fixes: https://github.com/jaypipes/ghw/issues/317

  • replace ethtool invocation with go packages

    replace ethtool invocation with go packages

    To get the network interface properties on linux, we invoke the ethtool command. This works, but we can simplify our flow and avoid to depend on a external, not-installed-by-default tool by consuming go packages. We should evaluate this option and change accordingly if we reach consensus.

  • expose SRIOV information as PCI devices

    expose SRIOV information as PCI devices

    Expose informations about SRIOV devices. After some more design iterations (https://github.com/jaypipes/ghw/pull/230#discussion_r755312597), we fixed these goals:

    • model SRIOV devices as special PCI devices, avoiding a new top-level package
    • to shape the API, we start checking what the k8s SRIOV operator needs
    • to further refine later, we will check what other projects like the k8s node feature discovery consume
    • the goal is to enable such projects to switch to ghw if/when they want.

    There are few more noteworthy items in this PR:

    • due to lack of resources, initial support is for linux only. Nothing linux-specific should have sneaked in the API. Should the current API design make unnecessarily hard to add support on other platforms (e.g. windows) this should be treated as bug
    • the code prefers Physical Functions for discovery, but we acknowledge that forcing consumers to traverse the physical functions to learn about virtual functions is awkward, so the API provides shallow references to the Virtual Functions for the sake of practicality.
    • this is an alternate implementation of issue #92 , conflicting with #230

    Fixes: https://github.com/jaypipes/ghw/issues/92

    Signed-off-by: Francesco Romani [email protected]

Hw-keyboard-remapper - This is key code remapper for the hardware keyboard

hw-keyboard-remapper hw-keyboard-remapper is key remapper independent the OS. ht

Jan 11, 2022
Arduino pluggable discovery for serial ports

Arduino pluggable discovery for serial ports The quarto-discovery tool is a command line program that interacts via stdio. It accepts commands as plai

Oct 29, 2021
Library to work with MimeHeaders and another mime types. Library support wildcards and parameters.

Mime header Motivation This library created to help people to parse media type data, like headers, and store and match it. The main features of the li

Nov 9, 2022
GoLang Library for Browser Capabilities Project

Browser Capabilities GoLang Project PHP has get_browser() function which tells what the user's browser is capable of. You can check original documenta

Sep 27, 2022
Type-safe Prometheus metrics builder library for golang

gotoprom A Prometheus metrics builder gotoprom offers an easy to use declarative API with type-safe labels for building and using Prometheus metrics.

Dec 5, 2022
Simple licensing library for golang.

license-key A simple licensing library in Golang, that generates license files containing arbitrary data. Note that this implementation is quite basic

Dec 24, 2022
A Golang library to manipulate strings according to the word parsing rules of the UNIX Bourne shell.

shellwords A Golang library to manipulate strings according to the word parsing rules of the UNIX Bourne shell. Installation go get github.com/Wing924

Sep 27, 2022
Flow-based and dataflow programming library for Go (golang)
Flow-based and dataflow programming library for Go (golang)

GoFlow - Dataflow and Flow-based programming library for Go (golang) Status of this branch (WIP) Warning: you are currently on v1 branch of GoFlow. v1

Dec 30, 2022
a Go (Golang) MusicBrainz WS2 client library - work in progress
a Go (Golang) MusicBrainz WS2 client library - work in progress

gomusicbrainz a Go (Golang) MusicBrainz WS2 client library - a work in progress. Current state Currently GoMusicBrainz provides methods to perform sea

Sep 28, 2022
Golang SSR-first Frontend Library
Golang SSR-first Frontend Library

kyoto Library that brings frontend-like components experience to the server side with native html/template on steroids. Supports any serving basis (ne

Nov 25, 2022
GoLang Library for Browser Capabilities Project

Browser Capabilities GoLang Project PHP has get_browser() function which tells what the user's browser is capable of. You can check original documenta

Nov 23, 2021
A WSL Library for Golang.

wsllib-go A WSL Library for Golang. Usage Get this package go get

Jun 13, 2022
Go-linq - A powerful language integrated query (LINQ) library for Golang

go-linq A powerful language integrated query (LINQ) library for Go. Written in v

Jan 7, 2023
libFFM-gp: Pure Golang implemented library for FM (factorization machines)

libFFM-gp: Pure Golang implemented library for FM (factorization machines)

Oct 10, 2022
Sa818 - Sa818 UHF/VHF walkie talkie module control library for golang
Sa818 - Sa818 UHF/VHF walkie talkie module control library for golang

SA818 golang library for serial control This library written in Go programming l

Jan 23, 2022
The Webhooks Listener-Plugin library consists of two component libraries written in GoLang

The Webhooks Listener-Plugin library consists of two component libraries written in GoLang: WebHook Listener Libraries and Plugin (Event Consumer) Libraries.

Feb 3, 2022
Golang CS:GO external base. Development currently halted due to compiler/runtime Golang bugs.

gogo Golang CS:GO External cheat/base. Also, my first Golang project. Wait! Development momentarily halted due to compiler/runtime bugs. Disclaimer Th

Jun 25, 2022
Belajar Golang Install Golang

Golang belajar Golang Install Golang = download di https://golang.org/dl/ = pilih yg Zip = extract file zipnya = buka foldernya - copy folder go = pas

Nov 15, 2021
Golang-module-references - A reference for how to setup a Golang project with modules - Task Management + Math Examples

Golang Module Project The purpose of this project is to act as a reference for setting up future Golang projects using modules. This project has a mat

Jan 2, 2022