diff --git a/cmd/gather-sysinfo/gather-sysinfo.go b/cmd/gather-sysinfo/gather-sysinfo.go index 9058d339e3..cdfb2e0d09 100644 --- a/cmd/gather-sysinfo/gather-sysinfo.go +++ b/cmd/gather-sysinfo/gather-sysinfo.go @@ -67,7 +67,14 @@ func collectMachineinfo(knitOpts *knit.KnitOptions, destPath string) error { } func makeSnapshot(cmd *cobra.Command, knitOpts *knit.KnitOptions, opts *snapshotOptions, args []string) error { - fileSpecs := dedupExpectedContent(kniExpectedCloneContent(), snapshot.ExpectedCloneContent()) + ctx := cmd.Context() + + fileSpecs, err := snapshot.ExpectedCloneContent(ctx) + if err != nil { + return fmt.Errorf("error getting expected clone content: %v", err) + } + fileSpecs = dedupExpectedContent(kniExpectedCloneContent(), fileSpecs) + if opts.dumpList { for _, fileSpec := range fileSpecs { fmt.Printf("%s\n", fileSpec) @@ -79,12 +86,6 @@ func makeSnapshot(cmd *cobra.Command, knitOpts *knit.KnitOptions, opts *snapshot return fmt.Errorf("--output is required") } - if knitOpts.Debug { - snapshot.SetTraceFunction(func(msg string, args ...interface{}) { - knitOpts.Log.Printf(msg, args...) - }) - } - scratchDir, err := os.MkdirTemp("", "perf-must-gather-*") if err != nil { return err @@ -95,7 +96,7 @@ func makeSnapshot(cmd *cobra.Command, knitOpts *knit.KnitOptions, opts *snapshot fileSpecs = chrootFileSpecs(fileSpecs, opts.rootDir) } - if err := snapshot.CopyFilesInto(fileSpecs, scratchDir, nil); err != nil { + if err := snapshot.CopyFilesInto(ctx, fileSpecs, scratchDir, nil); err != nil { return fmt.Errorf("error cloning extra files into %q: %v", scratchDir, err) } @@ -115,10 +116,10 @@ func makeSnapshot(cmd *cobra.Command, knitOpts *knit.KnitOptions, opts *snapshot dest := opts.output if dest == "-" { - err = snapshot.PackWithWriter(os.Stdout, scratchDir) + err = snapshot.PackWithWriter(ctx, os.Stdout, scratchDir) dest = "stdout" } else { - err = snapshot.PackFrom(dest, scratchDir) + err = snapshot.PackFrom(ctx, dest, scratchDir) } if err != nil { return fmt.Errorf("error packing %q to %q: %v", scratchDir, dest, err) diff --git a/go.mod b/go.mod index b2af46981a..f216fde410 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/go-units v0.5.0 github.com/go-logr/stdr v1.2.2 github.com/google/go-cmp v0.7.0 - github.com/jaypipes/ghw v0.20.0 + github.com/jaypipes/ghw v0.23.0 github.com/kevinburke/go-bindata v3.24.0+incompatible github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 diff --git a/go.sum b/go.sum index 5fba4ca8dd..1eb730349d 100644 --- a/go.sum +++ b/go.sum @@ -265,8 +265,8 @@ github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+h github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jaypipes/ghw v0.20.0 h1:8efvHHtyrj0P4qVZ9KE43iW9tMThKoh6dEOo38f3a4w= -github.com/jaypipes/ghw v0.20.0/go.mod h1:GPrvwbtPoxYUenr74+nAnWbardIZq600vJDD5HnPsPE= +github.com/jaypipes/ghw v0.23.0 h1:WOL4hpLcIu1kIm+z5Oz19Tk1HNw/Sncrx/6GS8O0Kl0= +github.com/jaypipes/ghw v0.23.0/go.mod h1:fUNUjMZ0cjahKo+/u+32m9FutIx53Nkbi0Ti0m7j5HY= github.com/jaypipes/pcidb v1.1.1 h1:QmPhpsbmmnCwZmHeYAATxEaoRuiMAJusKYkUncMC0ro= github.com/jaypipes/pcidb v1.1.1/go.mod h1:x27LT2krrUgjf875KxQXKB0Ha/YXLdZRVmw6hH0G7g8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= diff --git a/pkg/performanceprofile/profilecreator/cmd/info.go b/pkg/performanceprofile/profilecreator/cmd/info.go index 2b5d0caa6f..331d1e89b8 100644 --- a/pkg/performanceprofile/profilecreator/cmd/info.go +++ b/pkg/performanceprofile/profilecreator/cmd/info.go @@ -41,6 +41,16 @@ func executeInfoMode(mustGatherDirPath string, createForHypershift bool, infoOpt if err != nil { return fmt.Errorf("failed to parse the cluster data: %w", err) } + defer func() { + for _, handlers := range clusterData { + for _, handler := range handlers { + if err := handler.Cleanup(); err != nil { + Alert("Warning: failed to cleanup handler: %v\n", err) + } + } + } + }() + clusterInfo := makeClusterInfoFromClusterData(clusterData) if err := showClusterInfo(clusterInfo, infoOpts); err != nil { return fmt.Errorf("unable to show cluster info %w", err) diff --git a/pkg/performanceprofile/profilecreator/cmd/root.go b/pkg/performanceprofile/profilecreator/cmd/root.go index 037bfad7b8..0829ccfbba 100644 --- a/pkg/performanceprofile/profilecreator/cmd/root.go +++ b/pkg/performanceprofile/profilecreator/cmd/root.go @@ -159,6 +159,13 @@ func NewRootCommand() *cobra.Command { if err != nil { return err } + defer func() { + for _, handler := range nodesHandlers { + if err := handler.Cleanup(); err != nil { + Alert("Warning: failed to cleanup handler: %v\n", err) + } + } + }() err = profilecreator.EnsureNodesHaveTheSameHardware(nodesHandlers, tols) if err != nil { diff --git a/pkg/performanceprofile/profilecreator/ghwhandler.go b/pkg/performanceprofile/profilecreator/ghwhandler.go index 8488912f55..dd83ab2f74 100644 --- a/pkg/performanceprofile/profilecreator/ghwhandler.go +++ b/pkg/performanceprofile/profilecreator/ghwhandler.go @@ -21,10 +21,12 @@ import ( "os" "path" "sort" + "strings" "github.com/jaypipes/ghw" "github.com/jaypipes/ghw/pkg/cpu" "github.com/jaypipes/ghw/pkg/option" + "github.com/jaypipes/ghw/pkg/snapshot" "github.com/jaypipes/ghw/pkg/topology" v1 "k8s.io/api/core/v1" @@ -38,21 +40,50 @@ func NewGHWHandler(mustGatherDirPath string, node *v1.Node) (*GHWHandler, error) if err != nil { return nil, fmt.Errorf("can't obtain the node path %s: %v", nodeName, err) } - _, err = os.Stat(path.Join(nodepath, nodeName, sysInfoFileName)) + snapshotPath := path.Join(nodepath, nodeName, sysInfoFileName) + _, err = os.Stat(snapshotPath) if err != nil { return nil, fmt.Errorf("can't obtain the path: %s for node %s: %v", nodeName, nodepath, err) } - options := ghw.WithSnapshot(ghw.SnapshotOptions{ - Path: path.Join(nodepath, nodeName, sysInfoFileName), - }) - ghwHandler := &GHWHandler{snapShotOptions: options, Node: node} + + var chrootPath string + var tmpDir string + + if strings.HasSuffix(snapshotPath, ".tgz") || strings.HasSuffix(snapshotPath, ".tar.gz") { + tmpDir, err = snapshot.Unpack(snapshotPath) + if err != nil { + if tmpDir != "" { + _ = os.RemoveAll(tmpDir) + } + return nil, fmt.Errorf("failed to unpack snapshot %s: %v", snapshotPath, err) + } + chrootPath = tmpDir + } else { + chrootPath = snapshotPath + } + + options := option.WithChroot(chrootPath) + ghwHandler := &GHWHandler{ + snapShotOptions: options, + Node: node, + tmpDir: tmpDir, + } return ghwHandler, nil } // GHWHandler is a wrapper around ghw to get the API object type GHWHandler struct { - snapShotOptions *option.Option + snapShotOptions option.Option Node *v1.Node + tmpDir string +} + +// Cleanup removes any temporary directories created during handler initialization +func (ghwHandler *GHWHandler) Cleanup() error { + if ghwHandler.tmpDir != "" { + return os.RemoveAll(ghwHandler.tmpDir) + } + return nil } // CPU returns a CPUInfo struct that contains information about the CPUs on the host system diff --git a/pkg/performanceprofile/profilecreator/profilecreator_test.go b/pkg/performanceprofile/profilecreator/profilecreator_test.go index 43491b09e6..4cafd30a44 100644 --- a/pkg/performanceprofile/profilecreator/profilecreator_test.go +++ b/pkg/performanceprofile/profilecreator/profilecreator_test.go @@ -232,6 +232,7 @@ var _ = Describe("PerformanceProfileCreator: Consuming GHW Snapshot from Must Ga Expect(err).ToNot(HaveOccurred()) handle, err := NewGHWHandler(mustGatherDirAbsolutePath, node) Expect(err).ToNot(HaveOccurred()) + DeferCleanup(handle.Cleanup) cpuInfo, err := handle.CPU() Expect(err).ToNot(HaveOccurred()) Expect(len(cpuInfo.Processors)).To(Equal(2)) @@ -853,6 +854,14 @@ var _ = Describe("PerformanceProfileCreator: Populating Reserved and Isolated CP BeforeEach(func() { node = newTestNode(worker1) }) + + AfterEach(func() { + if handle != nil { + Expect(handle.Cleanup()).To(Succeed()) + handle = nil + } + }) + Context("Check if reserved and isolated CPUs are properly populated in the performance profile", func() { It("Ensure reserved CPUs populated are correctly when splitReservedCPUsAcrossNUMA is disabled and disableHT is disabled", func() { reservedCPUCount = 20 // random number, no special meaning @@ -1390,6 +1399,13 @@ var _ = Describe("PerformanceProfileCreator: Check if Hyperthreading enabled/dis var handle *GHWHandler var err error + AfterEach(func() { + if handle != nil { + Expect(handle.Cleanup()).To(Succeed()) + handle = nil + } + }) + Context("Check if hyperthreading is enabled on the system or not", func() { It("Ensure we detect correctly that hyperthreading is enabled on a system", func() { node = newTestNode(worker1) @@ -1608,11 +1624,13 @@ var _ = Describe("PerformanceProfileCreator: Ensuring Nodes hardware equality", Expect(err).ToNot(HaveOccurred()) node1Handle, err := NewGHWHandler(mustGatherDirAbsolutePath, node1) Expect(err).ToNot(HaveOccurred()) + DeferCleanup(node1Handle.Cleanup) node2, err := getNode(mustGatherDirAbsolutePath, worker1+".yaml") Expect(err).ToNot(HaveOccurred()) node2Handle, err := NewGHWHandler(mustGatherDirAbsolutePath, node2) Expect(err).ToNot(HaveOccurred()) + DeferCleanup(node2Handle.Cleanup) nodeHandles := []*GHWHandler{node1Handle, node2Handle} tols := toleration.Set{} @@ -1632,11 +1650,13 @@ var _ = Describe("PerformanceProfileCreator: Ensuring Nodes hardware equality", Expect(err).ToNot(HaveOccurred()) node1Handle, err := NewGHWHandler(mustGatherDirAbsolutePath, node1) Expect(err).ToNot(HaveOccurred()) + DeferCleanup(node1Handle.Cleanup) node2, err := getNode(mustGatherDirAbsolutePath, worker2+".yaml") Expect(err).ToNot(HaveOccurred()) node2Handle, err := NewGHWHandler(mustGatherDirAbsolutePath, node2) Expect(err).ToNot(HaveOccurred()) + DeferCleanup(node2Handle.Cleanup) nodeHandles := []*GHWHandler{node1Handle, node2Handle} err = EnsureNodesHaveTheSameHardware(nodeHandles, toleration.Set{}) diff --git a/vendor/github.com/jaypipes/ghw/.gitignore b/vendor/github.com/jaypipes/ghw/.gitignore index 34d0d840aa..a155166d3f 100644 --- a/vendor/github.com/jaypipes/ghw/.gitignore +++ b/vendor/github.com/jaypipes/ghw/.gitignore @@ -1,3 +1,4 @@ vendor/ coverage*.* *~ +bin/ diff --git a/vendor/github.com/jaypipes/ghw/Makefile b/vendor/github.com/jaypipes/ghw/Makefile index 75d2bcc8b5..b55e9dfbf4 100644 --- a/vendor/github.com/jaypipes/ghw/Makefile +++ b/vendor/github.com/jaypipes/ghw/Makefile @@ -1,16 +1,35 @@ -.PHONY: test +VERSION ?= $(shell git describe --tags --always --dirty) + +.PHONY: test clean vet fmt fmtcheck build run + +bin/ghwc: + @cd cmd/ghwc && go build -o ../../bin/ghwc main.go && cd ../../ + +# If the first argument is "run"... +ifeq (run,$(firstword $(MAKECMDGOALS))) + # use the rest as arguments for "run" + RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + # ...and turn them into do-nothing targets + $(eval $(RUN_ARGS):;@:) +endif + +build: clean bin/ghwc + +run: build + @bin/ghwc $(RUN_ARGS) + test: vet go test -v ./... -.PHONY: fmt fmt: @echo "Running gofmt on all sources..." @gofmt -s -l -w . -.PHONY: fmtcheck fmtcheck: @bash -c "diff -u <(echo -n) <(gofmt -d .)" -.PHONY: vet vet: go vet ./... + +clean: + @rm -f bin/ghwc diff --git a/vendor/github.com/jaypipes/ghw/README.md b/vendor/github.com/jaypipes/ghw/README.md index c0011e493a..a5f5b06ceb 100644 --- a/vendor/github.com/jaypipes/ghw/README.md +++ b/vendor/github.com/jaypipes/ghw/README.md @@ -14,7 +14,7 @@ Windows. There currently exists partial support for MacOSX. * No root privileges needed for discovery - `ghw` goes the extra mile to be useful without root priveleges. We query for + `ghw` goes the extra mile to be useful without root privileges. We query for host hardware information as directly as possible without relying on shellouts to programs like `dmidecode` that require root privileges to execute. @@ -520,7 +520,7 @@ information about the host computer's networking hardware. 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 + for each network interface controller found for the system Each `ghw.NIC` struct contains the following fields: @@ -1206,17 +1206,12 @@ feature to quiet things down. ### 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 +warning messages to `stderr`. To disable these warnings, 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 +WARN: 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) ``` @@ -1225,10 +1220,10 @@ $ GHW_DISABLE_WARNINGS=1 ghwc memory memory (24GB physical, 24GB usable) ``` -You can disable warning programmatically using the `WithDisableWarnings` option: +You can disable warning programmatically using the `WithDisableWarnings` +modifier: ```go - import ( "github.com/jaypipes/ghw" ) @@ -1236,14 +1231,172 @@ import ( 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. +### Controlling log output + +The default log output in `ghw` only writes WARN-level messages to `stderr` in +a simple `WARN: ` log record format: + +``` +$ ghwc baseboard +WARN: Unable to read board_serial: open /sys/class/dmi/id/board_serial: permission denied +baseboard vendor=System76 version=thelio-mira-b4.1 product=Thelio Mira +``` + +You can control a number of log output options programmatically or by using +environs variables. + +#### Change the log level + +To change the log level `ghw` uses, set the `GHW_LOG_LEVEL` environs variable: + +``` +$ GHW_LOG_LEVEL=debug ghwc baseboard +DEBUG: reading from "/sys/class/dmi/id/board_asset_tag" +DEBUG: reading from "/sys/class/dmi/id/board_serial" +WARN: Unable to read board_serial: open /sys/class/dmi/id/board_serial: permission denied +DEBUG: reading from "/sys/class/dmi/id/board_vendor" +DEBUG: reading from "/sys/class/dmi/id/board_version" +DEBUG: reading from "/sys/class/dmi/id/board_name" +baseboard vendor=System76 version=thelio-mira-b4.1 product=Thelio Mira +``` + +Changing `GHW_LOG_LEVEL` to `error` has the same effect of setting +`GHW_DISABLE_WARNINGS`: + +``` +$ GHW_LOG_LEVEL=error ghwc baseboard +baseboard vendor=System76 version=thelio-mira-b4.1 product=Thelio Mira +``` + +You can change the log level programmatically using the `WithLogLevel` +modifier: + +```go +import ( + "log/slog" + + "github.com/jaypipes/ghw" +) + +bb, err := ghw.Baseboard(ghw.WithLogLevel(slog.LevelDebug)) +``` + +#### Use logfmt output format + +To use the [logfmt][logfmt] standard log output format, set the +`GHW_LOG_LOGFMT` envrions variable: + +``` +$ GHW_LOG_LOGFMT=1 ghwc baseboard +time=2025-12-28T07:31:08.614-05:00 level=WARN msg="Unable to read board_serial: open /sys/class/dmi/id/board_serial: permission denied" +baseboard vendor=System76 version=thelio-mira-b4.1 product=Thelio Mira +``` + +You can tell `ghw` to use `logfmt` standard output formatting using the `WithLogLogfmt` +modifier: + +```go +import ( + "log/slog" + + "github.com/jaypipes/ghw" +) + +bb, err := ghw.Baseboard(ghw.WithLogLogfmt()) +``` + +[logfmt]: https://www.cloudbees.com/blog/logfmt-a-log-format-thats-easy-to-read-and-write + +#### Provide a custom logger + +You can programmatically override the logger that `ghw` uses with the +`WithLogger` modifier. You pass in an instance of `slog.Logger`, like this +example that shows how to use a simple logger with colored log output: + +```go +package main + +import ( + "context" + "encoding/json" + "io" + "log" + "log/slog" + + "github.com/fatih/color" + "github.com/jaypipes/ghw" +) + +type PrettyHandlerOptions struct { + SlogOpts slog.HandlerOptions +} + +type PrettyHandler struct { + slog.Handler + l *log.Logger +} + +func (h *PrettyHandler) Handle(ctx context.Context, r slog.Record) error { + level := r.Level.String() + ":" + + switch r.Level { + case slog.LevelDebug: + level = color.MagentaString(level) + case slog.LevelInfo: + level = color.BlueString(level) + case slog.LevelWarn: + level = color.YellowString(level) + case slog.LevelError: + level = color.RedString(level) + } + + fields := make(map[string]any, r.NumAttrs()) + r.Attrs(func(a slog.Attr) bool { + fields[a.Key] = a.Value.Any() + + return true + }) + + b, err := json.MarshalIndent(fields, "", " ") + if err != nil { + return err + } + + timeStr := r.Time.Format("[15:05:05.000]") + msg := color.CyanString(r.Message) + + h.l.Println(timeStr, level, msg, color.WhiteString(string(b))) + + return nil +} + +func NewPrettyHandler( + out io.Writer, + opts PrettyHandlerOptions, +) *PrettyHandler { + h := &PrettyHandler{ + Handler: slog.NewJSONHandler(out, &opts.SlogOpts), + l: log.New(out, "", 0), + } -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.Alerter` interface and the `ghw.WithAlerter()` -function. + return h +} + +func main() { + opts := PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + Level: slog.LevelDebug, + }, + } + handler := NewPrettyHandler(os.Stdout, opts) + logger := slog.New(handler) + bb, err := ghw.Baseboard(ghw.WithLogger(logger)) + if err != nil { + logger.Error(err.String()) + } + fmt.Println(bb) +} +``` ### Overriding the root mountpoint `ghw` uses @@ -1332,59 +1485,75 @@ cpu, err := ghw.CPU(ghw.WithPathOverrides(ghw.PathOverrides{ ### Reading hardware information from a `ghw` snapshot (Linux only) -The `ghw-snapshot` tool can create a snapshot of a host's hardware information. - -Please read [`SNAPSHOT.md`](SNAPSHOT.md) to learn about creating snapshots with -the `ghw-snapshot` tool. - -You can make `ghw` read hardware information from a snapshot created with -`ghw-snapshot` using environment variables or programmatically. - -Use the `GHW_SNAPSHOT_PATH` environment variable to specify the filepath to a -snapshot that `ghw` will read to determine hardware information. All the needed -chroot changes will be automatically performed. By default, the snapshot is -unpacked into a temporary directory managed by `ghw`. This temporary directory -is automatically deleted when `ghw` is finished reading the snapshot. +The `ghwc snapshot` command creates a snapshot of a host's hardware information. -Three other environment variables are relevant if and only if `GHW_SNAPSHOT_PATH` -is not empty: +Use the `ghwc -s` flag to supply a path to a snapshot to read with the `ghwc` +command-line program. -* `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* automatically clean up the content - unpacked into `GHW_SNAPSHOT_ROOT`. -* `GHW_SNAPSHOT_EXCLUSIVE` tells `ghw` that the directory is meant only to - contain the given snapshot, thus `ghw` will *not* attempt to unpack it 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 the - value of this environment variable to any non-empty string. -* `GHW_SNAPSHOT_PRESERVE` tells `ghw` not to clean up the unpacked snapshot. - Set the value of this environment variable to any non-empty string. - -```go -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, -})) +``` +$ ghwc -s testdata/snapshots/linux-amd64-amd-ryzen-1600.tar.gz +block storage (8 disks, 3TB physical storage) + dm-0 SSD (90GB) Unknown [@unknown (node #0)] vendor=unknown + dm-1 Unknown (16GB) Unknown [@unknown (node #0)] vendor=unknown + dm-2 SSD (60GB) Unknown [@unknown (node #0)] vendor=unknown + dm-3 SSD (13GB) Unknown [@unknown (node #0)] vendor=unknown + dm-4 SSD (436GB) Unknown [@unknown (node #0)] vendor=unknown + sda SSD (239GB) SCSI [@unknown (node #0)] vendor=unknown + sda1 (128MB) [unknown] + sda2 (384MB) [unknown] + sda3 (238GB) [unknown] + sdb HDD (932GB) SCSI [@unknown (node #0)] vendor=unknown + sdb1 (2GB) [unknown] + sdb2 (930GB) [unknown] + sdc SSD (466GB) SCSI [@unknown (node #0)] vendor=unknown + sdc1 (32GB) [unknown] + sdc2 (434GB) [unknown] +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 #5 (2 threads), logical processors [4 10] + processor core #6 (2 threads), logical processors [5 11] + processor core #2 (2 threads), logical processors [2 8] + processor core #4 (2 threads), logical processors [3 9] + capabilities: [msr pae mce cx8 apic sep + mtrr pge mca cmov pat pse36 + clflush mmx fxsr sse sse2 ht + syscall nx mmxext fxsr_opt pdpe1gb rdtscp + lm constant_tsc art rep_good nopl nonstop_tsc + extd_apicid aperfmperf eagerfpu pni pclmulqdq monitor + ssse3 fma cx16 sse4_1 sse4_2 movbe + popcnt aes xsave avx f16c rdrand + lahf_lm cmp_legacy svm extapic cr8_legacy abm + sse4a misalignsse 3dnowprefetch osvw skinit wdt + tce topoext perfctr_core perfctr_nb bpext perfctr_l2 + hw_pstate sme retpoline_amd ssbd ibpb vmmcall + fsgsbase bmi1 avx2 smep bmi2 rdseed + adx smap clflushopt sha_ni xsaveopt xsavec + xgetbv1 clzero irperf xsaveerptr arat npt + lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid + decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif + overflow_recov succor smca] +gpu (1 graphics card) + card #0 @0000:0a:00.0 -> driver: '' class: 'Display controller' vendor: 'Advanced Micro Devices, Inc. [AMD/ATI]' product: 'Turks XT [Radeon HD 6670/7670]' +processing accelerators (0 devices) +memory (32GB physical, 32GB usable) +net (4 NICs) + enp3s0 + enp6s0f0 + enp6s0f1 + enp8s0 +topology NUMA (0 nodes) +chassis type=unknown vendor=unknown version=unknown +bios vendor=unknown version=unknown +baseboard vendor=unknown version=unknown product=unknown +product family=unknown name=unknown vendor=unknown sku=unknown version=unknown +PCI (43 devices) ``` ### Creating snapshots -You can create `ghw` snapshots using the `ghw-snapshot` tool or +You can create `ghw` snapshots using the `ghwc snapshot` command or programmatically using the `pkg/snapshot` package. Below is an example of creating a `ghw` snapshot using the `pkg/snapshot` diff --git a/vendor/github.com/jaypipes/ghw/SNAPSHOT.md b/vendor/github.com/jaypipes/ghw/SNAPSHOT.md index 696a3ea635..ddd8157419 100644 --- a/vendor/github.com/jaypipes/ghw/SNAPSHOT.md +++ b/vendor/github.com/jaypipes/ghw/SNAPSHOT.md @@ -7,10 +7,11 @@ the paths ghw cares about. The snapshot concept was introduced [to make ghw easi ## Create and consume snapshot -The recommended way to create snapshots for ghw is to use the `ghw-snapshot` tool. +The recommended way to create snapshots for ghw is to use the `ghwc snapshot` command. + This tool is maintained by the ghw authors, and snapshots created with this tool are guaranteed to work. -To consume the ghw snapshots, please check the `README.md` document. +To display hardware information from a ghw snapshot, use the `ghwc -s` flag, passing the filepath to the snapshot to use. ## Snapshot design and definitions @@ -21,10 +22,6 @@ expect, we recommend to check also the [project issues](https://github.com/jaypi ### Scope ghw supports snapshots only on linux platforms. This restriction may be lifted in future releases. -Snapshots must be consumable in the following supported ways: - -1. (way 1) from docker (or podman), mounting them as volumes. See `hack/run-against-snapshot.sh` -2. (way 2) using the environment variables `GHW_SNAPSHOT_*`. See `README.md` for the full documentation. Other combinations are possible, but are unsupported and may stop working any time. You should depend only on the supported ways to consume snapshots. @@ -42,4 +39,3 @@ Stemming from the use cases, the snapshot content must have the following proper It must be noted that trivially cloning subtrees from `/proc` and `/sys` and creating a tarball out of them doesn't work because both pseudo filesystems make use of symlinks, and [docker doesn't really play nice with symlinks](https://github.com/jaypipes/ghw/commit/f8ffd4d24e62eb9017511f072ccf51f13d4a3399). This conflcits with (way 1) above. - diff --git a/vendor/github.com/jaypipes/ghw/alias.go b/vendor/github.com/jaypipes/ghw/alias.go index 9c403d9694..83c5fd5381 100644 --- a/vendor/github.com/jaypipes/ghw/alias.go +++ b/vendor/github.com/jaypipes/ghw/alias.go @@ -7,6 +7,7 @@ package ghw import ( + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/accelerator" "github.com/jaypipes/ghw/pkg/baseboard" "github.com/jaypipes/ghw/pkg/bios" @@ -21,24 +22,27 @@ import ( pciaddress "github.com/jaypipes/ghw/pkg/pci/address" "github.com/jaypipes/ghw/pkg/product" "github.com/jaypipes/ghw/pkg/topology" + "github.com/jaypipes/ghw/pkg/usb" ) -type WithOption = option.Option - var ( - WithChroot = option.WithChroot - WithSnapshot = option.WithSnapshot - WithAlerter = option.WithAlerter - WithNullAlerter = option.WithNullAlerter - // match the existing environ variable to minimize surprises - WithDisableWarnings = option.WithNullAlerter - WithDisableTools = option.WithDisableTools - WithPathOverrides = option.WithPathOverrides -) + ContextFromEnv = config.ContextFromEnv -type SnapshotOptions = option.SnapshotOptions + WithChroot = config.WithChroot + // DEPRECATED: Please use WithLogger + WithAlerter = option.WithAlerter + // DEPRECATED: Please use WithDisableWarnings + WithNullAlerter = option.WithNullAlerter + WithDisableWarnings = config.WithDisableWarnings + WithDisableTools = config.WithDisableTools + WithDisableTopology = config.WithDisableTopology + WithPathOverrides = config.WithPathOverrides + WithLogLevel = config.WithLogLevel + WithDebug = config.WithDebug + WithLogger = config.WithLogger +) -type PathOverrides = option.PathOverrides +type PathOverrides map[string]string type CPUInfo = cpu.Info @@ -68,6 +72,10 @@ var ( Memory = memory.New ) +var ( + USB = usb.New +) + type BlockInfo = block.Info type Disk = block.Disk type Partition = block.Partition diff --git a/vendor/github.com/jaypipes/ghw/host.go b/vendor/github.com/jaypipes/ghw/host.go index 89b1ad2776..9c76b172f4 100644 --- a/vendor/github.com/jaypipes/ghw/host.go +++ b/vendor/github.com/jaypipes/ghw/host.go @@ -9,8 +9,7 @@ package ghw import ( "fmt" - "github.com/jaypipes/ghw/pkg/context" - + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/accelerator" "github.com/jaypipes/ghw/pkg/baseboard" "github.com/jaypipes/ghw/pkg/bios" @@ -24,12 +23,12 @@ import ( "github.com/jaypipes/ghw/pkg/pci" "github.com/jaypipes/ghw/pkg/product" "github.com/jaypipes/ghw/pkg/topology" + "github.com/jaypipes/ghw/pkg/usb" ) // HostInfo is a wrapper struct containing information about the host system's // memory, block storage, CPU, etc type HostInfo struct { - ctx *context.Context Memory *memory.Info `json:"memory"` Block *block.Info `json:"block"` CPU *cpu.Info `json:"cpu"` @@ -42,63 +41,67 @@ type HostInfo struct { Baseboard *baseboard.Info `json:"baseboard"` Product *product.Info `json:"product"` PCI *pci.Info `json:"pci"` + USB *usb.Info `json:"usb"` } // Host returns a pointer to a HostInfo struct that contains fields with // information about the host system's CPU, memory, network devices, etc -func Host(opts ...*WithOption) (*HostInfo, error) { - ctx := context.New(opts...) - - memInfo, err := memory.New(opts...) +func Host(args ...any) (*HostInfo, error) { + ctx := config.ContextFromArgs(args...) + memInfo, err := memory.New(ctx) + if err != nil { + return nil, err + } + blockInfo, err := block.New(ctx) if err != nil { return nil, err } - blockInfo, err := block.New(opts...) + cpuInfo, err := cpu.New(ctx) if err != nil { return nil, err } - cpuInfo, err := cpu.New(opts...) + topologyInfo, err := topology.New(ctx) if err != nil { return nil, err } - topologyInfo, err := topology.New(opts...) + netInfo, err := net.New(ctx) if err != nil { return nil, err } - netInfo, err := net.New(opts...) + gpuInfo, err := gpu.New(ctx) if err != nil { return nil, err } - gpuInfo, err := gpu.New(opts...) + acceleratorInfo, err := accelerator.New(ctx) if err != nil { return nil, err } - acceleratorInfo, err := accelerator.New(opts...) + chassisInfo, err := chassis.New(ctx) if err != nil { return nil, err } - chassisInfo, err := chassis.New(opts...) + biosInfo, err := bios.New(ctx) if err != nil { return nil, err } - biosInfo, err := bios.New(opts...) + baseboardInfo, err := baseboard.New(ctx) if err != nil { return nil, err } - baseboardInfo, err := baseboard.New(opts...) + productInfo, err := product.New(ctx) if err != nil { return nil, err } - productInfo, err := product.New(opts...) + pciInfo, err := pci.New(ctx) if err != nil { return nil, err } - pciInfo, err := pci.New(opts...) + usbInfo, err := usb.New(ctx) if err != nil { return nil, err } + return &HostInfo{ - ctx: ctx, CPU: cpuInfo, Memory: memInfo, Block: blockInfo, @@ -111,6 +114,7 @@ func Host(opts ...*WithOption) (*HostInfo, error) { Baseboard: baseboardInfo, Product: productInfo, PCI: pciInfo, + USB: usbInfo, }, nil } @@ -118,7 +122,7 @@ func Host(opts ...*WithOption) (*HostInfo, error) { // structs' String-ified output func (info *HostInfo) String() string { return fmt.Sprintf( - "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", info.Block.String(), info.CPU.String(), info.GPU.String(), @@ -131,17 +135,18 @@ func (info *HostInfo) String() string { info.Baseboard.String(), info.Product.String(), info.PCI.String(), + info.USB.String(), ) } // YAMLString returns a string with the host information formatted as YAML // under a top-level "host:" key func (i *HostInfo) YAMLString() string { - return marshal.SafeYAML(i.ctx, i) + return marshal.SafeYAML(i) } // JSONString returns a string with the host information formatted as JSON // under a top-level "host:" key func (i *HostInfo) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, i, indent) + return marshal.SafeJSON(i, indent) } diff --git a/vendor/github.com/jaypipes/ghw/internal/config/context.go b/vendor/github.com/jaypipes/ghw/internal/config/context.go new file mode 100644 index 0000000000..72a7a0e8d6 --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/internal/config/context.go @@ -0,0 +1,235 @@ +// +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package config + +import ( + "context" + "os" + + "github.com/jaypipes/ghw/pkg/option" + "github.com/jaypipes/pcidb" +) + +const ( + envKeyChroot = "GHW_CHROOT" + envKeyDisableWarnings = "GHW_DISABLE_WARNINGS" + envKeyDisableTools = "GHW_DISABLE_TOOLS" + envKeyDisableTopology = "GHW_DISABLE_TOPOLOGY" +) + +type Key string + +var ( + defaultChroot = "/" + chrootKey = Key("ghw.chroot") + defaultToolsEnabled = true + toolsEnabledKey = Key("ghw.tools.enabled") + defaultTopologyEnabled = true + topologyEnabledKey = Key("ghw.topology.enabled") + pcidbKey = Key("ghw.pcidb") + pathOverridesKey = Key("ghw.path.overrides") +) + +// Modifier sets some value on the context +type Modifier func(context.Context) context.Context + +// WithChroot allows overriding the root directory ghw examines. +func WithChroot(path string) Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, chrootKey, path) + } +} + +// Chroot gets a context's chroot override or the default if none is set. +func Chroot(ctx context.Context) string { + if ctx == nil { + return defaultChroot + } + if v := ctx.Value(chrootKey); v != nil { + return v.(string) + } + return defaultChroot +} + +// EnvOrDefaultChroot returns the value of the GHW_CHROOT environs variable or +// the default value of "/" if not set +func EnvOrDefaultChroot() string { + // Grab options from the environs by default + if val, exists := os.LookupEnv(envKeyChroot); exists { + return val + } + return defaultChroot +} + +// WithDisableTools prevents ghw from calling external tools to discover +// hardware capabilities. +func WithDisableTools() Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, toolsEnabledKey, false) + } +} + +// ToolsEnabled returns true if external tools have been disabled. +func ToolsEnabled(ctx context.Context) bool { + if ctx == nil { + return defaultToolsEnabled + } + if v := ctx.Value(toolsEnabledKey); v != nil { + return v.(bool) + } + return defaultToolsEnabled +} + +// EnvOrDefaultDisableTools return true if ghw should use external tools to +// augment the data collected from sysfs. Most users want to do this most of +// time, so this is enabled by default. Users consuming snapshots may want to +// opt out, thus they can set the GHW_DISABLE_TOOLS environs variable to any +// value to make ghw skip calling external tools even if they are available. +func EnvOrDefaultDisableTools() bool { + if _, exists := os.LookupEnv(envKeyDisableTools); exists { + return true + } + return false +} + +// WithDisableTopology disables system topology detection to reduce memory +// consumption. When using this option, ghw will skip scanning NUMA topology, +// CPU cores, memory caches, and node distances. This is useful when you only +// need basic PCI or GPU information and want to minimize memory overhead. The +// system architecture will be assumed to be SMP, and device Node fields will +// be nil. +func WithDisableTopology() Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, topologyEnabledKey, false) + } +} + +// EnvOrDefaultEnableTopology return true if ghw should detect system topology. +func EnvOrDefaultEnableTopology() bool { + if _, exists := os.LookupEnv(envKeyDisableTopology); exists { + return false + } + return defaultTopologyEnabled +} + +// TopologyEnabled returns true if the detection of system topology is enabled. +func TopologyEnabled(ctx context.Context) bool { + if ctx == nil { + return defaultTopologyEnabled + } + if v := ctx.Value(topologyEnabledKey); v != nil { + return v.(bool) + } + return defaultTopologyEnabled +} + +// WithPCIDB allows you to provide a custom instance of the PCI database +// (pcidb.PCIDB) to ghw. This is useful if you want to use a preloaded or +// specially configured PCI database, such as one created with custom +// pcidb.WithOption settings, instead of letting ghw load the PCI database +// automatically. +func WithPCIDB(db *pcidb.PCIDB) Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, pcidbKey, db) + } +} + +// PCIDB returns any PCIDB pointer set in the supplied context. +func PCIDB(ctx context.Context) *pcidb.PCIDB { + if ctx == nil { + return nil + } + if v := ctx.Value(pcidbKey); v != nil { + return v.(*pcidb.PCIDB) + } + return nil +} + +// WithPathOverrides supplies path-specific overrides for the context +func WithPathOverrides(overrides map[string]string) Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, pathOverridesKey, overrides) + } +} + +// PathOverrides returns any path overrides set in the supplied context. +func PathOverrides(ctx context.Context) map[string]string { + if ctx == nil { + return nil + } + if v := ctx.Value(pathOverridesKey); v != nil { + return v.(map[string]string) + } + return nil +} + +// ContextFromEnv returns a new context.Context populated from the environs or +// default option values +func ContextFromEnv() context.Context { + ctx := context.TODO() + ctx = context.WithValue(ctx, chrootKey, EnvOrDefaultChroot()) + ctx = context.WithValue(ctx, toolsEnabledKey, EnvOrDefaultDisableTools()) + ll := EnvOrDefaultLogLevel() + logLevelVar.Set(ll) + ctx = context.WithValue(ctx, logLevelKey, ll) + disableWarn := EnvOrDefaultDisableWarnings() + if disableWarn { + ctx = WithDisableWarnings()(ctx) + } + useLogfmt := EnvOrDefaultLogLogfmt() + if useLogfmt { + ctx = WithLogLogfmt()(ctx) + } + return ctx +} + +// fromOptions converts old-style pkg/option.Options by setting any Options +// fields on the supplied context. +// +// TODO(jaypipes): Remove this when we fully deprecate the old-style +// pkg/options stuff. +func fromOptions(ctx context.Context, opts *option.Options) context.Context { + if opts == nil { + return ctx + } + if opts.Chroot != "" { + ctx = context.WithValue(ctx, chrootKey, opts.Chroot) + } + if opts.DisableTools { + ctx = context.WithValue(ctx, toolsEnabledKey, false) + } + if opts.PCIDB != nil { + ctx = context.WithValue(ctx, pcidbKey, opts.PCIDB) + } + if opts.PathOverrides != nil { + ctx = context.WithValue(ctx, pathOverridesKey, opts.PathOverrides) + } + return ctx +} + +// ContextFromArgs returns a context.Context populated with any old-style +// options or new-style arguments. +func ContextFromArgs(args ...any) context.Context { + ctx := context.TODO() + optsUsed := false + opts := &option.Options{} + for _, arg := range args { + switch arg := arg.(type) { + case context.Context: + ctx = arg + case Modifier: + ctx = arg(ctx) + case option.Option: + arg(opts) + optsUsed = true + } + } + if optsUsed { + ctx = fromOptions(ctx, opts) + } + return ctx +} diff --git a/vendor/github.com/jaypipes/ghw/internal/config/log.go b/vendor/github.com/jaypipes/ghw/internal/config/log.go new file mode 100644 index 0000000000..3ae156c83b --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/internal/config/log.go @@ -0,0 +1,156 @@ +// +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package config + +import ( + "context" + "log" + "log/slog" + "os" + "strings" +) + +const ( + envKeyLogLevel = "GHW_LOG_LEVEL" + envKeyLogLogfmt = "GHW_LOG_LOGFMT" +) + +var ( + logLevelKey = Key("ghw.log.level") + defaultLogLevel = slog.LevelWarn + logLevelVar = new(slog.LevelVar) + loggerKey = Key("ghw.logger") + logfmtLogger = slog.New( + slog.NewTextHandler( + os.Stderr, + &slog.HandlerOptions{ + Level: logLevelVar, + }, + ), + ) + defaultLogger = slog.New( + &simpleHandler{ + Handler: slog.NewTextHandler( + os.Stderr, + &slog.HandlerOptions{ + Level: logLevelVar, + }, + ), + l: log.New(os.Stderr, "", 0), + }, + ) +) + +// simpleHandler is a custom log handler meant to emulate the experience ghw had +// before we moved to log/slog. It implements log/slog.Handler. +type simpleHandler struct { + slog.Handler + l *log.Logger +} + +func (h *simpleHandler) Handle( + ctx context.Context, + r slog.Record, +) error { + level := r.Level.String() + ":" + + h.l.Printf("%-6s %s", level, r.Message) + + return nil +} + +// WithLogLevel allows overriding the default log level of WARN. +func WithLogLevel(level slog.Level) Modifier { + return func(ctx context.Context) context.Context { + logLevelVar.Set(level) + return context.WithValue(ctx, logLevelKey, level) + } +} + +// WithLogLogfmt sets the log output to the logfmt standard. +func WithLogLogfmt() Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, loggerKey, logfmtLogger) + } +} + +// EnvOrDefaultLogLogfmt return true if ghw should use logfmt standard output +// format based on the GHW_LOG_LOGFMT environs variable. +func EnvOrDefaultLogLogfmt() bool { + if _, exists := os.LookupEnv(envKeyLogLogfmt); exists { + return true + } + return false +} + +// WithLogger allows overriding the default logger +func WithLogger(logger *slog.Logger) Modifier { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, loggerKey, logger) + } +} + +// LogLevel gets a context's logger's log level or the default if none is set. +func LogLevel(ctx context.Context) slog.Level { + if ctx == nil { + return defaultLogLevel + } + if v := ctx.Value(logLevelKey); v != nil { + return v.(slog.Level) + } + return defaultLogLevel +} + +// EnvOrDefaultLogLevel return true if ghw should not output warnings +// based on the GHW_LOG_LEVEL environs variable. +func EnvOrDefaultLogLevel() slog.Level { + if ll, exists := os.LookupEnv(envKeyLogLevel); exists { + switch strings.ToLower(ll) { + case "debug": + return slog.LevelDebug + case "info": + return slog.LevelInfo + case "warn", "warning": + return slog.LevelWarn + case "err", "error": + return slog.LevelError + default: + return defaultLogLevel + } + } + return defaultLogLevel +} + +// Logger gets a context's logger override or the default if none is set. +func Logger(ctx context.Context) *slog.Logger { + if ctx == nil { + return defaultLogger + } + if v := ctx.Value(loggerKey); v != nil { + return v.(*slog.Logger) + } + return defaultLogger +} + +// WithDebug enables verbose debugging output. +func WithDebug() Modifier { + return WithLogLevel(slog.LevelDebug) +} + +// WithDisableWarnings tells ghw not to output warning messages. +func WithDisableWarnings() Modifier { + return WithLogLevel(slog.LevelError) +} + +// EnvOrDefaultDisableWarnings return true if ghw should not output warnings +// based on the GHW_DISABLE_WARNINGS environs variable. +func EnvOrDefaultDisableWarnings() bool { + if _, exists := os.LookupEnv(envKeyDisableWarnings); exists { + return true + } + return false +} diff --git a/vendor/github.com/jaypipes/ghw/internal/log/log.go b/vendor/github.com/jaypipes/ghw/internal/log/log.go new file mode 100644 index 0000000000..3fdad94ec8 --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/internal/log/log.go @@ -0,0 +1,57 @@ +// +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package log + +import ( + "context" + "fmt" + "log/slog" + "runtime" + "strings" + "time" + + "github.com/jaypipes/ghw/internal/config" +) + +// Info outputs an INFO-level log message to the logger configured in the +// supplied context. +func Info(ctx context.Context, format string, args ...any) { + logger := config.Logger(ctx) + if logger == nil || !logger.Enabled(ctx, slog.LevelInfo) { + return + } + var stack [1]uintptr + runtime.Callers(2, stack[:]) // skip [Callers, Info] + r := slog.NewRecord(time.Now(), slog.LevelInfo, strings.TrimSpace(fmt.Sprintf(format, args...)), stack[0]) + _ = logger.Handler().Handle(ctx, r) +} + +// Warn outputs an WARN-level log message to the logger configured in the +// supplied context. +func Warn(ctx context.Context, format string, args ...any) { + logger := config.Logger(ctx) + if logger == nil || !logger.Enabled(ctx, slog.LevelWarn) { + return + } + var stack [1]uintptr + runtime.Callers(2, stack[:]) // skip [Callers, Warn] + r := slog.NewRecord(time.Now(), slog.LevelWarn, strings.TrimSpace(fmt.Sprintf(format, args...)), stack[0]) + _ = logger.Handler().Handle(ctx, r) +} + +// Debug outputs an DEBUG-level log message to the logger configured in the +// supplied context. +func Debug(ctx context.Context, format string, args ...any) { + logger := config.Logger(ctx) + if logger == nil || !logger.Enabled(ctx, slog.LevelDebug) { + return + } + var stack [1]uintptr + runtime.Callers(2, stack[:]) // skip [Callers, Debug] + r := slog.NewRecord(time.Now(), slog.LevelDebug, strings.TrimSpace(fmt.Sprintf(format, args...)), stack[0]) + _ = logger.Handler().Handle(ctx, r) +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator.go b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator.go index b51ef2e211..c7dbc62fc3 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator.go +++ b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator.go @@ -9,9 +9,8 @@ package accelerator import ( "fmt" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/pci" ) @@ -37,17 +36,15 @@ func (dev *AcceleratorDevice) String() string { } type Info struct { - ctx *context.Context Devices []*AcceleratorDevice `json:"devices"` } // New returns a pointer to an Info struct that contains information about the // accelerator devices on the host system -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -59,7 +56,7 @@ func (i *Info) String() string { numDevsStr = "device" } return fmt.Sprintf( - "processing accelerators (%d %s)", + "accelerators (%d %s)", len(i.Devices), numDevsStr, ) @@ -74,11 +71,11 @@ type acceleratorPrinter struct { // YAMLString returns a string with the processing accelerators information formatted as YAML // under a top-level "accelerator:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, acceleratorPrinter{i}) + return marshal.SafeYAML(acceleratorPrinter{i}) } // JSONString returns a string with the processing accelerators information formatted as JSON // under a top-level "accelerator:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, acceleratorPrinter{i}, indent) + return marshal.SafeJSON(acceleratorPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_linux.go b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_linux.go index 39d7eecb62..68df541588 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_linux.go @@ -6,7 +6,9 @@ package accelerator import ( - "github.com/jaypipes/ghw/pkg/context" + "context" + + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/pci" ) @@ -31,13 +33,13 @@ var ( } ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { accelDevices := make([]*AcceleratorDevice, 0) // get PCI devices - pciInfo, err := pci.New(context.WithContext(i.ctx)) + pciInfo, err := pci.New(ctx) if err != nil { - i.ctx.Warn("error loading PCI information: %s", err) + log.Warn(ctx, "error loading PCI information: %s", err) return nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_stub.go b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_stub.go index 303f27d71e..07929d17d7 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_stub.go @@ -9,11 +9,12 @@ package accelerator import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("accelerator.Info.load not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_windows.go b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_windows.go index f8532ecbdc..c2cf56d340 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/accelerator/accelerator_windows.go @@ -5,7 +5,9 @@ package accelerator -func (i *Info) load() error { +import "context" + +func (i *Info) load(ctx context.Context) error { i.Devices = []*AcceleratorDevice{} return nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard.go b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard.go index ac4bf41a9b..681f4fbeed 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard.go +++ b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard.go @@ -7,15 +7,13 @@ package baseboard import ( - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/util" ) // Info defines baseboard release information type Info struct { - ctx *context.Context AssetTag string `json:"asset_tag"` SerialNumber string `json:"serial_number"` Vendor string `json:"vendor"` @@ -52,10 +50,10 @@ func (i *Info) String() string { // New returns a pointer to an Info struct containing information about the // host's baseboard -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -70,11 +68,11 @@ type baseboardPrinter struct { // YAMLString returns a string with the baseboard information formatted as YAML // under a top-level "dmi:" key func (info *Info) YAMLString() string { - return marshal.SafeYAML(info.ctx, baseboardPrinter{info}) + return marshal.SafeYAML(baseboardPrinter{info}) } // JSONString returns a string with the baseboard information formatted as JSON // under a top-level "baseboard:" key func (info *Info) JSONString(indent bool) string { - return marshal.SafeJSON(info.ctx, baseboardPrinter{info}, indent) + return marshal.SafeJSON(baseboardPrinter{info}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_linux.go b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_linux.go index c8c598d421..86f3986b7c 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_linux.go @@ -6,15 +6,17 @@ package baseboard import ( + "context" + "github.com/jaypipes/ghw/pkg/linuxdmi" ) -func (i *Info) load() error { - i.AssetTag = linuxdmi.Item(i.ctx, "board_asset_tag") - i.SerialNumber = linuxdmi.Item(i.ctx, "board_serial") - i.Vendor = linuxdmi.Item(i.ctx, "board_vendor") - i.Version = linuxdmi.Item(i.ctx, "board_version") - i.Product = linuxdmi.Item(i.ctx, "board_name") +func (i *Info) load(ctx context.Context) error { + i.AssetTag = linuxdmi.Item(ctx, "board_asset_tag") + i.SerialNumber = linuxdmi.Item(ctx, "board_serial") + i.Vendor = linuxdmi.Item(ctx, "board_vendor") + i.Version = linuxdmi.Item(ctx, "board_version") + i.Product = linuxdmi.Item(ctx, "board_name") return nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_stub.go b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_stub.go index f5b146919d..e2a3c5f758 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_stub.go @@ -9,11 +9,12 @@ package baseboard import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("baseboardFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_windows.go b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_windows.go index cdb50b4d51..c0a36f7049 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/baseboard/baseboard_windows.go @@ -6,6 +6,8 @@ package baseboard import ( + "context" + "github.com/yusufpapurcu/wmi" ) @@ -19,7 +21,7 @@ type win32Baseboard struct { Product *string } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting data from WMI var win32BaseboardDescriptions []win32Baseboard if err := wmi.Query(wqlBaseboard, &win32BaseboardDescriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/bios/bios.go b/vendor/github.com/jaypipes/ghw/pkg/bios/bios.go index 85a7c64b16..0961dc3c15 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/bios/bios.go +++ b/vendor/github.com/jaypipes/ghw/pkg/bios/bios.go @@ -9,15 +9,13 @@ package bios import ( "fmt" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/util" ) // Info defines BIOS release information type Info struct { - ctx *context.Context Vendor string `json:"vendor"` Version string `json:"version"` Date string `json:"date"` @@ -49,10 +47,10 @@ func (i *Info) String() string { // New returns a pointer to a Info struct containing information // about the host's BIOS -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -67,11 +65,11 @@ type biosPrinter struct { // YAMLString returns a string with the BIOS information formatted as YAML // under a top-level "dmi:" key func (info *Info) YAMLString() string { - return marshal.SafeYAML(info.ctx, biosPrinter{info}) + return marshal.SafeYAML(biosPrinter{info}) } // JSONString returns a string with the BIOS information formatted as JSON // under a top-level "bios:" key func (info *Info) JSONString(indent bool) string { - return marshal.SafeJSON(info.ctx, biosPrinter{info}, indent) + return marshal.SafeJSON(biosPrinter{info}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/bios/bios_linux.go b/vendor/github.com/jaypipes/ghw/pkg/bios/bios_linux.go index 9788f4f7a1..1305f10c11 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/bios/bios_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/bios/bios_linux.go @@ -5,12 +5,16 @@ package bios -import "github.com/jaypipes/ghw/pkg/linuxdmi" +import ( + "context" -func (i *Info) load() error { - i.Vendor = linuxdmi.Item(i.ctx, "bios_vendor") - i.Version = linuxdmi.Item(i.ctx, "bios_version") - i.Date = linuxdmi.Item(i.ctx, "bios_date") + "github.com/jaypipes/ghw/pkg/linuxdmi" +) + +func (i *Info) load(ctx context.Context) error { + i.Vendor = linuxdmi.Item(ctx, "bios_vendor") + i.Version = linuxdmi.Item(ctx, "bios_version") + i.Date = linuxdmi.Item(ctx, "bios_date") return nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/bios/bios_stub.go b/vendor/github.com/jaypipes/ghw/pkg/bios/bios_stub.go index 5307b4a0a9..5e9f25da7e 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/bios/bios_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/bios/bios_stub.go @@ -9,11 +9,12 @@ package bios import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("biosFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/bios/bios_windows.go b/vendor/github.com/jaypipes/ghw/pkg/bios/bios_windows.go index cb8244e095..e688e10513 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/bios/bios_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/bios/bios_windows.go @@ -6,6 +6,8 @@ package bios import ( + "context" + "github.com/yusufpapurcu/wmi" ) @@ -17,7 +19,7 @@ type win32BIOS struct { Version *string } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting data from WMI var win32BIOSDescriptions []win32BIOS if err := wmi.Query(wqlBIOS, &win32BIOSDescriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block.go b/vendor/github.com/jaypipes/ghw/pkg/block/block.go index 016b5c7de4..9e2ec766f9 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block.go @@ -13,9 +13,8 @@ import ( "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/unitutil" "github.com/jaypipes/ghw/pkg/util" ) @@ -276,7 +275,6 @@ type Partition struct { // Info describes all disk drives and partitions in the host system. type Info struct { - ctx *context.Context // TotalSizeBytes contains the total amount of storage, in bytes, on the // host system. TotalSizeBytes uint64 `json:"total_size_bytes"` @@ -292,10 +290,10 @@ type Info struct { // New returns a pointer to an Info struct that describes the block storage // resources of the host system. -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -407,11 +405,11 @@ type blockPrinter struct { // YAMLString returns a string with the block information formatted as YAML // under a top-level "block:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, blockPrinter{i}) + return marshal.SafeYAML(blockPrinter{i}) } // JSONString returns a string with the block information formatted as JSON // under a top-level "block:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, blockPrinter{i}, indent) + return marshal.SafeJSON(blockPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block_darwin.go b/vendor/github.com/jaypipes/ghw/pkg/block/block_darwin.go index c6b6c26647..0ce52b88fe 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block_darwin.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block_darwin.go @@ -6,6 +6,7 @@ package block import ( + "context" "fmt" "os" "os/exec" @@ -14,6 +15,8 @@ import ( "github.com/pkg/errors" "howett.net/plist" + + "github.com/jaypipes/ghw/internal/config" ) type diskOrPartitionPlistNode struct { @@ -146,7 +149,11 @@ func getIoregPlist(ioDeviceTreePath string) (*ioregPlist, error) { return nil, errors.Wrapf(err, "ioreg unmarshal for %q failed", ioDeviceTreePath) } if len(data) != 1 { - err := errors.Errorf("ioreg unmarshal resulted in %d I/O device tree nodes (expected 1)", len(data)) + err := errors.Errorf( + "ioreg unmarshal resulted in %d I/O device tree nodes "+ + "for path %q (expected 1)", + len(data), ioDeviceTreePath, + ) return nil, err } @@ -206,9 +213,9 @@ func storageControllerFromPlist(infoPlist *diskUtilInfoPlist) StorageController return sc } -func (info *Info) load() error { - if !info.ctx.EnableTools { - return fmt.Errorf("EnableTools=false on darwin disables block support entirely.") +func (info *Info) load(ctx context.Context) error { + if !config.ToolsEnabled(ctx) { + return fmt.Errorf("DisableTools=true on darwin disables block support entirely.") } listPlist, err := getDiskUtilListPlist() diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go b/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go index 6399317a59..c6298bc14e 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block_linux.go @@ -7,13 +7,14 @@ package block import ( "bufio" + "context" "io" "os" "path/filepath" "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/util" ) @@ -22,9 +23,8 @@ const ( sectorSize = 512 ) -func (i *Info) load() error { - paths := linuxpath.New(i.ctx) - i.Disks = disks(i.ctx, paths) +func (i *Info) load(ctx context.Context) error { + i.Disks = disks(ctx) var tsb uint64 for _, d := range i.Disks { tsb += d.SizeBytes @@ -219,12 +219,16 @@ func diskWWN(paths *linuxpath.Paths, disk string) string { // but just the name. In other words, "sda", not "/dev/sda" and "nvme0n1" not // "/dev/nvme0n1") and returns a slice of pointers to Partition structs // representing the partitions in that disk -func diskPartitions(ctx *context.Context, paths *linuxpath.Paths, disk string) []*Partition { +func diskPartitions( + ctx context.Context, + paths *linuxpath.Paths, + disk string, +) []*Partition { out := make([]*Partition, 0) path := filepath.Join(paths.SysBlock, disk) files, err := os.ReadDir(path) if err != nil { - ctx.Warn("failed to read disk partitions: %s\n", err) + log.Warn(ctx, "failed to read disk partitions: %s\n", err) return out } for _, file := range files { @@ -315,7 +319,8 @@ func diskIsRemovable(paths *linuxpath.Paths, disk string) bool { return removable == "1" } -func disks(ctx *context.Context, paths *linuxpath.Paths) []*Disk { +func disks(ctx context.Context) []*Disk { + paths := linuxpath.New(ctx) // In Linux, we could use the fdisk, lshw or blockdev commands to list disk // information, however all of these utilities require root privileges to // run. We can get all of this information by examining the /sys/block @@ -422,7 +427,11 @@ func diskTypes(dname string) ( return driveType, storageController } -func diskIsRotational(ctx *context.Context, paths *linuxpath.Paths, devName string) bool { +func diskIsRotational( + ctx context.Context, + paths *linuxpath.Paths, + devName string, +) bool { path := filepath.Join(paths.SysBlock, devName, "queue", "rotational") contents := util.SafeIntFromFile(ctx, path) return contents == 1 diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block_stub.go b/vendor/github.com/jaypipes/ghw/pkg/block/block_stub.go index f5b5164553..f398b1850b 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block_stub.go @@ -9,11 +9,12 @@ package block import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(_ context.Context) error { return errors.New("blockFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go b/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go index cbb052a853..cb7330a3c0 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/block/block_windows.go @@ -6,6 +6,7 @@ package block import ( + "context" "strconv" "strings" @@ -108,7 +109,7 @@ type win32PhysicalDisk struct { MediaType physicalDiskMediaType } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { win32DiskDriveDescriptions, err := getDiskDrives() if err != nil { return err diff --git a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis.go b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis.go index a7667bbc23..bb6b6c1c4c 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis.go +++ b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis.go @@ -7,9 +7,8 @@ package chassis import ( - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/util" ) @@ -56,7 +55,6 @@ var ( // Info defines chassis release information type Info struct { - ctx *context.Context AssetTag string `json:"asset_tag"` SerialNumber string `json:"serial_number"` Type string `json:"type"` @@ -89,10 +87,10 @@ func (i *Info) String() string { // New returns a pointer to a Info struct containing information // about the host's chassis -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -107,11 +105,11 @@ type chassisPrinter struct { // YAMLString returns a string with the chassis information formatted as YAML // under a top-level "dmi:" key func (info *Info) YAMLString() string { - return marshal.SafeYAML(info.ctx, chassisPrinter{info}) + return marshal.SafeYAML(chassisPrinter{info}) } // JSONString returns a string with the chassis information formatted as JSON // under a top-level "chassis:" key func (info *Info) JSONString(indent bool) string { - return marshal.SafeJSON(info.ctx, chassisPrinter{info}, indent) + return marshal.SafeJSON(chassisPrinter{info}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_linux.go b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_linux.go index 00f64de6e0..408af41370 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_linux.go @@ -6,21 +6,23 @@ package chassis import ( + "context" + "github.com/jaypipes/ghw/pkg/linuxdmi" "github.com/jaypipes/ghw/pkg/util" ) -func (i *Info) load() error { - i.AssetTag = linuxdmi.Item(i.ctx, "chassis_asset_tag") - i.SerialNumber = linuxdmi.Item(i.ctx, "chassis_serial") - i.Type = linuxdmi.Item(i.ctx, "chassis_type") +func (i *Info) load(ctx context.Context) error { + i.AssetTag = linuxdmi.Item(ctx, "chassis_asset_tag") + i.SerialNumber = linuxdmi.Item(ctx, "chassis_serial") + i.Type = linuxdmi.Item(ctx, "chassis_type") typeDesc, found := chassisTypeDescriptions[i.Type] if !found { typeDesc = util.UNKNOWN } i.TypeDescription = typeDesc - i.Vendor = linuxdmi.Item(i.ctx, "chassis_vendor") - i.Version = linuxdmi.Item(i.ctx, "chassis_version") + i.Vendor = linuxdmi.Item(ctx, "chassis_vendor") + i.Version = linuxdmi.Item(ctx, "chassis_version") return nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_stub.go b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_stub.go index 0e3fd94b6c..6945c27139 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_stub.go @@ -9,11 +9,12 @@ package chassis import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("chassisFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_windows.go b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_windows.go index 834fc74b8f..24db7efab4 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/chassis/chassis_windows.go @@ -6,6 +6,8 @@ package chassis import ( + "context" + "github.com/yusufpapurcu/wmi" "github.com/jaypipes/ghw/pkg/util" @@ -25,7 +27,7 @@ type win32Chassis struct { Version *string } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting data from WMI var win32ChassisDescriptions []win32Chassis if err := wmi.Query(wqlChassis, &win32ChassisDescriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/context/context.go b/vendor/github.com/jaypipes/ghw/pkg/context/context.go deleted file mode 100644 index fb8de528c7..0000000000 --- a/vendor/github.com/jaypipes/ghw/pkg/context/context.go +++ /dev/null @@ -1,178 +0,0 @@ -// -// Use and distribution licensed under the Apache license version 2. -// -// See the COPYING file in the root project directory for full text. -// - -package context - -import ( - "fmt" - - "github.com/jaypipes/ghw/pkg/option" - "github.com/jaypipes/ghw/pkg/snapshot" -) - -// Context contains the merged set of configuration switches that act as an -// execution context when calling internal discovery methods -type Context struct { - Chroot string - EnableTools bool - SnapshotPath string - SnapshotRoot string - SnapshotExclusive bool - PathOverrides option.PathOverrides - snapshotUnpackedPath string - alert option.Alerter - err error -} - -// WithContext returns an option.Option that contains a pre-existing Context -// struct. This is useful for some internal code that sets up snapshots. -func WithContext(ctx *Context) *option.Option { - return &option.Option{ - Context: ctx, - } -} - -// Exists returns true if the supplied (merged) Option already contains -// a context. -// -// TODO(jaypipes): We can get rid of this when we combine the option and -// context packages, which will make it easier to detect the presence of a -// pre-setup Context. -func Exists(opt *option.Option) bool { - return opt != nil && opt.Context != nil -} - -// New returns a Context struct pointer that has had various options set on it -func New(opts ...*option.Option) *Context { - merged := option.Merge(opts...) - var ctx *Context - if merged.Context != nil { - var castOK bool - ctx, castOK = merged.Context.(*Context) - if !castOK { - panic("passed in a non-Context for the WithContext() function!") - } - return ctx - } - ctx = &Context{ - alert: option.EnvOrDefaultAlerter(), - Chroot: *merged.Chroot, - } - - if merged.Snapshot != nil { - ctx.SnapshotPath = merged.Snapshot.Path - // root is optional, so a extra check is warranted - if merged.Snapshot.Root != nil { - ctx.SnapshotRoot = *merged.Snapshot.Root - } - ctx.SnapshotExclusive = merged.Snapshot.Exclusive - } - - if merged.Alerter != nil { - ctx.alert = merged.Alerter - } - - if merged.EnableTools != nil { - ctx.EnableTools = *merged.EnableTools - } - - if merged.PathOverrides != nil { - ctx.PathOverrides = merged.PathOverrides - } - - // New is not allowed to return error - it would break the established API. - // so the only way out is to actually do the checks here and record the error, - // and return it later, at the earliest possible occasion, in Setup() - if ctx.SnapshotPath != "" && ctx.Chroot != option.DefaultChroot { - // The env/client code supplied a value, but we are will overwrite it when unpacking shapshots! - ctx.err = fmt.Errorf("Conflicting options: chroot %q and snapshot path %q", ctx.Chroot, ctx.SnapshotPath) - } - return ctx -} - -// FromEnv returns a Context that has been populated from the environs or -// default options values -func FromEnv() *Context { - chrootVal := option.EnvOrDefaultChroot() - enableTools := option.EnvOrDefaultTools() - snapPathVal := option.EnvOrDefaultSnapshotPath() - snapRootVal := option.EnvOrDefaultSnapshotRoot() - snapExclusiveVal := option.EnvOrDefaultSnapshotExclusive() - return &Context{ - Chroot: chrootVal, - EnableTools: enableTools, - SnapshotPath: snapPathVal, - SnapshotRoot: snapRootVal, - SnapshotExclusive: snapExclusiveVal, - } -} - -// Do wraps a Setup/Teardown pair around the given function -func (ctx *Context) Do(fn func() error) error { - err := ctx.Setup() - if err != nil { - return err - } - defer func() { - err := ctx.Teardown() - if err != nil { - ctx.Warn("teardown error: %v", err) - } - }() - return fn() -} - -// Setup prepares the extra optional data a Context may use. -// `Context`s are ready to use once returned by `New`. Optional features, -// like snapshot unpacking, may require extra steps. Run `Setup` to perform them. -// You should call `Setup` just once. It is safe to call `Setup` if you don't make -// use of optional extra features - `Setup` will do nothing. -func (ctx *Context) Setup() error { - if ctx.err != nil { - return ctx.err - } - if ctx.SnapshotPath == "" { - // nothing to do! - return nil - } - - var err error - root := ctx.SnapshotRoot - if root == "" { - root, err = snapshot.Unpack(ctx.SnapshotPath) - if err == nil { - ctx.snapshotUnpackedPath = root - } - } else { - var flags uint - if ctx.SnapshotExclusive { - flags |= snapshot.OwnTargetDirectory - } - _, err = snapshot.UnpackInto(ctx.SnapshotPath, root, flags) - } - if err != nil { - return err - } - - ctx.Chroot = root - return nil -} - -// Teardown releases any resource acquired by Setup. -// You should always call `Teardown` if you called `Setup` to free any resources -// acquired by `Setup`. Check `Do` for more automated management. -func (ctx *Context) Teardown() error { - if ctx.snapshotUnpackedPath == "" { - // if the client code provided the unpack directory, - // then it is also in charge of the cleanup. - return nil - } - return snapshot.Cleanup(ctx.snapshotUnpackedPath) -} - -func (ctx *Context) Warn(msg string, args ...interface{}) { - ctx.alert.Printf("WARNING: "+msg, args...) -} diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go index 5c5e6babe7..91a9b4cad4 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu.go @@ -9,9 +9,8 @@ package cpu import ( "fmt" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" ) // ProcessorCore describes a physical host processor core. A processor core is @@ -122,7 +121,6 @@ func (p *Processor) String() string { // Info describes all central processing unit (CPU) functionality on a host. // Returned by the `ghw.CPU()` function. type Info struct { - ctx *context.Context // TotalCores is the total number of physical cores the host system // contains TotalCores uint32 `json:"total_cores"` @@ -140,10 +138,10 @@ type Info struct { // New returns a pointer to an Info struct that contains information about the // CPUs on the host system -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -183,11 +181,11 @@ type cpuPrinter struct { // YAMLString returns a string with the cpu information formatted as YAML // under a top-level "cpu:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, cpuPrinter{i}) + return marshal.SafeYAML(cpuPrinter{i}) } // JSONString returns a string with the cpu information formatted as JSON // under a top-level "cpu:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, cpuPrinter{i}, indent) + return marshal.SafeJSON(cpuPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_darwin.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_darwin.go index e4353e47b5..61a34b17a0 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_darwin.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_darwin.go @@ -1,11 +1,13 @@ package cpu import ( + "context" "fmt" - "github.com/pkg/errors" "os/exec" "strconv" "strings" + + "github.com/pkg/errors" ) var ( @@ -13,7 +15,7 @@ var ( sysctlOutput = make(map[string]string) // store all the sysctl output ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { err := populateSysctlOutput() if err != nil { return errors.Wrap(err, "unable to populate sysctl map") diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go index f403d29d96..80abe03890 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_linux.go @@ -7,6 +7,7 @@ package cpu import ( "bufio" + "context" "errors" "fmt" "os" @@ -16,7 +17,7 @@ import ( "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/util" ) @@ -26,8 +27,8 @@ var ( onlineFile = "online" ) -func (i *Info) load() error { - i.Processors = processorsGet(i.ctx) +func (i *Info) load(ctx context.Context) error { + i.Processors = processorsGet(ctx) var totCores uint32 var totThreads uint32 for _, p := range i.Processors { @@ -41,7 +42,7 @@ func (i *Info) load() error { return nil } -func processorsGet(ctx *context.Context) []*Processor { +func processorsGet(ctx context.Context) []*Processor { paths := linuxpath.New(ctx) lps := logicalProcessorsFromProcCPUInfo(ctx) @@ -53,7 +54,7 @@ func processorsGet(ctx *context.Context) []*Processor { // processor pseudodirs are of the pattern /sys/devices/system/cpu/cpu{N} fnames, err := os.ReadDir(paths.SysDevicesSystemCPU) if err != nil { - ctx.Warn("failed to read /sys/devices/system/cpu: %s", err) + log.Warn(ctx, "failed to read /sys/devices/system/cpu: %s", err) return []*Processor{} } for _, fname := range fnames { @@ -64,7 +65,7 @@ func processorsGet(ctx *context.Context) []*Processor { lpID, err := strconv.Atoi(matches[1]) if err != nil { - ctx.Warn("failed to find numeric logical processor ID: %s", err) + log.Warn(ctx, "failed to find numeric logical processor ID: %s", err) continue } @@ -83,7 +84,7 @@ func processorsGet(ctx *context.Context) []*Processor { proc = &Processor{ID: procID} lp, ok := lps[lpID] if !ok { - ctx.Warn( + log.Warn(ctx, "failed to find attributes for logical processor %d", lpID, ) @@ -94,11 +95,16 @@ func processorsGet(ctx *context.Context) []*Processor { // lps[lpID] describes logical processor `lpID`. // Once got a more robust way of fetching the following info, // can we drop /proc/cpuinfo. + // 1. Capabilities (Hardware Features) if len(lp.Attrs["flags"]) != 0 { // x86 proc.Capabilities = strings.Split(lp.Attrs["flags"], " ") } else if len(lp.Attrs["Features"]) != 0 { // ARM64 proc.Capabilities = strings.Split(lp.Attrs["Features"], " ") + } else if len(lp.Attrs["features"]) != 0 { // s390x + proc.Capabilities = strings.Split(lp.Attrs["features"], " ") } + + // 2. Model Detection if len(lp.Attrs["model name"]) != 0 { proc.Model = lp.Attrs["model name"] } else if len(lp.Attrs["Processor"]) != 0 { // ARM @@ -109,7 +115,10 @@ func processorsGet(ctx *context.Context) []*Processor { proc.Model = lp.Attrs["Model Name"] } else if len(lp.Attrs["uarch"]) != 0 { // SiFive proc.Model = lp.Attrs["uarch"] + } else if len(lp.Attrs["machine"]) != 0 { + proc.Model = lp.Attrs["machine"] } + // 3. Vendor Detection if len(lp.Attrs["vendor_id"]) != 0 { proc.Vendor = lp.Attrs["vendor_id"] } else if len(lp.Attrs["isa"]) != 0 { // RISCV64 @@ -155,7 +164,7 @@ func processorsGet(ctx *context.Context) []*Processor { // processorIDFromLogicalProcessorID returns the processor physical package ID // for the supplied logical processor ID -func processorIDFromLogicalProcessorID(ctx *context.Context, lpID int) int { +func processorIDFromLogicalProcessorID(ctx context.Context, lpID int) int { paths := linuxpath.New(ctx) // Fetch CPU ID path := filepath.Join( @@ -168,7 +177,7 @@ func processorIDFromLogicalProcessorID(ctx *context.Context, lpID int) int { // coreIDFromLogicalProcessorID returns the core ID for the supplied logical // processor ID -func coreIDFromLogicalProcessorID(ctx *context.Context, lpID int) int { +func coreIDFromLogicalProcessorID(ctx context.Context, lpID int) int { paths := linuxpath.New(ctx) // Fetch CPU ID path := filepath.Join( @@ -179,7 +188,7 @@ func coreIDFromLogicalProcessorID(ctx *context.Context, lpID int) int { return util.SafeIntFromFile(ctx, path) } -func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) { +func CoresForNode(ctx context.Context, nodeID int) ([]*ProcessorCore, error) { // The /sys/devices/system/node/nodeX directory contains a subdirectory // called 'cpuX' for each logical processor assigned to the node. Each of // those subdirectories contains a topology subdirectory which has a @@ -227,7 +236,7 @@ func CoresForNode(ctx *context.Context, nodeID int) ([]*ProcessorCore, error) { cpuPath := filepath.Join(path, filename) procID, err := strconv.Atoi(filename[3:]) if err != nil { - ctx.Warn( + log.Warn(ctx, "failed to determine procID from %s. Expected integer after 3rd char.", filename, ) @@ -339,7 +348,7 @@ type logicalProcessor struct { // with blank line-separated blocks of colon-delimited attribute name/value // pairs for a specific logical processor on the host. func logicalProcessorsFromProcCPUInfo( - ctx *context.Context, + ctx context.Context, ) map[int]*logicalProcessor { paths := linuxpath.New(ctx) r, err := os.Open(paths.ProcCpuinfo) @@ -360,25 +369,41 @@ func logicalProcessorsFromProcCPUInfo( // Output of /proc/cpuinfo has a blank newline to separate logical // processors, so here we collect up all the attributes we've // collected for this logical processor block - lpIDstr, ok := lpAttrs["processor"] + // s390x identifies CPUs via "cpu number", while most other + // architectures use "processor". + idStr, ok := lpAttrs["processor"] if !ok { - ctx.Warn("expected to find 'processor' key in /proc/cpuinfo attributes") - continue + idStr, ok = lpAttrs["cpu number"] } - lpID, _ := strconv.Atoi(lpIDstr) - lp := &logicalProcessor{ - ID: lpID, - Attrs: lpAttrs, + + if ok { + id, _ := strconv.Atoi(idStr) + lps[id] = &logicalProcessor{ + ID: id, + Attrs: lpAttrs, + } + // Only reset attributes after a valid processor block is saved. + // This ensures that shared header metadata (like vendor_id on s390x) + // is carried over and available to the processor entries. + lpAttrs = map[string]string{} + } else if len(lpAttrs) > 0 { + // s390x header check: if we have 'vendor_id' but no 'processor' ID, + // it's the summary block. We don't warn and we don't reset lpAttrs + // so the vendor_id carries over to the first actual CPU block. + if _, isS390Header := lpAttrs["vendor_id"]; !isS390Header { + log.Warn(ctx, + "expected to find 'processor' or 'cpu number' key "+ + "in /proc/cpuinfo attributes") + } } - lps[lpID] = lp - // Reset the current set of processor attributes... - lpAttrs = map[string]string{} continue } - parts := strings.Split(line, ":") - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - lpAttrs[key] = value + parts := strings.SplitN(line, ":", 2) + if len(parts) >= 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + lpAttrs[key] = value + } } return lps } diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_stub.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_stub.go index 85156069a1..970124c969 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_stub.go @@ -9,11 +9,12 @@ package cpu import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("cpu.Info.load not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_windows.go b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_windows.go index 012323c207..d0851a9c4f 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/cpu/cpu_windows.go @@ -9,6 +9,8 @@ package cpu import ( + "context" + "github.com/yusufpapurcu/wmi" ) @@ -21,7 +23,7 @@ type win32Processor struct { NumberOfCores uint32 } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting info from WMI var win32descriptions []win32Processor if err := wmi.Query(wmqlProcessor, &win32descriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu.go b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu.go index 65864c7e14..a620791201 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu.go +++ b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu.go @@ -9,9 +9,8 @@ package gpu import ( "fmt" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/pci" "github.com/jaypipes/ghw/pkg/topology" ) @@ -49,16 +48,15 @@ func (card *GraphicsCard) String() string { } type Info struct { - ctx *context.Context GraphicsCards []*GraphicsCard `json:"cards"` } // New returns a pointer to an Info struct that contains information about the // graphics cards on the host system -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -85,11 +83,11 @@ type gpuPrinter struct { // YAMLString returns a string with the gpu information formatted as YAML // under a top-level "gpu:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, gpuPrinter{i}) + return marshal.SafeYAML(gpuPrinter{i}) } // JSONString returns a string with the gpu information formatted as JSON // under a top-level "gpu:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, gpuPrinter{i}, indent) + return marshal.SafeJSON(gpuPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_linux.go b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_linux.go index f61e98bf68..4421ecc39c 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_linux.go @@ -6,13 +6,16 @@ package gpu import ( + "context" + "fmt" "os" "path/filepath" "regexp" "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/pci" "github.com/jaypipes/ghw/pkg/topology" @@ -26,14 +29,10 @@ const ( var reValidPCIAddress = regexp.MustCompile(validPCIAddress) const ( - _WARN_NO_SYS_CLASS_DRM = ` -/sys/class/drm does not exist on this system (likely the host system is a -virtual machine or container with no graphics). Therefore, -GPUInfo.GraphicsCards will be an empty array. -` + warnNoSysClassDRM = `/sys/class/drm does not exist on this system (likely the host system is a virtual machine or container with no graphics). Therefore, GPUInfo.GraphicsCards will be an empty array.` ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // In Linux, each graphics card is listed under the /sys/class/drm // directory as a symbolic link named "cardN", where N is a zero-based // index of the card in the system. "DRM" stands for Direct Rendering @@ -60,10 +59,14 @@ func (i *Info) load() error { // we follow to gather information about the actual device from the PCI // subsystem (we query the modalias file of the PCI device's sysfs // directory using the `ghw.PCIInfo.GetDevice()` function. - paths := linuxpath.New(i.ctx) + pci, err := pci.New(ctx) + if err != nil { + return fmt.Errorf("failed to initialize PCI device database: %w", err) + } + paths := linuxpath.New(ctx) links, err := os.ReadDir(paths.SysClassDRM) if err != nil { - i.ctx.Warn(_WARN_NO_SYS_CLASS_DRM) + log.Warn(ctx, warnNoSysClassDRM) return nil } cards := make([]*GraphicsCard, 0) @@ -109,20 +112,15 @@ func (i *Info) load() error { } cards = append(cards, card) } - gpuFillNUMANodes(i.ctx, cards) - gpuFillPCIDevice(i.ctx, cards) + gpuFillNUMANodes(ctx, cards) + gpuFillPCIDevice(pci, cards) i.GraphicsCards = cards return nil } // Loops through each GraphicsCard struct and attempts to fill the DeviceInfo // attribute with PCI device information -func gpuFillPCIDevice(ctx *context.Context, cards []*GraphicsCard) { - pci, err := pci.New(context.WithContext(ctx)) - if err != nil { - ctx.Warn("failed to PCI device database: %s", err) - return - } +func gpuFillPCIDevice(pci *pci.Info, cards []*GraphicsCard) { for _, card := range cards { if card.DeviceInfo == nil { card.DeviceInfo = pci.GetDevice(card.Address) @@ -133,16 +131,22 @@ func gpuFillPCIDevice(ctx *context.Context, cards []*GraphicsCard) { // Loops through each GraphicsCard struct and find which NUMA node the card is // affined to, setting the GraphicsCard.Node field accordingly. If the host // system is not a NUMA system, the Node field will be set to nil. -func gpuFillNUMANodes(ctx *context.Context, cards []*GraphicsCard) { +func gpuFillNUMANodes(ctx context.Context, cards []*GraphicsCard) { paths := linuxpath.New(ctx) - topo, err := topology.New(context.WithContext(ctx)) + // Skip topology detection if requested to reduce memory consumption + if !config.TopologyEnabled(ctx) { + for _, card := range cards { + card.Node = nil + } + return + } + + topo, err := topology.New(ctx) if err != nil { // Problem getting topology information so just set the graphics card's // node to nil for _, card := range cards { - if topo.Architecture != topology.ArchitectureNUMA { - card.Node = nil - } + card.Node = nil } return } diff --git a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_stub.go b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_stub.go index 48991ec8e2..01d223dd8a 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_stub.go @@ -9,11 +9,12 @@ package gpu import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("gpuFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go index 1a2f4d637f..7a58432cdd 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/gpu/gpu_windows.go @@ -6,6 +6,7 @@ package gpu import ( + "context" "strings" "github.com/jaypipes/pcidb" @@ -46,7 +47,7 @@ type win32PnPEntity struct { PNPDeviceID string } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting data from WMI var win32VideoControllerDescriptions []win32VideoController if err := wmi.Query(wqlVideoController, &win32VideoControllerDescriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/linuxdmi/dmi_linux.go b/vendor/github.com/jaypipes/ghw/pkg/linuxdmi/dmi_linux.go index 8e6d8302d5..6b373a7a2f 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/linuxdmi/dmi_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/linuxdmi/dmi_linux.go @@ -6,22 +6,24 @@ package linuxdmi import ( + "context" "os" "path/filepath" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/util" ) -func Item(ctx *context.Context, value string) string { +func Item(ctx context.Context, value string) string { paths := linuxpath.New(ctx) path := filepath.Join(paths.SysClassDMI, "id", value) + log.Debug(ctx, "reading from %q", path) b, err := os.ReadFile(path) if err != nil { - ctx.Warn("Unable to read %s: %s\n", value, err) + log.Warn(ctx, "Unable to read %s: %s\n", value, err) return util.UNKNOWN } diff --git a/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go b/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go index bbe81b64ef..c08869158c 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/linuxpath/path_linux.go @@ -6,10 +6,11 @@ package linuxpath import ( + "context" "fmt" "path/filepath" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" ) // PathRoots holds the roots of all the filesystem subtrees @@ -35,27 +36,29 @@ func DefaultPathRoots() PathRoots { // PathRootsFromContext initialize PathRoots from the given Context, // allowing overrides of the canonical default paths. -func PathRootsFromContext(ctx *context.Context) PathRoots { +func PathRootsFromContext(ctx context.Context) PathRoots { roots := DefaultPathRoots() - if pathEtc, ok := ctx.PathOverrides["/etc"]; ok { + overrides := config.PathOverrides(ctx) + if pathEtc, ok := overrides["/etc"]; ok { roots.Etc = pathEtc } - if pathProc, ok := ctx.PathOverrides["/proc"]; ok { + if pathProc, ok := overrides["/proc"]; ok { roots.Proc = pathProc } - if pathRun, ok := ctx.PathOverrides["/run"]; ok { + if pathRun, ok := overrides["/run"]; ok { roots.Run = pathRun } - if pathSys, ok := ctx.PathOverrides["/sys"]; ok { + if pathSys, ok := overrides["/sys"]; ok { roots.Sys = pathSys } - if pathVar, ok := ctx.PathOverrides["/var"]; ok { + if pathVar, ok := overrides["/var"]; ok { roots.Var = pathVar } return roots } type Paths struct { + SysRoot string VarLog string ProcMeminfo string ProcCpuinfo string @@ -66,6 +69,7 @@ type Paths struct { SysDevicesSystemMemory string SysDevicesSystemCPU string SysBusPciDevices string + SysBusUsbDevices string SysClassDRM string SysClassDMI string SysClassNet string @@ -74,23 +78,26 @@ type Paths struct { // New returns a new Paths struct containing filepath fields relative to the // supplied Context -func New(ctx *context.Context) *Paths { +func New(ctx context.Context) *Paths { roots := PathRootsFromContext(ctx) + chroot := config.Chroot(ctx) return &Paths{ - VarLog: filepath.Join(ctx.Chroot, roots.Var, "log"), - ProcMeminfo: filepath.Join(ctx.Chroot, roots.Proc, "meminfo"), - ProcCpuinfo: filepath.Join(ctx.Chroot, roots.Proc, "cpuinfo"), - ProcMounts: filepath.Join(ctx.Chroot, roots.Proc, "self", "mounts"), - SysKernelMMHugepages: filepath.Join(ctx.Chroot, roots.Sys, "kernel", "mm", "hugepages"), - SysBlock: filepath.Join(ctx.Chroot, roots.Sys, "block"), - SysDevicesSystemNode: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "node"), - SysDevicesSystemMemory: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "memory"), - SysDevicesSystemCPU: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "cpu"), - SysBusPciDevices: filepath.Join(ctx.Chroot, roots.Sys, "bus", "pci", "devices"), - SysClassDRM: filepath.Join(ctx.Chroot, roots.Sys, "class", "drm"), - SysClassDMI: filepath.Join(ctx.Chroot, roots.Sys, "class", "dmi"), - SysClassNet: filepath.Join(ctx.Chroot, roots.Sys, "class", "net"), - RunUdevData: filepath.Join(ctx.Chroot, roots.Run, "udev", "data"), + SysRoot: filepath.Join(chroot, roots.Sys), + VarLog: filepath.Join(chroot, roots.Var, "log"), + ProcMeminfo: filepath.Join(chroot, roots.Proc, "meminfo"), + ProcCpuinfo: filepath.Join(chroot, roots.Proc, "cpuinfo"), + ProcMounts: filepath.Join(chroot, roots.Proc, "self", "mounts"), + SysKernelMMHugepages: filepath.Join(chroot, roots.Sys, "kernel", "mm", "hugepages"), + SysBlock: filepath.Join(chroot, roots.Sys, "block"), + SysDevicesSystemNode: filepath.Join(chroot, roots.Sys, "devices", "system", "node"), + SysDevicesSystemMemory: filepath.Join(chroot, roots.Sys, "devices", "system", "memory"), + SysDevicesSystemCPU: filepath.Join(chroot, roots.Sys, "devices", "system", "cpu"), + SysBusPciDevices: filepath.Join(chroot, roots.Sys, "bus", "pci", "devices"), + SysBusUsbDevices: filepath.Join(chroot, roots.Sys, "bus", "usb", "devices"), + SysClassDRM: filepath.Join(chroot, roots.Sys, "class", "drm"), + SysClassDMI: filepath.Join(chroot, roots.Sys, "class", "dmi"), + SysClassNet: filepath.Join(chroot, roots.Sys, "class", "net"), + RunUdevData: filepath.Join(chroot, roots.Run, "udev", "data"), } } diff --git a/vendor/github.com/jaypipes/ghw/pkg/marshal/marshal.go b/vendor/github.com/jaypipes/ghw/pkg/marshal/marshal.go index e442d6aff3..c8567ce749 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/marshal/marshal.go +++ b/vendor/github.com/jaypipes/ghw/pkg/marshal/marshal.go @@ -9,27 +9,23 @@ package marshal import ( "encoding/json" - "github.com/jaypipes/ghw/pkg/context" yaml "gopkg.in/yaml.v3" ) // SafeYAML returns a string after marshalling the supplied parameter into YAML. -func SafeYAML(ctx *context.Context, p interface{}) string { +func SafeYAML(p interface{}) string { b, err := json.Marshal(p) if err != nil { - ctx.Warn("error marshalling JSON: %s", err) return "" } var jsonObj interface{} if err := yaml.Unmarshal(b, &jsonObj); err != nil { - ctx.Warn("error converting JSON to YAML: %s", err) return "" } yb, err := yaml.Marshal(jsonObj) if err != nil { - ctx.Warn("error marshalling YAML: %s", err) return "" } @@ -39,7 +35,7 @@ func SafeYAML(ctx *context.Context, p interface{}) string { // SafeJSON returns a string after marshalling the supplied parameter into // JSON. Accepts an optional argument to trigger pretty/indented formatting of // the JSON string. -func SafeJSON(ctx *context.Context, p interface{}, indent bool) string { +func SafeJSON(p interface{}, indent bool) string { var b []byte var err error if !indent { @@ -48,7 +44,6 @@ func SafeJSON(ctx *context.Context, p interface{}, indent bool) string { b, err = json.MarshalIndent(&p, "", " ") } if err != nil { - ctx.Warn("error marshalling JSON: %s", err) return "" } return string(b) diff --git a/vendor/github.com/jaypipes/ghw/pkg/memory/memory.go b/vendor/github.com/jaypipes/ghw/pkg/memory/memory.go index f58ba8b91e..e043adaede 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/memory/memory.go +++ b/vendor/github.com/jaypipes/ghw/pkg/memory/memory.go @@ -10,9 +10,8 @@ import ( "fmt" "math" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/unitutil" "github.com/jaypipes/ghw/pkg/util" ) @@ -78,15 +77,14 @@ func (a *Area) String() string { // Info contains information about the memory on a host system. type Info struct { - ctx *context.Context Area } // New returns an Info struct that describes the memory on a host system. -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -106,11 +104,11 @@ type memoryPrinter struct { // YAMLString returns a string with the memory information formatted as YAML // under a top-level "memory:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, memoryPrinter{i}) + return marshal.SafeYAML(memoryPrinter{i}) } // JSONString returns a string with the memory information formatted as JSON // under a top-level "memory:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, memoryPrinter{i}, indent) + return marshal.SafeJSON(memoryPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_cache_linux.go b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_cache_linux.go index 12258ea4ed..93df0d17e9 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_cache_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_cache_linux.go @@ -6,6 +6,7 @@ package memory import ( + "context" "errors" "fmt" "os" @@ -14,12 +15,12 @@ import ( "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/unitutil" ) -func CachesForNode(ctx *context.Context, nodeID int) ([]*Cache, error) { +func CachesForNode(ctx context.Context, nodeID int) ([]*Cache, error) { // The /sys/devices/node/nodeX directory contains a subdirectory called // 'cpuX' for each logical processor assigned to the node. Each of those // subdirectories containers a 'cache' subdirectory which contains a number @@ -81,8 +82,20 @@ func CachesForNode(ctx *context.Context, nodeID int) ([]*Cache, error) { // unique combination of level, type and processor map level := memoryCacheLevel(ctx, paths, nodeID, lpID, cacheIndex) cacheType := memoryCacheType(ctx, paths, nodeID, lpID, cacheIndex) - sharedCpuMap := memoryCacheSharedCPUMap(ctx, paths, nodeID, lpID, cacheIndex) - cacheKey := fmt.Sprintf("%d-%d-%s", level, cacheType, sharedCpuMap) + cacheID := memoryCacheID(ctx, paths, nodeID, lpID, cacheIndex) + + // Generate cache key for deduplication. + // Use cache ID if available (modern kernels), otherwise fall back + // to shared_cpu_map for older kernels. This ensures that caches + // shared across NUMA nodes (e.g., L3 in Sub-NUMA Clustering) are + // correctly merged into a single cache object. + var cacheKey string + if cacheID != -1 { + cacheKey = fmt.Sprintf("%d-%d-%d", level, cacheType, cacheID) + } else { + sharedCpuMap := memoryCacheSharedCPUMap(ctx, paths, nodeID, lpID, cacheIndex) + cacheKey = fmt.Sprintf("%d-%d-%s", level, cacheType, sharedCpuMap) + } cache, exists := caches[cacheKey] if !exists { @@ -114,53 +127,95 @@ func CachesForNode(ctx *context.Context, nodeID int) ([]*Cache, error) { return cacheVals, nil } -func memoryCacheLevel(ctx *context.Context, paths *linuxpath.Paths, nodeID int, lpID int, cacheIndex int) int { +func memoryCacheLevel( + ctx context.Context, + paths *linuxpath.Paths, + nodeID int, + lpID int, + cacheIndex int, +) int { levelPath := filepath.Join( paths.NodeCPUCacheIndex(nodeID, lpID, cacheIndex), "level", ) levelContents, err := os.ReadFile(levelPath) if err != nil { - ctx.Warn("%s", err) + log.Warn(ctx, "%s", err) return -1 } // levelContents is now a []byte with the last byte being a newline // character. Trim that off and convert the contents to an integer. level, err := strconv.Atoi(string(levelContents[:len(levelContents)-1])) if err != nil { - ctx.Warn("Unable to parse int from %s", levelContents) + log.Warn(ctx, "Unable to parse int from %s", levelContents) return -1 } return level } -func memoryCacheSize(ctx *context.Context, paths *linuxpath.Paths, nodeID int, lpID int, cacheIndex int) int { +func memoryCacheID( + ctx context.Context, + paths *linuxpath.Paths, + nodeID int, + lpID int, + cacheIndex int, +) int { + idPath := filepath.Join( + paths.NodeCPUCacheIndex(nodeID, lpID, cacheIndex), + "id", + ) + idContents, err := os.ReadFile(idPath) + if err != nil { + // The id file may not exist in early kernel versions, so we don't warn + return -1 + } + id, err := strconv.Atoi(string(idContents[:len(idContents)-1])) + if err != nil { + log.Warn(ctx, "Unable to parse int from %s", idContents) + return -1 + } + return id +} + +func memoryCacheSize( + ctx context.Context, + paths *linuxpath.Paths, + nodeID int, + lpID int, + cacheIndex int, +) int { sizePath := filepath.Join( paths.NodeCPUCacheIndex(nodeID, lpID, cacheIndex), "size", ) sizeContents, err := os.ReadFile(sizePath) if err != nil { - ctx.Warn("%s", err) + log.Warn(ctx, "%s", err) return -1 } // size comes as XK\n, so we trim off the K and the newline. size, err := strconv.Atoi(string(sizeContents[:len(sizeContents)-2])) if err != nil { - ctx.Warn("Unable to parse int from %s", sizeContents) + log.Warn(ctx, "Unable to parse int from %s", sizeContents) return -1 } return size } -func memoryCacheType(ctx *context.Context, paths *linuxpath.Paths, nodeID int, lpID int, cacheIndex int) CacheType { +func memoryCacheType( + ctx context.Context, + paths *linuxpath.Paths, + nodeID int, + lpID int, + cacheIndex int, +) CacheType { typePath := filepath.Join( paths.NodeCPUCacheIndex(nodeID, lpID, cacheIndex), "type", ) cacheTypeContents, err := os.ReadFile(typePath) if err != nil { - ctx.Warn("%s", err) + log.Warn(ctx, "%s", err) return CacheTypeUnified } switch string(cacheTypeContents[:len(cacheTypeContents)-1]) { @@ -173,14 +228,20 @@ func memoryCacheType(ctx *context.Context, paths *linuxpath.Paths, nodeID int, l } } -func memoryCacheSharedCPUMap(ctx *context.Context, paths *linuxpath.Paths, nodeID int, lpID int, cacheIndex int) string { +func memoryCacheSharedCPUMap( + ctx context.Context, + paths *linuxpath.Paths, + nodeID int, + lpID int, + cacheIndex int, +) string { scpuPath := filepath.Join( paths.NodeCPUCacheIndex(nodeID, lpID, cacheIndex), "shared_cpu_map", ) sharedCpuMap, err := os.ReadFile(scpuPath) if err != nil { - ctx.Warn("%s", err) + log.Warn(ctx, "%s", err) return "" } return string(sharedCpuMap[:len(sharedCpuMap)-1]) diff --git a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go index 031fcdef2c..4e6f211866 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_linux.go @@ -8,6 +8,7 @@ package memory import ( "bufio" "compress/gzip" + "context" "fmt" "io" "os" @@ -17,21 +18,14 @@ import ( "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/unitutil" "github.com/jaypipes/ghw/pkg/util" ) const ( - warnCannotDeterminePhysicalMemory = ` -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 /sys/devices/system/memory directory, 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 -` + warnCannotDeterminePhysicalMemory = `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 /sys/devices/system/memory directory, 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` ) var ( @@ -44,8 +38,8 @@ var ( regexMemoryBlockDirname = regexp.MustCompile(`memory\d+$`) ) -func (i *Info) load() error { - paths := linuxpath.New(i.ctx) +func (i *Info) load(ctx context.Context) error { + paths := linuxpath.New(ctx) tub := memTotalUsableBytes(paths) if tub < 1 { return fmt.Errorf("Could not determine total usable bytes of memory") @@ -54,7 +48,7 @@ func (i *Info) load() error { tpb := memTotalPhysicalBytes(paths) i.TotalPhysicalBytes = tpb if tpb < 1 { - i.ctx.Warn(warnCannotDeterminePhysicalMemory) + log.Warn(ctx, warnCannotDeterminePhysicalMemory) i.TotalPhysicalBytes = tub } i.SupportedPageSizes, _ = memorySupportedPageSizes(paths.SysKernelMMHugepages) @@ -72,8 +66,7 @@ func (i *Info) load() error { return nil } -func AreaForNode(ctx *context.Context, nodeID int) (*Area, error) { - paths := linuxpath.New(ctx) +func AreaForNode(paths *linuxpath.Paths, nodeID int) (*Area, error) { path := filepath.Join( paths.SysDevicesSystemNode, fmt.Sprintf("node%d", nodeID), diff --git a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_stub.go b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_stub.go index 6ce99e00dc..a7e69dd274 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_stub.go @@ -9,11 +9,12 @@ package memory import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("mem.Info.load not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_windows.go b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_windows.go index 606465527d..9ada287df8 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/memory/memory_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/memory/memory_windows.go @@ -7,6 +7,8 @@ package memory import ( + "context" + "github.com/yusufpapurcu/wmi" "github.com/jaypipes/ghw/pkg/unitutil" @@ -37,7 +39,7 @@ type win32PhysicalMemory struct { TotalWidth *uint16 } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting info from WMI var win32OSDescriptions []win32OperatingSystem if err := wmi.Query(wqlOperatingSystem, &win32OSDescriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/net/net.go b/vendor/github.com/jaypipes/ghw/pkg/net/net.go index e26dab702e..21b7e62120 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/net/net.go +++ b/vendor/github.com/jaypipes/ghw/pkg/net/net.go @@ -9,9 +9,8 @@ package net import ( "fmt" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" ) // NICCapability is a feature/capability of a Network Interface Controller @@ -95,7 +94,6 @@ func (n *NIC) String() string { // Info describes all network interface controllers (NICs) in the host system. type Info struct { - ctx *context.Context // NICs is a slice of pointers to `NIC` structs describing the network // interface controllers (NICs) on the host system. NICs []*NIC `json:"nics"` @@ -103,10 +101,10 @@ type Info struct { // New returns a pointer to an Info struct that contains information about the // network interface controllers (NICs) on the host system -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -130,11 +128,11 @@ type netPrinter struct { // YAMLString returns a string with the net information formatted as YAML // under a top-level "net:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, netPrinter{i}) + return marshal.SafeYAML(netPrinter{i}) } // JSONString returns a string with the net information formatted as JSON // under a top-level "net:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, netPrinter{i}, indent) + return marshal.SafeJSON(netPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go b/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go index d7d7e8caf3..86542b936d 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/net/net_linux.go @@ -8,13 +8,15 @@ package net import ( "bufio" "bytes" + "context" "fmt" "os" "os/exec" "path/filepath" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/util" ) @@ -23,12 +25,12 @@ const ( warnEthtoolNotInstalled = `ethtool not installed. Cannot grab NIC capabilities` ) -func (i *Info) load() error { - i.NICs = nics(i.ctx) +func (i *Info) load(ctx context.Context) error { + i.NICs = nics(ctx) return nil } -func nics(ctx *context.Context) []*NIC { +func nics(ctx context.Context) []*NIC { nics := make([]*NIC, 0) paths := linuxpath.New(ctx) @@ -37,18 +39,18 @@ func nics(ctx *context.Context) []*NIC { return nics } - etAvailable := ctx.EnableTools + etAvailable := config.ToolsEnabled(ctx) if etAvailable { if etInstalled := ethtoolInstalled(); !etInstalled { - ctx.Warn(warnEthtoolNotInstalled) + log.Warn(ctx, warnEthtoolNotInstalled) etAvailable = false } } for _, file := range files { filename := file.Name() - // Ignore loopback... - if filename == "lo" { + // Ignore loopback and bonding_masters + if filename == "lo" || filename == "bonding_masters" { continue } @@ -108,7 +110,7 @@ func ethtoolInstalled() bool { return err == nil } -func (n *NIC) netDeviceParseEthtool(ctx *context.Context, dev string) { +func (n *NIC) netDeviceParseEthtool(ctx context.Context, dev string) { var out bytes.Buffer path, _ := exec.LookPath("ethtool") @@ -133,7 +135,7 @@ func (n *NIC) netDeviceParseEthtool(ctx *context.Context, dev string) { n.AdvertisedFECModes = m["Advertised FEC modes"] } else { msg := fmt.Sprintf("could not grab NIC link info for %s: %s", dev, err) - ctx.Warn(msg) + log.Warn(ctx, msg) } // Get all other capabilities from "ethtool -k" @@ -171,9 +173,8 @@ func (n *NIC) netDeviceParseEthtool(ctx *context.Context, dev string) { } else { msg := fmt.Sprintf("could not grab NIC capabilities for %s: %s", dev, err) - ctx.Warn(msg) + log.Warn(ctx, msg) } - } // netParseEthtoolFeature parses a line from the ethtool -k output and returns diff --git a/vendor/github.com/jaypipes/ghw/pkg/net/net_stub.go b/vendor/github.com/jaypipes/ghw/pkg/net/net_stub.go index c8dfa090d5..fc6a447181 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/net/net_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/net/net_stub.go @@ -9,11 +9,12 @@ package net import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("netFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/net/net_windows.go b/vendor/github.com/jaypipes/ghw/pkg/net/net_windows.go index 5936ff641d..5d02441bd2 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/net/net_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/net/net_windows.go @@ -6,6 +6,7 @@ package net import ( + "context" "strings" "github.com/yusufpapurcu/wmi" @@ -27,7 +28,7 @@ type win32NetworkAdapter struct { PhysicalAdapter *bool } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting info from WMI var win32NetDescriptions []win32NetworkAdapter if err := wmi.Query(wqlNetworkAdapter, &win32NetDescriptions); err != nil { diff --git a/vendor/github.com/jaypipes/ghw/pkg/option/option.go b/vendor/github.com/jaypipes/ghw/pkg/option/option.go index 36b253b45b..59a310d375 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/option/option.go +++ b/vendor/github.com/jaypipes/ghw/pkg/option/option.go @@ -4,6 +4,9 @@ // See the COPYING file in the root project directory for full text. // +// NOTE(jaypipes): This entire package is deprecated and will be removed in the +// 1.0 release of ghw. Please use the aliased definitions of WithXXX functions +// in the main `ghw` package. package option import ( @@ -64,214 +67,125 @@ func EnvOrDefaultChroot() string { return DefaultChroot } -// EnvOrDefaultSnapshotPath returns the value of the GHW_SNAPSHOT_PATH environs variable -// or the default value of "" (disable snapshot consumption) if not set -func EnvOrDefaultSnapshotPath() string { - if val, exists := os.LookupEnv(envKeySnapshotPath); exists { - return val - } - return "" // default is no snapshot -} - -// EnvOrDefaultSnapshotRoot returns the value of the the GHW_SNAPSHOT_ROOT environs variable -// or the default value of "" (self-manage the snapshot unpack directory, if relevant) if not set -func EnvOrDefaultSnapshotRoot() string { - if val, exists := os.LookupEnv(envKeySnapshotRoot); exists { - return val - } - return "" // default is to self-manage the snapshot directory -} - -// EnvOrDefaultSnapshotExclusive returns the value of the GHW_SNAPSHOT_EXCLUSIVE environs variable -// or the default value of false if not set -func EnvOrDefaultSnapshotExclusive() bool { - if _, exists := os.LookupEnv(envKeySnapshotExclusive); exists { - return true - } - return false -} - -// EnvOrDefaultSnapshotPreserve returns the value of the GHW_SNAPSHOT_PRESERVE environs variable -// or the default value of false if not set -func EnvOrDefaultSnapshotPreserve() bool { - if _, exists := os.LookupEnv(envKeySnapshotPreserve); exists { - return true - } - return false -} - -// EnvOrDefaultTools return true if ghw should use external tools to augment the data collected -// from sysfs. Most users want to do this most of time, so this is enabled by default. -// Users consuming snapshots may want to opt out, thus they can set the GHW_DISABLE_TOOLS -// environs variable to any value to make ghw skip calling external tools even if they are available. -func EnvOrDefaultTools() bool { +// EnvOrDefaultDisableTools return true if ghw should use external tools to +// augment the data collected from sysfs. Most users want to do this most of +// time, so this is enabled by default. Users consuming snapshots may want to +// opt out, thus they can set the GHW_DISABLE_TOOLS environs variable to any +// value to make ghw skip calling external tools even if they are available. +func EnvOrDefaultDisableTools() bool { if _, exists := os.LookupEnv(envKeyDisableTools); exists { return false } return true } -// Option is used to represent optionally-configured settings. Each field is a +// Options is used to represent optionally-configured settings. Each field is a // pointer to some concrete value so that we can tell when something has been // set or left unset. -type Option struct { +type Options struct { // To facilitate querying of sysfs filesystems that are bind-mounted to a - // non-default root mountpoint, we allow users to set the GHW_CHROOT environ - // variable to an alternate mountpoint. For instance, assume that the user of - // ghw is a Golang binary being executed from an application container that has - // certain host filesystems bind-mounted into the container at /host. The user - // would ensure the GHW_CHROOT environ variable is set to "/host" and ghw will - // build its paths from that location instead of / - Chroot *string - - // Snapshot contains options for handling ghw snapshots - Snapshot *SnapshotOptions + // non-default root mountpoint, we allow users to set the GHW_CHROOT + // environ variable to an alternate mountpoint. For instance, assume that + // the user of ghw is a Golang binary being executed from an application + // container that has certain host filesystems bind-mounted into the + // container at /host. The user would ensure the GHW_CHROOT environ + // variable is set to "/host" and ghw will build its paths from that + // location instead of / + Chroot string // Alerter contains the target for ghw warnings Alerter Alerter - // EnableTools optionally request ghw to not call any external program to learn + // DisableTools optionally request ghw to not call any external program to learn // about the hardware. The default is to use such tools if available. - EnableTools *bool + DisableTools bool - // PathOverrides optionally allows to override the default paths ghw uses internally - // to learn about the system resources. + // PathOverrides optionally allows to override the default paths ghw uses + // internally to learn about the system resources. PathOverrides PathOverrides - // Context may contain a pointer to a `Context` struct that is constructed - // during a call to the `context.WithContext` function. Only used internally. - // This is an interface to get around recursive package import issues. - Context interface{} - - // PCIDB allows users to provide a custom instance of the PCI database (pcidb.PCIDB) - // to be used by ghw. This can be useful for testing, supplying a preloaded database, - // or providing an instance created with custom pcidb.WithOption settings, instead of - // letting ghw load the PCI database automatically. + // PCIDB allows users to provide a custom instance of the PCI database + // (pcidb.PCIDB) to be used by ghw. This can be useful for testing, + // supplying a preloaded database, or providing an instance created with + // custom pcidb.WithOption settings, instead of letting ghw load the PCI + // database automatically. PCIDB *pcidb.PCIDB } -// SnapshotOptions contains options for handling of ghw snapshots -type SnapshotOptions struct { - // Path allows users to specify a snapshot (captured using ghw-snapshot) to be - // automatically consumed. Users need to supply the path of the snapshot, and - // ghw will take care of unpacking it on a temporary directory. - // Set the environment variable "GHW_SNAPSHOT_PRESERVE" to make ghw skip the cleanup - // stage and keep the unpacked snapshot in the temporary directory. - Path string - // Root is the directory on which the snapshot must be unpacked. This allows - // the users to manage their snapshot directory instead of ghw doing that on - // their behalf. Relevant only if SnapshotPath is given. - Root *string - // Exclusive tells ghw if the given directory should be considered of exclusive - // usage of ghw or not If the user provides a Root. If the flag is set, ghw will - // unpack the snapshot in the given SnapshotRoot iff the directory is empty; otherwise - // any existing content will be left untouched and the unpack stage will exit silently. - // As additional side effect, give both this option and SnapshotRoot to make each - // context try to unpack the snapshot only once. - Exclusive bool +func (o *Options) Warn(msg string, args ...interface{}) { + if o.Alerter != nil { + o.Alerter.Printf("WARNING: "+msg, args...) + } } -// WithChroot allows to override the root directory ghw uses. -func WithChroot(dir string) *Option { - return &Option{Chroot: &dir} -} +type Option func(opts *Options) -// WithSnapshot sets snapshot-processing options for a ghw run -func WithSnapshot(opts SnapshotOptions) *Option { - return &Option{ - Snapshot: &opts, +// WithChroot allows to override the root directory ghw uses. +func WithChroot(dir string) Option { + return func(opts *Options) { + opts.Chroot = dir } } // WithAlerter sets alerting options for ghw -func WithAlerter(alerter Alerter) *Option { - return &Option{ - Alerter: alerter, +// +// DEPRECATED. Use `pkg/context.WithDisableWarnings` +func WithAlerter(alerter Alerter) Option { + return func(opts *Options) { + opts.Alerter = alerter } } // WithNullAlerter sets No-op alerting options for ghw -func WithNullAlerter() *Option { - return &Option{ - Alerter: NullAlerter, +// +// DEPRECATED. Use `pkg/context.WithDisableWarnings` +func WithNullAlerter() Option { + return func(opts *Options) { + opts.Alerter = NullAlerter } } -// WithDisableTools sets enables or prohibts ghw to call external tools to discover hardware capabilities. -func WithDisableTools() *Option { - false_ := false - return &Option{EnableTools: &false_} +// WithDisableTools revents ghw from calling external tools to discover +// hardware capabilities. +// +// DEPRECATED. Use `pkg/context.WithDisableTools` +func WithDisableTools() Option { + return func(opts *Options) { + opts.DisableTools = true + } } // WithPCIDB allows you to provide a custom instance of the PCI database (pcidb.PCIDB) // to ghw. This is useful if you want to use a preloaded or specially configured // PCI database, such as one created with custom pcidb.WithOption settings, instead // of letting ghw load the PCI database automatically. -func WithPCIDB(pcidb *pcidb.PCIDB) *Option { - return &Option{PCIDB: pcidb} +// +// DEPRECATED. Use `pkg/context.WithPCIDB` +func WithPCIDB(pcidb *pcidb.PCIDB) Option { + return func(opts *Options) { + opts.PCIDB = pcidb + } } // PathOverrides is a map, keyed by the string name of a mount path, of override paths type PathOverrides map[string]string // WithPathOverrides supplies path-specific overrides for the context -func WithPathOverrides(overrides PathOverrides) *Option { - return &Option{ - PathOverrides: overrides, +// +// DEPRECATED. Use `pkg/context.WithPathOverrides` +func WithPathOverrides(overrides PathOverrides) Option { + return func(opts *Options) { + opts.PathOverrides = overrides } } -// There is intentionally no Option related to GHW_SNAPSHOT_PRESERVE because we see that as -// a debug/troubleshoot aid more something users wants to do regularly. -// Hence we allow that only via the environment variable for the time being. - -// Merge accepts one or more Options and merges them together, returning the -// merged Option -func Merge(opts ...*Option) *Option { - merged := &Option{} - for _, opt := range opts { - if opt.Chroot != nil { - merged.Chroot = opt.Chroot - } - if opt.Snapshot != nil { - merged.Snapshot = opt.Snapshot - } - if opt.Alerter != nil { - merged.Alerter = opt.Alerter - } - if opt.EnableTools != nil { - merged.EnableTools = opt.EnableTools - } - // intentionally only programmatically - if opt.PathOverrides != nil { - merged.PathOverrides = opt.PathOverrides - } - if opt.Context != nil { - merged.Context = opt.Context - } - if opt.PCIDB != nil { - merged.PCIDB = opt.PCIDB - } - } - // Set the default value if missing from mergeOpts - if merged.Chroot == nil { - chroot := EnvOrDefaultChroot() - merged.Chroot = &chroot - } - if merged.Alerter == nil { - merged.Alerter = EnvOrDefaultAlerter() - } - if merged.Snapshot == nil { - snapRoot := EnvOrDefaultSnapshotRoot() - merged.Snapshot = &SnapshotOptions{ - Path: EnvOrDefaultSnapshotPath(), - Root: &snapRoot, - Exclusive: EnvOrDefaultSnapshotExclusive(), - } - } - if merged.EnableTools == nil { - enabled := EnvOrDefaultTools() - merged.EnableTools = &enabled +// FromEnv returns an Options populated from the environs or default option +// values +// +// DEPRECATED. Use `pkg/context.FromEnv` +func FromEnv() *Options { + return &Options{ + Chroot: EnvOrDefaultChroot(), + DisableTools: EnvOrDefaultDisableTools(), } - return merged } diff --git a/vendor/github.com/jaypipes/ghw/pkg/pci/pci.go b/vendor/github.com/jaypipes/ghw/pkg/pci/pci.go index 4afdc3f61d..38c8fcd54a 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/pci/pci.go +++ b/vendor/github.com/jaypipes/ghw/pkg/pci/pci.go @@ -12,20 +12,22 @@ import ( "github.com/jaypipes/pcidb" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/topology" "github.com/jaypipes/ghw/pkg/util" ) type Device struct { // The PCI address of the device - Address string `json:"address"` - Vendor *pcidb.Vendor `json:"vendor"` - Product *pcidb.Product `json:"product"` - Revision string `json:"revision"` - Subsystem *pcidb.Product `json:"subsystem"` + Address string `json:"address"` + // The PCI address of the parent device + ParentAddress string `json:"parent_address"` + Vendor *pcidb.Vendor `json:"vendor"` + Product *pcidb.Product `json:"product"` + Revision string `json:"revision"` + Subsystem *pcidb.Product `json:"subsystem"` // optional subvendor/sub-device information Class *pcidb.Class `json:"class"` // optional sub-class for the device @@ -47,15 +49,17 @@ type devIdent struct { } type devMarshallable struct { - Driver string `json:"driver"` - Address string `json:"address"` - Vendor devIdent `json:"vendor"` - Product devIdent `json:"product"` - Revision string `json:"revision"` - Subsystem devIdent `json:"subsystem"` - Class devIdent `json:"class"` - Subclass devIdent `json:"subclass"` - Interface devIdent `json:"programming_interface"` + Driver string `json:"driver"` + Address string `json:"address"` + ParentAddress string `json:"parent_address"` + Vendor devIdent `json:"vendor"` + Product devIdent `json:"product"` + Revision string `json:"revision"` + Subsystem devIdent `json:"subsystem"` + Class devIdent `json:"class"` + Subclass devIdent `json:"subclass"` + Interface devIdent `json:"programming_interface"` + IOMMUGroup string `json:"iommu_group"` } // NOTE(jaypipes) Device has a custom JSON marshaller because we don't want @@ -64,8 +68,9 @@ type devMarshallable struct { // human-readable name of the vendor, product, class, etc. func (d *Device) MarshalJSON() ([]byte, error) { dm := devMarshallable{ - Driver: d.Driver, - Address: d.Address, + Driver: d.Driver, + Address: d.Address, + ParentAddress: d.ParentAddress, Vendor: devIdent{ ID: d.Vendor.ID, Name: d.Vendor.Name, @@ -91,6 +96,7 @@ func (d *Device) MarshalJSON() ([]byte, error) { ID: d.ProgrammingInterface.ID, Name: d.ProgrammingInterface.Name, }, + IOMMUGroup: d.IOMMUGroup, } return json.Marshal(dm) } @@ -121,7 +127,6 @@ func (d *Device) String() string { type Info struct { db *pcidb.PCIDB arch topology.Architecture - ctx *context.Context // All PCI devices on the host system Devices []*Device } @@ -132,39 +137,30 @@ func (i *Info) String() string { // New returns a pointer to an Info struct that contains information about the // PCI devices on the host system -func New(opts ...*option.Option) (*Info, error) { - merged := option.Merge(opts...) - ctx := context.New(merged) +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) // by default we don't report NUMA information; // we will only if are sure we are running on NUMA architecture info := &Info{ - arch: topology.ArchitectureSMP, - ctx: ctx, + arch: topology.ArchitectureSMP, // default to SMP } - - // we do this trick because we need to make sure ctx.Setup() gets - // a chance to run before any subordinate package is created reusing - // our context. - loadDetectingTopology := func() error { - topo, err := topology.New(context.WithContext(ctx)) - if err == nil { - info.arch = topo.Architecture - } else { - ctx.Warn("error detecting system topology: %v", err) - } - if merged.PCIDB != nil { - info.db = merged.PCIDB - } - return info.load() - } - - var err error - if context.Exists(merged) { - err = loadDetectingTopology() + // Skip topology detection if requested to reduce memory consumption + if !config.TopologyEnabled(ctx) { + log.Warn( + ctx, "topology detection disabled, assuming SMP architecture", + ) } else { - err = ctx.Do(loadDetectingTopology) + topo, err := topology.New(ctx) + if err != nil { + return nil, fmt.Errorf( + "failed to initialize PCI info due to failure to initialize "+ + "Topology info: %w", + err, + ) + } + info.arch = topo.Architecture } - if err != nil { + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -189,11 +185,11 @@ type pciPrinter struct { // YAMLString returns a string with the PCI information formatted as YAML // under a top-level "pci:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, pciPrinter{i}) + return marshal.SafeYAML(pciPrinter{i}) } // JSONString returns a string with the PCI information formatted as JSON // under a top-level "pci:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, pciPrinter{i}, indent) + return marshal.SafeJSON(pciPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go b/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go index 879df436dd..f029dcf470 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/pci/pci_linux.go @@ -6,15 +6,15 @@ package pci import ( + "context" "os" "path/filepath" "strings" "github.com/jaypipes/pcidb" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/linuxpath" - "github.com/jaypipes/ghw/pkg/option" pciaddr "github.com/jaypipes/ghw/pkg/pci/address" "github.com/jaypipes/ghw/pkg/topology" "github.com/jaypipes/ghw/pkg/util" @@ -25,33 +25,23 @@ const ( modAliasExpectedLength = 54 ) -func (i *Info) load() error { - // when consuming snapshots - most notably, but not only, in tests, - // the context pkg forces the chroot value to the unpacked snapshot root. - // This is intentional, intentionally transparent and ghw is prepared to handle this case. - // However, `pcidb` is not. It doesn't know about ghw snaphots, nor it should. - // so we need to complicate things a bit. If the user explicitely supplied - // a chroot option, then we should honor it all across the stack, and passing down - // the chroot to pcidb is the right thing to do. If, however, the chroot was - // implcitely set by snapshot support, then this must be consumed by ghw only. - // In this case we should NOT pass it down to pcidb. - chroot := i.ctx.Chroot - if i.ctx.SnapshotPath != "" { - chroot = option.DefaultChroot +func (i *Info) load(ctx context.Context) error { + pcidbOpt := &pcidb.WithOption{} + if path := os.Getenv("PCIDB_PATH"); path != "" { + pcidbOpt = pcidb.WithPath(path) } if i.db == nil { - db, err := pcidb.New(pcidb.WithChroot(chroot)) + db, err := pcidb.New(pcidbOpt) if err != nil { return err } i.db = db } - i.Devices = i.getDevices() + i.Devices = i.getDevices(ctx) return nil } -func getDeviceModaliasPath(ctx *context.Context, pciAddr *pciaddr.Address) string { - paths := linuxpath.New(ctx) +func getDeviceModaliasPath(paths *linuxpath.Paths, pciAddr *pciaddr.Address) string { return filepath.Join( paths.SysBusPciDevices, pciAddr.String(), @@ -59,8 +49,7 @@ func getDeviceModaliasPath(ctx *context.Context, pciAddr *pciaddr.Address) strin ) } -func getDeviceRevision(ctx *context.Context, pciAddr *pciaddr.Address) string { - paths := linuxpath.New(ctx) +func getDeviceRevision(paths *linuxpath.Paths, pciAddr *pciaddr.Address) string { revisionPath := filepath.Join( paths.SysBusPciDevices, pciAddr.String(), @@ -77,7 +66,7 @@ func getDeviceRevision(ctx *context.Context, pciAddr *pciaddr.Address) string { return strings.TrimSpace(string(revision)) } -func getDeviceNUMANode(ctx *context.Context, pciAddr *pciaddr.Address) *topology.Node { +func getDeviceNUMANode(ctx context.Context, pciAddr *pciaddr.Address) *topology.Node { paths := linuxpath.New(ctx) numaNodePath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "numa_node") @@ -95,8 +84,7 @@ func getDeviceNUMANode(ctx *context.Context, pciAddr *pciaddr.Address) *topology } } -func getDeviceIommuGroup(ctx *context.Context, pciAddr *pciaddr.Address) string { - paths := linuxpath.New(ctx) +func getDeviceIommuGroup(paths *linuxpath.Paths, pciAddr *pciaddr.Address) string { iommuGroupPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "iommu_group") dest, err := os.Readlink(iommuGroupPath) @@ -106,8 +94,24 @@ func getDeviceIommuGroup(ctx *context.Context, pciAddr *pciaddr.Address) string return filepath.Base(dest) } -func getDeviceDriver(ctx *context.Context, pciAddr *pciaddr.Address) string { - paths := linuxpath.New(ctx) +func getDeviceParentAddress(paths *linuxpath.Paths, pciAddr *pciaddr.Address) string { + devPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String()) + + dest, err := os.Readlink(devPath) + if err != nil { + return "" + } + + parentAddr := filepath.Base(filepath.Dir(dest)) + + if pciaddr.FromString(parentAddr) == nil { + return "" + } + + return parentAddr +} + +func getDeviceDriver(paths *linuxpath.Paths, pciAddr *pciaddr.Address) string { driverPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "driver") if _, err := os.Stat(driverPath); err != nil { @@ -312,38 +316,7 @@ func findPCIProgrammingInterface( // GetDevice returns a pointer to a Device struct that describes the PCI // device at the requested address. If no such device could be found, returns nil. func (info *Info) GetDevice(address string) *Device { - // check cached data first - if dev := info.lookupDevice(address); dev != nil { - return dev - } - - pciAddr := pciaddr.FromString(address) - if pciAddr == nil { - info.ctx.Warn("error parsing the pci address %q", address) - return nil - } - - // no cached data, let's get the information from system. - fp := getDeviceModaliasPath(info.ctx, pciAddr) - if fp == "" { - info.ctx.Warn("error finding modalias info for device %q", address) - return nil - } - - modaliasInfo := parseModaliasFile(fp) - if modaliasInfo == nil { - info.ctx.Warn("error parsing modalias info for device %q", address) - return nil - } - - device := info.getDeviceFromModaliasInfo(address, modaliasInfo) - device.Revision = getDeviceRevision(info.ctx, pciAddr) - if info.arch == topology.ArchitectureNUMA { - device.Node = getDeviceNUMANode(info.ctx, pciAddr) - } - device.Driver = getDeviceDriver(info.ctx, pciAddr) - device.IOMMUGroup = getDeviceIommuGroup(info.ctx, pciAddr) - return device + return info.lookupDevice(address) } // ParseDevice returns a pointer to a Device given its describing data. @@ -400,8 +373,8 @@ func (info *Info) getDeviceFromModaliasInfo( // getDevices returns a list of pointers to Device structs present on the // host system -func (info *Info) getDevices() []*Device { - paths := linuxpath.New(info.ctx) +func (info *Info) getDevices(ctx context.Context) []*Device { + paths := linuxpath.New(ctx) devs := make([]*Device, 0) // We scan the /sys/bus/pci/devices directory which contains a collection // of symlinks. The names of the symlinks are all the known PCI addresses @@ -409,18 +382,39 @@ func (info *Info) getDevices() []*Device { // address and append to the returned array. links, err := os.ReadDir(paths.SysBusPciDevices) if err != nil { - info.ctx.Warn("failed to read /sys/bus/pci/devices") + log.Warn(ctx, "failed to read /sys/bus/pci/devices") return nil } - var dev *Device for _, link := range links { - addr := link.Name() - dev = info.GetDevice(addr) - if dev == nil { - info.ctx.Warn("failed to get device information for PCI address %s", addr) - } else { - devs = append(devs, dev) + address := link.Name() + pciAddr := pciaddr.FromString(address) + if pciAddr == nil { + log.Warn(ctx, "error parsing the pci address %q", address) + return nil + } + + // no cached data, let's get the information from system. + fp := getDeviceModaliasPath(paths, pciAddr) + if fp == "" { + log.Warn(ctx, "error finding modalias info for device %q", address) + return nil + } + + modaliasInfo := parseModaliasFile(fp) + if modaliasInfo == nil { + log.Warn(ctx, "error parsing modalias info for device %q", address) + return nil + } + + device := info.getDeviceFromModaliasInfo(address, modaliasInfo) + device.Revision = getDeviceRevision(paths, pciAddr) + if info.arch == topology.ArchitectureNUMA { + device.Node = getDeviceNUMANode(ctx, pciAddr) } + device.Driver = getDeviceDriver(paths, pciAddr) + device.ParentAddress = getDeviceParentAddress(paths, pciAddr) + device.IOMMUGroup = getDeviceIommuGroup(paths, pciAddr) + devs = append(devs, device) } return devs } diff --git a/vendor/github.com/jaypipes/ghw/pkg/pci/pci_stub.go b/vendor/github.com/jaypipes/ghw/pkg/pci/pci_stub.go index 9ebb396d2d..69e33c3c7d 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/pci/pci_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/pci/pci_stub.go @@ -9,12 +9,13 @@ package pci import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("pciFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/product/product.go b/vendor/github.com/jaypipes/ghw/pkg/product/product.go index 83d6541d30..f1a1d1897b 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/product/product.go +++ b/vendor/github.com/jaypipes/ghw/pkg/product/product.go @@ -7,15 +7,13 @@ package product import ( - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/marshal" - "github.com/jaypipes/ghw/pkg/option" "github.com/jaypipes/ghw/pkg/util" ) // Info defines product information type Info struct { - ctx *context.Context Family string `json:"family"` Name string `json:"name"` Vendor string `json:"vendor"` @@ -68,10 +66,10 @@ func (i *Info) String() string { // New returns a pointer to a Info struct containing information // about the host's product -func New(opts ...*option.Option) (*Info, error) { - ctx := context.New(opts...) - info := &Info{ctx: ctx} - if err := ctx.Do(info.load); err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } return info, nil @@ -86,11 +84,11 @@ type productPrinter struct { // YAMLString returns a string with the product information formatted as YAML // under a top-level "dmi:" key func (info *Info) YAMLString() string { - return marshal.SafeYAML(info.ctx, productPrinter{info}) + return marshal.SafeYAML(productPrinter{info}) } // JSONString returns a string with the product information formatted as JSON // under a top-level "product:" key func (info *Info) JSONString(indent bool) string { - return marshal.SafeJSON(info.ctx, productPrinter{info}, indent) + return marshal.SafeJSON(productPrinter{info}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/product/product_linux.go b/vendor/github.com/jaypipes/ghw/pkg/product/product_linux.go index 36b6b4471b..91adb5e9eb 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/product/product_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/product/product_linux.go @@ -6,18 +6,19 @@ package product import ( + "context" + "github.com/jaypipes/ghw/pkg/linuxdmi" ) -func (i *Info) load() error { - - i.Family = linuxdmi.Item(i.ctx, "product_family") - i.Name = linuxdmi.Item(i.ctx, "product_name") - i.Vendor = linuxdmi.Item(i.ctx, "sys_vendor") - i.SerialNumber = linuxdmi.Item(i.ctx, "product_serial") - i.UUID = linuxdmi.Item(i.ctx, "product_uuid") - i.SKU = linuxdmi.Item(i.ctx, "product_sku") - i.Version = linuxdmi.Item(i.ctx, "product_version") +func (i *Info) load(ctx context.Context) error { + i.Family = linuxdmi.Item(ctx, "product_family") + i.Name = linuxdmi.Item(ctx, "product_name") + i.Vendor = linuxdmi.Item(ctx, "sys_vendor") + i.SerialNumber = linuxdmi.Item(ctx, "product_serial") + i.UUID = linuxdmi.Item(ctx, "product_uuid") + i.SKU = linuxdmi.Item(ctx, "product_sku") + i.Version = linuxdmi.Item(ctx, "product_version") return nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/product/product_stub.go b/vendor/github.com/jaypipes/ghw/pkg/product/product_stub.go index 8fc9724fbf..221335940a 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/product/product_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/product/product_stub.go @@ -9,11 +9,12 @@ package product import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("productFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/product/product_windows.go b/vendor/github.com/jaypipes/ghw/pkg/product/product_windows.go index 36db6a8d4e..2d2a83753d 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/product/product_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/product/product_windows.go @@ -6,6 +6,8 @@ package product import ( + "context" + "github.com/yusufpapurcu/wmi" "github.com/jaypipes/ghw/pkg/util" @@ -24,7 +26,7 @@ type win32Product struct { UUID *string } -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { // Getting data from WMI var win32ProductDescriptions []win32Product // Assuming the first product is the host... diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree.go index 020e7e673f..8ffc0b97a7 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree.go @@ -7,10 +7,13 @@ package snapshot import ( + "context" "errors" "os" "path/filepath" "strings" + + "github.com/jaypipes/ghw/internal/log" ) // Attempting to tar up pseudofiles like /proc/cpuinfo is an exercise in @@ -22,13 +25,19 @@ import ( // CloneTreeInto copies all the pseudofiles that ghw will consume into the root // `scratchDir`, preserving the hieratchy. -func CloneTreeInto(scratchDir string) error { - err := setupScratchDir(scratchDir) +func CloneTreeInto( + ctx context.Context, + scratchDir string, +) error { + err := setupScratchDir(ctx, scratchDir) + if err != nil { + return err + } + fileSpecs, err := ExpectedCloneContent(ctx) if err != nil { return err } - fileSpecs := ExpectedCloneContent() - return CopyFilesInto(fileSpecs, scratchDir, nil) + return CopyFilesInto(ctx, fileSpecs, scratchDir, nil) } // ExpectedCloneContent return a slice of glob patterns which represent the pseudofiles @@ -37,12 +46,17 @@ func CloneTreeInto(scratchDir string) error { // content matches the expectations. // Beware: the content is host-specific, because the content pertaining some subsystems, // most notably PCI, is host-specific and unpredictable. -func ExpectedCloneContent() []string { +func ExpectedCloneContent(ctx context.Context) ([]string, error) { fileSpecs := ExpectedCloneStaticContent() fileSpecs = append(fileSpecs, ExpectedCloneNetContent()...) - fileSpecs = append(fileSpecs, ExpectedClonePCIContent()...) + fileSpecs = append(fileSpecs, ExpectedCloneUSBContent()...) + pciContent, err := ExpectedClonePCIContent(ctx) + if err != nil { + return nil, err + } + fileSpecs = append(fileSpecs, pciContent...) fileSpecs = append(fileSpecs, ExpectedCloneGPUContent()...) - return fileSpecs + return fileSpecs, nil } // ValidateClonedTree checks the content of a cloned tree, whose root is `clonedDir`, @@ -95,7 +109,12 @@ type CopyFileOptions struct { // - /some/deeply/ // ... // all glob patterns supported in `filepath.Glob` are supported. -func CopyFilesInto(fileSpecs []string, destDir string, opts *CopyFileOptions) error { +func CopyFilesInto( + ctx context.Context, + fileSpecs []string, + destDir string, + opts *CopyFileOptions, +) error { if opts == nil { opts = &CopyFileOptions{ IsSymlinkFn: isSymlink, @@ -103,21 +122,26 @@ func CopyFilesInto(fileSpecs []string, destDir string, opts *CopyFileOptions) er } } for _, fileSpec := range fileSpecs { - trace("copying spec: %q\n", fileSpec) + log.Debug(ctx, "copying spec: %q", fileSpec) matches, err := filepath.Glob(fileSpec) if err != nil { return err } - if err := copyFileTreeInto(matches, destDir, opts); err != nil { + if err := copyFileTreeInto(ctx, matches, destDir, opts); err != nil { return err } } return nil } -func copyFileTreeInto(paths []string, destDir string, opts *CopyFileOptions) error { +func copyFileTreeInto( + ctx context.Context, + paths []string, + destDir string, + opts *CopyFileOptions, +) error { for _, path := range paths { - trace(" copying path: %q\n", path) + log.Debug(ctx, " copying path: %q", path) baseDir := filepath.Dir(path) if err := os.MkdirAll(filepath.Join(destDir, baseDir), os.ModePerm); err != nil { return err @@ -137,18 +161,18 @@ func copyFileTreeInto(paths []string, destDir string, opts *CopyFileOptions) err return err } } else { - trace("expanded glob path %q is a directory - skipped\n", path) + log.Debug(ctx, "expanded glob path %q is a directory - skipped", path) } continue } if opts.IsSymlinkFn(path, fi) { - trace(" copying link: %q -> %q\n", path, destPath) - if err := copyLink(path, destPath); err != nil { + log.Debug(ctx, " copying link: %q -> %q", path, destPath) + if err := copyLink(ctx, path, destPath); err != nil { return err } } else { - trace(" copying file: %q -> %q\n", path, destPath) - if err := copyPseudoFile(path, destPath); err != nil && !errors.Is(err, os.ErrPermission) { + log.Debug(ctx, " copying file: %q -> %q", path, destPath) + if err := copyPseudoFile(ctx, path, destPath); err != nil && !errors.Is(err, os.ErrPermission) { return err } } @@ -164,12 +188,16 @@ func isDriversDir(path string, fi os.FileInfo) bool { return strings.Contains(path, "drivers") } -func copyLink(path, targetPath string) error { +func copyLink( + ctx context.Context, + path string, + targetPath string, +) error { target, err := os.Readlink(path) if err != nil { return err } - trace(" symlink %q -> %q\n", target, targetPath) + log.Debug(ctx, " symlink %q -> %q", target, targetPath) if err := os.Symlink(target, targetPath); err != nil { if errors.Is(err, os.ErrExist) { return nil @@ -180,12 +208,16 @@ func copyLink(path, targetPath string) error { return nil } -func copyPseudoFile(path, targetPath string) error { +func copyPseudoFile( + ctx context.Context, + path string, + targetPath string, +) error { buf, err := os.ReadFile(path) if err != nil { return err } - trace("creating %s\n", targetPath) + log.Debug(ctx, "creating %s", targetPath) f, err := os.Create(targetPath) if err != nil { return err diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_block_linux.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_block_linux.go index f692d41379..86048d55ce 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_block_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_block_linux.go @@ -7,13 +7,19 @@ package snapshot import ( + "context" "errors" "os" "path/filepath" "strings" + + "github.com/jaypipes/ghw/internal/log" ) -func createBlockDevices(buildDir string) error { +func createBlockDevices( + ctx context.Context, + buildDir string, +) error { // Grab all the block device pseudo-directories from /sys/block symlinks // (excluding loopback devices) and inject them into our build filesystem // with all but the circular symlink'd subsystem directories @@ -27,14 +33,14 @@ func createBlockDevices(buildDir string) error { continue } devPath := filepath.Join("/sys/block", dname) - trace("processing block device %q\n", devPath) + log.Debug(ctx, "processing block device %q", devPath) // from the sysfs layout, we know this is always a symlink linkContentPath, err := os.Readlink(devPath) if err != nil { return err } - trace("link target for block device %q is %q\n", devPath, linkContentPath) + log.Debug(ctx, "link target for block device %q is %q", devPath, linkContentPath) // Create a symlink in our build filesystem that is a directory // pointing to the actual device bus path where the block device's @@ -45,12 +51,12 @@ func createBlockDevices(buildDir string) error { "sys/block", strings.TrimPrefix(linkContentPath, string(os.PathSeparator)), ) - trace("creating device directory %s\n", linkTargetPath) + log.Debug(ctx, "creating device directory %s", linkTargetPath) if err = os.MkdirAll(linkTargetPath, os.ModePerm); err != nil { return err } - trace("linking device directory %s to %s\n", linkPath, linkContentPath) + log.Debug(ctx, "linking device directory %s to %s", linkPath, linkContentPath) // Make sure the link target is a relative path! // if we use absolute path, the link target will be an absolute path starting // with buildDir, hence the snapshot will contain broken link. @@ -65,15 +71,19 @@ func createBlockDevices(buildDir string) error { "/sys/block", strings.TrimPrefix(linkContentPath, string(os.PathSeparator)), ) - trace("creating device directory %q from %q\n", linkTargetPath, srcDeviceDir) - if err = createBlockDeviceDir(linkTargetPath, srcDeviceDir); err != nil { + log.Debug(ctx, "creating device directory %q from %q", linkTargetPath, srcDeviceDir) + if err = createBlockDeviceDir(ctx, linkTargetPath, srcDeviceDir); err != nil { return err } } return nil } -func createBlockDeviceDir(buildDeviceDir string, srcDeviceDir string) error { +func createBlockDeviceDir( + ctx context.Context, + buildDeviceDir string, + srcDeviceDir string, +) error { // Populate the supplied directory (in our build filesystem) with all the // appropriate information pseudofile contents for the block device. devName := filepath.Base(srcDeviceDir) @@ -104,12 +114,12 @@ func createBlockDeviceDir(buildDeviceDir string, srcDeviceDir string) error { srcPartitionDir := filepath.Join( srcDeviceDir, fname, ) - trace("creating partition directory %s\n", buildPartitionDir) + log.Debug(ctx, "creating partition directory %s", buildPartitionDir) err = os.MkdirAll(buildPartitionDir, os.ModePerm) if err != nil { return err } - err = createPartitionDir(buildPartitionDir, srcPartitionDir) + err = createPartitionDir(ctx, buildPartitionDir, srcPartitionDir) if err != nil { return err } @@ -122,13 +132,13 @@ func createBlockDeviceDir(buildDeviceDir string, srcDeviceDir string) error { if err != nil { if errors.Is(err, os.ErrPermission) { // example: /sys/devices/virtual/block/zram0/compact is 0400 - trace("permission denied reading %q - skipped\n", fp) + log.Debug(ctx, "permission denied reading %q - skipped", fp) continue } return err } targetPath := filepath.Join(buildDeviceDir, fname) - trace("creating %s\n", targetPath) + log.Debug(ctx, "creating %s", targetPath) f, err := os.Create(targetPath) if err != nil { return err @@ -160,7 +170,7 @@ func createBlockDeviceDir(buildDeviceDir string, srcDeviceDir string) error { return err } targetPath := filepath.Join(buildQueueDir, "rotational") - trace("creating %s\n", targetPath) + log.Debug(ctx, "creating %s", targetPath) f, err := os.Create(targetPath) if err != nil { return err @@ -173,7 +183,11 @@ func createBlockDeviceDir(buildDeviceDir string, srcDeviceDir string) error { return nil } -func createPartitionDir(buildPartitionDir string, srcPartitionDir string) error { +func createPartitionDir( + ctx context.Context, + buildPartitionDir string, + srcPartitionDir string, +) error { // Populate the supplied directory (in our build filesystem) with all the // appropriate information pseudofile contents for the partition. partFiles, err := os.ReadDir(srcPartitionDir) @@ -205,7 +219,7 @@ func createPartitionDir(buildPartitionDir string, srcPartitionDir string) error return err } targetPath := filepath.Join(buildPartitionDir, fname) - trace("creating %s\n", targetPath) + log.Debug(ctx, "creating %s", targetPath) f, err := os.Create(targetPath) if err != nil { return err diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_linux.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_linux.go index 8191e80c44..7b62017ad6 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_linux.go @@ -7,11 +7,15 @@ package snapshot import ( + "context" "os" "path/filepath" ) -func setupScratchDir(scratchDir string) error { +func setupScratchDir( + ctx context.Context, + scratchDir string, +) error { var createPaths = []string{ "sys/block", } @@ -22,7 +26,7 @@ func setupScratchDir(scratchDir string) error { } } - return createBlockDevices(scratchDir) + return createBlockDevices(ctx, scratchDir) } // ExpectedCloneStaticContent return a slice of glob patterns which represent the pseudofiles diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_pci_linux.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_pci_linux.go index 0c2c68b395..072343c34f 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_pci_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_pci_linux.go @@ -7,10 +7,11 @@ package snapshot import ( - "fmt" + "context" "os" "path/filepath" + "github.com/jaypipes/ghw/internal/log" pciaddr "github.com/jaypipes/ghw/pkg/pci/address" ) @@ -24,7 +25,9 @@ const ( // ExpectedClonePCIContent return a slice of glob patterns which represent the pseudofiles // ghw cares about, pertaining to PCI devices only. // Beware: the content is host-specific, because the PCI topology is host-dependent and unpredictable. -func ExpectedClonePCIContent() []string { +func ExpectedClonePCIContent( + ctx context.Context, +) ([]string, error) { fileSpecs := []string{ "/sys/bus/pci/drivers/*", } @@ -37,11 +40,14 @@ func ExpectedClonePCIContent() []string { } pciRoot := pciRoots[0] pciRoots = pciRoots[1:] - specs, roots := scanPCIDeviceRoot(pciRoot) + specs, roots, err := scanPCIDeviceRoot(ctx, pciRoot) + if err != nil { + return nil, err + } pciRoots = append(pciRoots, roots...) fileSpecs = append(fileSpecs, specs...) } - return fileSpecs + return fileSpecs, nil } // scanPCIDeviceRoot reports a slice of glob patterns which represent the pseudofiles @@ -50,13 +56,15 @@ func ExpectedClonePCIContent() []string { // level; more PCI bridges are (usually) attached to this level, creating deep nested trees. // hence we need to scan all possible roots, to make sure not to miss important devices. // -// note about notifying errors. This function and its helper functions do use trace() everywhere +// note about notifying errors. This function and its helper functions do use log.Debug(ctx, ) everywhere // to report recoverable errors, even though it would have been appropriate to use Warn(). // This is unfortunate, and again a byproduct of the fact we cannot use context.Context to avoid // circular dependencies. -// TODO(fromani): switch to Warn() as soon as we figure out how to break this circular dep. -func scanPCIDeviceRoot(root string) (fileSpecs []string, pciRoots []string) { - trace("scanning PCI device root %q\n", root) +func scanPCIDeviceRoot( + ctx context.Context, + root string, +) (fileSpecs []string, pciRoots []string, err error) { + log.Debug(ctx, "scanning PCI device root %q", root) perDevEntries := []string{ "class", @@ -72,7 +80,7 @@ func scanPCIDeviceRoot(root string) (fileSpecs []string, pciRoots []string) { } entries, err := os.ReadDir(root) if err != nil { - return []string{}, []string{} + return nil, nil, err } for _, entry := range entries { entryName := entry.Name() @@ -84,31 +92,39 @@ func scanPCIDeviceRoot(root string) (fileSpecs []string, pciRoots []string) { } entryPath := filepath.Join(root, entryName) - pciEntry, err := findPCIEntryFromPath(root, entryName) + pciEntry, err := findPCIEntryFromPath(ctx, root, entryName) if err != nil { - trace("error scanning %q: %v", entryName, err) + log.Debug(ctx, "error scanning %q: %v. skipping", entryName, err) continue } - trace("PCI entry is %q\n", pciEntry) + log.Debug(ctx, "PCI entry is %q", pciEntry) fileSpecs = append(fileSpecs, entryPath) for _, perNetEntry := range perDevEntries { fileSpecs = append(fileSpecs, filepath.Join(pciEntry, perNetEntry)) } - if isPCIBridge(entryPath) { - trace("adding new PCI root %q\n", entryName) + bridge, err := isPCIBridge(entryPath) + if err != nil { + return nil, nil, err + } + if bridge { + log.Debug(ctx, "adding new PCI root %q", entryName) pciRoots = append(pciRoots, pciEntry) } } - return fileSpecs, pciRoots + return fileSpecs, pciRoots, nil } -func findPCIEntryFromPath(root, entryName string) (string, error) { +func findPCIEntryFromPath( + ctx context.Context, + root string, + entryName string, +) (string, error) { entryPath := filepath.Join(root, entryName) fi, err := os.Lstat(entryPath) if err != nil { - return "", fmt.Errorf("stat(%s) failed: %v\n", entryPath, err) + return "", err } if fi.Mode()&os.ModeSymlink == 0 { // regular file, nothing to resolve @@ -116,19 +132,17 @@ func findPCIEntryFromPath(root, entryName string) (string, error) { } // resolve symlink target, err := os.Readlink(entryPath) - trace("entry %q is symlink resolved to %q\n", entryPath, target) if err != nil { - return "", fmt.Errorf("readlink(%s) failed: %v - skipped\n", entryPath, err) + return "", err } + log.Debug(ctx, "entry %q is symlink resolved to %q", entryPath, target) return filepath.Clean(filepath.Join(root, target)), nil } -func isPCIBridge(entryPath string) bool { +func isPCIBridge(entryPath string) (bool, error) { subNodes, err := os.ReadDir(entryPath) if err != nil { - // this is so unlikely we don't even return error. But we trace just in case. - trace("error scanning device entry path %q: %v", entryPath, err) - return false + return false, err } for _, subNode := range subNodes { if !subNode.IsDir() { @@ -144,8 +158,8 @@ func isPCIBridge(entryPath string) bool { // This approach duplicates the logic within the `pci` subkpg // - or forces us into awkward dep cycles, and has poorer forward // compatibility. - return true + return true, nil } } - return false + return false, nil } diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_stub.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_stub.go index af85a55b5d..a3024c6963 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_stub.go @@ -9,7 +9,9 @@ package snapshot -func setupScratchDir(scratchDir string) error { +import "context" + +func setupScratchDir(ctx context.Context, scratchDir string) error { return nil } @@ -25,6 +27,10 @@ func ExpectedCloneNetContent() []string { return []string{} } -func ExpectedClonePCIContent() []string { +func ExpectedClonePCIContent(ctx context.Context) ([]string, error) { + return nil, nil +} + +func ExpectedCloneUSBContent() []string { return []string{} } diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_usb_linux.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_usb_linux.go new file mode 100644 index 0000000000..323c88bc7d --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/clonetree_usb_linux.go @@ -0,0 +1,45 @@ +// +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package snapshot + +import ( + "os" + "path/filepath" +) + +// ExpectedCloneUSBContent returns a slice of strings pertaning to the USB interfaces +func ExpectedCloneUSBContent() []string { + const sysBusUSB = "/sys/bus/usb/devices/" + + paths := []string{sysBusUSB} + usbDevicesDirs, err := os.ReadDir(sysBusUSB) + if err != nil { + return []string{} + } + + for _, dir := range usbDevicesDirs { + susBusUSBLink := filepath.Join(sysBusUSB, dir.Name()) + paths = append(paths, susBusUSBLink) + + fullDir, err := os.Readlink(susBusUSBLink) + if err != nil { + continue + } + if !filepath.IsAbs(fullDir) { + fullDir, err = filepath.Abs(filepath.Join(sysBusUSB, fullDir)) + if err != nil { + continue + } + } + for _, fileName := range []string{"uevent", "interface", "product"} { + paths = append(paths, filepath.Join(fullDir, fileName)) + } + + } + + return paths +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/pack.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/pack.go index 73820c94bd..652ba5612e 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/pack.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/pack.go @@ -9,23 +9,30 @@ package snapshot import ( "archive/tar" "compress/gzip" + "context" "errors" "fmt" "io" "os" "path/filepath" + + "github.com/jaypipes/ghw/internal/log" ) // PackFrom creates the snapshot named `snapshotName` from the // directory tree whose root is `sourceRoot`. -func PackFrom(snapshotName, sourceRoot string) error { +func PackFrom( + ctx context.Context, + snapshotName string, + sourceRoot string, +) error { f, err := OpenDestination(snapshotName) if err != nil { return err } defer f.Close() - return PackWithWriter(f, sourceRoot) + return PackWithWriter(ctx, f, sourceRoot) } // OpenDestination opens the `snapshotName` file for writing, bailing out @@ -55,17 +62,25 @@ func OpenDestination(snapshotName string) (*os.File, error) { // PakcWithWriter creates a snapshot sending all the binary data to the // given `fw` writer. The snapshot is made from the directory tree whose // root is `sourceRoot`. -func PackWithWriter(fw io.Writer, sourceRoot string) error { +func PackWithWriter( + ctx context.Context, + fw io.Writer, + sourceRoot string, +) error { gzw := gzip.NewWriter(fw) defer gzw.Close() tw := tar.NewWriter(gzw) defer tw.Close() - return createSnapshot(tw, sourceRoot) + return createSnapshot(ctx, tw, sourceRoot) } -func createSnapshot(tw *tar.Writer, buildDir string) error { +func createSnapshot( + ctx context.Context, + tw *tar.Writer, + buildDir string, +) error { return filepath.Walk(buildDir, func(path string, fi os.FileInfo, _ error) error { if path == buildDir { return nil @@ -74,7 +89,7 @@ func createSnapshot(tw *tar.Writer, buildDir string) error { var err error if fi.Mode()&os.ModeSymlink != 0 { - trace("processing symlink %s\n", path) + log.Debug(ctx, "processing symlink %s\n", path) link, err = os.Readlink(path) if err != nil { return err diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/trace.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/trace.go deleted file mode 100644 index 78c76121ae..0000000000 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/trace.go +++ /dev/null @@ -1,17 +0,0 @@ -// -// Use and distribution licensed under the Apache license version 2. -// -// See the COPYING file in the root project directory for full text. -// - -package snapshot - -var trace func(msg string, args ...interface{}) - -func init() { - trace = func(msg string, args ...interface{}) {} -} - -func SetTraceFunction(fn func(msg string, args ...interface{})) { - trace = fn -} diff --git a/vendor/github.com/jaypipes/ghw/pkg/snapshot/unpack.go b/vendor/github.com/jaypipes/ghw/pkg/snapshot/unpack.go index f05f8f79eb..9eb1d550e4 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/snapshot/unpack.go +++ b/vendor/github.com/jaypipes/ghw/pkg/snapshot/unpack.go @@ -12,52 +12,33 @@ import ( "io" "os" "path/filepath" - - "github.com/jaypipes/ghw/pkg/option" ) const ( TargetRoot = "ghw-snapshot-*" ) -const ( - // If set, `ghw` will not unpack the snapshot in the user-supplied directory - // unless the aforementioned directory is empty. - OwnTargetDirectory = 1 << iota -) - -// Clanup removes the unpacket snapshot from the target root. -// Please not that the environs variable `GHW_SNAPSHOT_PRESERVE`, if set, -// will make this function silently skip. -func Cleanup(targetRoot string) error { - if option.EnvOrDefaultSnapshotPreserve() { - return nil - } - return os.RemoveAll(targetRoot) -} - -// Unpack expands the given snapshot in a temporary directory managed by `ghw`. Returns the path of that directory. +// Unpack expands the given snapshot in a temporary directory managed by `ghw`. +// Returns the path of that directory. Callers are responsible for cleaning up +// the temporary directory. func Unpack(snapshotName string) (string, error) { targetRoot, err := os.MkdirTemp("", TargetRoot) if err != nil { return "", err } - _, err = UnpackInto(snapshotName, targetRoot, 0) + err = UnpackInto(snapshotName, targetRoot) return targetRoot, err } // UnpackInto expands the given snapshot in a client-supplied directory. // Returns true if the snapshot was actually unpacked, false otherwise -func UnpackInto(snapshotName, targetRoot string, flags uint) (bool, error) { - if (flags&OwnTargetDirectory) == OwnTargetDirectory && !isEmptyDir(targetRoot) { - return false, nil - } +func UnpackInto(snapshotName, targetRoot string) error { snap, err := os.Open(snapshotName) if err != nil { - return false, err + return err } defer snap.Close() - return true, Untar(targetRoot, snap) + return Untar(targetRoot, snap) } // Untar extracts data from the given reader (providing data in tar.gz format) and unpacks it in the given directory. diff --git a/vendor/github.com/jaypipes/ghw/pkg/topology/topology.go b/vendor/github.com/jaypipes/ghw/pkg/topology/topology.go index 0210ab4863..8faf8a6027 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/topology/topology.go +++ b/vendor/github.com/jaypipes/ghw/pkg/topology/topology.go @@ -13,11 +13,10 @@ import ( "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/config" "github.com/jaypipes/ghw/pkg/cpu" "github.com/jaypipes/ghw/pkg/marshal" "github.com/jaypipes/ghw/pkg/memory" - "github.com/jaypipes/ghw/pkg/option" ) // Architecture describes the overall hardware architecture. It can be either @@ -107,24 +106,16 @@ func (n *Node) String() string { // Info describes the system topology for the host hardware type Info struct { - ctx *context.Context Architecture Architecture `json:"architecture"` Nodes []*Node `json:"nodes"` } // New returns a pointer to an Info struct that contains information about the // NUMA topology on the host system -func New(opts ...*option.Option) (*Info, error) { - merged := option.Merge(opts...) - ctx := context.New(merged) - info := &Info{ctx: ctx} - var err error - if context.Exists(merged) { - err = info.load() - } else { - err = ctx.Do(info.load) - } - if err != nil { +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { return nil, err } for _, node := range info.Nodes { @@ -155,11 +146,11 @@ type topologyPrinter struct { // YAMLString returns a string with the topology information formatted as YAML // under a top-level "topology:" key func (i *Info) YAMLString() string { - return marshal.SafeYAML(i.ctx, topologyPrinter{i}) + return marshal.SafeYAML(topologyPrinter{i}) } // JSONString returns a string with the topology information formatted as JSON // under a top-level "topology:" key func (i *Info) JSONString(indent bool) string { - return marshal.SafeJSON(i.ctx, topologyPrinter{i}, indent) + return marshal.SafeJSON(topologyPrinter{i}, indent) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/topology/topology_linux.go b/vendor/github.com/jaypipes/ghw/pkg/topology/topology_linux.go index dbd0811e6c..8262fd8e5d 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/topology/topology_linux.go +++ b/vendor/github.com/jaypipes/ghw/pkg/topology/topology_linux.go @@ -6,20 +6,21 @@ package topology import ( + "context" "fmt" "os" "path/filepath" "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" "github.com/jaypipes/ghw/pkg/cpu" "github.com/jaypipes/ghw/pkg/linuxpath" "github.com/jaypipes/ghw/pkg/memory" ) -func (i *Info) load() error { - i.Nodes = topologyNodes(i.ctx) +func (i *Info) load(ctx context.Context) error { + i.Nodes = topologyNodes(ctx) if len(i.Nodes) == 1 { i.Architecture = ArchitectureSMP } else { @@ -28,13 +29,13 @@ func (i *Info) load() error { return nil } -func topologyNodes(ctx *context.Context) []*Node { +func topologyNodes(ctx context.Context) []*Node { paths := linuxpath.New(ctx) nodes := make([]*Node, 0) files, err := os.ReadDir(paths.SysDevicesSystemNode) if err != nil { - ctx.Warn("failed to determine nodes: %s\n", err) + log.Warn(ctx, "failed to determine nodes: %s\n", err) return nodes } for _, file := range files { @@ -45,33 +46,33 @@ func topologyNodes(ctx *context.Context) []*Node { node := &Node{} nodeID, err := strconv.Atoi(filename[4:]) if err != nil { - ctx.Warn("failed to determine node ID: %s\n", err) + log.Warn(ctx, "failed to determine node ID: %s\n", err) return nodes } node.ID = nodeID cores, err := cpu.CoresForNode(ctx, nodeID) if err != nil { - ctx.Warn("failed to determine cores for node: %s\n", err) + log.Warn(ctx, "failed to determine cores for node: %s\n", err) return nodes } node.Cores = cores caches, err := memory.CachesForNode(ctx, nodeID) if err != nil { - ctx.Warn("failed to determine caches for node: %s\n", err) + log.Warn(ctx, "failed to determine caches for node: %s\n", err) return nodes } node.Caches = caches - distances, err := distancesForNode(ctx, nodeID) + distances, err := distancesForNode(paths, nodeID) if err != nil { - ctx.Warn("failed to determine node distances for node: %s\n", err) + log.Warn(ctx, "failed to determine node distances for node: %s\n", err) return nodes } node.Distances = distances - area, err := memory.AreaForNode(ctx, nodeID) + area, err := memory.AreaForNode(paths, nodeID) if err != nil { - ctx.Warn("failed to determine memory area for node: %s\n", err) + log.Warn(ctx, "failed to determine memory area for node: %s\n", err) return nodes } node.Memory = area @@ -81,8 +82,7 @@ func topologyNodes(ctx *context.Context) []*Node { return nodes } -func distancesForNode(ctx *context.Context, nodeID int) ([]int, error) { - paths := linuxpath.New(ctx) +func distancesForNode(paths *linuxpath.Paths, nodeID int) ([]int, error) { path := filepath.Join( paths.SysDevicesSystemNode, fmt.Sprintf("node%d", nodeID), diff --git a/vendor/github.com/jaypipes/ghw/pkg/topology/topology_stub.go b/vendor/github.com/jaypipes/ghw/pkg/topology/topology_stub.go index b5ee4354e0..152a209075 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/topology/topology_stub.go +++ b/vendor/github.com/jaypipes/ghw/pkg/topology/topology_stub.go @@ -9,11 +9,12 @@ package topology import ( + "context" "runtime" "github.com/pkg/errors" ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { return errors.New("topologyFillInfo not implemented on " + runtime.GOOS) } diff --git a/vendor/github.com/jaypipes/ghw/pkg/topology/topology_windows.go b/vendor/github.com/jaypipes/ghw/pkg/topology/topology_windows.go index 2991aaa938..a2475c84d3 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/topology/topology_windows.go +++ b/vendor/github.com/jaypipes/ghw/pkg/topology/topology_windows.go @@ -6,6 +6,7 @@ package topology import ( + "context" "encoding/binary" "fmt" "syscall" @@ -24,7 +25,7 @@ const ( relationGroup = 4 ) -func (i *Info) load() error { +func (i *Info) load(ctx context.Context) error { nodes, err := topologyNodes() if err != nil { return err diff --git a/vendor/github.com/jaypipes/ghw/pkg/usb/usb.go b/vendor/github.com/jaypipes/ghw/pkg/usb/usb.go new file mode 100644 index 0000000000..d446028fb6 --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/pkg/usb/usb.go @@ -0,0 +1,112 @@ +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package usb + +import ( + "fmt" + "strings" + + "github.com/jaypipes/ghw/internal/config" + "github.com/jaypipes/ghw/pkg/marshal" +) + +type Device struct { + Driver string `json:"driver"` + Type string `json:"type"` + VendorID string `json:"vendor_id"` + ProductID string `json:"product_id"` + Product string `json:"product"` + RevisionID string `json:"revision_id"` + Interface string `json:"interface"` +} + +func (d Device) String() string { + kvs := []struct { + name string + value string + }{ + {"driver", d.Driver}, + {"type", d.Type}, + {"vendorID", d.VendorID}, + {"productID", d.ProductID}, + {"product", d.Product}, + {"revisionID", d.RevisionID}, + {"interface", d.Interface}, + } + + var str strings.Builder + + i := 0 + for _, s := range kvs { + k := s.name + v := s.value + + if v == "" { + continue + } + needsQuotationMarks := strings.ContainsAny(v, " \t") + + if i > 0 { + str.WriteString(" ") + } + i++ + str.WriteString(k) + str.WriteString("=") + if needsQuotationMarks { + str.WriteString("\"") + } + str.WriteString(v) + if needsQuotationMarks { + str.WriteString("\"") + } + + } + + return str.String() +} + +// Info describes all network interface controllers (NICs) in the host system. +type Info struct { + Devices []*Device `json:"devices"` +} + +// String returns a short string with information about the networking on the +// host system. +func (i *Info) String() string { + return fmt.Sprintf( + "USB (%d USBs)", + len(i.Devices), + ) +} + +// New returns a pointer to an Info struct that contains information about the +// network interface controllers (NICs) on the host system +func New(args ...any) (*Info, error) { + ctx := config.ContextFromArgs(args...) + info := &Info{} + if err := info.load(ctx); err != nil { + return nil, err + } + return info, nil +} + +// simple private struct used to encapsulate usb information in a +// top-level "usb" YAML/JSON map/object key +type usbPrinter struct { + Info *Info `json:"usb"` +} + +// YAMLString returns a string with the net information formatted as YAML +// under a top-level "net:" key +func (i *Info) YAMLString() string { + return marshal.SafeYAML(usbPrinter{i}) +} + +// JSONString returns a string with the net information formatted as JSON +// under a top-level "net:" key +func (i *Info) JSONString(indent bool) string { + return marshal.SafeJSON(usbPrinter{i}, indent) +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/usb/usb_linux.go b/vendor/github.com/jaypipes/ghw/pkg/usb/usb_linux.go new file mode 100644 index 0000000000..87fa7ba530 --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/pkg/usb/usb_linux.go @@ -0,0 +1,116 @@ +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package usb + +import ( + "bufio" + "bytes" + "context" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/jaypipes/ghw/pkg/linuxpath" +) + +func (i *Info) load(ctx context.Context) error { + var errs []error + + i.Devices, errs = usbs(ctx) + + if len(errs) == 0 { + return nil + } + return fmt.Errorf("error(s) happened during reading usb info: %+v", errs) +} + +func fillUSBFromUevent(dir string, dev *Device) (err error) { + ueventFp, err := os.Open(filepath.Join(dir, "uevent")) + if err != nil { + return + } + defer func() { + err = ueventFp.Close() + }() + + sc := bufio.NewScanner(ueventFp) + for sc.Scan() { + line := sc.Text() + + splits := strings.SplitN(line, "=", 2) + if len(splits) != 2 { + continue + } + + key := strings.ToUpper(splits[0]) + val := splits[1] + + switch key { + case "DRIVER": + dev.Driver = val + case "TYPE": + dev.Type = val + case "PRODUCT": + splits := strings.SplitN(val, "/", 3) + if len(splits) != 3 { + continue + } + dev.VendorID = splits[0] + dev.ProductID = splits[1] + dev.RevisionID = splits[2] + } + } + return nil +} + +func slurp(path string) string { + bs, err := os.ReadFile(path) + if err != nil { + return "" + } + + return string(bytes.TrimSpace(bs)) +} + +func usbs(ctx context.Context) ([]*Device, []error) { + paths := linuxpath.New(ctx) + devs := make([]*Device, 0) + errs := []error{} + + usbDevicesDirs, err := os.ReadDir(paths.SysBusUsbDevices) + if err != nil { + return devs, []error{err} + } + + for _, dir := range usbDevicesDirs { + linkPath := filepath.Join(paths.SysBusUsbDevices, dir.Name()) + fullDir, err := os.Readlink(linkPath) + if err != nil { + continue + } + if !filepath.IsAbs(fullDir) { + fullDir, err = filepath.Abs(filepath.Join(paths.SysBusUsbDevices, fullDir)) + if err != nil { + continue + } + } + + dev := Device{} + + err = fillUSBFromUevent(fullDir, &dev) + if err != nil { + errs = append(errs, err) + } + + dev.Interface = slurp(filepath.Join(fullDir, "interface")) + dev.Product = slurp(filepath.Join(fullDir, "product")) + + devs = append(devs, &dev) + } + + return devs, errs +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/usb/usb_stub.go b/vendor/github.com/jaypipes/ghw/pkg/usb/usb_stub.go new file mode 100644 index 0000000000..6345fe0636 --- /dev/null +++ b/vendor/github.com/jaypipes/ghw/pkg/usb/usb_stub.go @@ -0,0 +1,20 @@ +//go:build !linux +// +build !linux + +// Use and distribution licensed under the Apache license version 2. +// +// See the COPYING file in the root project directory for full text. +// + +package usb + +import ( + "context" + "runtime" + + "errors" +) + +func (i *Info) load(ctx context.Context) error { + return errors.New("usb load not implemented on " + runtime.GOOS) +} diff --git a/vendor/github.com/jaypipes/ghw/pkg/util/util.go b/vendor/github.com/jaypipes/ghw/pkg/util/util.go index 816aeb1b3d..24e635a663 100644 --- a/vendor/github.com/jaypipes/ghw/pkg/util/util.go +++ b/vendor/github.com/jaypipes/ghw/pkg/util/util.go @@ -7,12 +7,13 @@ package util import ( + "context" "fmt" "os" "strconv" "strings" - "github.com/jaypipes/ghw/pkg/context" + "github.com/jaypipes/ghw/internal/log" ) const ( @@ -34,17 +35,17 @@ func SafeClose(c closer) { // -1 if there were file permissions or existence errors or if the contents // could not be successfully converted to an integer. In any error, a warning // message is printed to STDERR and -1 is returned. -func SafeIntFromFile(ctx *context.Context, path string) int { +func SafeIntFromFile(ctx context.Context, path string) int { msg := "failed to read int from file: %s\n" buf, err := os.ReadFile(path) if err != nil { - ctx.Warn(msg, err) + log.Warn(ctx, msg, err) return -1 } contents := strings.TrimSpace(string(buf)) res, err := strconv.Atoi(contents) if err != nil { - ctx.Warn(msg, err) + log.Warn(ctx, msg, err) return -1 } return res diff --git a/vendor/modules.txt b/vendor/modules.txt index 2f8a6dba27..340db41f65 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -200,15 +200,16 @@ github.com/imdario/mergo # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap -# github.com/jaypipes/ghw v0.20.0 +# github.com/jaypipes/ghw v0.23.0 ## explicit; go 1.21 github.com/jaypipes/ghw +github.com/jaypipes/ghw/internal/config +github.com/jaypipes/ghw/internal/log github.com/jaypipes/ghw/pkg/accelerator github.com/jaypipes/ghw/pkg/baseboard github.com/jaypipes/ghw/pkg/bios github.com/jaypipes/ghw/pkg/block github.com/jaypipes/ghw/pkg/chassis -github.com/jaypipes/ghw/pkg/context github.com/jaypipes/ghw/pkg/cpu github.com/jaypipes/ghw/pkg/gpu github.com/jaypipes/ghw/pkg/linuxdmi @@ -223,6 +224,7 @@ github.com/jaypipes/ghw/pkg/product github.com/jaypipes/ghw/pkg/snapshot github.com/jaypipes/ghw/pkg/topology github.com/jaypipes/ghw/pkg/unitutil +github.com/jaypipes/ghw/pkg/usb github.com/jaypipes/ghw/pkg/util # github.com/jaypipes/pcidb v1.1.1 ## explicit; go 1.21