github.com/jaypipes/ghw@v0.21.1/README.md (about) 1 # `ghw` - Go HardWare discovery/inspection library 2 3 [](https://pkg.go.dev/github.com/jaypipes/ghw) 4 [](https://goreportcard.com/report/github.com/jaypipes/ghw) 5 [](https://github.com/jaypipes/ghw/actions) 6 [](CODE_OF_CONDUCT.md) 7 8  9 10 `ghw` is a Go library providing hardware inspection and discovery for Linux and 11 Windows. There currently exists partial support for MacOSX. 12 13 ## Design Principles 14 15 * No root privileges needed for discovery 16 17 `ghw` goes the extra mile to be useful without root privileges. We query for 18 host hardware information as directly as possible without relying on shellouts 19 to programs like `dmidecode` that require root privileges to execute. 20 21 Elevated privileges are indeed required to query for some information, but 22 `ghw` will never error out if blocked from reading that information. Instead, 23 `ghw` will print a warning message about the information that could not be 24 retrieved. You may disable these warning messages with the 25 `GHW_DISABLE_WARNINGS` environment variable. 26 27 * Well-documented code and plenty of example code 28 29 The code itself should be well-documented with lots of usage examples. 30 31 * Interfaces should be consistent across modules 32 33 Each module in the library should be structured in a consistent fashion, and 34 the structs returned by various library functions should have consistent 35 attribute and method names. 36 37 ## Inspecting != Monitoring 38 39 `ghw` is a tool for gathering information about your hardware's **capacity** 40 and **capabilities**. 41 42 It is important to point out that `ghw` does **NOT** report information that is 43 temporary or variable. It is **NOT** a system monitor nor is it an appropriate 44 tool for gathering data points for metrics that change over time. If you are 45 looking for a system that tracks **usage** of CPU, memory, network I/O or disk 46 I/O, there are plenty of great open source tools that do this! Check out the 47 [Prometheus project](https://prometheus.io/) for a great example. 48 49 ## Usage 50 51 `ghw` has functions that return an `Info` object about a particular hardware 52 domain (e.g. CPU, Memory, Block storage, etc). 53 54 Use the following functions in `ghw` to inspect information about the host 55 hardware: 56 57 * [`ghw.CPU()`](#cpu) 58 * [`ghw.Memory()`](#memory) 59 * [`ghw.Block()`](#block-storage) (block storage) 60 * [`ghw.Topology()`](#topology) (processor architecture, NUMA topology and 61 memory cache hierarchy) 62 * [`ghw.Network()`](#network) 63 * [`ghw.PCI()`](#pci) 64 * [`ghw.GPU()`](#gpu) (graphical processing unit) 65 * [`ghw.Accelerator()`](#accelerator) (processing accelerators, AI) 66 * [`ghw.Chassis()`](#chassis) 67 * [`ghw.BIOS()`](#bios) 68 * [`ghw.Baseboard()`](#baseboard) 69 * [`ghw.Product()`](#product) 70 71 ### CPU 72 73 The `ghw.CPU()` function returns a `ghw.CPUInfo` struct that contains 74 information about the CPUs on the host system. 75 76 `ghw.CPUInfo` contains the following fields: 77 78 * `ghw.CPUInfo.TotalCores` has the total number of physical cores the host 79 system contains 80 * `ghw.CPUInfo.TotalHardwareThreads` has the total number of hardware threads 81 the host system contains 82 * `ghw.CPUInfo.Processors` is an array of `ghw.Processor` structs, one for each 83 physical processor package contained in the host 84 85 Each `ghw.Processor` struct contains a number of fields: 86 87 * `ghw.Processor.ID` is the physical processor `uint32` ID according to the 88 system 89 * `ghw.Processor.TotalCores` is the number of physical cores in the processor 90 package 91 * `ghw.Processor.TotalHardwareThreads` is the number of hardware threads in the 92 processor package 93 * `ghw.Processor.Vendor` is a string containing the vendor name 94 * `ghw.Processor.Model` is a string containing the vendor's model name 95 * `ghw.Processor.Capabilities` (Linux only) is an array of strings indicating 96 the features the processor has enabled 97 * `ghw.Processor.Cores` (Linux only) is an array of `ghw.ProcessorCore` structs 98 that are packed onto this physical processor 99 100 A `ghw.ProcessorCore` has the following fields: 101 102 * `ghw.ProcessorCore.ID` is the `uint32` identifier that the host gave this 103 core. Note that this does *not* necessarily equate to a zero-based index of 104 the core within a physical package. For example, the core IDs for an Intel Core 105 i7 are 0, 1, 2, 8, 9, and 10 106 * `ghw.ProcessorCore.TotalHardwareThreads` is the number of hardware threads 107 associated with the core 108 * `ghw.ProcessorCore.LogicalProcessors` is an array of ints representing the 109 logical processor IDs assigned to any processing unit for the core. These are 110 sometimes called the "thread siblings". Logical processor IDs are the 111 *zero-based* index of the processor on the host and are *not* related to the 112 core ID. 113 114 ```go 115 package main 116 117 import ( 118 "fmt" 119 "math" 120 "strings" 121 122 "github.com/jaypipes/ghw" 123 ) 124 125 func main() { 126 cpu, err := ghw.CPU() 127 if err != nil { 128 fmt.Printf("Error getting CPU info: %v", err) 129 } 130 131 fmt.Printf("%v\n", cpu) 132 133 for _, proc := range cpu.Processors { 134 fmt.Printf(" %v\n", proc) 135 for _, core := range proc.Cores { 136 fmt.Printf(" %v\n", core) 137 } 138 if len(proc.Capabilities) > 0 { 139 // pretty-print the (large) block of capability strings into rows 140 // of 6 capability strings 141 rows := int(math.Ceil(float64(len(proc.Capabilities)) / float64(6))) 142 for row := 1; row < rows; row = row + 1 { 143 rowStart := (row * 6) - 1 144 rowEnd := int(math.Min(float64(rowStart+6), float64(len(proc.Capabilities)))) 145 rowElems := proc.Capabilities[rowStart:rowEnd] 146 capStr := strings.Join(rowElems, " ") 147 if row == 1 { 148 fmt.Printf(" capabilities: [%s\n", capStr) 149 } else if rowEnd < len(proc.Capabilities) { 150 fmt.Printf(" %s\n", capStr) 151 } else { 152 fmt.Printf(" %s]\n", capStr) 153 } 154 } 155 } 156 } 157 } 158 ``` 159 160 Example output from my personal workstation: 161 162 ``` 163 cpu (1 physical package, 6 cores, 12 hardware threads) 164 physical package #0 (6 cores, 12 hardware threads) 165 processor core #0 (2 threads), logical processors [0 6] 166 processor core #1 (2 threads), logical processors [1 7] 167 processor core #2 (2 threads), logical processors [2 8] 168 processor core #3 (2 threads), logical processors [3 9] 169 processor core #4 (2 threads), logical processors [4 10] 170 processor core #5 (2 threads), logical processors [5 11] 171 capabilities: [msr pae mce cx8 apic sep 172 mtrr pge mca cmov pat pse36 173 clflush dts acpi mmx fxsr sse 174 sse2 ss ht tm pbe syscall 175 nx pdpe1gb rdtscp lm constant_tsc arch_perfmon 176 pebs bts rep_good nopl xtopology nonstop_tsc 177 cpuid aperfmperf pni pclmulqdq dtes64 monitor 178 ds_cpl vmx est tm2 ssse3 cx16 179 xtpr pdcm pcid sse4_1 sse4_2 popcnt 180 aes lahf_lm pti retpoline tpr_shadow vnmi 181 flexpriority ept vpid dtherm ida arat] 182 ``` 183 184 ### Memory 185 186 The `ghw.Memory()` function returns a `ghw.MemoryInfo` struct that contains 187 information about the RAM on the host system. 188 189 `ghw.MemoryInfo` contains the following fields: 190 191 * `ghw.MemoryInfo.TotalPhysicalBytes` contains the amount of physical memory on 192 the host 193 * `ghw.MemoryInfo.TotalUsableBytes` contains the amount of memory the 194 system can actually use. Usable memory accounts for things like the kernel's 195 resident memory size and some reserved system bits. Please note this value is 196 **NOT** the amount of memory currently in use by processes in the system. See 197 [the discussion][#physical-versus-usage-memory] about the difference. 198 * `ghw.MemoryInfo.SupportedPageSizes` is an array of integers representing the 199 size, in bytes, of memory pages the system supports 200 * `ghw.MemoryInfo.Modules` is an array of pointers to `ghw.MemoryModule` 201 structs, one for each physical [DIMM](https://en.wikipedia.org/wiki/DIMM). 202 Currently, this information is only included on Windows, with Linux support 203 [planned](https://github.com/jaypipes/ghw/pull/171#issuecomment-597082409). 204 205 ```go 206 package main 207 208 import ( 209 "fmt" 210 211 "github.com/jaypipes/ghw" 212 ) 213 214 func main() { 215 memory, err := ghw.Memory() 216 if err != nil { 217 fmt.Printf("Error getting memory info: %v", err) 218 } 219 220 fmt.Println(memory.String()) 221 } 222 ``` 223 224 Example output from my personal workstation: 225 226 ``` 227 memory (24GB physical, 24GB usable) 228 ``` 229 230 #### Physical versus Usable Memory 231 232 There has been [some](https://github.com/jaypipes/ghw/pull/171) 233 [confusion](https://github.com/jaypipes/ghw/issues/183) regarding the 234 difference between the total physical bytes versus total usable bytes of 235 memory. 236 237 Some of this confusion has been due to a misunderstanding of the term "usable". 238 As mentioned [above](#inspection!=monitoring), `ghw` does inspection of the 239 system's capacity. 240 241 A host computer has two capacities when it comes to RAM. The first capacity is 242 the amount of RAM that is contained in all memory banks (DIMMs) that are 243 attached to the motherboard. `ghw.MemoryInfo.TotalPhysicalBytes` refers to this 244 first capacity. 245 246 There is a (usually small) amount of RAM that is consumed by the bootloader 247 before the operating system is started (booted). Once the bootloader has booted 248 the operating system, the amount of RAM that may be used by the operating 249 system and its applications is fixed. `ghw.MemoryInfo.TotalUsableBytes` refers 250 to this second capacity. 251 252 You can determine the amount of RAM that the bootloader used (that is not made 253 available to the operating system) by subtracting 254 `ghw.MemoryInfo.TotalUsableBytes` from `ghw.MemoryInfo.TotalPhysicalBytes`: 255 256 ```go 257 package main 258 259 import ( 260 "fmt" 261 262 "github.com/jaypipes/ghw" 263 ) 264 265 func main() { 266 memory, err := ghw.Memory() 267 if err != nil { 268 fmt.Printf("Error getting memory info: %v", err) 269 } 270 271 phys := memory.TotalPhysicalBytes 272 usable := memory.TotalUsableBytes 273 274 fmt.Printf("The bootloader consumes %d bytes of RAM\n", phys - usable) 275 } 276 ``` 277 278 Example output from my personal workstation booted into a Windows10 operating 279 system with a Linux GRUB bootloader: 280 281 ``` 282 The bootloader consumes 3832720 bytes of RAM 283 ``` 284 285 ### Block storage 286 287 The `ghw.Block()` function returns a `ghw.BlockInfo` struct that contains 288 information about the block storage on the host system. 289 290 `ghw.BlockInfo` contains the following fields: 291 292 * `ghw.BlockInfo.TotalSizeBytes` contains the amount of physical block storage 293 on the host. 294 * `ghw.BlockInfo.Disks` is an array of pointers to `ghw.Disk` structs, one for 295 each disk found by the system 296 297 Each `ghw.Disk` struct contains the following fields: 298 299 * `ghw.Disk.Name` contains a string with the short name of the disk, e.g. "sda" 300 * `ghw.Disk.SizeBytes` contains the amount of storage the disk provides 301 * `ghw.Disk.PhysicalBlockSizeBytes` contains the size of the physical blocks 302 used on the disk, in bytes. This is typically the minimum amount of data that 303 will be written in a single write operation for the disk. 304 * `ghw.Disk.IsRemovable` contains a boolean indicating if the disk drive is 305 removable 306 * `ghw.Disk.DriveType` is the type of drive. It is of type `ghw.DriveType` 307 which has a `ghw.DriveType.String()` method that can be called to return a 308 string representation of the bus. This string will be `HDD`, `FDD`, `ODD`, 309 or `SSD`, which correspond to a hard disk drive (rotational), floppy drive, 310 optical (CD/DVD) drive and solid-state drive. 311 * `ghw.Disk.StorageController` is the type of storage controller. It is of type 312 `ghw.StorageController` which has a `ghw.StorageController.String()` method 313 that can be called to return a string representation of the bus. This string 314 will be `SCSI`, `IDE`, `virtio`, `MMC`, or `NVMe` 315 * `ghw.Disk.BusPath` (Linux, Darwin only) is the filepath to the bus used by 316 the disk. 317 * `ghw.Disk.NUMANodeID` (Linux only) is the numeric index of the NUMA node this 318 disk is local to, or -1 if the host system is not a NUMA system or is not 319 Linux. 320 * `ghw.Disk.Vendor` contains a string with the name of the hardware vendor for 321 the disk 322 * `ghw.Disk.Model` contains a string with the vendor-assigned disk model name 323 * `ghw.Disk.SerialNumber` contains a string with the disk's serial number 324 * `ghw.Disk.WWN` contains a string with the disk's 325 [World Wide Name](https://en.wikipedia.org/wiki/World_Wide_Name) 326 * `ghw.Disk.Partitions` contains an array of pointers to `ghw.Partition` 327 structs, one for each partition on the disk 328 329 Each `ghw.Partition` struct contains these fields: 330 331 * `ghw.Partition.Name` contains a string with the short name of the partition, 332 e.g. `sda1` 333 * `ghw.Partition.Label` contains the label for the partition itself. On Linux 334 systems, this is derived from the `ID_PART_ENTRY_NAME` [udev][udev] entry for 335 the partition. 336 * `ghw.Partition.FilesystemLabel` contains the label for the filesystem housed 337 on the partition. On Linux systems, this is derived from the `ID_FS_NAME` 338 [udev][udev] entry for the partition. 339 * `ghw.Partition.SizeBytes` contains the amount of storage the partition 340 provides 341 * `ghw.Partition.MountPoint` contains a string with the partition's mount 342 point, or `""` if no mount point was discovered 343 * `ghw.Partition.Type` contains a string indicated the filesystem type for the 344 partition, or `""` if the system could not determine the type 345 * `ghw.Partition.IsReadOnly` is a bool indicating the partition is read-only 346 * `ghw.Partition.Disk` is a pointer to the `ghw.Disk` object associated with 347 the partition. 348 * `ghw.Partition.UUID` is a string containing the partition UUID on Linux and MacOS, 349 and the `VolumeSerialNumber` on Windows (e.g. "A8C3D032"). On Linux systems, this is 350 derived from the `ID_PART_ENTRY_UUID` [udev][udev] entry for the partition. 351 352 [udev]: https://en.wikipedia.org/wiki/Udev 353 354 ```go 355 package main 356 357 import ( 358 "fmt" 359 360 "github.com/jaypipes/ghw" 361 ) 362 363 func main() { 364 block, err := ghw.Block() 365 if err != nil { 366 fmt.Printf("Error getting block storage info: %v", err) 367 } 368 369 fmt.Printf("%v\n", block) 370 371 for _, disk := range block.Disks { 372 fmt.Printf(" %v\n", disk) 373 for _, part := range disk.Partitions { 374 fmt.Printf(" %v\n", part) 375 } 376 } 377 } 378 ``` 379 380 Example output from my personal workstation: 381 382 ``` 383 block storage (1 disk, 2TB physical storage) 384 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 385 /dev/sda1 (100MB) 386 /dev/sda2 (187GB) 387 /dev/sda3 (449MB) 388 /dev/sda4 (1KB) 389 /dev/sda5 (15GB) 390 /dev/sda6 (2TB) [ext4] mounted@/ 391 ``` 392 393 > **NOTE**: `ghw` looks in the udev runtime database for some information. If 394 > you are using `ghw` in a container, remember to bind mount `/dev/disk` and 395 > `/run` into your container, otherwise `ghw` won't be able to query the udev 396 > DB or sysfs paths for information. 397 398 ### Topology 399 400 > **NOTE**: Topology support is currently Linux-only. Windows support is 401 > [planned](https://github.com/jaypipes/ghw/issues/166). 402 403 The `ghw.Topology()` function returns a `ghw.TopologyInfo` struct that contains 404 information about the host computer's architecture (NUMA vs. SMP), the host's 405 NUMA node layout and processor-specific memory caches. 406 407 The `ghw.TopologyInfo` struct contains two fields: 408 409 * `ghw.TopologyInfo.Architecture` contains an enum with the value `ghw.NUMA` or 410 `ghw.SMP` depending on what the topology of the system is 411 * `ghw.TopologyInfo.Nodes` is an array of pointers to `ghw.TopologyNode` 412 structs, one for each topology node (typically physical processor package) 413 found by the system 414 415 Each `ghw.TopologyNode` struct contains the following fields: 416 417 * `ghw.TopologyNode.ID` is the system's `uint32` identifier for the node 418 * `ghw.TopologyNode.Memory` is a `ghw.MemoryArea` struct describing the memory 419 attached to this node. 420 * `ghw.TopologyNode.Cores` is an array of pointers to `ghw.ProcessorCore` structs that 421 are contained in this node 422 * `ghw.TopologyNode.Caches` is an array of pointers to `ghw.MemoryCache` structs that 423 represent the low-level caches associated with processors and cores on the 424 system 425 * `ghw.TopologyNode.Distance` is an array of distances between NUMA nodes as reported 426 by the system. 427 428 `ghw.MemoryArea` describes a collection of *physical* RAM on the host. 429 430 In the simplest and most common case, all system memory fits in a single memory 431 area. In more complex host systems, like [NUMA systems][numa], many memory 432 areas may be present in the host system (e.g. one for each NUMA cell). 433 434 [numa]: https://en.wikipedia.org/wiki/Non-uniform_memory_access 435 436 The `ghw.MemoryArea` struct contains the following fields: 437 438 * `ghw.MemoryArea.TotalPhysicalBytes` contains the amount of physical memory 439 associated with this memory area. 440 * `ghw.MemoryArea.TotalUsableBytes` contains the amount of memory of this 441 memory area the system can actually use. Usable memory accounts for things 442 like the kernel's resident memory size and some reserved system bits. Please 443 note this value is **NOT** the amount of memory currently in use by processes 444 in the system. See [the discussion][#physical-versus-usage-memory] about 445 the difference. 446 447 See above in the [CPU](#cpu) section for information about the 448 `ghw.ProcessorCore` struct and how to use and query it. 449 450 Each `ghw.MemoryCache` struct contains the following fields: 451 452 * `ghw.MemoryCache.Type` is an enum that contains one of `ghw.DATA`, 453 `ghw.INSTRUCTION` or `ghw.UNIFIED` depending on whether the cache stores CPU 454 instructions, program data, or both 455 * `ghw.MemoryCache.Level` is a positive integer indicating how close the cache 456 is to the processor. The lower the number, the closer the cache is to the 457 processor and the faster the processor can access its contents 458 * `ghw.MemoryCache.SizeBytes` is an integer containing the number of bytes the 459 cache can contain 460 * `ghw.MemoryCache.LogicalProcessors` is an array of integers representing the 461 logical processors that use the cache 462 463 ```go 464 package main 465 466 import ( 467 "fmt" 468 469 "github.com/jaypipes/ghw" 470 ) 471 472 func main() { 473 topology, err := ghw.Topology() 474 if err != nil { 475 fmt.Printf("Error getting topology info: %v", err) 476 } 477 478 fmt.Printf("%v\n", topology) 479 480 for _, node := range topology.Nodes { 481 fmt.Printf(" %v\n", node) 482 for _, cache := range node.Caches { 483 fmt.Printf(" %v\n", cache) 484 } 485 } 486 } 487 ``` 488 489 Example output from my personal workstation: 490 491 ``` 492 topology SMP (1 nodes) 493 node #0 (6 cores) 494 L1i cache (32 KB) shared with logical processors: 3,9 495 L1i cache (32 KB) shared with logical processors: 2,8 496 L1i cache (32 KB) shared with logical processors: 11,5 497 L1i cache (32 KB) shared with logical processors: 10,4 498 L1i cache (32 KB) shared with logical processors: 0,6 499 L1i cache (32 KB) shared with logical processors: 1,7 500 L1d cache (32 KB) shared with logical processors: 11,5 501 L1d cache (32 KB) shared with logical processors: 10,4 502 L1d cache (32 KB) shared with logical processors: 3,9 503 L1d cache (32 KB) shared with logical processors: 1,7 504 L1d cache (32 KB) shared with logical processors: 0,6 505 L1d cache (32 KB) shared with logical processors: 2,8 506 L2 cache (256 KB) shared with logical processors: 2,8 507 L2 cache (256 KB) shared with logical processors: 3,9 508 L2 cache (256 KB) shared with logical processors: 0,6 509 L2 cache (256 KB) shared with logical processors: 10,4 510 L2 cache (256 KB) shared with logical processors: 1,7 511 L2 cache (256 KB) shared with logical processors: 11,5 512 L3 cache (12288 KB) shared with logical processors: 0,1,10,11,2,3,4,5,6,7,8,9 513 ``` 514 515 ### Network 516 517 The `ghw.Network()` function returns a `ghw.NetworkInfo` struct that contains 518 information about the host computer's networking hardware. 519 520 The `ghw.NetworkInfo` struct contains one field: 521 522 * `ghw.NetworkInfo.NICs` is an array of pointers to `ghw.NIC` structs, one 523 for each network interface controller found for the system 524 525 Each `ghw.NIC` struct contains the following fields: 526 527 * `ghw.NIC.Name` is the system's identifier for the NIC 528 * `ghw.NIC.MACAddress` is the Media Access Control (MAC) address for the NIC, 529 if any 530 * `ghw.NIC.IsVirtual` is a boolean indicating if the NIC is a virtualized 531 device 532 * `ghw.NIC.Capabilities` (Linux only) is an array of pointers to 533 `ghw.NICCapability` structs that can describe the things the NIC supports. 534 These capabilities match the returned values from the `ethtool -k <DEVICE>` 535 call on Linux as well as the AutoNegotiation and PauseFrameUse capabilities 536 from `ethtool`. 537 * `ghw.NIC.PCIAddress` (Linux only) is the PCI device address of the device 538 backing the NIC. this is not-nil only if the backing device is indeed a PCI 539 device; more backing devices (e.g. USB) will be added in future versions. 540 * `ghw.NIC.Speed` (Linux only) is a string showing the current link speed. On 541 Linux, this field will be present even if `ethtool` is not available. 542 * `ghw.NIC.Duplex` (Linux only) is a string showing the current link duplex. On 543 Linux, this field will be present even if `ethtool` is not available. 544 * `ghw.NIC.SupportedLinkModes` (Linux only) is a string slice containing a list 545 of supported link modes, e.g. "10baseT/Half", "1000baseT/Full". 546 * `ghw.NIC.SupportedPorts` (Linux only) is a string slice containing the list 547 of supported port types, e.g. "MII", "TP", "FIBRE", "Twisted Pair". 548 * `ghw.NIC.SupportedFECModes` (Linux only) is a string slice containing a list 549 of supported Forward Error Correction (FEC) Modes. 550 * `ghw.NIC.AdvertisedLinkModes` (Linux only) is a string slice containing the 551 link modes being advertised during auto negotiation. 552 * `ghw.NIC.AdvertisedFECModes` (Linux only) is a string slice containing the 553 Forward Error Correction (FEC) modes advertised during auto negotiation. 554 555 The `ghw.NICCapability` struct contains the following fields: 556 557 * `ghw.NICCapability.Name` is the string name of the capability (e.g. 558 "tcp-segmentation-offload") 559 * `ghw.NICCapability.IsEnabled` is a boolean indicating whether the capability 560 is currently enabled/active on the NIC 561 * `ghw.NICCapability.CanEnable` is a boolean indicating whether the capability 562 may be enabled 563 564 ```go 565 package main 566 567 import ( 568 "fmt" 569 570 "github.com/jaypipes/ghw" 571 ) 572 573 func main() { 574 net, err := ghw.Network() 575 if err != nil { 576 fmt.Printf("Error getting network info: %v", err) 577 } 578 579 fmt.Printf("%v\n", net) 580 581 for _, nic := range net.NICs { 582 fmt.Printf(" %v\n", nic) 583 584 enabledCaps := make([]int, 0) 585 for x, cap := range nic.Capabilities { 586 if cap.IsEnabled { 587 enabledCaps = append(enabledCaps, x) 588 } 589 } 590 if len(enabledCaps) > 0 { 591 fmt.Printf(" enabled capabilities:\n") 592 for _, x := range enabledCaps { 593 fmt.Printf(" - %s\n", nic.Capabilities[x].Name) 594 } 595 } 596 } 597 } 598 ``` 599 600 Example output from my personal laptop: 601 602 ``` 603 net (3 NICs) 604 docker0 605 enabled capabilities: 606 - tx-checksumming 607 - tx-checksum-ip-generic 608 - scatter-gather 609 - tx-scatter-gather 610 - tx-scatter-gather-fraglist 611 - tcp-segmentation-offload 612 - tx-tcp-segmentation 613 - tx-tcp-ecn-segmentation 614 - tx-tcp-mangleid-segmentation 615 - tx-tcp6-segmentation 616 - udp-fragmentation-offload 617 - generic-segmentation-offload 618 - generic-receive-offload 619 - tx-vlan-offload 620 - highdma 621 - tx-lockless 622 - netns-local 623 - tx-gso-robust 624 - tx-fcoe-segmentation 625 - tx-gre-segmentation 626 - tx-gre-csum-segmentation 627 - tx-ipxip4-segmentation 628 - tx-ipxip6-segmentation 629 - tx-udp_tnl-segmentation 630 - tx-udp_tnl-csum-segmentation 631 - tx-gso-partial 632 - tx-sctp-segmentation 633 - tx-esp-segmentation 634 - tx-vlan-stag-hw-insert 635 enp58s0f1 636 enabled capabilities: 637 - rx-checksumming 638 - generic-receive-offload 639 - rx-vlan-offload 640 - tx-vlan-offload 641 - highdma 642 - auto-negotiation 643 wlp59s0 644 enabled capabilities: 645 - scatter-gather 646 - tx-scatter-gather 647 - generic-segmentation-offload 648 - generic-receive-offload 649 - highdma 650 - netns-local 651 ``` 652 653 ### PCI 654 655 `ghw` contains a PCI database inspection and querying facility that allows 656 developers to not only gather information about devices on a local PCI bus but 657 also query for information about hardware device classes, vendor and product 658 information. 659 660 > **NOTE**: Parsing of the PCI-IDS file database is provided by the separate 661 > [github.com/jaypipes/pcidb library](http://github.com/jaypipes/pcidb). You 662 > can read that library's README for more information about the various structs 663 > that are exposed on the `ghw.PCIInfo` struct. 664 665 The `ghw.PCI()` function returns a `ghw.PCIInfo` struct that contains 666 information about the host computer's PCI devices. 667 668 The `ghw.PCIInfo` struct contains one field: 669 670 * `ghw.PCIInfo.Devices` is a slice of pointers to `ghw.PCIDevice` structs that 671 describe the PCI devices on the host system 672 673 > **NOTE**: PCI products are often referred to by their "device ID". We use the 674 > term "product ID" in `ghw` because it more accurately reflects what the 675 > identifier is for: a specific product line produced by the vendor. 676 677 The `ghw.PCIDevice` struct has the following fields: 678 679 * `ghw.PCIDevice.Vendor` is a pointer to a `pcidb.Vendor` struct that 680 describes the device's primary vendor. This will always be non-nil. 681 * `ghw.PCIDevice.Product` is a pointer to a `pcidb.Product` struct that 682 describes the device's primary product. This will always be non-nil. 683 * `ghw.PCIDevice.Subsystem` is a pointer to a `pcidb.Product` struct that 684 describes the device's secondary/sub-product. This will always be non-nil. 685 * `ghw.PCIDevice.Class` is a pointer to a `pcidb.Class` struct that 686 describes the device's class. This will always be non-nil. 687 * `ghw.PCIDevice.Subclass` is a pointer to a `pcidb.Subclass` struct 688 that describes the device's subclass. This will always be non-nil. 689 * `ghw.PCIDevice.ProgrammingInterface` is a pointer to a 690 `pcidb.ProgrammingInterface` struct that describes the device subclass' 691 programming interface. This will always be non-nil. 692 * `ghw.PCIDevice.Driver` is a string representing the device driver the 693 system is using to handle this device. Can be empty string if this 694 information is not available. If the information is not available, this does 695 not mean the device is not functioning, but rather that `ghw` was not able to 696 retrieve driver information. 697 698 The `ghw.PCIAddress` (which is an alias for the `ghw.pci.address.Address` 699 struct) contains the PCI address fields. It has a `ghw.PCIAddress.String()` 700 method that returns the canonical Domain:Bus:Device.Function ([D]BDF) 701 representation of this Address. 702 703 The `ghw.PCIAddress` struct has the following fields: 704 705 * `ghw.PCIAddress.Domain` is a string representing the PCI domain component of 706 the address. 707 * `ghw.PCIAddress.Bus` is a string representing the PCI bus component of 708 the address. 709 * `ghw.PCIAddress.Device` is a string representing the PCI device component of 710 the address. 711 * `ghw.PCIAddress.Function` is a string representing the PCI function component of 712 the address. 713 714 > **NOTE**: Older versions (pre-`v0.9.0`) erroneously referred to the `Device` 715 > field as the `Slot` field. As noted by [@pearsonk](https://github.com/pearsonk) 716 > in [#220](https://github.com/jaypipes/ghw/issues/220), this was a misnomer. 717 718 The following code snippet shows how to list the PCI devices on the host system 719 and output a simple list of PCI address and vendor/product information: 720 721 ```go 722 package main 723 724 import ( 725 "fmt" 726 727 "github.com/jaypipes/ghw" 728 ) 729 730 func main() { 731 pci, err := ghw.PCI() 732 if err != nil { 733 fmt.Printf("Error getting PCI info: %v", err) 734 } 735 fmt.Printf("host PCI devices:\n") 736 fmt.Println("====================================================") 737 738 for _, device := range pci.Devices { 739 vendor := device.Vendor 740 vendorName := vendor.Name 741 if len(vendor.Name) > 20 { 742 vendorName = string([]byte(vendorName)[0:17]) + "..." 743 } 744 product := device.Product 745 productName := product.Name 746 if len(product.Name) > 40 { 747 productName = string([]byte(productName)[0:37]) + "..." 748 } 749 fmt.Printf("%-12s\t%-20s\t%-40s\n", device.Address, vendorName, productName) 750 } 751 } 752 ``` 753 754 on my local workstation the output of the above looks like the following: 755 756 ``` 757 host PCI devices: 758 ==================================================== 759 0000:00:00.0 Intel Corporation 5520/5500/X58 I/O Hub to ESI Port 760 0000:00:01.0 Intel Corporation 5520/5500/X58 I/O Hub PCI Express Roo... 761 0000:00:02.0 Intel Corporation 5520/5500/X58 I/O Hub PCI Express Roo... 762 0000:00:03.0 Intel Corporation 5520/5500/X58 I/O Hub PCI Express Roo... 763 0000:00:07.0 Intel Corporation 5520/5500/X58 I/O Hub PCI Express Roo... 764 0000:00:10.0 Intel Corporation 7500/5520/5500/X58 Physical and Link ... 765 0000:00:10.1 Intel Corporation 7500/5520/5500/X58 Routing and Protoc... 766 0000:00:14.0 Intel Corporation 7500/5520/5500/X58 I/O Hub System Man... 767 0000:00:14.1 Intel Corporation 7500/5520/5500/X58 I/O Hub GPIO and S... 768 0000:00:14.2 Intel Corporation 7500/5520/5500/X58 I/O Hub Control St... 769 0000:00:14.3 Intel Corporation 7500/5520/5500/X58 I/O Hub Throttle R... 770 0000:00:19.0 Intel Corporation 82567LF-2 Gigabit Network Connection 771 0000:00:1a.0 Intel Corporation 82801JI (ICH10 Family) USB UHCI Contr... 772 0000:00:1a.1 Intel Corporation 82801JI (ICH10 Family) USB UHCI Contr... 773 0000:00:1a.2 Intel Corporation 82801JI (ICH10 Family) USB UHCI Contr... 774 0000:00:1a.7 Intel Corporation 82801JI (ICH10 Family) USB2 EHCI Cont... 775 0000:00:1b.0 Intel Corporation 82801JI (ICH10 Family) HD Audio Contr... 776 0000:00:1c.0 Intel Corporation 82801JI (ICH10 Family) PCI Express Ro... 777 0000:00:1c.1 Intel Corporation 82801JI (ICH10 Family) PCI Express Po... 778 0000:00:1c.4 Intel Corporation 82801JI (ICH10 Family) PCI Express Ro... 779 0000:00:1d.0 Intel Corporation 82801JI (ICH10 Family) USB UHCI Contr... 780 0000:00:1d.1 Intel Corporation 82801JI (ICH10 Family) USB UHCI Contr... 781 0000:00:1d.2 Intel Corporation 82801JI (ICH10 Family) USB UHCI Contr... 782 0000:00:1d.7 Intel Corporation 82801JI (ICH10 Family) USB2 EHCI Cont... 783 0000:00:1e.0 Intel Corporation 82801 PCI Bridge 784 0000:00:1f.0 Intel Corporation 82801JIR (ICH10R) LPC Interface Contr... 785 0000:00:1f.2 Intel Corporation 82801JI (ICH10 Family) SATA AHCI Cont... 786 0000:00:1f.3 Intel Corporation 82801JI (ICH10 Family) SMBus Controller 787 0000:01:00.0 NEC Corporation uPD720200 USB 3.0 Host Controller 788 0000:02:00.0 Marvell Technolog... 88SE9123 PCIe SATA 6.0 Gb/s controller 789 0000:02:00.1 Marvell Technolog... 88SE912x IDE Controller 790 0000:03:00.0 NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] 791 0000:03:00.1 NVIDIA Corporation UNKNOWN 792 0000:04:00.0 LSI Logic / Symbi... SAS2004 PCI-Express Fusion-MPT SAS-2 ... 793 0000:06:00.0 Qualcomm Atheros AR5418 Wireless Network Adapter [AR50... 794 0000:08:03.0 LSI Corporation FW322/323 [TrueFire] 1394a Controller 795 0000:3f:00.0 Intel Corporation UNKNOWN 796 0000:3f:00.1 Intel Corporation Xeon 5600 Series QuickPath Architectu... 797 0000:3f:02.0 Intel Corporation Xeon 5600 Series QPI Link 0 798 0000:3f:02.1 Intel Corporation Xeon 5600 Series QPI Physical 0 799 0000:3f:02.2 Intel Corporation Xeon 5600 Series Mirror Port Link 0 800 0000:3f:02.3 Intel Corporation Xeon 5600 Series Mirror Port Link 1 801 0000:3f:03.0 Intel Corporation Xeon 5600 Series Integrated Memory Co... 802 0000:3f:03.1 Intel Corporation Xeon 5600 Series Integrated Memory Co... 803 0000:3f:03.4 Intel Corporation Xeon 5600 Series Integrated Memory Co... 804 0000:3f:04.0 Intel Corporation Xeon 5600 Series Integrated Memory Co... 805 0000:3f:04.1 Intel Corporation Xeon 5600 Series Integrated Memory Co... 806 0000:3f:04.2 Intel Corporation Xeon 5600 Series Integrated Memory Co... 807 0000:3f:04.3 Intel Corporation Xeon 5600 Series Integrated Memory Co... 808 0000:3f:05.0 Intel Corporation Xeon 5600 Series Integrated Memory Co... 809 0000:3f:05.1 Intel Corporation Xeon 5600 Series Integrated Memory Co... 810 0000:3f:05.2 Intel Corporation Xeon 5600 Series Integrated Memory Co... 811 0000:3f:05.3 Intel Corporation Xeon 5600 Series Integrated Memory Co... 812 0000:3f:06.0 Intel Corporation Xeon 5600 Series Integrated Memory Co... 813 0000:3f:06.1 Intel Corporation Xeon 5600 Series Integrated Memory Co... 814 0000:3f:06.2 Intel Corporation Xeon 5600 Series Integrated Memory Co... 815 0000:3f:06.3 Intel Corporation Xeon 5600 Series Integrated Memory Co... 816 ``` 817 818 #### Finding a PCI device by PCI address 819 820 In addition to the above information, the `ghw.PCIInfo` struct has the 821 following method: 822 823 * `ghw.PCIInfo.GetDevice(address string)` 824 825 The following code snippet shows how to call the `ghw.PCIInfo.GetDevice()` 826 method and use its returned `ghw.PCIDevice` struct pointer: 827 828 ```go 829 package main 830 831 import ( 832 "fmt" 833 "os" 834 835 "github.com/jaypipes/ghw" 836 ) 837 838 func main() { 839 pci, err := ghw.PCI() 840 if err != nil { 841 fmt.Printf("Error getting PCI info: %v", err) 842 } 843 844 addr := "0000:00:00.0" 845 if len(os.Args) == 2 { 846 addr = os.Args[1] 847 } 848 fmt.Printf("PCI device information for %s\n", addr) 849 fmt.Println("====================================================") 850 deviceInfo := pci.GetDevice(addr) 851 if deviceInfo == nil { 852 fmt.Printf("could not retrieve PCI device information for %s\n", addr) 853 return 854 } 855 856 vendor := deviceInfo.Vendor 857 fmt.Printf("Vendor: %s [%s]\n", vendor.Name, vendor.ID) 858 product := deviceInfo.Product 859 fmt.Printf("Product: %s [%s]\n", product.Name, product.ID) 860 subsystem := deviceInfo.Subsystem 861 subvendor := pci.Vendors[subsystem.VendorID] 862 subvendorName := "UNKNOWN" 863 if subvendor != nil { 864 subvendorName = subvendor.Name 865 } 866 fmt.Printf("Subsystem: %s [%s] (Subvendor: %s)\n", subsystem.Name, subsystem.ID, subvendorName) 867 class := deviceInfo.Class 868 fmt.Printf("Class: %s [%s]\n", class.Name, class.ID) 869 subclass := deviceInfo.Subclass 870 fmt.Printf("Subclass: %s [%s]\n", subclass.Name, subclass.ID) 871 progIface := deviceInfo.ProgrammingInterface 872 fmt.Printf("Programming Interface: %s [%s]\n", progIface.Name, progIface.ID) 873 } 874 ``` 875 876 Here's a sample output from my local workstation: 877 878 ``` 879 PCI device information for 0000:03:00.0 880 ==================================================== 881 Vendor: NVIDIA Corporation [10de] 882 Product: GP107 [GeForce GTX 1050 Ti] [1c82] 883 Subsystem: UNKNOWN [8613] (Subvendor: ASUSTeK Computer Inc.) 884 Class: Display controller [03] 885 Subclass: VGA compatible controller [00] 886 Programming Interface: VGA controller [00] 887 ``` 888 889 ### GPU 890 891 The `ghw.GPU()` function returns a `ghw.GPUInfo` struct that contains 892 information about the host computer's graphics hardware. 893 894 The `ghw.GPUInfo` struct contains one field: 895 896 * `ghw.GPUInfo.GraphicCards` is an array of pointers to `ghw.GraphicsCard` 897 structs, one for each graphics card found for the system 898 899 Each `ghw.GraphicsCard` struct contains the following fields: 900 901 * `ghw.GraphicsCard.Index` is the system's numeric zero-based index for the 902 card on the bus 903 * `ghw.GraphicsCard.Address` is the PCI address for the graphics card 904 * `ghw.GraphicsCard.DeviceInfo` is a pointer to a `ghw.PCIDevice` struct 905 describing the graphics card. This may be `nil` if no PCI device information 906 could be determined for the card. 907 * `ghw.GraphicsCard.Node` is an pointer to a `ghw.TopologyNode` struct that the 908 GPU/graphics card is affined to. On non-NUMA systems, this will always be 909 `nil`. 910 911 ```go 912 package main 913 914 import ( 915 "fmt" 916 917 "github.com/jaypipes/ghw" 918 ) 919 920 func main() { 921 gpu, err := ghw.GPU() 922 if err != nil { 923 fmt.Printf("Error getting GPU info: %v", err) 924 } 925 926 fmt.Printf("%v\n", gpu) 927 928 for _, card := range gpu.GraphicsCards { 929 fmt.Printf(" %v\n", card) 930 } 931 } 932 ``` 933 934 Example output from my personal workstation: 935 936 ``` 937 gpu (1 graphics card) 938 card #0 @0000:03:00.0 -> class: 'Display controller' vendor: 'NVIDIA Corporation' product: 'GP107 [GeForce GTX 1050 Ti]' 939 ``` 940 941 **NOTE**: You can [read more](#pci) about the fields of the `ghw.PCIDevice` 942 struct if you'd like to dig deeper into PCI subsystem and programming interface 943 information 944 945 **NOTE**: You can [read more](#topology) about the fields of the 946 `ghw.TopologyNode` struct if you'd like to dig deeper into the NUMA/topology 947 subsystem 948 949 ### Accelerator 950 951 The `ghw.Accelerator()` function returns a `ghw.AcceleratorInfo` struct that contains 952 information about the host computer's processing accelerator hardware. In this category 953 we can find used hardware for AI. The hardware detected in this category will be 954 processing accelerators (PCI class `1200`), 3D controllers (`0302`) and Display 955 controllers (`0380`). 956 957 The `ghw.AcceleratorInfo` struct contains one field: 958 959 * `ghw.AcceleratorInfo.Devices` is an array of pointers to `ghw.AcceleratorDevice` 960 structs, one for each processing accelerator card found for the system. 961 962 Each `ghw.AcceleratorDevice` struct contains the following fields: 963 964 * `ghw.AcceleratorDevice.Address` is the PCI address for the processing accelerator card. 965 * `ghw.AcceleratorDevice.PCIDevice` is a pointer to a `ghw.PCIDevice` struct. 966 describing the processing accelerator card. This may be `nil` if no PCI device 967 information could be determined for the card. 968 969 ```go 970 package main 971 972 import ( 973 "fmt" 974 975 "github.com/jaypipes/ghw" 976 ) 977 978 func main() { 979 accel, err := ghw.Accelerator() 980 if err != nil { 981 fmt.Printf("Error getting processing accelerator info: %v", err) 982 } 983 984 fmt.Printf("%v\n", accel) 985 986 for _, card := range accel.Devices { 987 fmt.Printf(" %v\n", device) 988 } 989 } 990 ``` 991 992 Example output from a testing machine: 993 994 ``` 995 processing accelerators (1 device) 996 device @0000:00:04.0 -> driver: 'fake_pci_driver' class: 'Processing accelerators' vendor: 'Red Hat, Inc.' product: 'QEMU PCI Test Device' 997 ``` 998 999 **NOTE**: You can [read more](#pci) about the fields of the `ghw.PCIDevice` 1000 struct if you'd like to dig deeper into PCI subsystem and programming interface 1001 information 1002 1003 ### Chassis 1004 1005 The `ghw.Chassis()` function returns a `ghw.ChassisInfo` struct that contains 1006 information about the host computer's hardware chassis. 1007 1008 The `ghw.ChassisInfo` struct contains multiple fields: 1009 1010 * `ghw.ChassisInfo.AssetTag` is a string with the chassis asset tag 1011 * `ghw.ChassisInfo.SerialNumber` is a string with the chassis serial number 1012 * `ghw.ChassisInfo.Type` is a string with the chassis type *code* 1013 * `ghw.ChassisInfo.TypeDescription` is a string with a description of the 1014 chassis type 1015 * `ghw.ChassisInfo.Vendor` is a string with the chassis vendor 1016 * `ghw.ChassisInfo.Version` is a string with the chassis version 1017 1018 > **NOTE**: These fields are often missing for non-server hardware. Don't be 1019 > surprised to see empty string or "None" values. 1020 1021 ```go 1022 package main 1023 1024 import ( 1025 "fmt" 1026 1027 "github.com/jaypipes/ghw" 1028 ) 1029 1030 func main() { 1031 chassis, err := ghw.Chassis() 1032 if err != nil { 1033 fmt.Printf("Error getting chassis info: %v", err) 1034 } 1035 1036 fmt.Printf("%v\n", chassis) 1037 } 1038 ``` 1039 1040 Example output from my personal workstation: 1041 1042 ``` 1043 chassis type=Desktop vendor=System76 version=thelio-r1 1044 ``` 1045 1046 > **NOTE**: Some of the values such as serial numbers are shown as unknown 1047 > because the Linux kernel by default disallows access to those fields if 1048 > you're not running as root. They will be populated if it runs as root or 1049 > otherwise you may see warnings like the following: 1050 1051 ``` 1052 WARNING: Unable to read chassis_serial: open /sys/class/dmi/id/chassis_serial: permission denied 1053 ``` 1054 1055 You can ignore them or use the [Disabling warning messages](#disabling-warning-messages) 1056 feature to quiet things down. 1057 1058 ### BIOS 1059 1060 The `ghw.BIOS()` function returns a `ghw.BIOSInfo` struct that contains 1061 information about the host computer's basis input/output system (BIOS). 1062 1063 The `ghw.BIOSInfo` struct contains multiple fields: 1064 1065 * `ghw.BIOSInfo.Vendor` is a string with the BIOS vendor 1066 * `ghw.BIOSInfo.Version` is a string with the BIOS version 1067 * `ghw.BIOSInfo.Date` is a string with the date the BIOS was flashed/created 1068 1069 ```go 1070 package main 1071 1072 import ( 1073 "fmt" 1074 1075 "github.com/jaypipes/ghw" 1076 ) 1077 1078 func main() { 1079 bios, err := ghw.BIOS() 1080 if err != nil { 1081 fmt.Printf("Error getting BIOS info: %v", err) 1082 } 1083 1084 fmt.Printf("%v\n", bios) 1085 } 1086 ``` 1087 1088 Example output from my personal workstation: 1089 1090 ``` 1091 bios vendor=System76 version=F2 Z5 date=11/14/2018 1092 ``` 1093 1094 ### Baseboard 1095 1096 The `ghw.Baseboard()` function returns a `ghw.BaseboardInfo` struct that 1097 contains information about the host computer's hardware baseboard. 1098 1099 The `ghw.BaseboardInfo` struct contains multiple fields: 1100 1101 * `ghw.BaseboardInfo.AssetTag` is a string with the baseboard asset tag 1102 * `ghw.BaseboardInfo.SerialNumber` is a string with the baseboard serial number 1103 * `ghw.BaseboardInfo.Vendor` is a string with the baseboard vendor 1104 * `ghw.BaseboardInfo.Product` is a string with the baseboard name on Linux and 1105 Product on Windows 1106 * `ghw.BaseboardInfo.Version` is a string with the baseboard version 1107 1108 > **NOTE**: These fields are often missing for non-server hardware. Don't be 1109 > surprised to see empty string or "None" values. 1110 1111 ```go 1112 package main 1113 1114 import ( 1115 "fmt" 1116 1117 "github.com/jaypipes/ghw" 1118 ) 1119 1120 func main() { 1121 baseboard, err := ghw.Baseboard() 1122 if err != nil { 1123 fmt.Printf("Error getting baseboard info: %v", err) 1124 } 1125 1126 fmt.Printf("%v\n", baseboard) 1127 } 1128 ``` 1129 1130 Example output from my personal workstation: 1131 1132 ``` 1133 baseboard vendor=System76 version=thelio-r1 1134 ``` 1135 1136 > **NOTE**: Some of the values such as serial numbers are shown as unknown 1137 > because the Linux kernel by default disallows access to those fields if 1138 > you're not running as root. They will be populated if it runs as root or 1139 > otherwise you may see warnings like the following: 1140 1141 ``` 1142 WARNING: Unable to read board_serial: open /sys/class/dmi/id/board_serial: permission denied 1143 ``` 1144 1145 You can ignore them or use the [Disabling warning messages](#disabling-warning-messages) 1146 feature to quiet things down. 1147 1148 ### Product 1149 1150 The `ghw.Product()` function returns a `ghw.ProductInfo` struct that 1151 contains information about the host computer's hardware product line. 1152 1153 The `ghw.ProductInfo` struct contains multiple fields: 1154 1155 * `ghw.ProductInfo.Family` is a string describing the product family 1156 * `ghw.ProductInfo.Name` is a string with the product name 1157 * `ghw.ProductInfo.SerialNumber` is a string with the product serial number 1158 * `ghw.ProductInfo.UUID` is a string with the product UUID 1159 * `ghw.ProductInfo.SKU` is a string with the product stock unit identifier 1160 (SKU) 1161 * `ghw.ProductInfo.Vendor` is a string with the product vendor 1162 * `ghw.ProductInfo.Version` is a string with the product version 1163 1164 > **NOTE**: These fields are often missing for non-server hardware. Don't be 1165 > surprised to see empty string, "Default string" or "None" values. 1166 1167 ```go 1168 package main 1169 1170 import ( 1171 "fmt" 1172 1173 "github.com/jaypipes/ghw" 1174 ) 1175 1176 func main() { 1177 product, err := ghw.Product() 1178 if err != nil { 1179 fmt.Printf("Error getting product info: %v", err) 1180 } 1181 1182 fmt.Printf("%v\n", product) 1183 } 1184 ``` 1185 1186 Example output from my personal workstation: 1187 1188 ``` 1189 product family=Default string name=Thelio vendor=System76 sku=Default string version=thelio-r1 1190 ``` 1191 1192 > **NOTE**: Some of the values such as serial numbers are shown as unknown 1193 > because the Linux kernel by default disallows access to those fields if 1194 > you're not running as root. They will be populated if it runs as root or 1195 > otherwise you may see warnings like the following: 1196 1197 ``` 1198 WARNING: Unable to read product_serial: open /sys/class/dmi/id/product_serial: permission denied 1199 ``` 1200 1201 You can ignore them or use the [Disabling warning messages](#disabling-warning-messages) 1202 feature to quiet things down. 1203 1204 ## Advanced Usage 1205 1206 ### Disabling warning messages 1207 1208 When `ghw` isn't able to retrieve some information, it may print certain 1209 warning messages to `stderr`. To disable these warnings, simply set the 1210 `GHW_DISABLE_WARNINGS` environs variable: 1211 1212 ``` 1213 $ ghwc memory 1214 WARNING: 1215 Could not determine total physical bytes of memory. This may 1216 be due to the host being a virtual machine or container with no 1217 /var/log/syslog file, or the current user may not have necessary 1218 privileges to read the syslog. We are falling back to setting the 1219 total physical amount of memory to the total usable amount of memory 1220 memory (24GB physical, 24GB usable) 1221 ``` 1222 1223 ``` 1224 $ GHW_DISABLE_WARNINGS=1 ghwc memory 1225 memory (24GB physical, 24GB usable) 1226 ``` 1227 1228 You can disable warning programmatically using the `WithDisableWarnings` option: 1229 1230 ```go 1231 1232 import ( 1233 "github.com/jaypipes/ghw" 1234 ) 1235 1236 mem, err := ghw.Memory(ghw.WithDisableWarnings()) 1237 ``` 1238 1239 `WithDisableWarnings` is a alias for the `WithNullAlerter` option, which in turn 1240 leverages the more general `Alerter` feature of ghw. 1241 1242 You may supply a `Alerter` to ghw to redirect all the warnings there, like 1243 logger objects (see for example golang's stdlib `log.Logger`). 1244 `Alerter` is in fact the minimal logging interface `ghw needs. 1245 To learn more, please check the `option.Alerter` interface and the `ghw.WithAlerter()` 1246 function. 1247 1248 ### Overriding the root mountpoint `ghw` uses 1249 1250 When `ghw` looks for information about the host system, it considers `/` as its 1251 root mountpoint. So, for example, when looking up CPU information on a Linux 1252 system, `ghw.CPU()` will use the path `/proc/cpuinfo`. 1253 1254 If you are calling `ghw` from a system that has an alternate root mountpoint, 1255 you can either set the `GHW_CHROOT` environment variable to that alternate 1256 path, or call one of the functions like `ghw.CPU()` or `ghw.Memory()` with the 1257 `ghw.WithChroot()` modifier. 1258 1259 For example, if you are executing from within an application container that has 1260 bind-mounted the root host filesystem to the mount point `/host`, you would set 1261 `GHW_CHROOT` to `/host` so that `ghw` can find `/proc/cpuinfo` at 1262 `/host/proc/cpuinfo`. 1263 1264 Alternately, you can use the `ghw.WithChroot()` function like so: 1265 1266 ```go 1267 cpu, err := ghw.CPU(ghw.WithChroot("/host")) 1268 ``` 1269 1270 ### Serialization to JSON or YAML 1271 1272 All of the `ghw` `XXXInfo` structs -- e.g. `ghw.CPUInfo` -- have two methods 1273 for producing a serialized JSON or YAML string representation of the contained 1274 information: 1275 1276 * `JSONString()` returns a string containing the information serialized into 1277 JSON. It accepts a single boolean parameter indicating whether to use 1278 indentation when outputting the string 1279 * `YAMLString()` returns a string containing the information serialized into 1280 YAML 1281 1282 ```go 1283 package main 1284 1285 import ( 1286 "fmt" 1287 1288 "github.com/jaypipes/ghw" 1289 ) 1290 1291 func main() { 1292 mem, err := ghw.Memory() 1293 if err != nil { 1294 fmt.Printf("Error getting memory info: %v", err) 1295 } 1296 1297 fmt.Printf("%s", mem.YAMLString()) 1298 } 1299 ``` 1300 1301 the above example code prints the following out on my local workstation: 1302 1303 ``` 1304 memory: 1305 supported_page_sizes: 1306 - 1073741824 1307 - 2097152 1308 total_physical_bytes: 25263415296 1309 total_usable_bytes: 25263415296 1310 ``` 1311 1312 ### Overriding a specific mountpoint (Linux only) 1313 1314 When running inside containers, it can be cumbersome to only override the root 1315 mountpoint. Inside containers, when granting access to the host file systems, 1316 it is common to bind-mount them to a non-standard location, like `/sys` on 1317 `/host-sys` or `/proc` to `/host-proc`. It is rarer to mount them to a common 1318 subtree (e.g. `/sys` to `/host/sys` and `/proc` to `/host/proc`...) 1319 1320 To better cover this use case, `ghw.WithPathOverrides()` can be used to supply 1321 a mapping of directories to mountpoints, like this example shows: 1322 1323 ```go 1324 cpu, err := ghw.CPU(ghw.WithPathOverrides(ghw.PathOverrides{ 1325 "/proc": "/host-proc", 1326 "/sys": "/host-sys", 1327 })) 1328 ``` 1329 1330 **NOTE**: This feature works in addition and is composable with the 1331 `ghw.WithChroot()` function and `GHW_CHROOT` environment variable. 1332 1333 ### Reading hardware information from a `ghw` snapshot (Linux only) 1334 1335 The `ghw-snapshot` tool can create a snapshot of a host's hardware information. 1336 1337 Please read [`SNAPSHOT.md`](SNAPSHOT.md) to learn about creating snapshots with 1338 the `ghw-snapshot` tool. 1339 1340 You can make `ghw` read hardware information from a snapshot created with 1341 `ghw-snapshot` using environment variables or programmatically. 1342 1343 Use the `GHW_SNAPSHOT_PATH` environment variable to specify the filepath to a 1344 snapshot that `ghw` will read to determine hardware information. All the needed 1345 chroot changes will be automatically performed. By default, the snapshot is 1346 unpacked into a temporary directory managed by `ghw`. This temporary directory 1347 is automatically deleted when `ghw` is finished reading the snapshot. 1348 1349 Three other environment variables are relevant if and only if `GHW_SNAPSHOT_PATH` 1350 is not empty: 1351 1352 * `GHW_SNAPSHOT_ROOT` let users specify the directory on which the snapshot 1353 should be unpacked. This moves the ownership of that directory from `ghw` to 1354 users. For this reason, `ghw` will *not* automatically clean up the content 1355 unpacked into `GHW_SNAPSHOT_ROOT`. 1356 * `GHW_SNAPSHOT_EXCLUSIVE` tells `ghw` that the directory is meant only to 1357 contain the given snapshot, thus `ghw` will *not* attempt to unpack it unless 1358 the directory is empty. You can use both `GHW_SNAPSHOT_ROOT` and 1359 `GHW_SNAPSHOT_EXCLUSIVE` to make sure `ghw` unpacks the snapshot only once 1360 regardless of how many `ghw` packages (e.g. cpu, memory) access it. Set the 1361 value of this environment variable to any non-empty string. 1362 * `GHW_SNAPSHOT_PRESERVE` tells `ghw` not to clean up the unpacked snapshot. 1363 Set the value of this environment variable to any non-empty string. 1364 1365 ```go 1366 cpu, err := ghw.CPU(ghw.WithSnapshot(ghw.SnapshotOptions{ 1367 Path: "/path/to/linux-amd64-d4771ed3300339bc75f856be09fc6430.tar.gz", 1368 })) 1369 1370 1371 myRoot := "/my/safe/directory" 1372 cpu, err := ghw.CPU(ghw.WithSnapshot(ghw.SnapshotOptions{ 1373 Path: "/path/to/linux-amd64-d4771ed3300339bc75f856be09fc6430.tar.gz", 1374 Root: &myRoot, 1375 })) 1376 1377 myOtherRoot := "/my/other/safe/directory" 1378 cpu, err := ghw.CPU(ghw.WithSnapshot(ghw.SnapshotOptions{ 1379 Path: "/path/to/linux-amd64-d4771ed3300339bc75f856be09fc6430.tar.gz", 1380 Root: &myOtherRoot, 1381 Exclusive: true, 1382 })) 1383 ``` 1384 1385 ### Creating snapshots 1386 1387 You can create `ghw` snapshots using the `ghw-snapshot` tool or 1388 programmatically using the `pkg/snapshot` package. 1389 1390 Below is an example of creating a `ghw` snapshot using the `pkg/snapshot` 1391 package. 1392 1393 ```go 1394 1395 import ( 1396 "fmt" 1397 "os" 1398 1399 "github.com/jaypipes/ghw/pkg/snapshot" 1400 ) 1401 1402 // ... 1403 1404 scratchDir, err := os.MkdirTemp("", "ghw-snapshot-*") 1405 if err != nil { 1406 fmt.Printf("Error creating clone directory: %v", err) 1407 } 1408 defer os.RemoveAll(scratchDir) 1409 1410 // this step clones all the files and directories ghw cares about 1411 if err := snapshot.CloneTreeInto(scratchDir); err != nil { 1412 fmt.Printf("error cloning into %q: %v", scratchDir, err) 1413 } 1414 1415 // optionally, you may add extra content into your snapshot. 1416 // ghw will ignore the extra content. 1417 // Glob patterns like `filepath.Glob` are supported. 1418 fileSpecs := []string{ 1419 "/proc/cmdline", 1420 } 1421 1422 // options allows the client code to optionally deference symlinks, or copy 1423 // them into the cloned tree as symlinks 1424 var opts *snapshot.CopyFileOptions 1425 if err := snapshot.CopyFilesInto(fileSpecs, scratchDir, opts); err != nil { 1426 fmt.Printf("error cloning extra files into %q: %v", scratchDir, err) 1427 } 1428 1429 // automates the creation of the gzipped tarball out of the given tree. 1430 if err := snapshot.PackFrom("my-snapshot.tgz", scratchDir); err != nil { 1431 fmt.Printf("error packing %q into %q: %v", scratchDir, *output, err) 1432 } 1433 ``` 1434 1435 ## Calling external programs 1436 1437 By default `ghw` may call external programs, for example `ethtool`, to learn 1438 about hardware capabilities. In some rare circumstances it may be useful to 1439 opt out from this behaviour and rely only on the data provided by 1440 pseudo-filesystems, like sysfs. 1441 1442 The most common use case is when we want to read a snapshot from `ghw`. In 1443 these cases the information provided by tools will be inconsistent with the 1444 data from the snapshot - since they will be run on a different host than the 1445 host the snapshot was created for. 1446 1447 To prevent `ghw` from calling external tools, set the `GHW_DISABLE_TOOLS` 1448 environment variable to any value, or, programmatically, use the 1449 `ghw.WithDisableTools()` function. The default behaviour of ghw is to call 1450 external tools when available. 1451 1452 > **WARNING**: on all platforms, disabling external tools make ghw return less 1453 > data. Unless noted otherwise, there is _no fallback flow_ if external tools 1454 > are disabled. On MacOSX/Darwin, disabling external tools disables block 1455 > support entirely 1456 1457 ## Developers 1458 1459 [Contributions](CONTRIBUTING.md) to `ghw` are welcomed! Fork the repo on GitHub 1460 and submit a pull request with your proposed changes. Or, feel free to log an 1461 issue for a feature request or bug report.