introduce basic fact collection

This commit is contained in:
2025-07-20 23:14:21 -04:00
parent 62336753c6
commit 3a47fa6706
58 changed files with 2801 additions and 107 deletions

21
client/vendor/github.com/zcalusic/sysinfo/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2016 Zlatko Čalušić
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

204
client/vendor/github.com/zcalusic/sysinfo/README.md generated vendored Normal file
View File

@@ -0,0 +1,204 @@
# Sysinfo
[![Go Report Card](https://goreportcard.com/badge/github.com/zcalusic/sysinfo)](https://goreportcard.com/report/github.com/zcalusic/sysinfo)
[![GoDoc](https://godoc.org/github.com/zcalusic/sysinfo?status.svg)](https://godoc.org/github.com/zcalusic/sysinfo)
[![License](https://img.shields.io/badge/license-MIT-a31f34.svg?maxAge=2592000)](https://github.com/zcalusic/sysinfo/blob/master/LICENSE)
[![Powered by](https://img.shields.io/badge/powered_by-Go-5272b4.svg?maxAge=2592000)](https://go.dev/)
[![Platform](https://img.shields.io/badge/platform-Linux-009bde.svg?maxAge=2592000)](https://www.linuxfoundation.org/)
Package sysinfo is a Go library providing Linux OS / kernel / hardware system information. It's completely standalone,
has no dependencies on the host system and doesn't execute external programs. It collects only "inventory type"
information, things that don't change often.
## Code Example
```go
package main
import (
"encoding/json"
"fmt"
"log"
"os/user"
"github.com/zcalusic/sysinfo"
)
func main() {
current, err := user.Current()
if err != nil {
log.Fatal(err)
}
if current.Uid != "0" {
log.Fatal("requires superuser privilege")
}
var si sysinfo.SysInfo
si.GetSysInfo()
data, err := json.MarshalIndent(&si, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
}
```
## Motivation
I couldn't find any self-contained library that would provide set of data/features I needed. So another sysinfo was
born.
The purpose of the library is to collect only inventory info. No metrics like CPU usage or load average will be added.
The rule of thumb is, if it's changing during the day, every day, it doesn't belong in the library.
The library should work well on any modern/supported Linux distribution. There are no plans to add support for older
unsupported Linux distributions/kernels, to keep the code clean and robust and reduce the maintenance burden.
## Requirements
Sysinfo requires:
- Linux kernel 4.2 or later
- access to /sys & /proc Linux virtual file systems
- access to various files in /etc, /var, /run FS hierarchy
- superuser privileges (to access SMBIOS/DMI table and detect RAM size and properties)
Sysinfo doesn't require ANY other external utility on the target system, which is its primary strength, IMHO.
It depends on Linux internals heavily, so there are no plans to support other operating systems.
## Installation
Just use go get.
```
go get github.com/zcalusic/sysinfo
```
There's also a very simple utility demonstrating sysinfo library capabilities. Start it (as superuser) to get pretty
formatted JSON output of all the info that sysinfo library provides. Due to its simplicity, the source code of the
utility also doubles down as an example of how to use the library.
```
go get github.com/zcalusic/sysinfo/cmd/sysinfo
```
--
Build demo utility in Docker container: https://github.com/mattscilipoti/compile_sysinfo
## Sample output
```json
{
"sysinfo": {
"version": "0.9.1",
"timestamp": "2016-09-24T13:30:28.369498856+02:00"
},
"node": {
"hostname": "web12",
"machineid": "04aa55927ebd390829367c1757b98cac",
"timezone": "Europe/Zagreb"
},
"os": {
"name": "CentOS Linux 7 (Core)",
"vendor": "centos",
"version": "7",
"release": "7.2.1511",
"architecture": "amd64"
},
"kernel": {
"release": "3.10.0-327.13.1.el7.x86_64",
"version": "#1 SMP Thu Mar 31 16:04:38 UTC 2016",
"architecture": "x86_64"
},
"product": {
"name": "RH2288H V3",
"vendor": "Huawei",
"version": "V100R003",
"serial": "2103711GEL10F3430658"
},
"board": {
"name": "BC11HGSA0",
"vendor": "Huawei",
"version": "V100R003",
"serial": "033HXVCNG3107624"
},
"chassis": {
"type": 17,
"vendor": "Huawei"
},
"bios": {
"vendor": "Insyde Corp.",
"version": "3.16",
"date": "03/16/2016"
},
"cpu": {
"vendor": "GenuineIntel",
"model": "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz",
"speed": 2400,
"cache": 20480,
"cpus": 1,
"cores": 8,
"threads": 16
},
"memory": {
"type": "DRAM",
"speed": 2133,
"size": 65536
},
"storage": [
{
"name": "sda",
"driver": "sd",
"vendor": "ATA",
"model": "ST9500620NS",
"serial": "9XF2HZ9K",
"size": 500
}
],
"network": [
{
"name": "enp3s0f1",
"driver": "ixgbe",
"macaddress": "84:ad:5a:e3:79:71",
"port": "fibre",
"speed": 10000
}
]
}
```
## Contributors
Contributors are welcome, just open a new issue / pull request.
## License
```
The MIT License (MIT)
Copyright © 2016 Zlatko Čalušić
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

18
client/vendor/github.com/zcalusic/sysinfo/bios.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
// BIOS information.
type BIOS struct {
Vendor string `json:"vendor,omitempty"`
Version string `json:"version,omitempty"`
Date string `json:"date,omitempty"`
}
func (si *SysInfo) getBIOSInfo() {
si.BIOS.Vendor = slurpFile("/sys/class/dmi/id/bios_vendor")
si.BIOS.Version = slurpFile("/sys/class/dmi/id/bios_version")
si.BIOS.Date = slurpFile("/sys/class/dmi/id/bios_date")
}

22
client/vendor/github.com/zcalusic/sysinfo/board.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
// Board information.
type Board struct {
Name string `json:"name,omitempty"`
Vendor string `json:"vendor,omitempty"`
Version string `json:"version,omitempty"`
Serial string `json:"serial,omitempty"`
AssetTag string `json:"assettag,omitempty"`
}
func (si *SysInfo) getBoardInfo() {
si.Board.Name = slurpFile("/sys/class/dmi/id/board_name")
si.Board.Vendor = slurpFile("/sys/class/dmi/id/board_vendor")
si.Board.Version = slurpFile("/sys/class/dmi/id/board_version")
si.Board.Serial = slurpFile("/sys/class/dmi/id/board_serial")
si.Board.AssetTag = slurpFile("/sys/class/dmi/id/board_asset_tag")
}

26
client/vendor/github.com/zcalusic/sysinfo/chassis.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import "strconv"
// Chassis information.
type Chassis struct {
Type uint `json:"type,omitempty"`
Vendor string `json:"vendor,omitempty"`
Version string `json:"version,omitempty"`
Serial string `json:"serial,omitempty"`
AssetTag string `json:"assettag,omitempty"`
}
func (si *SysInfo) getChassisInfo() {
if chtype, err := strconv.ParseUint(slurpFile("/sys/class/dmi/id/chassis_type"), 10, 64); err == nil {
si.Chassis.Type = uint(chtype)
}
si.Chassis.Vendor = slurpFile("/sys/class/dmi/id/chassis_vendor")
si.Chassis.Version = slurpFile("/sys/class/dmi/id/chassis_version")
si.Chassis.Serial = slurpFile("/sys/class/dmi/id/chassis_serial")
si.Chassis.AssetTag = slurpFile("/sys/class/dmi/id/chassis_asset_tag")
}

85
client/vendor/github.com/zcalusic/sysinfo/cpu.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"bufio"
"fmt"
"os"
"regexp"
"runtime"
"strconv"
"strings"
)
// CPU information.
type CPU struct {
Vendor string `json:"vendor,omitempty"`
Model string `json:"model,omitempty"`
Speed uint `json:"speed,omitempty"` // CPU clock rate in MHz
Cache uint `json:"cache,omitempty"` // CPU cache size in KB
Cpus uint `json:"cpus,omitempty"` // number of physical CPUs
Cores uint `json:"cores,omitempty"` // number of physical CPU cores
Threads uint `json:"threads,omitempty"` // number of logical (HT) CPU cores
}
var (
reTwoColumns = regexp.MustCompile("\t+: ")
reExtraSpace = regexp.MustCompile(" +")
reCacheSize = regexp.MustCompile(`^(\d+) KB$`)
)
func (si *SysInfo) getCPUInfo() {
si.CPU.Threads = uint(runtime.NumCPU())
f, err := os.Open("/proc/cpuinfo")
if err != nil {
return
}
defer f.Close()
cpu := make(map[string]bool)
core := make(map[string]bool)
var cpuID string
s := bufio.NewScanner(f)
for s.Scan() {
if sl := reTwoColumns.Split(s.Text(), 2); sl != nil {
switch sl[0] {
case "physical id":
cpuID = sl[1]
cpu[cpuID] = true
case "core id":
coreID := fmt.Sprintf("%s/%s", cpuID, sl[1])
core[coreID] = true
case "vendor_id":
if si.CPU.Vendor == "" {
si.CPU.Vendor = sl[1]
}
case "model name":
if si.CPU.Model == "" {
// CPU model, as reported by /proc/cpuinfo, can be a bit ugly. Clean up...
model := reExtraSpace.ReplaceAllLiteralString(sl[1], " ")
si.CPU.Model = strings.Replace(model, "- ", "-", 1)
}
case "cache size":
if si.CPU.Cache == 0 {
if m := reCacheSize.FindStringSubmatch(sl[1]); m != nil {
if cache, err := strconv.ParseUint(m[1], 10, 64); err == nil {
si.CPU.Cache = uint(cache)
}
}
}
}
}
}
if s.Err() != nil {
return
}
si.CPU.Cpus = uint(len(cpu))
si.CPU.Cores = uint(len(core))
}

View File

@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,9 @@
// Copyright © 2018 Zlatko Čalušić
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
// Package cpuid gives Go programs access to CPUID opcode.
package cpuid
// CPUID returns processor identification and feature information.
func CPUID(info *[4]uint32, ax uint32)

View File

@@ -0,0 +1,16 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !gccgo
TEXT ·CPUID(SB),$0-8
MOVL ax+4(FP), AX
CPUID
MOVL info+0(FP), DI
MOVL AX, 0(DI)
MOVL BX, 4(DI)
MOVL CX, 8(DI)
MOVL DX, 12(DI)
RET

View File

@@ -0,0 +1,16 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !gccgo
TEXT ·CPUID(SB),$0-12
MOVL ax+8(FP), AX
CPUID
MOVQ info+0(FP), DI
MOVL AX, 0(DI)
MOVL BX, 4(DI)
MOVL CX, 8(DI)
MOVL DX, 12(DI)
RET

View File

@@ -0,0 +1,10 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !386,!amd64,!gccgo
#include "textflag.h"
TEXT ·CPUID(SB),NOSPLIT,$0-0
RET

View File

@@ -0,0 +1,65 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"strings"
"unsafe"
"github.com/zcalusic/sysinfo/cpuid"
)
// https://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID
var hvmap = map[string]string{
"bhyve bhyve ": "bhyve",
"KVMKVMKVM": "kvm",
"Microsoft Hv": "hyperv",
" lrpepyh vr": "parallels",
"VMwareVMware": "vmware",
"XenVMMXenVMM": "xenhvm",
}
func isHypervisorActive() bool {
var info [4]uint32
cpuid.CPUID(&info, 0x1)
return info[2]&(1<<31) != 0
}
func getHypervisorCpuid(ax uint32) string {
var info [4]uint32
cpuid.CPUID(&info, ax)
return hvmap[strings.TrimRight(string((*[12]byte)(unsafe.Pointer(&info[1]))[:]), "\000")]
}
func (si *SysInfo) getHypervisor() {
if !isHypervisorActive() {
if hypervisorType := slurpFile("/sys/hypervisor/type"); hypervisorType != "" {
if hypervisorType == "xen" {
si.Node.Hypervisor = "xenpv"
}
}
return
}
// KVM has been caught to move its real signature to this leaf, and put something completely different in the
// standard location. So this leaf must be checked first.
if hv := getHypervisorCpuid(0x40000100); hv != "" {
si.Node.Hypervisor = hv
return
}
if hv := getHypervisorCpuid(0x40000000); hv != "" {
si.Node.Hypervisor = hv
return
}
// getBIOSInfo() must have run first, to detect BIOS vendor
if si.BIOS.Vendor == "Bochs" {
si.Node.Hypervisor = "bochs"
return
}
si.Node.Hypervisor = "unknown"
}

View File

@@ -0,0 +1,14 @@
//go:build darwin
// +build darwin
package sysinfo
// Kernel information.
type Kernel struct {
Release string `json:"release,omitempty"`
Version string `json:"version,omitempty"`
Architecture string `json:"architecture,omitempty"`
}
func (si *SysInfo) getKernelInfo() {
}

View File

@@ -0,0 +1,33 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
//go:build linux
// +build linux
package sysinfo
import (
"strings"
"syscall"
"unsafe"
)
// Kernel information.
type Kernel struct {
Release string `json:"release,omitempty"`
Version string `json:"version,omitempty"`
Architecture string `json:"architecture,omitempty"`
}
func (si *SysInfo) getKernelInfo() {
si.Kernel.Release = slurpFile("/proc/sys/kernel/osrelease")
si.Kernel.Version = slurpFile("/proc/sys/kernel/version")
var uname syscall.Utsname
if err := syscall.Uname(&uname); err != nil {
return
}
si.Kernel.Architecture = strings.TrimRight(string((*[65]byte)(unsafe.Pointer(&uname.Machine))[:]), "\000")
}

121
client/vendor/github.com/zcalusic/sysinfo/memory.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"bytes"
"encoding/binary"
"os"
"strconv"
)
// Memory information.
type Memory struct {
Type string `json:"type,omitempty"`
Speed uint `json:"speed,omitempty"` // RAM data rate in MT/s
Size uint `json:"size,omitempty"` // RAM size in MB
}
func word(data []byte, index int) uint16 {
return binary.LittleEndian.Uint16(data[index : index+2])
}
func dword(data []byte, index int) uint32 {
return binary.LittleEndian.Uint32(data[index : index+4])
}
func qword(data []byte, index int) uint64 {
return binary.LittleEndian.Uint64(data[index : index+8])
}
func (si *SysInfo) getMemoryInfo() {
dmi, err := os.ReadFile("/sys/firmware/dmi/tables/DMI")
if err != nil {
// Xen hypervisor
if targetKB := slurpFile("/sys/devices/system/xen_memory/xen_memory0/target_kb"); targetKB != "" {
si.Memory.Type = "DRAM"
size, _ := strconv.ParseUint(targetKB, 10, 64)
si.Memory.Size = uint(size) / 1024
}
return
}
si.Memory.Size = 0
var memSizeAlt uint
loop:
for p := 0; p < len(dmi)-1; {
recType := dmi[p]
recLen := dmi[p+1]
switch recType {
case 4:
if si.CPU.Speed == 0 {
si.CPU.Speed = uint(word(dmi, p+0x16))
}
case 17:
size := uint(word(dmi, p+0x0c))
if size == 0 || size == 0xffff || size&0x8000 == 0x8000 {
break
}
if size == 0x7fff {
if recLen >= 0x20 {
size = uint(dword(dmi, p+0x1c))
} else {
break
}
}
si.Memory.Size += size
if si.Memory.Type == "" {
// SMBIOS Reference Specification Version 3.0.0, page 92
memTypes := [...]string{
"Other", "Unknown", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM", "ROM", "FLASH",
"EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", "SDRAM", "SGRAM", "RDRAM",
"DDR", "DDR2", "DDR2 FB-DIMM", "Reserved", "Reserved", "Reserved", "DDR3",
"FBD2", "DDR4", "LPDDR", "LPDDR2", "LPDDR3", "LPDDR4",
}
if index := int(dmi[p+0x12]); index >= 1 && index <= len(memTypes) {
si.Memory.Type = memTypes[index-1]
}
}
if si.Memory.Speed == 0 && recLen >= 0x17 {
if speed := uint(word(dmi, p+0x15)); speed != 0 {
si.Memory.Speed = speed
}
}
case 19:
start := uint(dword(dmi, p+0x04))
end := uint(dword(dmi, p+0x08))
if start == 0xffffffff && end == 0xffffffff {
if recLen >= 0x1f {
start64 := qword(dmi, p+0x0f)
end64 := qword(dmi, p+0x17)
memSizeAlt += uint((end64 - start64 + 1) / 1048576)
}
} else {
memSizeAlt += (end - start + 1) / 1024
}
case 127:
break loop
}
for p += int(recLen); p < len(dmi)-1; {
if bytes.Equal(dmi[p:p+2], []byte{0, 0}) {
p += 2
break
}
p++
}
}
// Sometimes DMI type 17 has no information, so we fall back to DMI type 19, to at least get the RAM size.
if si.Memory.Size == 0 && memSizeAlt > 0 {
si.Memory.Type = "DRAM"
si.Memory.Size = memSizeAlt
}
}

18
client/vendor/github.com/zcalusic/sysinfo/meta.go generated vendored Normal file
View File

@@ -0,0 +1,18 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import "time"
// Meta information.
type Meta struct {
Version string `json:"version"`
Timestamp time.Time `json:"timestamp"`
}
func (si *SysInfo) getMetaInfo() {
si.Meta.Version = Version
si.Meta.Timestamp = time.Now()
}

146
client/vendor/github.com/zcalusic/sysinfo/network.go generated vendored Normal file
View File

@@ -0,0 +1,146 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"os"
"path"
"strings"
"syscall"
"unsafe"
)
// NetworkDevice information.
type NetworkDevice struct {
Name string `json:"name,omitempty"`
Driver string `json:"driver,omitempty"`
MACAddress string `json:"macaddress,omitempty"`
Port string `json:"port,omitempty"`
Speed uint `json:"speed,omitempty"` // device max supported speed in Mbps
}
func getPortType(supp uint32) (port string) {
for i, p := range [...]string{"tp", "aui", "mii", "fibre", "bnc"} {
if supp&(1<<uint(i+7)) > 0 {
port += p + "/"
}
}
port = strings.TrimRight(port, "/")
return
}
func getMaxSpeed(supp uint32) (speed uint) {
// Fancy, right?
switch {
case supp&0x78000000 > 0:
speed = 56000
case supp&0x07800000 > 0:
speed = 40000
case supp&0x00600000 > 0:
speed = 20000
case supp&0x001c1000 > 0:
speed = 10000
case supp&0x00008000 > 0:
speed = 2500
case supp&0x00020030 > 0:
speed = 1000
case supp&0x0000000c > 0:
speed = 100
case supp&0x00000003 > 0:
speed = 10
}
return
}
func getSupported(name string) uint32 {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
if err != nil {
return 0
}
defer syscall.Close(fd)
// struct ethtool_cmd from /usr/include/linux/ethtool.h
var ethtool struct {
Cmd uint32
Supported uint32
Advertising uint32
Speed uint16
Duplex uint8
Port uint8
PhyAddress uint8
Transceiver uint8
Autoneg uint8
MdioSupport uint8
Maxtxpkt uint32
Maxrxpkt uint32
SpeedHi uint16
EthTpMdix uint8
Reserved2 uint8
LpAdvertising uint32
Reserved [2]uint32
}
// ETHTOOL_GSET from /usr/include/linux/ethtool.h
const GSET = 0x1
ethtool.Cmd = GSET
// struct ifreq from /usr/include/linux/if.h
var ifr struct {
Name [16]byte
Data uintptr
}
copy(ifr.Name[:], name+"\000")
ifr.Data = uintptr(unsafe.Pointer(&ethtool))
// SIOCETHTOOL from /usr/include/linux/sockios.h
const SIOCETHTOOL = 0x8946
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(SIOCETHTOOL), uintptr(unsafe.Pointer(&ifr)))
if errno == 0 {
return ethtool.Supported
}
return 0
}
func (si *SysInfo) getNetworkInfo() {
sysClassNet := "/sys/class/net"
devices, err := os.ReadDir(sysClassNet)
if err != nil {
return
}
si.Network = make([]NetworkDevice, 0)
for _, link := range devices {
fullpath := path.Join(sysClassNet, link.Name())
dev, err := os.Readlink(fullpath)
if err != nil {
continue
}
if strings.HasPrefix(dev, "../../devices/virtual/") {
continue
}
supp := getSupported(link.Name())
device := NetworkDevice{
Name: link.Name(),
MACAddress: slurpFile(path.Join(fullpath, "address")),
Port: getPortType(supp),
Speed: getMaxSpeed(supp),
}
if driver, err := os.Readlink(path.Join(fullpath, "device", "driver")); err == nil {
device.Driver = path.Base(driver)
}
si.Network = append(si.Network, device)
}
}

99
client/vendor/github.com/zcalusic/sysinfo/node.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"bufio"
"os"
"strings"
)
// Node information.
type Node struct {
Hostname string `json:"hostname,omitempty"`
MachineID string `json:"machineid,omitempty"`
Hypervisor string `json:"hypervisor,omitempty"`
Timezone string `json:"timezone,omitempty"`
}
func (si *SysInfo) getHostname() {
si.Node.Hostname = slurpFile("/proc/sys/kernel/hostname")
}
func (si *SysInfo) getSetMachineID() {
const pathSystemdMachineID = "/etc/machine-id"
const pathDbusMachineID = "/var/lib/dbus/machine-id"
systemdMachineID := slurpFile(pathSystemdMachineID)
dbusMachineID := slurpFile(pathDbusMachineID)
if systemdMachineID != "" && dbusMachineID != "" {
// All OK, just return the machine id.
if systemdMachineID == dbusMachineID {
si.Node.MachineID = systemdMachineID
return
}
// They both exist, but they don't match! Copy systemd machine id to DBUS machine id.
spewFile(pathDbusMachineID, systemdMachineID, 0444)
si.Node.MachineID = systemdMachineID
return
}
// Copy DBUS machine id to non-existent systemd machine id.
if systemdMachineID == "" && dbusMachineID != "" {
spewFile(pathSystemdMachineID, dbusMachineID, 0444)
si.Node.MachineID = dbusMachineID
return
}
// Copy systemd machine id to non-existent DBUS machine id.
if systemdMachineID != "" && dbusMachineID == "" {
spewFile(pathDbusMachineID, systemdMachineID, 0444)
si.Node.MachineID = systemdMachineID
return
}
}
func (si *SysInfo) getTimezone() {
const zoneInfoPrefix = "/usr/share/zoneinfo/"
if fi, err := os.Lstat("/etc/localtime"); err == nil {
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
if tzfile, err := os.Readlink("/etc/localtime"); err == nil {
tzfile = strings.TrimPrefix(tzfile, "..")
if strings.HasPrefix(tzfile, zoneInfoPrefix) {
si.Node.Timezone = strings.TrimPrefix(tzfile, zoneInfoPrefix)
return
}
}
}
}
if timezone := slurpFile("/etc/timezone"); timezone != "" {
si.Node.Timezone = timezone
return
}
if f, err := os.Open("/etc/sysconfig/clock"); err == nil {
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if sl := strings.Split(s.Text(), "="); len(sl) == 2 {
if sl[0] == "ZONE" {
si.Node.Timezone = strings.Trim(sl[1], `"`)
return
}
}
}
}
}
func (si *SysInfo) getNodeInfo() {
si.getHostname()
si.getSetMachineID()
si.getHypervisor()
si.getTimezone()
}

101
client/vendor/github.com/zcalusic/sysinfo/os.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"bufio"
"os"
"regexp"
"strings"
)
// OS information.
type OS struct {
Name string `json:"name,omitempty"`
Vendor string `json:"vendor,omitempty"`
Version string `json:"version,omitempty"`
Release string `json:"release,omitempty"`
Architecture string `json:"architecture,omitempty"`
}
var (
rePrettyName = regexp.MustCompile(`^PRETTY_NAME=(.*)$`)
reID = regexp.MustCompile(`^ID=(.*)$`)
reVersionID = regexp.MustCompile(`^VERSION_ID=(.*)$`)
reUbuntu = regexp.MustCompile(`[\( ]([\d\.]+)`)
reAlma = regexp.MustCompile(`^AlmaLinux release ([\d\.]+)`)
reCentOS = regexp.MustCompile(`^CentOS( Linux)? release ([\d\.]+)`)
reRocky = regexp.MustCompile(`^Rocky Linux release ([\d\.]+)`)
reRedHat = regexp.MustCompile(`[\( ]([\d\.]+)`)
)
func (si *SysInfo) getOSInfo() {
// This seems to be the best and most portable way to detect OS architecture (NOT kernel!)
if _, err := os.Stat("/lib64/ld-linux-x86-64.so.2"); err == nil {
si.OS.Architecture = "amd64"
} else if _, err := os.Stat("/lib/ld-linux.so.2"); err == nil {
si.OS.Architecture = "i386"
}
f, err := os.Open("/etc/os-release")
if err != nil {
return
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if m := rePrettyName.FindStringSubmatch(s.Text()); m != nil {
si.OS.Name = strings.Trim(m[1], `"`)
} else if m := reID.FindStringSubmatch(s.Text()); m != nil {
si.OS.Vendor = strings.Trim(m[1], `"`)
} else if m := reVersionID.FindStringSubmatch(s.Text()); m != nil {
si.OS.Version = strings.Trim(m[1], `"`)
}
}
switch si.OS.Vendor {
case "debian":
si.OS.Release = slurpFile("/etc/debian_version")
case "ubuntu":
if m := reUbuntu.FindStringSubmatch(si.OS.Name); m != nil {
si.OS.Release = m[1]
}
case "almalinux":
if release := slurpFile("/etc/almalinux-release"); release != "" {
if m := reAlma.FindStringSubmatch(release); m != nil {
si.OS.Release = m[1]
}
}
si.OS.Version = strings.Split(si.OS.Release, ".")[0]
case "centos":
if release := slurpFile("/etc/centos-release"); release != "" {
if m := reCentOS.FindStringSubmatch(release); m != nil {
si.OS.Release = m[2]
}
}
case "rocky":
if release := slurpFile("/etc/rocky-release"); release != "" {
if m := reRocky.FindStringSubmatch(release); m != nil {
si.OS.Release = m[1]
}
}
si.OS.Version = strings.Split(si.OS.Release, ".")[0]
case "rhel":
if release := slurpFile("/etc/redhat-release"); release != "" {
if m := reRedHat.FindStringSubmatch(release); m != nil {
si.OS.Release = m[1]
}
}
if si.OS.Release == "" {
if m := reRedHat.FindStringSubmatch(si.OS.Name); m != nil {
si.OS.Release = m[1]
}
}
}
}

42
client/vendor/github.com/zcalusic/sysinfo/product.go generated vendored Normal file
View File

@@ -0,0 +1,42 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import "github.com/google/uuid"
// Product information.
type Product struct {
Name string `json:"name,omitempty"`
Vendor string `json:"vendor,omitempty"`
Version string `json:"version,omitempty"`
Serial string `json:"serial,omitempty"`
UUID uuid.UUID `json:"uuid,omitempty"`
SKU string `json:"sku,omitempty"`
}
func (si *SysInfo) getProductInfo() {
si.Product.Name = slurpFile("/sys/class/dmi/id/product_name")
si.Product.Vendor = slurpFile("/sys/class/dmi/id/sys_vendor")
si.Product.Version = slurpFile("/sys/class/dmi/id/product_version")
si.Product.Serial = slurpFile("/sys/class/dmi/id/product_serial")
si.Product.SKU = slurpFile("/sys/class/dmi/id/product_sku")
uid, err := uuid.Parse(slurpFile("/sys/class/dmi/id/product_uuid"))
if err == nil {
si.Product.UUID = uid
}
// try a fallback to device-tree (ex: dmi is not available on ARM devices)
// full details: https://www.devicetree.org/specifications/
// on linux root path is /proc/device-tree (see: https://github.com/torvalds/linux/blob/v5.9/Documentation/ABI/testing/sysfs-firmware-ofw)
if si.Product.Name == "" {
si.Product.Name = slurpFile("/proc/device-tree/model")
}
if si.Product.Serial == "" {
si.Product.Serial = slurpFile("/proc/device-tree/serial-number")
}
}

104
client/vendor/github.com/zcalusic/sysinfo/storage.go generated vendored Normal file
View File

@@ -0,0 +1,104 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"bufio"
"os"
"path"
"strconv"
"strings"
)
// StorageDevice information.
type StorageDevice struct {
Name string `json:"name,omitempty"`
Driver string `json:"driver,omitempty"`
Vendor string `json:"vendor,omitempty"`
Model string `json:"model,omitempty"`
Serial string `json:"serial,omitempty"`
Size uint `json:"size,omitempty"` // device size in GB
}
func getSerial(name, fullpath string) (serial string) {
var f *os.File
var err error
// Modern location/format of the udev database.
if dev := slurpFile(path.Join(fullpath, "dev")); dev != "" {
if f, err = os.Open(path.Join("/run/udev/data", "b"+dev)); err == nil {
goto scan
}
}
// Legacy location/format of the udev database.
if f, err = os.Open(path.Join("/dev/.udev/db", "block:"+name)); err == nil {
goto scan
}
// No serial :(
return
scan:
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if sl := strings.Split(s.Text(), "="); len(sl) == 2 {
if sl[0] == "E:ID_SERIAL_SHORT" {
serial = sl[1]
break
}
}
}
return
}
func (si *SysInfo) getStorageInfo() {
sysBlock := "/sys/block"
devices, err := os.ReadDir(sysBlock)
if err != nil {
return
}
si.Storage = make([]StorageDevice, 0)
for _, link := range devices {
fullpath := path.Join(sysBlock, link.Name())
dev, err := os.Readlink(fullpath)
if err != nil {
continue
}
if strings.HasPrefix(dev, "../devices/virtual/") {
continue
}
// We could filter all removable devices here, but some systems boot from USB flash disks, and then we
// would filter them, too. So, let's filter only floppies and CD/DVD devices, and see how it pans out.
if strings.HasPrefix(dev, "../devices/platform/floppy") || slurpFile(path.Join(fullpath, "device", "type")) == "5" {
continue
}
device := StorageDevice{
Name: link.Name(),
Model: slurpFile(path.Join(fullpath, "device", "model")),
Serial: getSerial(link.Name(), fullpath),
}
if driver, err := os.Readlink(path.Join(fullpath, "device", "driver")); err == nil {
device.Driver = path.Base(driver)
}
if vendor := slurpFile(path.Join(fullpath, "device", "vendor")); !strings.HasPrefix(vendor, "0x") {
device.Vendor = vendor
}
size, _ := strconv.ParseUint(slurpFile(path.Join(fullpath, "size")), 10, 64)
device.Size = uint(size) / 1953125 // GiB
si.Storage = append(si.Storage, device)
}
}

49
client/vendor/github.com/zcalusic/sysinfo/sysinfo.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
// Package sysinfo is a Go library providing Linux OS / kernel / hardware system information.
package sysinfo
// SysInfo struct encapsulates all other information structs.
type SysInfo struct {
Meta Meta `json:"sysinfo"`
Node Node `json:"node"`
OS OS `json:"os"`
Kernel Kernel `json:"kernel"`
Product Product `json:"product"`
Board Board `json:"board"`
Chassis Chassis `json:"chassis"`
BIOS BIOS `json:"bios"`
CPU CPU `json:"cpu"`
Memory Memory `json:"memory"`
Storage []StorageDevice `json:"storage,omitempty"`
Network []NetworkDevice `json:"network,omitempty"`
}
// GetSysInfo gathers all available system information.
func (si *SysInfo) GetSysInfo() {
// Meta info
si.getMetaInfo()
// DMI info
si.getProductInfo()
si.getBoardInfo()
si.getChassisInfo()
si.getBIOSInfo()
// SMBIOS info
si.getMemoryInfo()
// Node info
si.getNodeInfo() // depends on BIOS info
// Hardware info
si.getCPUInfo() // depends on Node info
si.getStorageInfo()
si.getNetworkInfo()
// Software info
si.getOSInfo()
si.getKernelInfo()
}

26
client/vendor/github.com/zcalusic/sysinfo/util.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
import (
"os"
"strings"
)
// Read one-liner text files, strip newline.
func slurpFile(path string) string {
data, err := os.ReadFile(path)
if err != nil {
return ""
}
// Trim spaces & \u0000 \uffff
return strings.Trim(string(data), " \r\n\t\u0000\uffff")
}
// Write one-liner text files, add newline, ignore errors (best effort).
func spewFile(path string, data string, perm os.FileMode) {
_ = os.WriteFile(path, []byte(data+"\n"), perm)
}

8
client/vendor/github.com/zcalusic/sysinfo/version.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// Copyright © 2016 Zlatko Čalušić
//
// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
package sysinfo
// Version of the sysinfo library.
const Version = "1.1.3"