From 2db06409b32243583434aa12208801aca4862076 Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Tue, 5 Feb 2019 14:23:23 -0800 Subject: [PATCH 01/61] Add HostInfo support for FreeBSD Initial implementation of HostInfo for FreeBSD platforms. --- providers/freebsd/arch.go | 35 ++++ providers/freebsd/boottime_freebsd.go | 39 +++++ providers/freebsd/defs_freebsd.go | 34 ++++ providers/freebsd/doc.go | 20 +++ providers/freebsd/host_freebsd.go | 228 ++++++++++++++++++++++++++ providers/freebsd/kernel.go | 35 ++++ providers/freebsd/machineid.go | 35 ++++ providers/freebsd/memory_freebsd.go | 109 ++++++++++++ providers/freebsd/os_freebsd.go | 67 ++++++++ providers/freebsd/process_freebsd.go | 48 ++++++ providers/freebsd/syscall_freebsd.go | 92 +++++++++++ providers/freebsd/ztypes_freebsd.go | 39 +++++ system.go | 3 +- 13 files changed, 783 insertions(+), 1 deletion(-) create mode 100644 providers/freebsd/arch.go create mode 100644 providers/freebsd/boottime_freebsd.go create mode 100644 providers/freebsd/defs_freebsd.go create mode 100644 providers/freebsd/doc.go create mode 100644 providers/freebsd/host_freebsd.go create mode 100644 providers/freebsd/kernel.go create mode 100644 providers/freebsd/machineid.go create mode 100644 providers/freebsd/memory_freebsd.go create mode 100644 providers/freebsd/os_freebsd.go create mode 100644 providers/freebsd/process_freebsd.go create mode 100644 providers/freebsd/syscall_freebsd.go create mode 100644 providers/freebsd/ztypes_freebsd.go diff --git a/providers/freebsd/arch.go b/providers/freebsd/arch.go new file mode 100644 index 00000000..844c331a --- /dev/null +++ b/providers/freebsd/arch.go @@ -0,0 +1,35 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package freebsd + +import ( + "syscall" + + "github.com/pkg/errors" +) + +const hardwareMIB = "hw.machine" + +func Architecture() (string, error) { + arch, err := syscall.Sysctl(hardwareMIB) + if err != nil { + return "", errors.Wrap(err, "failed to get architecture") + } + + return arch, nil +} diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd.go new file mode 100644 index 00000000..eaee3cc8 --- /dev/null +++ b/providers/freebsd/boottime_freebsd.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build freebsd,cgo + +package freebsd + +import ( + "syscall" + "time" + + "github.com/pkg/errors" +) + +const kernBoottimeMIB = "kern.boottime" + +func BootTime() (time.Time, error) { + var tv syscall.Timeval + if err := sysctlByName(kernBoottimeMIB, &tv); err != nil { + return time.Time{}, errors.Wrap(err, "failed to get host uptime") + } + + bootTime := time.Unix(int64(tv.Sec), int64(tv.Usec)*int64(time.Microsecond)) + return bootTime, nil +} diff --git a/providers/freebsd/defs_freebsd.go b/providers/freebsd/defs_freebsd.go new file mode 100644 index 00000000..553cd371 --- /dev/null +++ b/providers/freebsd/defs_freebsd.go @@ -0,0 +1,34 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build ignore + +package freebsd + +/* +#include +#include +#include +#include +*/ +import "C" + +type vmTotal C.struct_vmtotal + +type kvmSwap C.struct_kvm_swap + +type clockInfo C.struct_clockinfo diff --git a/providers/freebsd/doc.go b/providers/freebsd/doc.go new file mode 100644 index 00000000..336d45aa --- /dev/null +++ b/providers/freebsd/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package freebsd implements the HostProvider and ProcessProvider interfaces +// for providing information about FreeBSD +package freebsd diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go new file mode 100644 index 00000000..efd36ba9 --- /dev/null +++ b/providers/freebsd/host_freebsd.go @@ -0,0 +1,228 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build,freebsd,cgo + +package freebsd + +//#include +//#include +import "C" + +import ( + "os" + "time" + + "github.com/joeshaw/multierror" + "github.com/pkg/errors" + + "github.com/dbolcsfoldi/go-sysinfo/internal/registry" + "github.com/elastic/go-sysinfo/providers/shared" + "github.com/elastic/go-sysinfo/types" +) + +func init() { + registry.Register(freebsdSystem{}) +} + +type freebsdSystem struct{} + +func (s freebsdSystem) Host() (types.Host, error) { + return newHost() +} + +type host struct { + info types.HostInfo +} + +func (h *host) Info() types.HostInfo { + return h.info +} + +func (h *host) CPUTime() (types.CPUTimes, error) { + cpu := types.CPUTimes{} + r := &reader{} + r.cpuTime(&cpu) + + return cpu, nil +} + +func (h *host) Memory() (*types.HostMemoryInfo, error) { + m := &types.HostMemoryInfo{} + r := &reader{} + r.memInfo(m) + return m, r.Err() +} + +func newHost() (*host, error) { + h := &host{} + r := &reader{} + r.architecture(h) + r.bootTime(h) + r.hostname(h) + r.network(h) + r.kernelVersion(h) + r.os(h) + r.time(h) + r.uniqueID(h) + return h, r.Err() +} + +type reader struct { + errs []error +} + +func (r *reader) addErr(err error) bool { + if err != nil { + if errors.Cause(err) != types.ErrNotImplemented { + r.errs = append(r.errs, err) + } + return true + } + return false +} + +func (r *reader) Err() error { + if len(r.errs) > 0 { + return &multierror.MultiError{Errors: r.errs} + } + return nil +} + +func (r *reader) cpuTime(cpu *types.CPUTimes) { + cptime, err := Cptime() + + if r.addErr(err) { + return + } + + cpu.User = time.Duration(cptime["User"]) + cpu.System = time.Duration(cptime["System"]) + cpu.Idle = time.Duration(cptime["Idle"]) + cpu.Nice = time.Duration(cptime["Nice"]) + cpu.IRQ = time.Duration(cptime["IRQ"]) +} + +func (r *reader) memInfo(m *types.HostMemoryInfo) { + pageSize, err := PageSize() + + if r.addErr(err) { + return + } + + totalMemory, err := TotalMemory() + if r.addErr(err) { + return + } + + m.Total = totalMemory + + vm, err := VmTotal() + if r.addErr(err) { + return + } + + m.Free = uint64(vm.Free) * uint64(pageSize) + m.Used = m.Total - m.Free + + numFreeBuffers, err := NumFreeBuffers() + if r.addErr(err) { + return + } + + m.Available = m.Free + (uint64(numFreeBuffers) * uint64(pageSize)) + + swap, err := KvmGetSwapInfo() + if r.addErr(err) { + return + } + + swapMaxPages, err := SwapMaxPages() + if r.addErr(err) { + return + } + + if swap.Total > swapMaxPages { + swap.Total = swapMaxPages + } + + m.VirtualTotal = uint64(swap.Total) * uint64(pageSize) + m.VirtualUsed = uint64(swap.Used) * uint64(pageSize) + m.VirtualFree = m.VirtualTotal - m.VirtualUsed +} + +func (r *reader) architecture(h *host) { + v, err := Architecture() + if r.addErr(err) { + return + } + h.info.Architecture = v +} + +func (r *reader) bootTime(h *host) { + v, err := BootTime() + if r.addErr(err) { + return + } + + h.info.BootTime = v +} + +func (r *reader) hostname(h *host) { + v, err := os.Hostname() + if r.addErr(err) { + return + } + h.info.Hostname = v +} + +func (r *reader) network(h *host) { + ips, macs, err := shared.Network() + if r.addErr(err) { + return + } + h.info.IPs = ips + h.info.MACs = macs +} + +func (r *reader) kernelVersion(h *host) { + v, err := KernelVersion() + if r.addErr(err) { + return + } + h.info.KernelVersion = v +} + +func (r *reader) os(h *host) { + v, err := OperatingSystem() + if r.addErr(err) { + return + } + h.info.OS = v +} + +func (r *reader) time(h *host) { + h.info.Timezone, h.info.TimezoneOffsetSec = time.Now().Zone() +} + +func (r *reader) uniqueID(h *host) { + v, err := MachineID() + if r.addErr(err) { + return + } + h.info.UniqueID = v +} diff --git a/providers/freebsd/kernel.go b/providers/freebsd/kernel.go new file mode 100644 index 00000000..3ade2725 --- /dev/null +++ b/providers/freebsd/kernel.go @@ -0,0 +1,35 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package freebsd + +import ( + "syscall" + + "github.com/pkg/errors" +) + +const kernelReleaseMIB = "kern.osrelease" + +func KernelVersion() (string, error) { + version, err := syscall.Sysctl(kernelReleaseMIB) + if err != nil { + return "", errors.Wrap(err, "failed to get kernel version") + } + + return version, nil +} diff --git a/providers/freebsd/machineid.go b/providers/freebsd/machineid.go new file mode 100644 index 00000000..2857ee1e --- /dev/null +++ b/providers/freebsd/machineid.go @@ -0,0 +1,35 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package freebsd + +import ( + "syscall" + + "github.com/pkg/errors" +) + +const kernelHostUUIDMIB = "kern.hostuuid" + +func MachineID() (string, error) { + uuid, err := syscall.Sysctl(kernelHostUUIDMIB) + if err != nil { + return "", errors.Wrap(err, "failed to get machine id") + } + + return uuid, nil +} diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd.go new file mode 100644 index 00000000..d2fbcf66 --- /dev/null +++ b/providers/freebsd/memory_freebsd.go @@ -0,0 +1,109 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build freebsd,cgo + +package freebsd + +//#cgo LDFLAGS:-lkvm +//#include +//#include +//#include + +//#include +//#include +//#include +import "C" + +import ( + "github.com/pkg/errors" + "syscall" + "unsafe" +) + +const hwPhysmemMIB = "hw.physmem" +const hwPagesizeMIB = "hw.pagesize" +const vmVmtotalMIB = "vm.vmtotal" +const vmSwapmaxpagesMIB = "vm.swap_maxpages" +const vfsNumfreebuffersMIB = "vfs.numfreebuffers" + +func PageSize() (uint32, error) { + var pageSize uint32 + if err := sysctlByName(hwPagesizeMIB, &pageSize); err != nil { + return 0, errors.Wrap(err, "failed to get hw.pagesize") + } + + return pageSize, nil +} + +func SwapMaxPages() (uint32, error) { + var maxPages uint32 + if err := sysctlByName(hwPhysmemMIB, &maxPages); err != nil { + return 0, errors.Wrap(err, "failed to get vm.swap_maxpages") + } + + return maxPages, nil +} + +func TotalMemory() (uint64, error) { + var size uint64 + if err := sysctlByName(hwPhysmemMIB, &size); err != nil { + return 0, errors.Wrap(err, "failed to get hw.physmem") + } + + return size, nil +} + +func VmTotal() (vmTotal, error) { + var vm vmTotal + if err := sysctlByName(vmVmtotalMIB, &vm); err != nil { + return vmTotal{}, errors.Wrap(err, "failed to get vm.vmtotal") + } + + return vm, nil +} + +func NumFreeBuffers() (uint32, error) { + var numfreebuffers uint32 + if err := sysctlByName(vfsNumfreebuffersMIB, &numfreebuffers); err != nil { + return 0, errors.Wrap(err, "failed to get vfs.numfreebuffers") + } + + return numfreebuffers, nil +} + +func KvmGetSwapInfo() (kvmSwap, error) { + var kdC *C.struct_kvm_t + + devNull := C.CString("/dev/null") + defer C.free(unsafe.Pointer(devNull)) + kvmOpen := C.CString("kvm_open") + defer C.free(unsafe.Pointer(kvmOpen)) + + if kdC, err := C.kvm_open(nil, devNull, nil, syscall.O_RDONLY, kvmOpen); kdC == nil { + return kvmSwap{}, errors.Wrap(err, "failed to open kvm") + } + + defer C.kvm_close((*C.struct___kvm)(unsafe.Pointer(kdC))) + + var swap kvmSwap + if n, err := C.kvm_getswapinfo((*C.struct___kvm)(unsafe.Pointer(kdC)), (*C.struct_kvm_swap)(unsafe.Pointer(&swap)), 1, 0); n != 0 { + return kvmSwap{}, errors.Wrap(err, "failed to get kvm_getswapinfo") + } + + return swap, nil +} diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go new file mode 100644 index 00000000..c575f98c --- /dev/null +++ b/providers/freebsd/os_freebsd.go @@ -0,0 +1,67 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build freebsd,cgo + +package freebsd + +import "C" + +import ( + "strconv" + "strings" + "syscall" + + "github.com/elastic/go-sysinfo/types" +) + +const ostypeMIB = "kern.ostype" +const osreleaseMIB = "kern.osrelease" +const osrevisionMIB = "kern.osrevision" + +func OperatingSystem() (*types.OSInfo, error) { + info := &types.OSInfo{ + Family: "freebsd", + Platform: "freebsd", + } + + ostype, err := syscall.Sysctl(ostypeMIB) + if err != nil { + return info, err + } + info.Name = ostype + + osrelease, err := syscall.Sysctl(osreleaseMIB) + if err != nil { + return info, err + } + info.Version = osrelease + + elems := strings.Split(osrelease, "-") + majorminor := strings.Split(elems[0], ".") + + if len(majorminor) > 0 { + info.Major, _ = strconv.Atoi(majorminor[0]) + } + + if len(majorminor) > 1 { + info.Minor, _ = strconv.Atoi(majorminor[1]) + } + + info.Patch, _ = strconv.Atoi(strings.TrimPrefix(elems[2], "p")) + return info, nil +} diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go new file mode 100644 index 00000000..1d1b2f72 --- /dev/null +++ b/providers/freebsd/process_freebsd.go @@ -0,0 +1,48 @@ +//+build,freebsd,cgo + +package freebsd + +//#include +//#include +import "C" + +import ( + "strconv" + "strings" + "syscall" + + "github.com/pkg/errors" +) + +const kernCptimeMIB = "kern.cp_time" +const kernClockrateMIB = "kern.clockrate" + +func Cptime() (map[string]uint64, error) { + var clock clockInfo + + if err := sysctlByName(kernClockrateMIB, &clock); err != nil { + return make(map[string]uint64), errors.Wrap(err, "failed to get kern.clockrate") + } + + cptime, err := syscall.Sysctl(kernCptimeMIB) + if err != nil { + return make(map[string]uint64), errors.Wrap(err, "failed to get kern.cp_time") + } + + cpMap := make(map[string]uint64) + + times := strings.Split(cptime, " ") + names := [5]string{"User", "Nice", "System", "IRQ", "Idle"} + + for index, time := range times { + i, err := strconv.ParseUint(time, 10, 64) + + if err != nil { + return cpMap, errors.Wrap(err, "error parsing kern.cp_time") + } + + cpMap[names[index]] = i * uint64(clock.Tick) * 1000 + } + + return cpMap, nil +} diff --git a/providers/freebsd/syscall_freebsd.go b/providers/freebsd/syscall_freebsd.go new file mode 100644 index 00000000..b2752ae8 --- /dev/null +++ b/providers/freebsd/syscall_freebsd.go @@ -0,0 +1,92 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build freebsd,cgo + +package freebsd + +/* +#cgo LDFLAGS:-lc +#include +#include +*/ +import "C" + +import ( + "bytes" + "encoding/binary" + "sync" + "unsafe" +) + +// Single-word zero for use when we need a valid pointer to 0 bytes. +// See mksyscall.pl. +var _zero uintptr + +// Buffer Pool + +var bufferPool = sync.Pool{ + New: func() interface{} { + return &poolMem{ + buf: make([]byte, 512), + } + }, +} + +type poolMem struct { + buf []byte + pool *sync.Pool +} + +func getPoolMem() *poolMem { + pm := bufferPool.Get().(*poolMem) + pm.buf = pm.buf[0:cap(pm.buf)] + pm.pool = &bufferPool + return pm +} + +func (m *poolMem) Release() { m.pool.Put(m) } + +func sysctlbyname(name string, value interface{}) error { + mem := getPoolMem() + defer mem.Release() + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + size := C.ulong(len(mem.buf)) + if n, err := C.sysctlbyname(cname, unsafe.Pointer(&mem.buf[0]), &size, nil, C.ulong(0)); n != 0 { + return err + } + + data := mem.buf[0:size] + + switch v := value.(type) { + case *[]byte: + out := make([]byte, len(data)) + copy(out, data) + *v = out + return nil + default: + return binary.Read(bytes.NewReader(data), binary.LittleEndian, v) + } + +} + +func sysctlByName(name string, out interface{}) error { + return sysctlbyname(name, out) +} diff --git a/providers/freebsd/ztypes_freebsd.go b/providers/freebsd/ztypes_freebsd.go new file mode 100644 index 00000000..9d3c2c90 --- /dev/null +++ b/providers/freebsd/ztypes_freebsd.go @@ -0,0 +1,39 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_freebsd.go + +package freebsd + +type vmTotal struct { + Rq int16 + Dw int16 + Pw int16 + Sl int16 + _ int16 // cgo doesn't generate the same alignment as C does + Sw int16 + Vm int32 + Avm int32 + Rm int32 + Arm int32 + Vmshr int32 + Avmshr int32 + Rmshr int32 + Armshr int32 + Free int32 +} + +type kvmSwap struct { + Devname [32]int8 + Used uint32 + Total uint32 + Flags int32 + Reserved1 uint32 + Reserved2 uint32 +} + +type clockInfo struct { + Hz int32 + Tick int32 + Spare int32 + Stathz int32 + Profhz int32 +} diff --git a/system.go b/system.go index 90f81691..dcecd965 100644 --- a/system.go +++ b/system.go @@ -20,13 +20,14 @@ package sysinfo import ( "runtime" - "github.com/elastic/go-sysinfo/internal/registry" + "github.com/dbolcsfoldi/go-sysinfo/internal/registry" "github.com/elastic/go-sysinfo/types" // Register host and process providers. _ "github.com/elastic/go-sysinfo/providers/darwin" _ "github.com/elastic/go-sysinfo/providers/linux" _ "github.com/elastic/go-sysinfo/providers/windows" + _ "github.com/dbolcsfoldi/go-sysinfo/providers/freebsd" ) // Go returns information about the Go runtime. From d6b209f0c18eb75623c312b324db4d025bf18e5f Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Sun, 10 Feb 2019 05:30:43 -0800 Subject: [PATCH 02/61] Use os.SameFile to compare cwds On some platforms (e.g. FreeBSD) user home directories are symlinks and os.Getcwd will return the symlink path. On FreeBSD CWD for processes that's not the running process will not give the symlink but the resolved path causing the test to fail at this point. Fix is to use the os.SameFile call to resolve both Os.Getcwd and CWD. --- system_test.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/system_test.go b/system_test.go index 97a2de5d..a1935b6f 100644 --- a/system_test.go +++ b/system_test.go @@ -19,6 +19,7 @@ package sysinfo import ( "encoding/json" + "fmt" "os" osUser "os/user" "runtime" @@ -62,6 +63,10 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ OpenHandleEnumerator: false, OpenHandleCounter: true, }, + "freebsd": &ProcessFeatures{ + ProcessInfo: true, + Environment: false, + }, } func TestProcessFeaturesMatrix(t *testing.T) { @@ -90,6 +95,7 @@ func TestProcessFeaturesMatrix(t *testing.T) { } func TestSelf(t *testing.T) { + fmt.Printf("Getting Self()...\n") process, err := Self() if err == types.ErrNotImplemented { t.Skip("process provider not implemented on", runtime.GOOS) @@ -107,6 +113,7 @@ func TestSelf(t *testing.T) { } output := map[string]interface{}{} + fmt.Printf("Getting ProcessInfo...\n") info, err := process.Info() if err != nil { t.Fatal(err) @@ -120,7 +127,18 @@ func TestSelf(t *testing.T) { if err != nil { t.Fatal(err) } - assert.EqualValues(t, wd, info.CWD) + + wdStat, err := os.Stat(wd) + if err != nil { + t.Fatal(err) + } + + cwdStat, err := os.Stat(info.CWD) + if err != nil { + t.Fatal(err) + } + + assert.EqualValues(t, os.SameFile(wdStat, cwdStat), true) exe, err := os.Executable() if err != nil { @@ -133,6 +151,7 @@ func TestSelf(t *testing.T) { } assert.WithinDuration(t, info.StartTime, time.Now(), 10*time.Second) + fmt.Printf("Getting UserInfo...\n") user, err := process.User() if err != nil { t.Fatal(err) @@ -151,6 +170,7 @@ func TestSelf(t *testing.T) { assert.EqualValues(t, strconv.Itoa(os.Getegid()), user.EGID) } + fmt.Printf("Getting Environment...\n") if v, ok := process.(types.Environment); ok { expectedEnv := map[string]string{} for _, keyValue := range os.Environ() { @@ -168,6 +188,7 @@ func TestSelf(t *testing.T) { output["process.env"] = actualEnv } + fmt.Printf("Getting MemoryInfo...\n") memInfo, err := process.Memory() require.NoError(t, err) if runtime.GOOS != "windows" { @@ -178,6 +199,7 @@ func TestSelf(t *testing.T) { assert.NotZero(t, memInfo.Resident) output["process.mem"] = memInfo + fmt.Printf("Getting CPUTimes...\n") for { cpuTimes, err := process.CPUTime() require.NoError(t, err) @@ -191,6 +213,7 @@ func TestSelf(t *testing.T) { // measurement. } + fmt.Printf("Getting OpenHandleEnumerator...\n") if v, ok := process.(types.OpenHandleEnumerator); ok { fds, err := v.OpenHandles() if assert.NoError(t, err) { From 08f7b57683ed1e2a8d732bd0eeb51cf6494db75a Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Sun, 10 Feb 2019 05:33:43 -0800 Subject: [PATCH 03/61] Add basic support for ProcessInfo on FreeBSD. Implement Process(), Self() and Processes() to the bare minimum. That is passing all basic tests. --- providers/freebsd/process_freebsd.go | 285 +++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index 1d1b2f72..430c44e4 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -2,18 +2,303 @@ package freebsd +//#include //#include //#include +//#include +//#include +//#include +//#include +// +//#include +//#include +//struct kinfo_proc getProcInfoAt(struct kinfo_proc *procs, unsigned int index) { +// return procs[index]; +//} +//unsigned int countArrayItems(char **items) { +// unsigned int i = 0; +// for (i = 0; items[i] != NULL; ++i); +// return i; +//} +//char * itemAtIndex(char **items, unsigned int index) { +// return items[index]; +//} +//unsigned int countFileStats(struct filestat_list *head) { +// unsigned int count = 0; +// struct filestat *fst; +// STAILQ_FOREACH(fst, head, next) { +// ++count; +// } +// +// return count; +//} +//void copyFileStats(struct filestat_list *head, struct filestat *out, unsigned int size) { +// unsigned int index = 0; +// struct filestat *fst; +// STAILQ_FOREACH(fst, head, next) { +// if (!size) { +// break; +// } +// memcpy(out, fst, sizeof(*fst)); +// ++out; +// --size; +// } +//} +// import "C" import ( + "fmt" + "os" "strconv" "strings" "syscall" + "time" "github.com/pkg/errors" + + "github.com/elastic/go-sysinfo/types" ) +func getProcInfo(op int, arg int) ([]process, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return nil, errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + var count C.uint = 0 + kprocs := C.procstat_getprocs(procstat, C.int(op), C.int(arg), &count) + if kprocs == nil { + return nil, errors.New("getprocs failed") + } + defer C.procstat_freeprocs(procstat, kprocs) + + procs := make([]process, count) + var index C.uint + for index = 0; index < count; index++ { + proc := C.getProcInfoAt(kprocs, index) + procs[index].kinfo = proc + procs[index].pid = int(proc.ki_pid) + } + + return procs, nil +} + +func copyArray(from **C.char) []string { + if from == nil { + return nil + } + + count := C.countArrayItems(from) + out := make([]string, count) + + for index := C.uint(0); index < count; index++ { + out[index] = C.GoString(C.itemAtIndex(from, index)) + } + + return out +} + +func makeMap(from []string) map[string]string { + out := make(map[string]string, len(from)) + + for _, env := range from { + parts := strings.Split(env, "=") + if len(parts) > 1 { + out[parts[0]] = parts[1] + } + } + + fmt.Printf("makeMap: %v\n", out) + return out +} + +func getProcEnv(p *process) (map[string]string, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return nil, errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + env := C.procstat_getenvv(procstat, &p.kinfo, 0) + defer C.procstat_freeenvv(procstat) + + return makeMap(copyArray(env)), nil +} + +func getProcArgs(p *process) ([]string, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return nil, errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + args := C.procstat_getargv(procstat, &p.kinfo, 0) + defer C.procstat_freeargv(procstat) + + return copyArray(args), nil +} + +func getProcPathname(p *process) (string, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return "", errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + maxlen := 4096 + out := make([]C.char, maxlen) + if res, err := C.procstat_getpathname(procstat, &p.kinfo, &out[0], C.ulong(maxlen)); res != 0 { + return "", err + } + return C.GoString(&out[0]), nil +} + +func getFileStats(fileStats *C.struct_filestat_list) []C.struct_filestat { + count := C.countFileStats(fileStats) + + if count < 1 { + return nil + } + + out := make([]C.struct_filestat, count) + + C.copyFileStats(fileStats, &out[0], count) + return out +} + +func getProcCWD(p *process) (string, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return "", errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + fs := C.procstat_getfiles(procstat, &p.kinfo, 0) + defer C.procstat_freefiles(procstat, fs) + + files := getFileStats(fs) + for _, f := range files { + if f.fs_uflags == C.PS_FST_UFLAG_CDIR { + return C.GoString(f.fs_path), nil + } + } + + return "", nil +} + +type process struct { + pid int + kinfo C.struct_kinfo_proc +} + +func timevalToDuration(tm C.struct_timeval) time.Duration { + return (time.Duration(tm.tv_sec) * 1000000) + (time.Duration(tm.tv_usec) * 1000) +} + +func (p *process) CPUTime() (types.CPUTimes, error) { + procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) + + if err != nil { + return types.CPUTimes{}, err + } + p.kinfo = procs[0].kinfo + + return types.CPUTimes{ + User: timevalToDuration(p.kinfo.ki_rusage.ru_utime), + System: timevalToDuration(p.kinfo.ki_rusage.ru_stime), + }, nil +} + +func (p *process) Info() (types.ProcessInfo, error) { + procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) + + if err != nil { + return types.ProcessInfo{}, err + } + p.kinfo = procs[0].kinfo + + cwd, err := getProcCWD(p) + args, err := getProcArgs(p) + exe, err := getProcPathname(p) + + return types.ProcessInfo{ + Name: C.GoString(&p.kinfo.ki_comm[0]), + PID: int(p.kinfo.ki_pid), + PPID: int(p.kinfo.ki_ppid), + CWD: cwd, + Exe: exe, + Args: args, + StartTime: time.Unix(int64(p.kinfo.ki_start.tv_sec), int64(p.kinfo.ki_start.tv_usec)*1000), + }, nil +} + +func (p *process) Memory() (types.MemoryInfo, error) { + procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) + + if err != nil { + return types.MemoryInfo{}, err + } + p.kinfo = procs[0].kinfo + + return types.MemoryInfo{ + Resident: uint64(p.kinfo.ki_rssize), + Virtual: uint64(p.kinfo.ki_size), + }, nil +} + +func (p *process) User() (types.UserInfo, error) { + procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) + + if err != nil { + return types.UserInfo{}, err + } + + p.kinfo = procs[0].kinfo + + return types.UserInfo{ + UID: strconv.FormatUint(uint64(p.kinfo.ki_ruid), 10), + EUID: strconv.FormatUint(uint64(p.kinfo.ki_uid), 10), + SUID: strconv.FormatUint(uint64(p.kinfo.ki_svuid), 10), + GID: strconv.FormatUint(uint64(p.kinfo.ki_rgid), 10), + EGID: strconv.FormatUint(uint64(p.kinfo.ki_groups[0]), 10), + SGID: strconv.FormatUint(uint64(p.kinfo.ki_svgid), 10), + }, nil +} + +func (p *process) PID() int { + return p.pid +} + +func (s freebsdSystem) Processes() ([]types.Process, error) { + procs, err := getProcInfo(C.KERN_PROC_PROC, 0) + out := make([]types.Process, 0, len(procs)) + + for _, proc := range procs { + out = append(out, &process{ + pid: proc.pid, + kinfo: proc.kinfo, + }) + } + + return out, err +} + +func (s freebsdSystem) Process(pid int) (types.Process, error) { + p := process{pid: pid} + return &p, nil +} + +func (s freebsdSystem) Self() (types.Process, error) { + return s.Process(os.Getpid()) +} + const kernCptimeMIB = "kern.cp_time" const kernClockrateMIB = "kern.clockrate" From f8ea09483e0418c187807539060ef1e9f823b2d1 Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Sun, 10 Feb 2019 22:04:17 -0800 Subject: [PATCH 04/61] Add support for OpenHandleEnumerator and OpenHandleCounter. --- providers/freebsd/process_freebsd.go | 36 ++++++++++++++++++++++++++++ system_test.go | 6 +++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index 430c44e4..dc8065d2 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -276,6 +276,42 @@ func (p *process) PID() int { return p.pid } +func (p *process) OpenHandles() ([]string, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return nil, errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + fs := C.procstat_getfiles(procstat, &p.kinfo, 0) + defer C.procstat_freefiles(procstat, fs) + + files := getFileStats(fs) + names := make([]string, 0, len(files)) + + for _, file := range files { + if file.fs_uflags == 0 { + names = append(names, C.GoString(file.fs_path)) + } + } + + return names, nil +} + +func (p *process) OpenHandleCount() (int, error) { + procstat := C.procstat_open_sysctl() + + if procstat == nil { + return 0, errors.New("failed to open procstat sysctl") + } + defer C.procstat_close(procstat) + + fs := C.procstat_getfiles(procstat, &p.kinfo, 0) + defer C.procstat_freefiles(procstat, fs) + return int(C.countFileStats(fs)), nil +} + func (s freebsdSystem) Processes() ([]types.Process, error) { procs, err := getProcInfo(C.KERN_PROC_PROC, 0) out := make([]types.Process, 0, len(procs)) diff --git a/system_test.go b/system_test.go index a1935b6f..cc5a3e85 100644 --- a/system_test.go +++ b/system_test.go @@ -64,8 +64,10 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ OpenHandleCounter: true, }, "freebsd": &ProcessFeatures{ - ProcessInfo: true, - Environment: false, + ProcessInfo: true, + Environment: false, + OpenHandleEnumerator: true, + OpenHandleCounter: true, }, } From 7d8b0f667be80348d8d29b7e2df7f8b05c413396 Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Sun, 10 Feb 2019 22:08:09 -0800 Subject: [PATCH 05/61] Add support for Environment --- providers/freebsd/process_freebsd.go | 6 ++++-- system_test.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index dc8065d2..2882a1bb 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -48,7 +48,6 @@ package freebsd import "C" import ( - "fmt" "os" "strconv" "strings" @@ -111,7 +110,6 @@ func makeMap(from []string) map[string]string { } } - fmt.Printf("makeMap: %v\n", out) return out } @@ -312,6 +310,10 @@ func (p *process) OpenHandleCount() (int, error) { return int(C.countFileStats(fs)), nil } +func (p *process) Environment() (map[string]string, error) { + return getProcEnv(p) +} + func (s freebsdSystem) Processes() ([]types.Process, error) { procs, err := getProcInfo(C.KERN_PROC_PROC, 0) out := make([]types.Process, 0, len(procs)) diff --git a/system_test.go b/system_test.go index cc5a3e85..a0b6f4f7 100644 --- a/system_test.go +++ b/system_test.go @@ -65,7 +65,7 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ }, "freebsd": &ProcessFeatures{ ProcessInfo: true, - Environment: false, + Environment: true, OpenHandleEnumerator: true, OpenHandleCounter: true, }, From 4388e47e92236ebeee6a734d2696d6f727f6f54a Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Tue, 12 Feb 2019 03:08:24 -0800 Subject: [PATCH 06/61] Review fixes. - Fix issue on FreeBSD 12. - Minor cosmetic issues. --- providers/freebsd/memory_freebsd.go | 13 +++-- providers/freebsd/os_freebsd.go | 5 +- providers/freebsd/process_freebsd.go | 67 ++++++++++++++++------- providers/freebsd/process_freebsd_test.go | 8 +++ providers/freebsd/syscall_freebsd.go | 5 -- providers/freebsd/ztypes_freebsd.go | 3 +- 6 files changed, 69 insertions(+), 32 deletions(-) create mode 100644 providers/freebsd/process_freebsd_test.go diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd.go index d2fbcf66..3de5aa5a 100644 --- a/providers/freebsd/memory_freebsd.go +++ b/providers/freebsd/memory_freebsd.go @@ -19,7 +19,6 @@ package freebsd -//#cgo LDFLAGS:-lkvm //#include //#include //#include @@ -40,6 +39,8 @@ const hwPagesizeMIB = "hw.pagesize" const vmVmtotalMIB = "vm.vmtotal" const vmSwapmaxpagesMIB = "vm.swap_maxpages" const vfsNumfreebuffersMIB = "vfs.numfreebuffers" +const devNull = "/dev/null" +const kvmOpen = "kvm_open" func PageSize() (uint32, error) { var pageSize uint32 @@ -89,12 +90,12 @@ func NumFreeBuffers() (uint32, error) { func KvmGetSwapInfo() (kvmSwap, error) { var kdC *C.struct_kvm_t - devNull := C.CString("/dev/null") - defer C.free(unsafe.Pointer(devNull)) - kvmOpen := C.CString("kvm_open") - defer C.free(unsafe.Pointer(kvmOpen)) + devNullC := C.CString(devNull) + defer C.free(unsafe.Pointer(devNullC)) + kvmOpenC := C.CString(kvmOpen) + defer C.free(unsafe.Pointer(kvmOpenC)) - if kdC, err := C.kvm_open(nil, devNull, nil, syscall.O_RDONLY, kvmOpen); kdC == nil { + if kdC, err := C.kvm_open(nil, devNullC, nil, syscall.O_RDONLY, kvmOpenC); kdC == nil { return kvmSwap{}, errors.Wrap(err, "failed to open kvm") } diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go index c575f98c..8e5803f4 100644 --- a/providers/freebsd/os_freebsd.go +++ b/providers/freebsd/os_freebsd.go @@ -62,6 +62,9 @@ func OperatingSystem() (*types.OSInfo, error) { info.Minor, _ = strconv.Atoi(majorminor[1]) } - info.Patch, _ = strconv.Atoi(strings.TrimPrefix(elems[2], "p")) + if len(elems) > 1 { + info.Patch, _ = strconv.Atoi(strings.TrimPrefix(elems[2], "p")) + } + return info, nil } diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index 2882a1bb..3f94091a 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -1,3 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + //+build,freebsd,cgo package freebsd @@ -60,17 +77,17 @@ import ( ) func getProcInfo(op int, arg int) ([]process, error) { - procstat := C.procstat_open_sysctl() + procstat, err := C.procstat_open_sysctl() if procstat == nil { - return nil, errors.New("failed to open procstat sysctl") + return nil, errors.Wrap(err, "failed to open procstat sysctl") } defer C.procstat_close(procstat) var count C.uint = 0 - kprocs := C.procstat_getprocs(procstat, C.int(op), C.int(arg), &count) + kprocs, err := C.procstat_getprocs(procstat, C.int(op), C.int(arg), &count) if kprocs == nil { - return nil, errors.New("getprocs failed") + return nil, errors.Wrap(err, "getprocs failed") } defer C.procstat_freeprocs(procstat, kprocs) @@ -114,42 +131,42 @@ func makeMap(from []string) map[string]string { } func getProcEnv(p *process) (map[string]string, error) { - procstat := C.procstat_open_sysctl() + procstat, err := C.procstat_open_sysctl() if procstat == nil { - return nil, errors.New("failed to open procstat sysctl") + return nil, errors.Wrap(err, "failed to open procstat sysctl") } defer C.procstat_close(procstat) - env := C.procstat_getenvv(procstat, &p.kinfo, 0) + env, err := C.procstat_getenvv(procstat, &p.kinfo, 0) defer C.procstat_freeenvv(procstat) - return makeMap(copyArray(env)), nil + return makeMap(copyArray(env)), err } func getProcArgs(p *process) ([]string, error) { - procstat := C.procstat_open_sysctl() + procstat, err := C.procstat_open_sysctl() if procstat == nil { - return nil, errors.New("failed to open procstat sysctl") + return nil, errors.Wrap(err, "failed to open procstat sysctl") } defer C.procstat_close(procstat) - args := C.procstat_getargv(procstat, &p.kinfo, 0) + args, err := C.procstat_getargv(procstat, &p.kinfo, 0) defer C.procstat_freeargv(procstat) - return copyArray(args), nil + return copyArray(args), err } func getProcPathname(p *process) (string, error) { - procstat := C.procstat_open_sysctl() + procstat, err := C.procstat_open_sysctl() if procstat == nil { - return "", errors.New("failed to open procstat sysctl") + return "", errors.Wrap(err, "failed to open procstat sysctl") } defer C.procstat_close(procstat) - maxlen := 4096 + const maxlen = uint(1024) out := make([]C.char, maxlen) if res, err := C.procstat_getpathname(procstat, &p.kinfo, &out[0], C.ulong(maxlen)); res != 0 { return "", err @@ -171,14 +188,18 @@ func getFileStats(fileStats *C.struct_filestat_list) []C.struct_filestat { } func getProcCWD(p *process) (string, error) { - procstat := C.procstat_open_sysctl() + procstat, err := C.procstat_open_sysctl() if procstat == nil { - return "", errors.New("failed to open procstat sysctl") + return "", errors.Wrap(err, "failed to open procstat sysctl") } defer C.procstat_close(procstat) - fs := C.procstat_getfiles(procstat, &p.kinfo, 0) + fs, err := C.procstat_getfiles(procstat, &p.kinfo, 0) + if fs == nil { + return "", errors.Wrap(err, "failed to get files") + } + defer C.procstat_freefiles(procstat, fs) files := getFileStats(fs) @@ -223,8 +244,16 @@ func (p *process) Info() (types.ProcessInfo, error) { p.kinfo = procs[0].kinfo cwd, err := getProcCWD(p) + if err != nil { + return types.ProcessInfo{}, err + } + args, err := getProcArgs(p) - exe, err := getProcPathname(p) + if err != nil { + return types.ProcessInfo{}, err + } + + exe, _ := getProcPathname(p) return types.ProcessInfo{ Name: C.GoString(&p.kinfo.ki_comm[0]), diff --git a/providers/freebsd/process_freebsd_test.go b/providers/freebsd/process_freebsd_test.go new file mode 100644 index 00000000..4b00f2d8 --- /dev/null +++ b/providers/freebsd/process_freebsd_test.go @@ -0,0 +1,8 @@ +package freebsd + +import ( + "github.com/elastic/go-sysinfo/internal/registry" +) + +var _ registry.HostProvider = freebsdSystem{} +var _ registry.ProcessProvider = freebsdSystem{} diff --git a/providers/freebsd/syscall_freebsd.go b/providers/freebsd/syscall_freebsd.go index b2752ae8..6171678c 100644 --- a/providers/freebsd/syscall_freebsd.go +++ b/providers/freebsd/syscall_freebsd.go @@ -33,10 +33,6 @@ import ( "unsafe" ) -// Single-word zero for use when we need a valid pointer to 0 bytes. -// See mksyscall.pl. -var _zero uintptr - // Buffer Pool var bufferPool = sync.Pool{ @@ -84,7 +80,6 @@ func sysctlbyname(name string, value interface{}) error { default: return binary.Read(bytes.NewReader(data), binary.LittleEndian, v) } - } func sysctlByName(name string, out interface{}) error { diff --git a/providers/freebsd/ztypes_freebsd.go b/providers/freebsd/ztypes_freebsd.go index 9d3c2c90..230b857e 100644 --- a/providers/freebsd/ztypes_freebsd.go +++ b/providers/freebsd/ztypes_freebsd.go @@ -1,4 +1,5 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// Code generated by cmd/cgo -godefs; and then patched up to fix +// an alignment issue // cgo -godefs defs_freebsd.go package freebsd From 926d715eb16e0be82782f2baa0961a5082281c16 Mon Sep 17 00:00:00 2001 From: David Bolcsfoldi Date: Tue, 12 Feb 2019 03:19:32 -0800 Subject: [PATCH 07/61] Clean up build process. - Remove cgo build directives from things not needing it. - Add linker flags into .go files. --- providers/freebsd/boottime_freebsd.go | 2 -- providers/freebsd/host_freebsd.go | 1 + providers/freebsd/memory_freebsd.go | 1 + providers/freebsd/os_freebsd.go | 4 ---- providers/freebsd/process_freebsd.go | 1 + providers/freebsd/process_freebsd_test.go | 17 +++++++++++++++++ providers/freebsd/syscall_freebsd.go | 7 ++----- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd.go index eaee3cc8..f5364e27 100644 --- a/providers/freebsd/boottime_freebsd.go +++ b/providers/freebsd/boottime_freebsd.go @@ -15,8 +15,6 @@ // specific language governing permissions and limitations // under the License. -// +build freebsd,cgo - package freebsd import ( diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go index efd36ba9..a6fe2bd6 100644 --- a/providers/freebsd/host_freebsd.go +++ b/providers/freebsd/host_freebsd.go @@ -19,6 +19,7 @@ package freebsd +// #cgo LDFLAGS: -lkvm //#include //#include import "C" diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd.go index 3de5aa5a..e824db7d 100644 --- a/providers/freebsd/memory_freebsd.go +++ b/providers/freebsd/memory_freebsd.go @@ -19,6 +19,7 @@ package freebsd +// #cgo LDFLAGS: -lkvm //#include //#include //#include diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go index 8e5803f4..1c1bab48 100644 --- a/providers/freebsd/os_freebsd.go +++ b/providers/freebsd/os_freebsd.go @@ -15,12 +15,8 @@ // specific language governing permissions and limitations // under the License. -// +build freebsd,cgo - package freebsd -import "C" - import ( "strconv" "strings" diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index 3f94091a..a2dd00ff 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -19,6 +19,7 @@ package freebsd +// #cgo LDFLAGS: -lkvm -lprocstat //#include //#include //#include diff --git a/providers/freebsd/process_freebsd_test.go b/providers/freebsd/process_freebsd_test.go index 4b00f2d8..566fa001 100644 --- a/providers/freebsd/process_freebsd_test.go +++ b/providers/freebsd/process_freebsd_test.go @@ -1,3 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package freebsd import ( diff --git a/providers/freebsd/syscall_freebsd.go b/providers/freebsd/syscall_freebsd.go index 6171678c..5a6ddb1f 100644 --- a/providers/freebsd/syscall_freebsd.go +++ b/providers/freebsd/syscall_freebsd.go @@ -19,11 +19,8 @@ package freebsd -/* -#cgo LDFLAGS:-lc -#include -#include -*/ +//#include +//#include import "C" import ( From 042a3048ac687c19cd39737b22e7b08a4d8e9d4f Mon Sep 17 00:00:00 2001 From: redanthrax Date: Fri, 5 Aug 2022 10:36:02 -0700 Subject: [PATCH 08/61] Removed unsupported /proc directives --- providers/freebsd/host_freebsd.go | 25 ---- providers/freebsd/host_freebsd_test.go | 25 ---- providers/freebsd/process_freebsd_test.go | 50 ------- providers/freebsd/util.go | 111 ++++++++++++++ providers/freebsd/vmstat.go | 69 +++++++++ providers/freebsd/vmstat_test.go | 171 ++++++++++++++++++++++ system_test.go | 1 + 7 files changed, 352 insertions(+), 100 deletions(-) delete mode 100644 providers/freebsd/process_freebsd_test.go create mode 100644 providers/freebsd/util.go create mode 100644 providers/freebsd/vmstat.go create mode 100644 providers/freebsd/vmstat_test.go diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go index bdc1ecc9..2f3e87bf 100644 --- a/providers/freebsd/host_freebsd.go +++ b/providers/freebsd/host_freebsd.go @@ -24,7 +24,6 @@ import "C" import ( "os" - "io/ioutil" "path/filepath" "time" @@ -95,30 +94,6 @@ func newHost() (*host, error) { return h, r.Err() } - -// NetworkCounters reports data from /proc/net on linux -func (h *host) NetworkCounters() (*types.NetworkCountersInfo, error) { - snmpRaw, err := ioutil.ReadFile(h.procFS.path("net/snmp")) - if err != nil { - return nil, err - } - snmp, err := getNetSnmpStats(snmpRaw) - if err != nil { - return nil, err - } - - netstatRaw, err := ioutil.ReadFile(h.procFS.path("net/netstat")) - if err != nil { - return nil, err - } - netstat, err := getNetstatStats(netstatRaw) - if err != nil { - return nil, err - } - - return &types.NetworkCountersInfo{SNMP: snmp, Netstat: netstat}, nil -} - type reader struct { errs []error } diff --git a/providers/freebsd/host_freebsd_test.go b/providers/freebsd/host_freebsd_test.go index a2de4dbe..ff25fcff 100644 --- a/providers/freebsd/host_freebsd_test.go +++ b/providers/freebsd/host_freebsd_test.go @@ -21,10 +21,7 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" - "github.com/elastic/go-sysinfo/internal/registry" - "github.com/elastic/go-sysinfo/types" ) var _ registry.HostProvider = freebsdSystem{} @@ -50,25 +47,3 @@ func TestHostMemoryInfo(t *testing.T) { t.Fatal(err) } } - -func TestHostNetworkCounters(t *testing.T) { - host, err := newFreeBSDSystem("").Host() - if err != nil { - t.Fatal(err) - } - - s, err := host.(types.NetworkCounters).NetworkCounters() - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, s.Netstat.IPExt) - assert.NotEmpty(t, s.Netstat.TCPExt) - assert.NotEmpty(t, s.SNMP.IP) - - data, err := json.MarshalIndent(s, "", " ") - if err != nil { - t.Fatal(err) - } - t.Log(string(data)) -} diff --git a/providers/freebsd/process_freebsd_test.go b/providers/freebsd/process_freebsd_test.go deleted file mode 100644 index a26c661f..00000000 --- a/providers/freebsd/process_freebsd_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package freebsd - -import ( - "testing" - - "github.com/elastic/go-sysinfo/internal/registry" - "github.com/elastic/go-sysinfo/types" - "github.com/stretchr/testify/assert" -) - -var _ registry.HostProvider = freebsdSystem{} -var _ registry.ProcessProvider = freebsdSystem{} - -func TestProcessNetstat(t *testing.T) { - proc, err := newFreeBSDSystem("").Self() - if err != nil { - t.Fatal(err) - } - t.Log(proc) - procNetwork, ok := proc.(types.NetworkCounters) - if !ok { - t.Fatalf("error, cannot cast to types.NetworkCounters") - } - stats, err := procNetwork.NetworkCounters() - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, stats.SNMP.ICMP, "ICMP") - assert.NotEmpty(t, stats.SNMP.IP, "IP") - assert.NotEmpty(t, stats.SNMP.TCP, "TCP") - assert.NotEmpty(t, stats.SNMP.UDP, "UDP") -} diff --git a/providers/freebsd/util.go b/providers/freebsd/util.go new file mode 100644 index 00000000..317c693d --- /dev/null +++ b/providers/freebsd/util.go @@ -0,0 +1,111 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package freebsd + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io/ioutil" + "strconv" +) + +func parseKeyValue(content []byte, separator string, callback func(key, value []byte) error) error { + sc := bufio.NewScanner(bytes.NewReader(content)) + for sc.Scan() { + parts := bytes.SplitN(sc.Bytes(), []byte(separator), 2) + if len(parts) != 2 { + continue + } + + if err := callback(parts[0], bytes.TrimSpace(parts[1])); err != nil { + return err + } + } + + return sc.Err() +} + +func findValue(filename, separator, key string) (string, error) { + content, err := ioutil.ReadFile(filename) + if err != nil { + return "", err + } + + var line []byte + sc := bufio.NewScanner(bytes.NewReader(content)) + for sc.Scan() { + if bytes.HasPrefix(sc.Bytes(), []byte(key)) { + line = sc.Bytes() + break + } + } + if len(line) == 0 { + return "", fmt.Errorf("%v not found", key) + } + + parts := bytes.SplitN(line, []byte(separator), 2) + if len(parts) != 2 { + return "", fmt.Errorf("unexpected line format for '%v'", string(line)) + } + + return string(bytes.TrimSpace(parts[1])), nil +} + +func decodeBitMap(s string, lookupName func(int) string) ([]string, error) { + mask, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return nil, err + } + + var names []string + for i := 0; i < 64; i++ { + bit := mask & (1 << uint(i)) + if bit > 0 { + names = append(names, lookupName(i)) + } + } + + return names, nil +} + +func parseBytesOrNumber(data []byte) (uint64, error) { + parts := bytes.Fields(data) + + if len(parts) == 0 { + return 0, errors.New("empty value") + } + + num, err := strconv.ParseUint(string(parts[0]), 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse value: %w", err) + } + + var multiplier uint64 = 1 + if len(parts) >= 2 { + switch string(parts[1]) { + case "kB": + multiplier = 1024 + default: + return 0, fmt.Errorf("unhandled unit %v", string(parts[1])) + } + } + + return num * multiplier, nil +} diff --git a/providers/freebsd/vmstat.go b/providers/freebsd/vmstat.go new file mode 100644 index 00000000..f077267d --- /dev/null +++ b/providers/freebsd/vmstat.go @@ -0,0 +1,69 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package freebsd + +import ( + "fmt" + "reflect" + + "github.com/elastic/go-sysinfo/types" +) + +// vmstatTagToFieldIndex contains a mapping of json struct tags to struct field indices. +var vmstatTagToFieldIndex = make(map[string]int) + +func init() { + var vmstat types.VMStatInfo + val := reflect.ValueOf(vmstat) + typ := reflect.TypeOf(vmstat) + + for i := 0; i < val.NumField(); i++ { + field := typ.Field(i) + if tag := field.Tag.Get("json"); tag != "" { + vmstatTagToFieldIndex[tag] = i + } + } +} + +// parseVMStat parses the contents of /proc/vmstat. +func parseVMStat(content []byte) (*types.VMStatInfo, error) { + var vmStat types.VMStatInfo + refValues := reflect.ValueOf(&vmStat).Elem() + + err := parseKeyValue(content, " ", func(key, value []byte) error { + // turn our []byte value into an int + val, err := parseBytesOrNumber(value) + if err != nil { + return fmt.Errorf("failed to parse %v value of %v: %w", string(key), string(value), err) + } + + idx, ok := vmstatTagToFieldIndex[string(key)] + if !ok { + return nil + } + + sval := refValues.Field(idx) + + if sval.CanSet() { + sval.SetUint(val) + } + return nil + }) + + return &vmStat, err +} diff --git a/providers/freebsd/vmstat_test.go b/providers/freebsd/vmstat_test.go new file mode 100644 index 00000000..8351d4fb --- /dev/null +++ b/providers/freebsd/vmstat_test.go @@ -0,0 +1,171 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package freebsd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var rawInput = ` +nr_free_pages 50545 +nr_zone_inactive_anon 66 +nr_zone_active_anon 26799 +nr_zone_inactive_file 31849 +nr_zone_active_file 94164 +nr_zone_unevictable 0 +nr_zone_write_pending 7 +nr_mlock 0 +nr_page_table_pages 1225 +nr_kernel_stack 2496 +nr_bounce 0 +nr_zspages 0 +nr_free_cma 0 +numa_hit 44470329 +numa_miss 0 +numa_foreign 0 +numa_interleave 16296 +numa_local 44470329 +numa_other 0 +nr_inactive_anon 66 +nr_active_anon 26799 +nr_inactive_file 31849 +nr_active_file 94164 +nr_unevictable 0 +nr_slab_reclaimable 31763 +nr_slab_unreclaimable 10329 +nr_isolated_anon 0 +nr_isolated_file 0 +workingset_refault 302914 +workingset_activate 108959 +workingset_nodereclaim 6422 +nr_anon_pages 26218 +nr_mapped 8641 +nr_file_pages 126182 +nr_dirty 7 +nr_writeback 0 +nr_writeback_temp 0 +nr_shmem 169 +nr_shmem_hugepages 0 +nr_shmem_pmdmapped 0 +nr_anon_transparent_hugepages 0 +nr_unstable 0 +nr_vmscan_write 35 +nr_vmscan_immediate_reclaim 9832 +nr_dirtied 7188920 +nr_written 6479005 +nr_dirty_threshold 31736 +nr_dirty_background_threshold 15848 +pgpgin 17010697 +pgpgout 27734292 +pswpin 0 +pswpout 0 +pgalloc_dma 241378 +pgalloc_dma32 45788683 +pgalloc_normal 0 +pgalloc_movable 0 +allocstall_dma 0 +allocstall_dma32 0 +allocstall_normal 5 +allocstall_movable 8 +pgskip_dma 0 +pgskip_dma32 0 +pgskip_normal 0 +pgskip_movable 0 +pgfree 46085578 +pgactivate 2475069 +pgdeactivate 636658 +pglazyfree 9426 +pgfault 46777498 +pgmajfault 19204 +pglazyfreed 0 +pgrefill 707817 +pgsteal_kswapd 3798890 +pgsteal_direct 1466 +pgscan_kswapd 3868525 +pgscan_direct 1483 +pgscan_direct_throttle 0 +zone_reclaim_failed 0 +pginodesteal 1710 +slabs_scanned 8348560 +kswapd_inodesteal 3142001 +kswapd_low_wmark_hit_quickly 541 +kswapd_high_wmark_hit_quickly 332 +pageoutrun 1492 +pgrotated 29725 +drop_pagecache 0 +drop_slab 0 +oom_kill 0 +numa_pte_updates 0 +numa_huge_pte_updates 0 +numa_hint_faults 0 +numa_hint_faults_local 0 +numa_pages_migrated 0 +pgmigrate_success 4539 +pgmigrate_fail 156 +compact_migrate_scanned 9331 +compact_free_scanned 136266 +compact_isolated 9407 +compact_stall 2 +compact_fail 0 +compact_success 2 +compact_daemon_wake 21 +compact_daemon_migrate_scanned 8311 +compact_daemon_free_scanned 107086 +htlb_buddy_alloc_success 0 +htlb_buddy_alloc_fail 0 +unevictable_pgs_culled 19 +unevictable_pgs_scanned 0 +unevictable_pgs_rescued 304 +unevictable_pgs_mlocked 304 +unevictable_pgs_munlocked 304 +unevictable_pgs_cleared 0 +unevictable_pgs_stranded 0 +thp_fault_alloc 2 +thp_fault_fallback 0 +thp_collapse_alloc 2 +thp_collapse_alloc_failed 0 +thp_file_alloc 0 +thp_file_mapped 0 +thp_split_page 0 +thp_split_page_failed 0 +thp_deferred_split_page 4 +thp_split_pmd 1 +thp_split_pud 0 +thp_zero_page_alloc 0 +thp_zero_page_alloc_failed 0 +thp_swpout 0 +thp_swpout_fallback 0 +balloon_inflate 0 +balloon_deflate 0 +balloon_migrate 0 +swap_ra 0 +swap_ra_hit 0 +` + +func TestVmStatParse(t *testing.T) { + data, err := parseVMStat([]byte(rawInput)) + if err != nil { + t.Fatal(err) + } + // Check a few values + assert.Equal(t, uint64(8348560), data.SlabsScanned) + assert.Equal(t, uint64(0), data.SwapRa) + assert.Equal(t, uint64(108959), data.WorkingsetActivate) +} diff --git a/system_test.go b/system_test.go index 10260596..6668866d 100644 --- a/system_test.go +++ b/system_test.go @@ -71,6 +71,7 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ Environment: true, OpenHandleEnumerator: false, OpenHandleCounter: false, + }, "freebsd": &ProcessFeatures{ ProcessInfo: true, Environment: true, From cf4130f676a5230d7a3175b623b2e53d479ca46f Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:18:52 -0400 Subject: [PATCH 09/61] gofumpt the code [git-generate] gofumpt -w --extra $(find . -name '*.go') --- providers/freebsd/defs_freebsd.go | 1 + providers/freebsd/host_freebsd.go | 2 +- providers/freebsd/memory_freebsd.go | 20 ++++++++++++-------- providers/freebsd/os_freebsd.go | 8 +++++--- providers/freebsd/process_freebsd.go | 18 +++++++----------- providers/freebsd/procnet.go | 6 +++--- providers/freebsd/syscall_freebsd.go | 1 + providers/windows/arch_windows.go | 2 +- providers/windows/device_windows.go | 4 ++-- system.go | 2 +- system_test.go | 2 +- 11 files changed, 35 insertions(+), 31 deletions(-) diff --git a/providers/freebsd/defs_freebsd.go b/providers/freebsd/defs_freebsd.go index 553cd371..a08389d5 100644 --- a/providers/freebsd/defs_freebsd.go +++ b/providers/freebsd/defs_freebsd.go @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +//go:build ignore // +build ignore package freebsd diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go index 2f3e87bf..3f6546d1 100644 --- a/providers/freebsd/host_freebsd.go +++ b/providers/freebsd/host_freebsd.go @@ -58,7 +58,7 @@ func (s freebsdSystem) Host() (types.Host, error) { type host struct { procFS procFS - info types.HostInfo + info types.HostInfo } func (h *host) Info() types.HostInfo { diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd.go index e824db7d..7834611f 100644 --- a/providers/freebsd/memory_freebsd.go +++ b/providers/freebsd/memory_freebsd.go @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo // +build freebsd,cgo package freebsd @@ -30,18 +31,21 @@ package freebsd import "C" import ( - "github.com/pkg/errors" "syscall" "unsafe" + + "github.com/pkg/errors" ) -const hwPhysmemMIB = "hw.physmem" -const hwPagesizeMIB = "hw.pagesize" -const vmVmtotalMIB = "vm.vmtotal" -const vmSwapmaxpagesMIB = "vm.swap_maxpages" -const vfsNumfreebuffersMIB = "vfs.numfreebuffers" -const devNull = "/dev/null" -const kvmOpen = "kvm_open" +const ( + hwPhysmemMIB = "hw.physmem" + hwPagesizeMIB = "hw.pagesize" + vmVmtotalMIB = "vm.vmtotal" + vmSwapmaxpagesMIB = "vm.swap_maxpages" + vfsNumfreebuffersMIB = "vfs.numfreebuffers" + devNull = "/dev/null" + kvmOpen = "kvm_open" +) func PageSize() (uint32, error) { var pageSize uint32 diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go index 8b96d7d2..4522b61f 100644 --- a/providers/freebsd/os_freebsd.go +++ b/providers/freebsd/os_freebsd.go @@ -25,9 +25,11 @@ import ( "github.com/elastic/go-sysinfo/types" ) -const ostypeMIB = "kern.ostype" -const osreleaseMIB = "kern.osrelease" -const osrevisionMIB = "kern.osrevision" +const ( + ostypeMIB = "kern.ostype" + osreleaseMIB = "kern.osrelease" + osrevisionMIB = "kern.osrevision" +) func OperatingSystem() (*types.OSInfo, error) { return getOSInfo("") diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index 117d56ce..f54a9248 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -64,8 +64,8 @@ package freebsd import "C" import ( - "os" "io/ioutil" + "os" "strconv" "strings" "syscall" @@ -76,7 +76,7 @@ import ( "github.com/elastic/go-sysinfo/types" ) -func getProcInfo(op int, arg int) ([]process, error) { +func getProcInfo(op, arg int) ([]process, error) { procstat, err := C.procstat_open_sysctl() if procstat == nil { @@ -218,7 +218,7 @@ func getProcCWD(p *process) (string, error) { type process struct { pid int - fs procFS + fs procFS kinfo C.struct_kinfo_proc } @@ -228,7 +228,6 @@ func timevalToDuration(tm C.struct_timeval) time.Duration { func (p *process) CPUTime() (types.CPUTimes, error) { procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) - if err != nil { return types.CPUTimes{}, err } @@ -242,7 +241,6 @@ func (p *process) CPUTime() (types.CPUTimes, error) { func (p *process) Info() (types.ProcessInfo, error) { procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) - if err != nil { return types.ProcessInfo{}, err } @@ -273,7 +271,6 @@ func (p *process) Info() (types.ProcessInfo, error) { func (p *process) Memory() (types.MemoryInfo, error) { procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) - if err != nil { return types.MemoryInfo{}, err } @@ -287,7 +284,6 @@ func (p *process) Memory() (types.MemoryInfo, error) { func (p *process) User() (types.UserInfo, error) { procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) - if err != nil { return types.UserInfo{}, err } @@ -304,7 +300,6 @@ func (p *process) User() (types.UserInfo, error) { }, nil } - // NetworkStats reports network stats for an individual PID. func (p *process) NetworkCounters() (*types.NetworkCountersInfo, error) { snmpRaw, err := ioutil.ReadFile(p.path("net/snmp")) @@ -395,8 +390,10 @@ func (s freebsdSystem) Self() (types.Process, error) { return s.Process(os.Getpid()) } -const kernCptimeMIB = "kern.cp_time" -const kernClockrateMIB = "kern.clockrate" +const ( + kernCptimeMIB = "kern.cp_time" + kernClockrateMIB = "kern.clockrate" +) func Cptime() (map[string]uint64, error) { var clock clockInfo @@ -417,7 +414,6 @@ func Cptime() (map[string]uint64, error) { for index, time := range times { i, err := strconv.ParseUint(time, 10, 64) - if err != nil { return cpMap, errors.Wrap(err, "error parsing kern.cp_time") } diff --git a/providers/freebsd/procnet.go b/providers/freebsd/procnet.go index c0b578db..a676fb61 100644 --- a/providers/freebsd/procnet.go +++ b/providers/freebsd/procnet.go @@ -18,11 +18,11 @@ package freebsd import ( - "strings" - "fmt" "errors" - "strconv" + "fmt" "reflect" + "strconv" + "strings" "github.com/elastic/go-sysinfo/types" ) diff --git a/providers/freebsd/syscall_freebsd.go b/providers/freebsd/syscall_freebsd.go index 5a6ddb1f..061ce276 100644 --- a/providers/freebsd/syscall_freebsd.go +++ b/providers/freebsd/syscall_freebsd.go @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo // +build freebsd,cgo package freebsd diff --git a/providers/windows/arch_windows.go b/providers/windows/arch_windows.go index 6a889a0f..81afb81c 100644 --- a/providers/windows/arch_windows.go +++ b/providers/windows/arch_windows.go @@ -44,7 +44,7 @@ func Architecture() (string, error) { func NativeArchitecture() (string, error) { var processMachine, nativeMachine uint16 // the pseudo handle doesn't need to be closed - var currentProcessHandle = windows.CurrentProcess() + currentProcessHandle := windows.CurrentProcess() // IsWow64Process2 was introduced in version 1709 (build 16299 acording to the tables) // https://learn.microsoft.com/en-us/windows/release-health/release-information diff --git a/providers/windows/device_windows.go b/providers/windows/device_windows.go index 372f125f..37fb81fb 100644 --- a/providers/windows/device_windows.go +++ b/providers/windows/device_windows.go @@ -153,7 +153,7 @@ func (winapiDeviceProvider) GetLogicalDrives() (uint32, error) { return windows.GetLogicalDrives() } -func (winapiDeviceProvider) QueryDosDevice(name *uint16, buf *uint16, length uint32) (uint32, error) { +func (winapiDeviceProvider) QueryDosDevice(name, buf *uint16, length uint32) (uint32, error) { return windows.QueryDosDevice(name, buf, length) } @@ -168,7 +168,7 @@ func ptrOffset(ptr *uint16, off uint32) *uint16 { return (*uint16)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + uintptr(off*2))) } -func (m testingDeviceProvider) QueryDosDevice(nameW *uint16, buf *uint16, length uint32) (uint32, error) { +func (m testingDeviceProvider) QueryDosDevice(nameW, buf *uint16, length uint32) (uint32, error) { drive := byte(*nameW) if byte(*ptrOffset(nameW, 1)) != ':' { return 0, errors.New("not a drive") diff --git a/system.go b/system.go index b025e44d..b2b2825c 100644 --- a/system.go +++ b/system.go @@ -26,9 +26,9 @@ import ( // Register host and process providers. _ "github.com/elastic/go-sysinfo/providers/aix" _ "github.com/elastic/go-sysinfo/providers/darwin" + _ "github.com/elastic/go-sysinfo/providers/freebsd" _ "github.com/elastic/go-sysinfo/providers/linux" _ "github.com/elastic/go-sysinfo/providers/windows" - _ "github.com/elastic/go-sysinfo/providers/freebsd" ) // Go returns information about the Go runtime. diff --git a/system_test.go b/system_test.go index 23316de2..094ef33f 100644 --- a/system_test.go +++ b/system_test.go @@ -73,7 +73,7 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ OpenHandleEnumerator: false, OpenHandleCounter: false, }, - "freebsd": &ProcessFeatures{ + "freebsd": { ProcessInfo: true, Environment: true, OpenHandleEnumerator: true, From 68c3cbebac95f8bac0256cdf4a98a7201940c5c6 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:21:01 -0400 Subject: [PATCH 10/61] Add changelog file --- .changelog/124.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/124.txt diff --git a/.changelog/124.txt b/.changelog/124.txt new file mode 100644 index 00000000..ffba75e1 --- /dev/null +++ b/.changelog/124.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +freebsd: Implement a new provider for FreeBSD that requires CGO. +``` From 3cba1213496aabc29f354db5dae7faf31df78645 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:36:23 -0400 Subject: [PATCH 11/61] go.mod - tidy up --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f4260bdb..70149c2d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/docker/docker v24.0.7+incompatible github.com/elastic/go-windows v1.0.0 github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 + github.com/pkg/errors v0.9.1 github.com/prometheus/procfs v0.8.0 github.com/stretchr/testify v1.7.0 golang.org/x/sys v0.13.0 @@ -23,7 +24,6 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.17.0 // indirect From 47ee9c3fc9376a3d4e387248fbd878b8c0df9ad6 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:37:49 -0400 Subject: [PATCH 12/61] implement type.Host by adding FQDN() and FQDNWithContext() providers/freebsd/host_freebsd.go:56:9: cannot use newHost() (value of type *host) as types.Host value in return statement: *host does not implement types.Host (missing method FQDN) --- providers/freebsd/host_freebsd.go | 9 +++++++++ providers/shared/fqdn.go | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go index 3f6546d1..f97df21b 100644 --- a/providers/freebsd/host_freebsd.go +++ b/providers/freebsd/host_freebsd.go @@ -23,6 +23,7 @@ package freebsd import "C" import ( + "context" "os" "path/filepath" "time" @@ -80,6 +81,14 @@ func (h *host) Memory() (*types.HostMemoryInfo, error) { return m, r.Err() } +func (h *host) FQDNWithContext(ctx context.Context) (string, error) { + return shared.FQDNWithContext(ctx) +} + +func (h *host) FQDN() (string, error) { + return h.FQDNWithContext(context.Background()) +} + func newHost() (*host, error) { h := &host{} r := &reader{} diff --git a/providers/shared/fqdn.go b/providers/shared/fqdn.go index 48043bf5..1d1efb07 100644 --- a/providers/shared/fqdn.go +++ b/providers/shared/fqdn.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -//go:build linux || darwin || aix +//go:build linux || darwin || aix || freebsd package shared From 1b786edf00ac1b762b3ea15fe251424f962621d4 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:39:23 -0400 Subject: [PATCH 13/61] Fix changelog PR number --- .changelog/{124.txt => 126.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{124.txt => 126.txt} (100%) diff --git a/.changelog/124.txt b/.changelog/126.txt similarity index 100% rename from .changelog/124.txt rename to .changelog/126.txt From 984d22f4bf39b9e03e027db2e236d748c877dce5 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:41:42 -0400 Subject: [PATCH 14/61] add license headers with 'make update' --- Makefile | 2 +- providers/freebsd/ztypes_freebsd.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9d4e6b17..4cc3108f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.phony: update +.PHONY: update update: fmt lic imports .PHONY: lic diff --git a/providers/freebsd/ztypes_freebsd.go b/providers/freebsd/ztypes_freebsd.go index 230b857e..221b6739 100644 --- a/providers/freebsd/ztypes_freebsd.go +++ b/providers/freebsd/ztypes_freebsd.go @@ -1,3 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + // Code generated by cmd/cgo -godefs; and then patched up to fix // an alignment issue // cgo -godefs defs_freebsd.go From 98a3c4f27522636bbd800fac673de09924e03848 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 14 Mar 2024 11:46:22 -0400 Subject: [PATCH 15/61] TestOperatingSystem/freebsd14 - make test pass --- providers/freebsd/os_freebsd_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/providers/freebsd/os_freebsd_test.go b/providers/freebsd/os_freebsd_test.go index 99e8f771..005896d6 100644 --- a/providers/freebsd/os_freebsd_test.go +++ b/providers/freebsd/os_freebsd_test.go @@ -26,7 +26,7 @@ import ( ) func TestOperatingSystem(t *testing.T) { - t.Run("freebsd13", func(t *testing.T) { + t.Run("freebsd14", func(t *testing.T) { os, err := getOSInfo("") if err != nil { t.Fatal(err) @@ -36,9 +36,9 @@ func TestOperatingSystem(t *testing.T) { Family: "freebsd", Platform: "freebsd", Name: "FreeBSD", - Version: "13.1-RELEASE", - Major: 13, - Minor: 1, + Version: "14.0-RELEASE", + Major: 14, + Minor: 0, Patch: 0, }, *os) t.Logf("%#v", os) From 608fba5407c02c64f3de62763906ae3b17da3e45 Mon Sep 17 00:00:00 2001 From: redanthrax Date: Tue, 9 Apr 2024 13:48:05 -0700 Subject: [PATCH 16/61] moved files --- providers/freebsd/{arch.go => arch_freebsd.go} | 0 providers/freebsd/{kernel.go => kernel_freebsd.go} | 0 providers/freebsd/{machineid.go => machineid_freebsd.go} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename providers/freebsd/{arch.go => arch_freebsd.go} (100%) rename providers/freebsd/{kernel.go => kernel_freebsd.go} (100%) rename providers/freebsd/{machineid.go => machineid_freebsd.go} (100%) diff --git a/providers/freebsd/arch.go b/providers/freebsd/arch_freebsd.go similarity index 100% rename from providers/freebsd/arch.go rename to providers/freebsd/arch_freebsd.go diff --git a/providers/freebsd/kernel.go b/providers/freebsd/kernel_freebsd.go similarity index 100% rename from providers/freebsd/kernel.go rename to providers/freebsd/kernel_freebsd.go diff --git a/providers/freebsd/machineid.go b/providers/freebsd/machineid_freebsd.go similarity index 100% rename from providers/freebsd/machineid.go rename to providers/freebsd/machineid_freebsd.go From 056b9f1d5c5fefd44f80295cb670a0d50697d412 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 11:18:07 -0400 Subject: [PATCH 17/61] Use stdlib errors Replace usages of github.com/pkg/errors and github.com/joeshaw/multierror. --- go.mod | 2 +- providers/freebsd/arch_freebsd.go | 5 ++--- providers/freebsd/boottime_freebsd.go | 5 ++--- providers/freebsd/host_freebsd.go | 7 +++---- providers/freebsd/kernel_freebsd.go | 5 ++--- providers/freebsd/machineid_freebsd.go | 5 ++--- providers/freebsd/memory_freebsd.go | 17 ++++++++--------- providers/freebsd/process_freebsd.go | 21 +++++++++++---------- 8 files changed, 31 insertions(+), 36 deletions(-) diff --git a/go.mod b/go.mod index 4ddc6255..516813ae 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/elastic/go-windows v1.0.0 + github.com/pkg/errors v0.9.1 github.com/prometheus/procfs v0.8.0 github.com/stretchr/testify v1.7.0 golang.org/x/sys v0.13.0 @@ -12,7 +13,6 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.0 // indirect ) diff --git a/providers/freebsd/arch_freebsd.go b/providers/freebsd/arch_freebsd.go index 844c331a..d51f9313 100644 --- a/providers/freebsd/arch_freebsd.go +++ b/providers/freebsd/arch_freebsd.go @@ -18,9 +18,8 @@ package freebsd import ( + "fmt" "syscall" - - "github.com/pkg/errors" ) const hardwareMIB = "hw.machine" @@ -28,7 +27,7 @@ const hardwareMIB = "hw.machine" func Architecture() (string, error) { arch, err := syscall.Sysctl(hardwareMIB) if err != nil { - return "", errors.Wrap(err, "failed to get architecture") + return "", fmt.Errorf("failed to get architecture: %w", err) } return arch, nil diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd.go index f5364e27..6fe50d81 100644 --- a/providers/freebsd/boottime_freebsd.go +++ b/providers/freebsd/boottime_freebsd.go @@ -18,10 +18,9 @@ package freebsd import ( + "fmt" "syscall" "time" - - "github.com/pkg/errors" ) const kernBoottimeMIB = "kern.boottime" @@ -29,7 +28,7 @@ const kernBoottimeMIB = "kern.boottime" func BootTime() (time.Time, error) { var tv syscall.Timeval if err := sysctlByName(kernBoottimeMIB, &tv); err != nil { - return time.Time{}, errors.Wrap(err, "failed to get host uptime") + return time.Time{}, fmt.Errorf("failed to get host uptime: %w", err) } bootTime := time.Unix(int64(tv.Sec), int64(tv.Usec)*int64(time.Microsecond)) diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go index f97df21b..df3b1e7f 100644 --- a/providers/freebsd/host_freebsd.go +++ b/providers/freebsd/host_freebsd.go @@ -24,12 +24,11 @@ import "C" import ( "context" + "errors" "os" "path/filepath" "time" - "github.com/joeshaw/multierror" - "github.com/pkg/errors" "github.com/prometheus/procfs" "github.com/elastic/go-sysinfo/internal/registry" @@ -109,7 +108,7 @@ type reader struct { func (r *reader) addErr(err error) bool { if err != nil { - if errors.Cause(err) != types.ErrNotImplemented { + if !errors.Is(err, types.ErrNotImplemented) { r.errs = append(r.errs, err) } return true @@ -119,7 +118,7 @@ func (r *reader) addErr(err error) bool { func (r *reader) Err() error { if len(r.errs) > 0 { - return &multierror.MultiError{Errors: r.errs} + return errors.Join(r.errs...) } return nil } diff --git a/providers/freebsd/kernel_freebsd.go b/providers/freebsd/kernel_freebsd.go index 3ade2725..cbd3dd47 100644 --- a/providers/freebsd/kernel_freebsd.go +++ b/providers/freebsd/kernel_freebsd.go @@ -18,9 +18,8 @@ package freebsd import ( + "fmt" "syscall" - - "github.com/pkg/errors" ) const kernelReleaseMIB = "kern.osrelease" @@ -28,7 +27,7 @@ const kernelReleaseMIB = "kern.osrelease" func KernelVersion() (string, error) { version, err := syscall.Sysctl(kernelReleaseMIB) if err != nil { - return "", errors.Wrap(err, "failed to get kernel version") + return "", fmt.Errorf("failed to get kernel version: %w", err) } return version, nil diff --git a/providers/freebsd/machineid_freebsd.go b/providers/freebsd/machineid_freebsd.go index 2857ee1e..c1e42a4c 100644 --- a/providers/freebsd/machineid_freebsd.go +++ b/providers/freebsd/machineid_freebsd.go @@ -18,9 +18,8 @@ package freebsd import ( + "fmt" "syscall" - - "github.com/pkg/errors" ) const kernelHostUUIDMIB = "kern.hostuuid" @@ -28,7 +27,7 @@ const kernelHostUUIDMIB = "kern.hostuuid" func MachineID() (string, error) { uuid, err := syscall.Sysctl(kernelHostUUIDMIB) if err != nil { - return "", errors.Wrap(err, "failed to get machine id") + return "", fmt.Errorf("failed to get machine id: %w", err) } return uuid, nil diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd.go index 7834611f..c466ac71 100644 --- a/providers/freebsd/memory_freebsd.go +++ b/providers/freebsd/memory_freebsd.go @@ -31,10 +31,9 @@ package freebsd import "C" import ( + "fmt" "syscall" "unsafe" - - "github.com/pkg/errors" ) const ( @@ -50,7 +49,7 @@ const ( func PageSize() (uint32, error) { var pageSize uint32 if err := sysctlByName(hwPagesizeMIB, &pageSize); err != nil { - return 0, errors.Wrap(err, "failed to get hw.pagesize") + return 0, fmt.Errorf("failed to get hw.pagesize: %w", err) } return pageSize, nil @@ -59,7 +58,7 @@ func PageSize() (uint32, error) { func SwapMaxPages() (uint32, error) { var maxPages uint32 if err := sysctlByName(hwPhysmemMIB, &maxPages); err != nil { - return 0, errors.Wrap(err, "failed to get vm.swap_maxpages") + return 0, fmt.Errorf("failed to get vm.swap_maxpages: %w", err) } return maxPages, nil @@ -68,7 +67,7 @@ func SwapMaxPages() (uint32, error) { func TotalMemory() (uint64, error) { var size uint64 if err := sysctlByName(hwPhysmemMIB, &size); err != nil { - return 0, errors.Wrap(err, "failed to get hw.physmem") + return 0, fmt.Errorf("failed to get hw.physmem: %w", err) } return size, nil @@ -77,7 +76,7 @@ func TotalMemory() (uint64, error) { func VmTotal() (vmTotal, error) { var vm vmTotal if err := sysctlByName(vmVmtotalMIB, &vm); err != nil { - return vmTotal{}, errors.Wrap(err, "failed to get vm.vmtotal") + return vmTotal{}, fmt.Errorf("failed to get vm.vmtotal: %w", err) } return vm, nil @@ -86,7 +85,7 @@ func VmTotal() (vmTotal, error) { func NumFreeBuffers() (uint32, error) { var numfreebuffers uint32 if err := sysctlByName(vfsNumfreebuffersMIB, &numfreebuffers); err != nil { - return 0, errors.Wrap(err, "failed to get vfs.numfreebuffers") + return 0, fmt.Errorf("failed to get vfs.numfreebuffers: %w", err) } return numfreebuffers, nil @@ -101,14 +100,14 @@ func KvmGetSwapInfo() (kvmSwap, error) { defer C.free(unsafe.Pointer(kvmOpenC)) if kdC, err := C.kvm_open(nil, devNullC, nil, syscall.O_RDONLY, kvmOpenC); kdC == nil { - return kvmSwap{}, errors.Wrap(err, "failed to open kvm") + return kvmSwap{}, fmt.Errorf("failed to open kvm: %w", err) } defer C.kvm_close((*C.struct___kvm)(unsafe.Pointer(kdC))) var swap kvmSwap if n, err := C.kvm_getswapinfo((*C.struct___kvm)(unsafe.Pointer(kdC)), (*C.struct_kvm_swap)(unsafe.Pointer(&swap)), 1, 0); n != 0 { - return kvmSwap{}, errors.Wrap(err, "failed to get kvm_getswapinfo") + return kvmSwap{}, fmt.Errorf("failed to get kvm_getswapinfo: %w", err) } return swap, nil diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index f54a9248..91770a17 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -64,6 +64,7 @@ package freebsd import "C" import ( + "fmt" "io/ioutil" "os" "strconv" @@ -80,14 +81,14 @@ func getProcInfo(op, arg int) ([]process, error) { procstat, err := C.procstat_open_sysctl() if procstat == nil { - return nil, errors.Wrap(err, "failed to open procstat sysctl") + return nil, fmt.Errorf("failed to open procstat sysctl: %w", err) } defer C.procstat_close(procstat) var count C.uint = 0 kprocs, err := C.procstat_getprocs(procstat, C.int(op), C.int(arg), &count) if kprocs == nil { - return nil, errors.Wrap(err, "getprocs failed") + return nil, fmt.Errorf("getprocs failed: %w", err) } defer C.procstat_freeprocs(procstat, kprocs) @@ -138,7 +139,7 @@ func getProcEnv(p *process) (map[string]string, error) { procstat, err := C.procstat_open_sysctl() if procstat == nil { - return nil, errors.Wrap(err, "failed to open procstat sysctl") + return nil, fmt.Errorf("failed to open procstat sysctl: %w", err) } defer C.procstat_close(procstat) @@ -152,7 +153,7 @@ func getProcArgs(p *process) ([]string, error) { procstat, err := C.procstat_open_sysctl() if procstat == nil { - return nil, errors.Wrap(err, "failed to open procstat sysctl") + return nil, fmt.Errorf("failed to open procstat sysctl: %w", err) } defer C.procstat_close(procstat) @@ -166,7 +167,7 @@ func getProcPathname(p *process) (string, error) { procstat, err := C.procstat_open_sysctl() if procstat == nil { - return "", errors.Wrap(err, "failed to open procstat sysctl") + return "", fmt.Errorf("failed to open procstat sysctl: %w", err) } defer C.procstat_close(procstat) @@ -195,13 +196,13 @@ func getProcCWD(p *process) (string, error) { procstat, err := C.procstat_open_sysctl() if procstat == nil { - return "", errors.Wrap(err, "failed to open procstat sysctl") + return "", fmt.Errorf("failed to open procstat sysctl: %w", err) } defer C.procstat_close(procstat) fs, err := C.procstat_getfiles(procstat, &p.kinfo, 0) if fs == nil { - return "", errors.Wrap(err, "failed to get files") + return "", fmt.Errorf("failed to get files: %w", err) } defer C.procstat_freefiles(procstat, fs) @@ -399,12 +400,12 @@ func Cptime() (map[string]uint64, error) { var clock clockInfo if err := sysctlByName(kernClockrateMIB, &clock); err != nil { - return make(map[string]uint64), errors.Wrap(err, "failed to get kern.clockrate") + return make(map[string]uint64), fmt.Errorf("failed to get kern.clockrate: %w", err) } cptime, err := syscall.Sysctl(kernCptimeMIB) if err != nil { - return make(map[string]uint64), errors.Wrap(err, "failed to get kern.cp_time") + return make(map[string]uint64), fmt.Errorf("failed to get kern.cp_time: %w", err) } cpMap := make(map[string]uint64) @@ -415,7 +416,7 @@ func Cptime() (map[string]uint64, error) { for index, time := range times { i, err := strconv.ParseUint(time, 10, 64) if err != nil { - return cpMap, errors.Wrap(err, "error parsing kern.cp_time") + return cpMap, fmt.Errorf("error parsing kern.cp_time: %w", err) } cpMap[names[index]] = i * uint64(clock.Tick) * 1000 From e7fcbe6d820d47093d821315bd384f7ecbbff40c Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 11:42:03 -0400 Subject: [PATCH 18/61] align go:build tags Make sure that all code depending on cgo is tagged appropriately. Replace "syscall" with golang.org/x/sys/unix as per the recommendation of the stdlib syscall package for new code. Add a cross-compile sanity check for freebsd/amd64. --- .ci/scripts/check-cross-compile.sh | 1 + providers/freebsd/arch_freebsd.go | 7 +++++-- providers/freebsd/boottime_freebsd.go | 7 +++++-- providers/freebsd/boottime_freebsd_test.go | 2 ++ providers/freebsd/doc.go | 2 +- providers/freebsd/host_freebsd.go | 2 ++ providers/freebsd/host_freebsd_test.go | 2 ++ providers/freebsd/kernel_freebsd.go | 7 +++++-- providers/freebsd/machineid_freebsd.go | 7 +++++-- providers/freebsd/memory_freebsd.go | 6 +++--- providers/freebsd/os_freebsd.go | 9 ++++++--- providers/freebsd/os_freebsd_test.go | 2 ++ providers/freebsd/process_freebsd.go | 8 +++++--- providers/freebsd/syscall_freebsd.go | 1 - 14 files changed, 44 insertions(+), 19 deletions(-) diff --git a/.ci/scripts/check-cross-compile.sh b/.ci/scripts/check-cross-compile.sh index 82de7c22..021e3faf 100755 --- a/.ci/scripts/check-cross-compile.sh +++ b/.ci/scripts/check-cross-compile.sh @@ -9,6 +9,7 @@ export CGO_ENABLED=0 GOOS=aix GOARCH=ppc64 go build ./... GOOS=darwin GOARCH=amd64 go build ./... GOOS=darwin GOARCH=arm64 go build ./... +GOOS=freebsd GOARCH=amd64 go build ./... GOOS=linux GOARCH=386 go build ./... GOOS=linux GOARCH=amd64 go build ./... GOOS=linux GOARCH=arm go build ./... diff --git a/providers/freebsd/arch_freebsd.go b/providers/freebsd/arch_freebsd.go index d51f9313..5f382d66 100644 --- a/providers/freebsd/arch_freebsd.go +++ b/providers/freebsd/arch_freebsd.go @@ -15,17 +15,20 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd + package freebsd import ( "fmt" - "syscall" + + "golang.org/x/sys/unix" ) const hardwareMIB = "hw.machine" func Architecture() (string, error) { - arch, err := syscall.Sysctl(hardwareMIB) + arch, err := unix.Sysctl(hardwareMIB) if err != nil { return "", fmt.Errorf("failed to get architecture: %w", err) } diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd.go index 6fe50d81..3092c1e8 100644 --- a/providers/freebsd/boottime_freebsd.go +++ b/providers/freebsd/boottime_freebsd.go @@ -15,18 +15,21 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo + package freebsd import ( "fmt" - "syscall" "time" + + "golang.org/x/sys/unix" ) const kernBoottimeMIB = "kern.boottime" func BootTime() (time.Time, error) { - var tv syscall.Timeval + var tv unix.Timeval if err := sysctlByName(kernBoottimeMIB, &tv); err != nil { return time.Time{}, fmt.Errorf("failed to get host uptime: %w", err) } diff --git a/providers/freebsd/boottime_freebsd_test.go b/providers/freebsd/boottime_freebsd_test.go index f5cf632c..46570f50 100644 --- a/providers/freebsd/boottime_freebsd_test.go +++ b/providers/freebsd/boottime_freebsd_test.go @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo + package freebsd import ( diff --git a/providers/freebsd/doc.go b/providers/freebsd/doc.go index 336d45aa..b8beca21 100644 --- a/providers/freebsd/doc.go +++ b/providers/freebsd/doc.go @@ -16,5 +16,5 @@ // under the License. // Package freebsd implements the HostProvider and ProcessProvider interfaces -// for providing information about FreeBSD +// for providing information about FreeBSD. package freebsd diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd.go index df3b1e7f..26a5e321 100644 --- a/providers/freebsd/host_freebsd.go +++ b/providers/freebsd/host_freebsd.go @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo + package freebsd // #cgo LDFLAGS: -lkvm diff --git a/providers/freebsd/host_freebsd_test.go b/providers/freebsd/host_freebsd_test.go index ff25fcff..7a1ea746 100644 --- a/providers/freebsd/host_freebsd_test.go +++ b/providers/freebsd/host_freebsd_test.go @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo + package freebsd import ( diff --git a/providers/freebsd/kernel_freebsd.go b/providers/freebsd/kernel_freebsd.go index cbd3dd47..f9aa0296 100644 --- a/providers/freebsd/kernel_freebsd.go +++ b/providers/freebsd/kernel_freebsd.go @@ -15,17 +15,20 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd + package freebsd import ( "fmt" - "syscall" + + "golang.org/x/sys/unix" ) const kernelReleaseMIB = "kern.osrelease" func KernelVersion() (string, error) { - version, err := syscall.Sysctl(kernelReleaseMIB) + version, err := unix.Sysctl(kernelReleaseMIB) if err != nil { return "", fmt.Errorf("failed to get kernel version: %w", err) } diff --git a/providers/freebsd/machineid_freebsd.go b/providers/freebsd/machineid_freebsd.go index c1e42a4c..45d8bad8 100644 --- a/providers/freebsd/machineid_freebsd.go +++ b/providers/freebsd/machineid_freebsd.go @@ -15,17 +15,20 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd + package freebsd import ( "fmt" - "syscall" + + "golang.org/x/sys/unix" ) const kernelHostUUIDMIB = "kern.hostuuid" func MachineID() (string, error) { - uuid, err := syscall.Sysctl(kernelHostUUIDMIB) + uuid, err := unix.Sysctl(kernelHostUUIDMIB) if err != nil { return "", fmt.Errorf("failed to get machine id: %w", err) } diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd.go index c466ac71..555587c1 100644 --- a/providers/freebsd/memory_freebsd.go +++ b/providers/freebsd/memory_freebsd.go @@ -16,7 +16,6 @@ // under the License. //go:build freebsd && cgo -// +build freebsd,cgo package freebsd @@ -32,8 +31,9 @@ import "C" import ( "fmt" - "syscall" "unsafe" + + "golang.org/x/sys/unix" ) const ( @@ -99,7 +99,7 @@ func KvmGetSwapInfo() (kvmSwap, error) { kvmOpenC := C.CString(kvmOpen) defer C.free(unsafe.Pointer(kvmOpenC)) - if kdC, err := C.kvm_open(nil, devNullC, nil, syscall.O_RDONLY, kvmOpenC); kdC == nil { + if kdC, err := C.kvm_open(nil, devNullC, nil, unix.O_RDONLY, kvmOpenC); kdC == nil { return kvmSwap{}, fmt.Errorf("failed to open kvm: %w", err) } diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go index 4522b61f..ad0c8cfc 100644 --- a/providers/freebsd/os_freebsd.go +++ b/providers/freebsd/os_freebsd.go @@ -15,12 +15,15 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd + package freebsd import ( "strconv" "strings" - "syscall" + + "golang.org/x/sys/unix" "github.com/elastic/go-sysinfo/types" ) @@ -41,13 +44,13 @@ func getOSInfo(baseDir string) (*types.OSInfo, error) { Platform: "freebsd", } - ostype, err := syscall.Sysctl(ostypeMIB) + ostype, err := unix.Sysctl(ostypeMIB) if err != nil { return info, err } info.Name = ostype - osrelease, err := syscall.Sysctl(osreleaseMIB) + osrelease, err := unix.Sysctl(osreleaseMIB) if err != nil { return info, err } diff --git a/providers/freebsd/os_freebsd_test.go b/providers/freebsd/os_freebsd_test.go index 005896d6..b3738d93 100644 --- a/providers/freebsd/os_freebsd_test.go +++ b/providers/freebsd/os_freebsd_test.go @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd + package freebsd import ( diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd.go index 91770a17..ff74eb4b 100644 --- a/providers/freebsd/process_freebsd.go +++ b/providers/freebsd/process_freebsd.go @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +//go:build freebsd && cgo + package freebsd // #cgo LDFLAGS: -lkvm -lprocstat @@ -64,15 +66,15 @@ package freebsd import "C" import ( + "errors" "fmt" "io/ioutil" "os" "strconv" "strings" - "syscall" "time" - "github.com/pkg/errors" + "golang.org/x/sys/unix" "github.com/elastic/go-sysinfo/types" ) @@ -403,7 +405,7 @@ func Cptime() (map[string]uint64, error) { return make(map[string]uint64), fmt.Errorf("failed to get kern.clockrate: %w", err) } - cptime, err := syscall.Sysctl(kernCptimeMIB) + cptime, err := unix.Sysctl(kernCptimeMIB) if err != nil { return make(map[string]uint64), fmt.Errorf("failed to get kern.cp_time: %w", err) } diff --git a/providers/freebsd/syscall_freebsd.go b/providers/freebsd/syscall_freebsd.go index 061ce276..8bd72a28 100644 --- a/providers/freebsd/syscall_freebsd.go +++ b/providers/freebsd/syscall_freebsd.go @@ -16,7 +16,6 @@ // under the License. //go:build freebsd && cgo -// +build freebsd,cgo package freebsd From 1ba0ba609685a28d7a28be4d4ef08354817db0ef Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 11:43:54 -0400 Subject: [PATCH 19/61] Rename files to include _cgo --- .../freebsd/{boottime_freebsd.go => boottime_freebsd_cgo.go} | 0 .../{boottime_freebsd_test.go => boottime_freebsd_cgo_test.go} | 0 providers/freebsd/{host_freebsd.go => host_freebsd_cgo.go} | 0 .../freebsd/{host_freebsd_test.go => host_freebsd_cgo_test.go} | 0 providers/freebsd/{memory_freebsd.go => memory_freebsd_cgo.go} | 0 providers/freebsd/{process_freebsd.go => process_freebsd_cgo.go} | 0 providers/freebsd/{syscall_freebsd.go => syscall_freebsd_cgo.go} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename providers/freebsd/{boottime_freebsd.go => boottime_freebsd_cgo.go} (100%) rename providers/freebsd/{boottime_freebsd_test.go => boottime_freebsd_cgo_test.go} (100%) rename providers/freebsd/{host_freebsd.go => host_freebsd_cgo.go} (100%) rename providers/freebsd/{host_freebsd_test.go => host_freebsd_cgo_test.go} (100%) rename providers/freebsd/{memory_freebsd.go => memory_freebsd_cgo.go} (100%) rename providers/freebsd/{process_freebsd.go => process_freebsd_cgo.go} (100%) rename providers/freebsd/{syscall_freebsd.go => syscall_freebsd_cgo.go} (100%) diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd_cgo.go similarity index 100% rename from providers/freebsd/boottime_freebsd.go rename to providers/freebsd/boottime_freebsd_cgo.go diff --git a/providers/freebsd/boottime_freebsd_test.go b/providers/freebsd/boottime_freebsd_cgo_test.go similarity index 100% rename from providers/freebsd/boottime_freebsd_test.go rename to providers/freebsd/boottime_freebsd_cgo_test.go diff --git a/providers/freebsd/host_freebsd.go b/providers/freebsd/host_freebsd_cgo.go similarity index 100% rename from providers/freebsd/host_freebsd.go rename to providers/freebsd/host_freebsd_cgo.go diff --git a/providers/freebsd/host_freebsd_test.go b/providers/freebsd/host_freebsd_cgo_test.go similarity index 100% rename from providers/freebsd/host_freebsd_test.go rename to providers/freebsd/host_freebsd_cgo_test.go diff --git a/providers/freebsd/memory_freebsd.go b/providers/freebsd/memory_freebsd_cgo.go similarity index 100% rename from providers/freebsd/memory_freebsd.go rename to providers/freebsd/memory_freebsd_cgo.go diff --git a/providers/freebsd/process_freebsd.go b/providers/freebsd/process_freebsd_cgo.go similarity index 100% rename from providers/freebsd/process_freebsd.go rename to providers/freebsd/process_freebsd_cgo.go diff --git a/providers/freebsd/syscall_freebsd.go b/providers/freebsd/syscall_freebsd_cgo.go similarity index 100% rename from providers/freebsd/syscall_freebsd.go rename to providers/freebsd/syscall_freebsd_cgo.go From 6870e0ac62cce33c178ff3be6d055967153a55fa Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 11:44:05 -0400 Subject: [PATCH 20/61] go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 516813ae..4ddc6255 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.21 require ( github.com/elastic/go-windows v1.0.0 - github.com/pkg/errors v0.9.1 github.com/prometheus/procfs v0.8.0 github.com/stretchr/testify v1.7.0 golang.org/x/sys v0.13.0 @@ -13,6 +12,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.0 // indirect ) From 00cb2d395a7f8f7ddb988ae90b65ca0b27ee1af1 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 12:20:26 -0400 Subject: [PATCH 21/61] use t.Log instead of fmt.Print --- system_test.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/system_test.go b/system_test.go index 094ef33f..fee220a7 100644 --- a/system_test.go +++ b/system_test.go @@ -20,7 +20,6 @@ package sysinfo import ( "encoding/json" "errors" - "fmt" "io/fs" "os" osUser "os/user" @@ -107,7 +106,7 @@ func TestProcessFeaturesMatrix(t *testing.T) { } func TestSelf(t *testing.T) { - fmt.Printf("Getting Self()...\n") + t.Log("Getting Self() process") process, err := Self() if err == types.ErrNotImplemented { t.Skip("process provider not implemented on", runtime.GOOS) @@ -124,8 +123,8 @@ func TestSelf(t *testing.T) { } } + t.Log("Getting process Info()") output := map[string]interface{}{} - fmt.Printf("Getting ProcessInfo...\n") info, err := process.Info() if err != nil { t.Fatal(err) @@ -159,12 +158,13 @@ func TestSelf(t *testing.T) { assert.Equal(t, exe, info.Exe) if user, err := process.User(); !errors.Is(err, types.ErrNotImplemented) { + t.Log("Getting process User()") + if err != nil { t.Fatal(err) } output["process.user"] = user - fmt.Printf("Getting UserInfo...\n") user, err := process.User() if err != nil { t.Fatal(err) @@ -184,8 +184,9 @@ func TestSelf(t *testing.T) { } } - fmt.Printf("Getting Environment...\n") if v, ok := process.(types.Environment); ok { + t.Log("Getting process Environment()") + actualEnv, err := v.Environment() if err != nil { t.Fatal(err) @@ -205,8 +206,9 @@ func TestSelf(t *testing.T) { assert.Equal(t, expectedEnv, keyEqualsValueList) } - fmt.Printf("Getting MemoryInfo...\n") if memInfo, err := process.Memory(); !errors.Is(err, types.ErrNotImplemented) { + t.Log("Getting process Memory()") + require.NoError(t, err) if runtime.GOOS != "windows" { // Virtual memory may be reported as @@ -217,7 +219,7 @@ func TestSelf(t *testing.T) { output["process.mem"] = memInfo } - fmt.Printf("Getting CPUTimes...\n") + t.Log("Getting process CPUTime()") for { cpuTimes, err := process.CPUTime() if errors.Is(err, types.ErrNotImplemented) { @@ -235,8 +237,9 @@ func TestSelf(t *testing.T) { // measurement. } - fmt.Printf("Getting OpenHandleEnumerator...\n") if v, ok := process.(types.OpenHandleEnumerator); ok { + t.Log("Getting process OpenHandles()") + fds, err := v.OpenHandles() if assert.NoError(t, err) { output["process.fd"] = fds @@ -244,6 +247,8 @@ func TestSelf(t *testing.T) { } if v, ok := process.(types.OpenHandleCounter); ok { + t.Log("Getting process OpenHandleCount()") + count, err := v.OpenHandleCount() if assert.NoError(t, err) { t.Log("open handles count:", count) @@ -251,6 +256,8 @@ func TestSelf(t *testing.T) { } if v, ok := process.(types.Seccomp); ok { + t.Log("Getting process Seccomp()") + seccompInfo, err := v.Seccomp() if assert.NoError(t, err) { assert.NotZero(t, seccompInfo) @@ -259,6 +266,8 @@ func TestSelf(t *testing.T) { } if v, ok := process.(types.Capabilities); ok { + t.Log("Getting process Capabilities()") + capInfo, err := v.Capabilities() if assert.NoError(t, err) { assert.NotZero(t, capInfo) From 174d0bb289daf4bc81cc8ad13991c06a695115ae Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 14:25:15 -0400 Subject: [PATCH 22/61] Fix system_test.go for macos without CGO --- internal/cgo/disabled.go | 23 ++++++++++++ internal/cgo/enabled.go | 23 ++++++++++++ system_test.go | 80 ++++++++++++++++++++-------------------- 3 files changed, 87 insertions(+), 39 deletions(-) create mode 100644 internal/cgo/disabled.go create mode 100644 internal/cgo/enabled.go diff --git a/internal/cgo/disabled.go b/internal/cgo/disabled.go new file mode 100644 index 00000000..b1a5879d --- /dev/null +++ b/internal/cgo/disabled.go @@ -0,0 +1,23 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build !cgo + +package cgo + +// Enabled is true if cgo was enabled at compile-time. +const Enabled = false diff --git a/internal/cgo/enabled.go b/internal/cgo/enabled.go new file mode 100644 index 00000000..036f1524 --- /dev/null +++ b/internal/cgo/enabled.go @@ -0,0 +1,23 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build cgo + +package cgo + +// Enabled is true if cgo was enabled at compile-time. +const Enabled = true diff --git a/system_test.go b/system_test.go index fee220a7..3c5349d0 100644 --- a/system_test.go +++ b/system_test.go @@ -34,11 +34,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/elastic/go-sysinfo/internal/cgo" "github.com/elastic/go-sysinfo/types" ) type ProcessFeatures struct { - ProcessInfo bool Environment bool OpenHandleEnumerator bool OpenHandleCounter bool @@ -48,13 +48,9 @@ type ProcessFeatures struct { var expectedProcessFeatures = map[string]*ProcessFeatures{ "darwin": { - ProcessInfo: true, - Environment: true, - OpenHandleEnumerator: false, - OpenHandleCounter: false, + Environment: true, }, "linux": { - ProcessInfo: true, Environment: true, OpenHandleEnumerator: true, OpenHandleCounter: true, @@ -62,44 +58,39 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ Capabilities: true, }, "windows": { - ProcessInfo: true, - OpenHandleEnumerator: false, - OpenHandleCounter: true, + OpenHandleCounter: true, }, "aix": { - ProcessInfo: true, - Environment: true, - OpenHandleEnumerator: false, - OpenHandleCounter: false, + Environment: true, }, "freebsd": { - ProcessInfo: true, Environment: true, OpenHandleEnumerator: true, OpenHandleCounter: true, }, } -func TestProcessFeaturesMatrix(t *testing.T) { - const GOOS = runtime.GOOS - var features ProcessFeatures +var startTime = time.Now().UTC() +func TestProcessFeaturesMatrix(t *testing.T) { process, err := Self() - if err == types.ErrNotImplemented { - assert.Nil(t, expectedProcessFeatures[GOOS], "unexpected ErrNotImplemented for %v", GOOS) + switch { + // Direct equality comparison because this is the API contract. + case types.ErrNotImplemented == err: + assert.Nil(t, expectedProcessFeatures[runtime.GOOS], "unexpected ErrNotImplemented for %v", runtime.GOOS) return - } else if err != nil { + case err != nil: t.Fatal(err) } - features.ProcessInfo = true + var features ProcessFeatures _, features.Environment = process.(types.Environment) _, features.OpenHandleEnumerator = process.(types.OpenHandleEnumerator) _, features.OpenHandleCounter = process.(types.OpenHandleCounter) _, features.Seccomp = process.(types.Seccomp) _, features.Capabilities = process.(types.Capabilities) + assert.Equal(t, expectedProcessFeatures[runtime.GOOS], &features) - assert.Equal(t, expectedProcessFeatures[GOOS], &features) logAsJSON(t, map[string]interface{}{ "features": features, }) @@ -132,25 +123,13 @@ func TestSelf(t *testing.T) { output["process.info"] = info assert.EqualValues(t, os.Getpid(), info.PID) assert.Equal(t, os.Args, info.Args) - assert.WithinDuration(t, info.StartTime, time.Now(), 10*time.Second) - - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - wdStat, err := os.Stat(wd) - if err != nil { - t.Fatal(err) + switch { + case runtime.GOOS == "darwin" && !cgo.Enabled: + default: + assert.WithinDurationf(t, startTime, info.StartTime, 10*time.Second, "StartTime does not match test start") + assertWorkingDirectory(t, info.CWD) } - cwdStat, err := os.Stat(info.CWD) - if err != nil { - t.Fatal(err) - } - - assert.EqualValues(t, os.SameFile(wdStat, cwdStat), true) - exe, err := os.Executable() if err != nil { t.Fatal(err) @@ -357,3 +336,26 @@ func TestProcesses(t *testing.T) { info.StartTime) } } + +func assertWorkingDirectory(t *testing.T, observedWD string) { + t.Helper() + + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + expectedInfo, err := os.Stat(wd) + if err != nil { + t.Fatal(err) + } + observedInfo, err := os.Stat(observedWD) + if err != nil { + t.Fatal(err) + } + + if !os.SameFile(expectedInfo, observedInfo) { + t.Errorf("working directory does not match observed working directory, want=%#v, got=%#v", + expectedInfo, observedInfo) + } +} From 2002a734fcd1386660ce1d4b572f226464a04d96 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 14:52:09 -0400 Subject: [PATCH 23/61] update README with freebsd --- README.md | 45 +++++++++++++++++++++++---------------------- system_test.go | 4 ++++ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index cd2bd514..e6cce66d 100644 --- a/README.md +++ b/README.md @@ -30,28 +30,28 @@ if handleCounter, ok := process.(types.OpenHandleCounter); ok { These tables show what methods are implemented as well as the extra interfaces that are implemented. -| `Host` Features | Darwin | Linux | Windows | AIX | -|------------------|--------|-------|---------|-----| -| `Info()` | x | x | x | x | -| `Memory()` | x | x | x | x | -| `CPUTimer` | x | x | x | x | -| `LoadAverage` | x | x | | | -| `VMStat` | | x | | | -| `NetworkCounters`| | x | | | - -| `Process` Features | Darwin | Linux | Windows | AIX | -|------------------------|--------|-------|---------|-----| -| `Info()` | x | x | x | x | -| `Memory()` | x | x | x | x | -| `User()` | x | x | x | x | -| `Parent()` | x | x | x | x | -| `CPUTimer` | x | x | x | x | -| `Environment` | x | x | | x | -| `OpenHandleEnumerator` | | x | | | -| `OpenHandleCounter` | | x | | | -| `Seccomp` | | x | | | -| `Capabilities` | | x | | | -| `NetworkCounters` | | x | | | +| `Host` Features | Darwin | Linux | Windows | AIX | FreeBSD | +|-------------------|--------|-------|---------|-----|---------| +| `Info()` | x | x | x | x | x | +| `Memory()` | x | x | x | x | x | +| `CPUTimer` | x | x | x | x | x | +| `LoadAverage` | x | x | | | | +| `VMStat` | | x | | | | +| `NetworkCounters` | | x | | | | + +| `Process` Features | Darwin | Linux | Windows | AIX | FreeBSD | +|------------------------|--------|-------|---------|-----|---------| +| `Info()` | x | x | x | x | x | +| `Memory()` | x | x | x | x | x | +| `User()` | x | x | x | x | x | +| `Parent()` | x | x | x | x | x | +| `CPUTimer` | x | x | x | x | x | +| `Environment` | x | x | | x | x | +| `OpenHandleEnumerator` | | x | | | x | +| `OpenHandleCounter` | | x | | | x | +| `Seccomp` | | x | | | | +| `Capabilities` | | x | | | | +| `NetworkCounters` | | x | | | x | ### GOOS / GOARCH Pairs @@ -62,6 +62,7 @@ This table lists the OS and architectures for which a "provider" is implemented. | aix/ppc64 | x | | | darwin/amd64 | optional * | x | | darwin/arm64 | optional * | x | +| freebsd/amd64 | x | x | | linux/386 | | | | linux/amd64 | | x | | linux/arm | | | diff --git a/system_test.go b/system_test.go index 3c5349d0..1d2b0677 100644 --- a/system_test.go +++ b/system_test.go @@ -44,6 +44,7 @@ type ProcessFeatures struct { OpenHandleCounter bool Seccomp bool Capabilities bool + NetworkCounters bool } var expectedProcessFeatures = map[string]*ProcessFeatures{ @@ -56,6 +57,7 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ OpenHandleCounter: true, Seccomp: true, Capabilities: true, + NetworkCounters: true, }, "windows": { OpenHandleCounter: true, @@ -67,6 +69,7 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ Environment: true, OpenHandleEnumerator: true, OpenHandleCounter: true, + NetworkCounters: true, }, } @@ -89,6 +92,7 @@ func TestProcessFeaturesMatrix(t *testing.T) { _, features.OpenHandleCounter = process.(types.OpenHandleCounter) _, features.Seccomp = process.(types.Seccomp) _, features.Capabilities = process.(types.Capabilities) + _, features.NetworkCounters = process.(types.NetworkCounters) assert.Equal(t, expectedProcessFeatures[runtime.GOOS], &features) logAsJSON(t, map[string]interface{}{ From a15881dbde22141fd004943dda703306e790c172 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 14:52:28 -0400 Subject: [PATCH 24/61] add go:generate for ztypes_freebsd.go --- providers/freebsd/doc.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/providers/freebsd/doc.go b/providers/freebsd/doc.go index b8beca21..afdc9c1d 100644 --- a/providers/freebsd/doc.go +++ b/providers/freebsd/doc.go @@ -18,3 +18,5 @@ // Package freebsd implements the HostProvider and ProcessProvider interfaces // for providing information about FreeBSD. package freebsd + +//go:generate sh -c "go tool cgo -godefs defs_freebsd.go > ztypes_freebsd.go" From bfbc2e6ce4f807d3d2ccf9caad8a14ddae667a8d Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 15:23:21 -0400 Subject: [PATCH 25/61] os_freebsd.go - make test less strict --- providers/freebsd/os_freebsd.go | 27 +++++++++++++++------------ providers/freebsd/os_freebsd_test.go | 22 +++++++++++----------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go index ad0c8cfc..3dfb9c6b 100644 --- a/providers/freebsd/os_freebsd.go +++ b/providers/freebsd/os_freebsd.go @@ -29,9 +29,8 @@ import ( ) const ( - ostypeMIB = "kern.ostype" - osreleaseMIB = "kern.osrelease" - osrevisionMIB = "kern.osrevision" + ostypeMIB = "kern.ostype" + osreleaseMIB = "kern.osrelease" ) func OperatingSystem() (*types.OSInfo, error) { @@ -40,6 +39,7 @@ func OperatingSystem() (*types.OSInfo, error) { func getOSInfo(baseDir string) (*types.OSInfo, error) { info := &types.OSInfo{ + Type: "freebsd", Family: "freebsd", Platform: "freebsd", } @@ -50,25 +50,28 @@ func getOSInfo(baseDir string) (*types.OSInfo, error) { } info.Name = ostype + // Example: 13.0-RELEASE-p11 osrelease, err := unix.Sysctl(osreleaseMIB) if err != nil { return info, err } info.Version = osrelease - elems := strings.Split(osrelease, "-") - majorminor := strings.Split(elems[0], ".") + releaseParts := strings.Split(osrelease, "-") - if len(majorminor) > 0 { - info.Major, _ = strconv.Atoi(majorminor[0]) + majorMinor := strings.Split(releaseParts[0], ".") + if len(majorMinor) > 0 { + info.Major, _ = strconv.Atoi(majorMinor[0]) } - - if len(majorminor) > 1 { - info.Minor, _ = strconv.Atoi(majorminor[1]) + if len(majorMinor) > 1 { + info.Minor, _ = strconv.Atoi(majorMinor[1]) } - if len(elems) > 2 { - info.Patch, _ = strconv.Atoi(strings.TrimPrefix(elems[2], "p")) + if len(releaseParts) > 1 { + info.Build = releaseParts[1] + } + if len(releaseParts) > 2 { + info.Patch, _ = strconv.Atoi(strings.TrimPrefix(releaseParts[2], "p")) } return info, nil diff --git a/providers/freebsd/os_freebsd_test.go b/providers/freebsd/os_freebsd_test.go index b3738d93..398737aa 100644 --- a/providers/freebsd/os_freebsd_test.go +++ b/providers/freebsd/os_freebsd_test.go @@ -28,21 +28,21 @@ import ( ) func TestOperatingSystem(t *testing.T) { - t.Run("freebsd14", func(t *testing.T) { + t.Run("freebsd", func(t *testing.T) { os, err := getOSInfo("") if err != nil { t.Fatal(err) } - assert.Equal(t, types.OSInfo{ - Type: "", - Family: "freebsd", - Platform: "freebsd", - Name: "FreeBSD", - Version: "14.0-RELEASE", - Major: 14, - Minor: 0, - Patch: 0, - }, *os) + assert.IsType(t, types.OSInfo{}, *os) + assert.Equal(t, "freebsd", os.Type) + assert.Equal(t, "freebsd", os.Family) + assert.Equal(t, "freebsd", os.Platform) + assert.Equal(t, "FreeBSD", os.Name) + assert.Regexp(t, `\d{1,2}\.\d{1,2}-(RELEASE|STABLE|CURRENT|RC[0-9]|ALPHA(\d{0,2})|BETA(\d{0,2}))(-p\d)?`, os.Version) + assert.Regexp(t, `\d{1,2}`, os.Major) + assert.Regexp(t, `\d{1,2}`, os.Minor) + assert.Regexp(t, `\d{1,2}`, os.Patch) + assert.Regexp(t, `(RELEASE|STABLE|CURRENT|RC[0-9]|ALPHA([0-9]{0,2})|BETA([0-9]{0,2}))`, os.Build) t.Logf("%#v", os) }) } From ff57aec928fd801a70d161c91877e36509795af7 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 15:28:31 -0400 Subject: [PATCH 26/61] remove getOSInfo(baseDir) The baseDir was never used so implement this directly within OperativingSystem(). --- providers/freebsd/os_freebsd.go | 4 ---- providers/freebsd/os_freebsd_test.go | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go index 3dfb9c6b..d286eccb 100644 --- a/providers/freebsd/os_freebsd.go +++ b/providers/freebsd/os_freebsd.go @@ -34,10 +34,6 @@ const ( ) func OperatingSystem() (*types.OSInfo, error) { - return getOSInfo("") -} - -func getOSInfo(baseDir string) (*types.OSInfo, error) { info := &types.OSInfo{ Type: "freebsd", Family: "freebsd", diff --git a/providers/freebsd/os_freebsd_test.go b/providers/freebsd/os_freebsd_test.go index 398737aa..9797f31a 100644 --- a/providers/freebsd/os_freebsd_test.go +++ b/providers/freebsd/os_freebsd_test.go @@ -29,7 +29,7 @@ import ( func TestOperatingSystem(t *testing.T) { t.Run("freebsd", func(t *testing.T) { - os, err := getOSInfo("") + os, err := OperatingSystem() if err != nil { t.Fatal(err) } From 496d7425178ac44ade22176573c35d8b3c25cc38 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 15:35:33 -0400 Subject: [PATCH 27/61] use unix.SysctlTimeval to get boot time --- .../{boottime_freebsd_cgo.go => boottime_freebsd.go} | 6 +++--- ...oottime_freebsd_cgo_test.go => boottime_freebsd_test.go} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename providers/freebsd/{boottime_freebsd_cgo.go => boottime_freebsd.go} (91%) rename providers/freebsd/{boottime_freebsd_cgo_test.go => boottime_freebsd_test.go} (100%) diff --git a/providers/freebsd/boottime_freebsd_cgo.go b/providers/freebsd/boottime_freebsd.go similarity index 91% rename from providers/freebsd/boottime_freebsd_cgo.go rename to providers/freebsd/boottime_freebsd.go index 3092c1e8..c15b7bd2 100644 --- a/providers/freebsd/boottime_freebsd_cgo.go +++ b/providers/freebsd/boottime_freebsd.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -//go:build freebsd && cgo +//go:build freebsd package freebsd @@ -29,8 +29,8 @@ import ( const kernBoottimeMIB = "kern.boottime" func BootTime() (time.Time, error) { - var tv unix.Timeval - if err := sysctlByName(kernBoottimeMIB, &tv); err != nil { + tv, err := unix.SysctlTimeval(kernBoottimeMIB) + if err != nil { return time.Time{}, fmt.Errorf("failed to get host uptime: %w", err) } diff --git a/providers/freebsd/boottime_freebsd_cgo_test.go b/providers/freebsd/boottime_freebsd_test.go similarity index 100% rename from providers/freebsd/boottime_freebsd_cgo_test.go rename to providers/freebsd/boottime_freebsd_test.go From 8f5721447ac4afd6d7e02234aeeff53cfffe2471 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 16:15:56 -0400 Subject: [PATCH 28/61] use unix.SysctlUint{32,64} --- providers/freebsd/memory_freebsd_cgo.go | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index 555587c1..d6ef8396 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -47,27 +47,27 @@ const ( ) func PageSize() (uint32, error) { - var pageSize uint32 - if err := sysctlByName(hwPagesizeMIB, &pageSize); err != nil { - return 0, fmt.Errorf("failed to get hw.pagesize: %w", err) + pageSize, err := unix.SysctlUint32(hwPagesizeMIB) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", hwPagesizeMIB, err) } return pageSize, nil } func SwapMaxPages() (uint32, error) { - var maxPages uint32 - if err := sysctlByName(hwPhysmemMIB, &maxPages); err != nil { - return 0, fmt.Errorf("failed to get vm.swap_maxpages: %w", err) + maxPages, err := unix.SysctlUint32(hwPhysmemMIB) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", hwPhysmemMIB, err) } return maxPages, nil } func TotalMemory() (uint64, error) { - var size uint64 - if err := sysctlByName(hwPhysmemMIB, &size); err != nil { - return 0, fmt.Errorf("failed to get hw.physmem: %w", err) + size, err := unix.SysctlUint64(hwPhysmemMIB) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", hwPhysmemMIB, err) } return size, nil @@ -83,12 +83,12 @@ func VmTotal() (vmTotal, error) { } func NumFreeBuffers() (uint32, error) { - var numfreebuffers uint32 - if err := sysctlByName(vfsNumfreebuffersMIB, &numfreebuffers); err != nil { - return 0, fmt.Errorf("failed to get vfs.numfreebuffers: %w", err) + numFreeBuffers, err := unix.SysctlUint32(vfsNumfreebuffersMIB) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", vfsNumfreebuffersMIB, err) } - return numfreebuffers, nil + return numFreeBuffers, nil } func KvmGetSwapInfo() (kvmSwap, error) { From 97534087ca091a6a82408af71157738bc61d7ccc Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 16:29:28 -0400 Subject: [PATCH 29/61] use block comment for C code --- providers/freebsd/host_freebsd_cgo.go | 5 -- providers/freebsd/memory_freebsd_cgo.go | 18 +++-- providers/freebsd/process_freebsd_cgo.go | 96 +++++++++++++----------- 3 files changed, 62 insertions(+), 57 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index 26a5e321..a5bda414 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -19,11 +19,6 @@ package freebsd -// #cgo LDFLAGS: -lkvm -//#include -//#include -import "C" - import ( "context" "errors" diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index d6ef8396..8d6d49c7 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -19,14 +19,16 @@ package freebsd -// #cgo LDFLAGS: -lkvm -//#include -//#include -//#include - -//#include -//#include -//#include +/* +#cgo LDFLAGS: -lkvm +#include +#include +#include + +#include +#include +#include +*/ import "C" import ( diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index ff74eb4b..c798ddfc 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -19,50 +19,58 @@ package freebsd -// #cgo LDFLAGS: -lkvm -lprocstat -//#include -//#include -//#include -//#include -//#include -//#include -//#include -// -//#include -//#include -//struct kinfo_proc getProcInfoAt(struct kinfo_proc *procs, unsigned int index) { -// return procs[index]; -//} -//unsigned int countArrayItems(char **items) { -// unsigned int i = 0; -// for (i = 0; items[i] != NULL; ++i); -// return i; -//} -//char * itemAtIndex(char **items, unsigned int index) { -// return items[index]; -//} -//unsigned int countFileStats(struct filestat_list *head) { -// unsigned int count = 0; -// struct filestat *fst; -// STAILQ_FOREACH(fst, head, next) { -// ++count; -// } -// -// return count; -//} -//void copyFileStats(struct filestat_list *head, struct filestat *out, unsigned int size) { -// unsigned int index = 0; -// struct filestat *fst; -// STAILQ_FOREACH(fst, head, next) { -// if (!size) { -// break; -// } -// memcpy(out, fst, sizeof(*fst)); -// ++out; -// --size; -// } -//} -// +import "C" + +/* +#cgo LDFLAGS: -lprocstat +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct kinfo_proc getProcInfoAt(struct kinfo_proc *procs, unsigned int index) { + return procs[index]; +} + +unsigned int countArrayItems(char **items) { + unsigned int i = 0; + for (i = 0; items[i] != NULL; ++i); + return i; +} + +char * itemAtIndex(char **items, unsigned int index) { + return items[index]; +} + +unsigned int countFileStats(struct filestat_list *head) { + unsigned int count = 0; + struct filestat *fst; + STAILQ_FOREACH(fst, head, next) { + ++count; + } + + return count; +} + +void copyFileStats(struct filestat_list *head, struct filestat *out, unsigned int size) { + unsigned int index = 0; + struct filestat *fst; + STAILQ_FOREACH(fst, head, next) { + if (!size) { + break; + } + memcpy(out, fst, sizeof(*fst)); + ++out; + --size; + } +} +*/ import "C" import ( From ff2ef7b70a57b4abd25e2a97be3be907a1b5e3ba Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 16:37:53 -0400 Subject: [PATCH 30/61] boottime - add assertion on returned time --- providers/freebsd/boottime_freebsd.go | 2 +- providers/freebsd/boottime_freebsd_test.go | 12 ++++++------ providers/freebsd/host_freebsd_cgo.go | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd.go index c15b7bd2..1706383f 100644 --- a/providers/freebsd/boottime_freebsd.go +++ b/providers/freebsd/boottime_freebsd.go @@ -34,6 +34,6 @@ func BootTime() (time.Time, error) { return time.Time{}, fmt.Errorf("failed to get host uptime: %w", err) } - bootTime := time.Unix(int64(tv.Sec), int64(tv.Usec)*int64(time.Microsecond)) + bootTime := time.Unix(tv.Sec, tv.Usec*int64(time.Microsecond)) return bootTime, nil } diff --git a/providers/freebsd/boottime_freebsd_test.go b/providers/freebsd/boottime_freebsd_test.go index 46570f50..83394dc9 100644 --- a/providers/freebsd/boottime_freebsd_test.go +++ b/providers/freebsd/boottime_freebsd_test.go @@ -15,23 +15,23 @@ // specific language governing permissions and limitations // under the License. -//go:build freebsd && cgo +//go:build freebsd package freebsd import ( "testing" + "time" - "github.com/elastic/go-sysinfo/internal/registry" + "github.com/stretchr/testify/assert" ) -var _ registry.HostProvider = freebsdSystem{} - func TestBootTime(t *testing.T) { - boottime, err := BootTime() + bootTime, err := BootTime() if err != nil { t.Fatal(err) } - t.Log(boottime) + // Apply a sanity check. This assumes the host has rebooted in the last year. + assert.WithinDuration(t, time.Now().UTC(), bootTime, 365*24*time.Hour) } diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index a5bda414..87bb0c5a 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -33,6 +33,7 @@ import ( "github.com/elastic/go-sysinfo/types" ) + func init() { registry.Register(newFreeBSDSystem("")) } From 26dfaba6c10360f26b62dfe37de9fe1cb775efe9 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 16:56:10 -0400 Subject: [PATCH 31/61] remove dead code --- providers/freebsd/util.go | 44 --------------------------------------- 1 file changed, 44 deletions(-) diff --git a/providers/freebsd/util.go b/providers/freebsd/util.go index 317c693d..e8f9fd11 100644 --- a/providers/freebsd/util.go +++ b/providers/freebsd/util.go @@ -22,7 +22,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "strconv" ) @@ -42,49 +41,6 @@ func parseKeyValue(content []byte, separator string, callback func(key, value [] return sc.Err() } -func findValue(filename, separator, key string) (string, error) { - content, err := ioutil.ReadFile(filename) - if err != nil { - return "", err - } - - var line []byte - sc := bufio.NewScanner(bytes.NewReader(content)) - for sc.Scan() { - if bytes.HasPrefix(sc.Bytes(), []byte(key)) { - line = sc.Bytes() - break - } - } - if len(line) == 0 { - return "", fmt.Errorf("%v not found", key) - } - - parts := bytes.SplitN(line, []byte(separator), 2) - if len(parts) != 2 { - return "", fmt.Errorf("unexpected line format for '%v'", string(line)) - } - - return string(bytes.TrimSpace(parts[1])), nil -} - -func decodeBitMap(s string, lookupName func(int) string) ([]string, error) { - mask, err := strconv.ParseUint(s, 16, 64) - if err != nil { - return nil, err - } - - var names []string - for i := 0; i < 64; i++ { - bit := mask & (1 << uint(i)) - if bit > 0 { - names = append(names, lookupName(i)) - } - } - - return names, nil -} - func parseBytesOrNumber(data []byte) (uint64, error) { parts := bytes.Fields(data) From 395233601ce91ba05ddd9cb55a1757c50a8b1965 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 17:24:22 -0400 Subject: [PATCH 32/61] refactor kvmGetSwapInfo to address shadowing --- providers/freebsd/host_freebsd_cgo.go | 3 +-- providers/freebsd/memory_freebsd_cgo.go | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index 87bb0c5a..a842201f 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -33,7 +33,6 @@ import ( "github.com/elastic/go-sysinfo/types" ) - func init() { registry.Register(newFreeBSDSystem("")) } @@ -164,7 +163,7 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { m.Available = m.Free + (uint64(numFreeBuffers) * uint64(pageSize)) - swap, err := KvmGetSwapInfo() + swap, err := kvmGetSwapInfo() if r.addErr(err) { return } diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index 8d6d49c7..0e180b3f 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -93,24 +93,18 @@ func NumFreeBuffers() (uint32, error) { return numFreeBuffers, nil } -func KvmGetSwapInfo() (kvmSwap, error) { - var kdC *C.struct_kvm_t - - devNullC := C.CString(devNull) - defer C.free(unsafe.Pointer(devNullC)) - kvmOpenC := C.CString(kvmOpen) - defer C.free(unsafe.Pointer(kvmOpenC)) - - if kdC, err := C.kvm_open(nil, devNullC, nil, unix.O_RDONLY, kvmOpenC); kdC == nil { - return kvmSwap{}, fmt.Errorf("failed to open kvm: %w", err) +func kvmGetSwapInfo() (*kvmSwap, error) { + var errstr *C.char + kd := C.kvm_open(nil, nil, nil, unix.O_RDONLY, errstr) + if errstr != nil { + return nil, fmt.Errorf("failed calling kvm_open: %s", C.GoString(errstr)) } - - defer C.kvm_close((*C.struct___kvm)(unsafe.Pointer(kdC))) + defer C.kvm_close(kd) var swap kvmSwap - if n, err := C.kvm_getswapinfo((*C.struct___kvm)(unsafe.Pointer(kdC)), (*C.struct_kvm_swap)(unsafe.Pointer(&swap)), 1, 0); n != 0 { - return kvmSwap{}, fmt.Errorf("failed to get kvm_getswapinfo: %w", err) + if n, err := C.kvm_getswapinfo(kd, (*C.struct_kvm_swap)(unsafe.Pointer(&swap)), 1, 0); n != 0 { + return nil, fmt.Errorf("failed to get kvm_getswapinfo: %w", err) } - return swap, nil + return &swap, nil } From 21e4945794675f0849473edf27f48528dccbc725 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 17:54:02 -0400 Subject: [PATCH 33/61] hw.physmem is a uint64 --- providers/freebsd/memory_freebsd_cgo.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index 0e180b3f..afd63636 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -58,12 +58,13 @@ func PageSize() (uint32, error) { } func SwapMaxPages() (uint32, error) { - maxPages, err := unix.SysctlUint32(hwPhysmemMIB) + maxPages, err := unix.SysctlUint64(hwPhysmemMIB) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", hwPhysmemMIB, err) } - return maxPages, nil + // TODO: Change return type to uint64. + return uint32(maxPages), nil } func TotalMemory() (uint64, error) { From 772144c18dbe806705122393ad7ad807658fea28 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 22:41:22 -0400 Subject: [PATCH 34/61] change memory data sources Prior to this change the used memory value looked incorrect: { "host.memory": { "total_bytes": 6402035712, "used_bytes": 6402035712, "available_bytes": 162770944, "free_bytes": 0, "virtual_total_bytes": 1073610752, "virtual_used_bytes": 0, "virtual_free_bytes": 1073610752 } } --- providers/freebsd/host_freebsd_cgo.go | 46 ++++++++----- providers/freebsd/memory_freebsd_cgo.go | 92 ++++++++++++++++--------- 2 files changed, 91 insertions(+), 47 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index a842201f..c3e967aa 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -135,50 +135,64 @@ func (r *reader) cpuTime(cpu *types.CPUTimes) { } func (r *reader) memInfo(m *types.HostMemoryInfo) { - pageSize, err := PageSize() - + pageSize, err := pageSizeBytes() if r.addErr(err) { return } - totalMemory, err := TotalMemory() + m.Total, err = totalPhysicalMem() if r.addErr(err) { return } - m.Total = totalMemory - - vm, err := VmTotal() + activePages, err := activePageCount() if r.addErr(err) { return } + m.Metrics["active_bytes"] = activePages * pageSize - m.Free = uint64(vm.Free) * uint64(pageSize) - m.Used = m.Total - m.Free + wirePages, err := wirePageCount() + if r.addErr(err) { + return + } + m.Metrics["wired_bytes"] = wirePages * pageSize - numFreeBuffers, err := NumFreeBuffers() + inactivePages, err := inactivePageCount() if r.addErr(err) { return } + m.Metrics["inactive_bytes"] = inactivePages * pageSize - m.Available = m.Free + (uint64(numFreeBuffers) * uint64(pageSize)) + cachePages, err := cachePageCount() + if r.addErr(err) { + return + } + m.Metrics["cache_bytes"] = cachePages * pageSize - swap, err := kvmGetSwapInfo() + freePages, err := freePageCount() if r.addErr(err) { return } + m.Metrics["free_bytes"] = freePages * pageSize - swapMaxPages, err := SwapMaxPages() + buffers, err := buffersUsedBytes() if r.addErr(err) { return } + m.Metrics["buffer_bytes"] = buffers + + m.Used = (activePages + wirePages) * pageSize + m.Free = freePages * pageSize + m.Available = (inactivePages+cachePages+freePages)*pageSize + buffers - if swap.Total > swapMaxPages { - swap.Total = swapMaxPages + // Virtual (swap) Memory + swap, err := kvmGetSwapInfo() + if r.addErr(err) { + return } - m.VirtualTotal = uint64(swap.Total) * uint64(pageSize) - m.VirtualUsed = uint64(swap.Used) * uint64(pageSize) + m.VirtualTotal = uint64(swap.Total) * pageSize + m.VirtualUsed = uint64(swap.Used) * pageSize m.VirtualFree = m.VirtualTotal - m.VirtualUsed } diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index afd63636..1df1918c 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -38,60 +38,90 @@ import ( "golang.org/x/sys/unix" ) -const ( - hwPhysmemMIB = "hw.physmem" - hwPagesizeMIB = "hw.pagesize" - vmVmtotalMIB = "vm.vmtotal" - vmSwapmaxpagesMIB = "vm.swap_maxpages" - vfsNumfreebuffersMIB = "vfs.numfreebuffers" - devNull = "/dev/null" - kvmOpen = "kvm_open" -) +func totalPhysicalMem() (uint64, error) { + const mib = "hw.physmem" + + v, err := unix.SysctlUint64(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + return v, nil +} + +func pageSizeBytes() (uint64, error) { + const mib = "vm.stats.vm.v_page_size" -func PageSize() (uint32, error) { - pageSize, err := unix.SysctlUint32(hwPagesizeMIB) + v, err := unix.SysctlUint64(mib) if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", hwPagesizeMIB, err) + return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return pageSize, nil + return v, nil } -func SwapMaxPages() (uint32, error) { - maxPages, err := unix.SysctlUint64(hwPhysmemMIB) +func activePageCount() (uint64, error) { + const mib = "vm.stats.vm.v_active_count" + + v, err := unix.SysctlUint64(mib) if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", hwPhysmemMIB, err) + return 0, fmt.Errorf("failed to get %s: %w", mib, err) } + return v, nil +} + +func wirePageCount() (uint64, error) { + const mib = "vm.stats.vm.v_wire_count" - // TODO: Change return type to uint64. - return uint32(maxPages), nil + v, err := unix.SysctlUint64(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + return v, nil } -func TotalMemory() (uint64, error) { - size, err := unix.SysctlUint64(hwPhysmemMIB) +func inactivePageCount() (uint64, error) { + const mib = "vm.stats.vm.v_inactive_count" + + v, err := unix.SysctlUint64(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +} + +func cachePageCount() (uint64, error) { + const mib = "vm.stats.vm.v_cache_count" + + v, err := unix.SysctlUint64(mib) if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", hwPhysmemMIB, err) + return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return size, nil + return v, nil } -func VmTotal() (vmTotal, error) { - var vm vmTotal - if err := sysctlByName(vmVmtotalMIB, &vm); err != nil { - return vmTotal{}, fmt.Errorf("failed to get vm.vmtotal: %w", err) +func freePageCount() (uint64, error) { + const mib = "vm.stats.vm.v_free_count" + + v, err := unix.SysctlUint64(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return vm, nil + return v, nil } -func NumFreeBuffers() (uint32, error) { - numFreeBuffers, err := unix.SysctlUint32(vfsNumfreebuffersMIB) +// buffersUsedBytes returns the number memory bytes used as disk cache. +func buffersUsedBytes() (uint64, error) { + const mib = "vfs.bufspace" + + v, err := unix.SysctlUint64(mib) if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", vfsNumfreebuffersMIB, err) + return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return numFreeBuffers, nil + return v, nil } func kvmGetSwapInfo() (*kvmSwap, error) { From ceb2852af2c0e8aa59b5c8833c45b27c3628dc5b Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 22:59:15 -0400 Subject: [PATCH 35/61] vm.stats.vm.v_page_size is uint32 --- providers/freebsd/host_freebsd_cgo.go | 3 ++- providers/freebsd/memory_freebsd_cgo.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index c3e967aa..d2ac5713 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -135,10 +135,11 @@ func (r *reader) cpuTime(cpu *types.CPUTimes) { } func (r *reader) memInfo(m *types.HostMemoryInfo) { - pageSize, err := pageSizeBytes() + ps, err := pageSizeBytes() if r.addErr(err) { return } + pageSize := uint64(ps) m.Total, err = totalPhysicalMem() if r.addErr(err) { diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index 1df1918c..aa7aa606 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -48,10 +48,10 @@ func totalPhysicalMem() (uint64, error) { return v, nil } -func pageSizeBytes() (uint64, error) { +func pageSizeBytes() (uint32, error) { const mib = "vm.stats.vm.v_page_size" - v, err := unix.SysctlUint64(mib) + v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } From c4a0b0dfef42de50389aa87afbae403964087b3e Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 23:09:13 -0400 Subject: [PATCH 36/61] use uint32 for all sysctl vm.stats.vm.* --- providers/freebsd/host_freebsd_cgo.go | 16 ++++++++-------- providers/freebsd/memory_freebsd_cgo.go | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index d2ac5713..a417a46e 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -150,31 +150,31 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { if r.addErr(err) { return } - m.Metrics["active_bytes"] = activePages * pageSize + m.Metrics["active_bytes"] = uint64(activePages) * pageSize wirePages, err := wirePageCount() if r.addErr(err) { return } - m.Metrics["wired_bytes"] = wirePages * pageSize + m.Metrics["wired_bytes"] = uint64(wirePages) * pageSize inactivePages, err := inactivePageCount() if r.addErr(err) { return } - m.Metrics["inactive_bytes"] = inactivePages * pageSize + m.Metrics["inactive_bytes"] = uint64(inactivePages) * pageSize cachePages, err := cachePageCount() if r.addErr(err) { return } - m.Metrics["cache_bytes"] = cachePages * pageSize + m.Metrics["cache_bytes"] = uint64(cachePages) * pageSize freePages, err := freePageCount() if r.addErr(err) { return } - m.Metrics["free_bytes"] = freePages * pageSize + m.Metrics["free_bytes"] = uint64(freePages) * pageSize buffers, err := buffersUsedBytes() if r.addErr(err) { @@ -182,9 +182,9 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { } m.Metrics["buffer_bytes"] = buffers - m.Used = (activePages + wirePages) * pageSize - m.Free = freePages * pageSize - m.Available = (inactivePages+cachePages+freePages)*pageSize + buffers + m.Used = uint64(activePages+wirePages) * pageSize + m.Free = uint64(freePages) * pageSize + m.Available = uint64(inactivePages+cachePages+freePages)*pageSize + buffers // Virtual (swap) Memory swap, err := kvmGetSwapInfo() diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index aa7aa606..328a56b2 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -59,30 +59,30 @@ func pageSizeBytes() (uint32, error) { return v, nil } -func activePageCount() (uint64, error) { +func activePageCount() (uint32, error) { const mib = "vm.stats.vm.v_active_count" - v, err := unix.SysctlUint64(mib) + v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } return v, nil } -func wirePageCount() (uint64, error) { +func wirePageCount() (uint32, error) { const mib = "vm.stats.vm.v_wire_count" - v, err := unix.SysctlUint64(mib) + v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } return v, nil } -func inactivePageCount() (uint64, error) { +func inactivePageCount() (uint32, error) { const mib = "vm.stats.vm.v_inactive_count" - v, err := unix.SysctlUint64(mib) + v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } @@ -90,10 +90,10 @@ func inactivePageCount() (uint64, error) { return v, nil } -func cachePageCount() (uint64, error) { +func cachePageCount() (uint32, error) { const mib = "vm.stats.vm.v_cache_count" - v, err := unix.SysctlUint64(mib) + v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } @@ -101,10 +101,10 @@ func cachePageCount() (uint64, error) { return v, nil } -func freePageCount() (uint64, error) { +func freePageCount() (uint32, error) { const mib = "vm.stats.vm.v_free_count" - v, err := unix.SysctlUint64(mib) + v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } From eedf8035dcb502d184f93fcdd037e0f210b0f390 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 23:15:41 -0400 Subject: [PATCH 37/61] initialize types.HostMemoryInfo.Metrics map --- providers/freebsd/host_freebsd_cgo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index a417a46e..ea687372 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -150,6 +150,7 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { if r.addErr(err) { return } + m.Metrics = make(map[string]uint64, 6) m.Metrics["active_bytes"] = uint64(activePages) * pageSize wirePages, err := wirePageCount() From 0398d569f6ac4efd4c7669b30c8ed8a36a17f99f Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 23:48:26 -0400 Subject: [PATCH 38/61] improve code locality - combine sysctl sources Combine all non-cgo sysctl based data sources into one file to improve the code locality. --- providers/freebsd/arch_freebsd.go | 37 ---- providers/freebsd/boottime_freebsd.go | 39 ---- providers/freebsd/boottime_freebsd_test.go | 37 ---- providers/freebsd/defs_freebsd.go | 2 - providers/freebsd/host_freebsd_cgo.go | 10 +- providers/freebsd/kernel_freebsd.go | 37 ---- providers/freebsd/machineid_freebsd.go | 37 ---- providers/freebsd/memory_freebsd_cgo.go | 86 -------- providers/freebsd/os_freebsd.go | 74 ------- providers/freebsd/sysctl_freebsd.go | 202 ++++++++++++++++++ ...freebsd_test.go => sysctl_freebsd_test.go} | 43 +++- providers/freebsd/ztypes_freebsd.go | 18 -- 12 files changed, 246 insertions(+), 376 deletions(-) delete mode 100644 providers/freebsd/arch_freebsd.go delete mode 100644 providers/freebsd/boottime_freebsd.go delete mode 100644 providers/freebsd/boottime_freebsd_test.go delete mode 100644 providers/freebsd/kernel_freebsd.go delete mode 100644 providers/freebsd/machineid_freebsd.go delete mode 100644 providers/freebsd/os_freebsd.go create mode 100644 providers/freebsd/sysctl_freebsd.go rename providers/freebsd/{os_freebsd_test.go => sysctl_freebsd_test.go} (68%) diff --git a/providers/freebsd/arch_freebsd.go b/providers/freebsd/arch_freebsd.go deleted file mode 100644 index 5f382d66..00000000 --- a/providers/freebsd/arch_freebsd.go +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd - -package freebsd - -import ( - "fmt" - - "golang.org/x/sys/unix" -) - -const hardwareMIB = "hw.machine" - -func Architecture() (string, error) { - arch, err := unix.Sysctl(hardwareMIB) - if err != nil { - return "", fmt.Errorf("failed to get architecture: %w", err) - } - - return arch, nil -} diff --git a/providers/freebsd/boottime_freebsd.go b/providers/freebsd/boottime_freebsd.go deleted file mode 100644 index 1706383f..00000000 --- a/providers/freebsd/boottime_freebsd.go +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd - -package freebsd - -import ( - "fmt" - "time" - - "golang.org/x/sys/unix" -) - -const kernBoottimeMIB = "kern.boottime" - -func BootTime() (time.Time, error) { - tv, err := unix.SysctlTimeval(kernBoottimeMIB) - if err != nil { - return time.Time{}, fmt.Errorf("failed to get host uptime: %w", err) - } - - bootTime := time.Unix(tv.Sec, tv.Usec*int64(time.Microsecond)) - return bootTime, nil -} diff --git a/providers/freebsd/boottime_freebsd_test.go b/providers/freebsd/boottime_freebsd_test.go deleted file mode 100644 index 83394dc9..00000000 --- a/providers/freebsd/boottime_freebsd_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd - -package freebsd - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestBootTime(t *testing.T) { - bootTime, err := BootTime() - if err != nil { - t.Fatal(err) - } - - // Apply a sanity check. This assumes the host has rebooted in the last year. - assert.WithinDuration(t, time.Now().UTC(), bootTime, 365*24*time.Hour) -} diff --git a/providers/freebsd/defs_freebsd.go b/providers/freebsd/defs_freebsd.go index a08389d5..34490a4b 100644 --- a/providers/freebsd/defs_freebsd.go +++ b/providers/freebsd/defs_freebsd.go @@ -28,8 +28,6 @@ package freebsd */ import "C" -type vmTotal C.struct_vmtotal - type kvmSwap C.struct_kvm_swap type clockInfo C.struct_clockinfo diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index ea687372..e27044d0 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -199,7 +199,7 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { } func (r *reader) architecture(h *host) { - v, err := Architecture() + v, err := architecture() if r.addErr(err) { return } @@ -207,7 +207,7 @@ func (r *reader) architecture(h *host) { } func (r *reader) bootTime(h *host) { - v, err := BootTime() + v, err := bootTime() if r.addErr(err) { return } @@ -233,7 +233,7 @@ func (r *reader) network(h *host) { } func (r *reader) kernelVersion(h *host) { - v, err := KernelVersion() + v, err := kernelVersion() if r.addErr(err) { return } @@ -241,7 +241,7 @@ func (r *reader) kernelVersion(h *host) { } func (r *reader) os(h *host) { - v, err := OperatingSystem() + v, err := operatingSystem() if r.addErr(err) { return } @@ -253,7 +253,7 @@ func (r *reader) time(h *host) { } func (r *reader) uniqueID(h *host) { - v, err := MachineID() + v, err := machineID() if r.addErr(err) { return } diff --git a/providers/freebsd/kernel_freebsd.go b/providers/freebsd/kernel_freebsd.go deleted file mode 100644 index f9aa0296..00000000 --- a/providers/freebsd/kernel_freebsd.go +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd - -package freebsd - -import ( - "fmt" - - "golang.org/x/sys/unix" -) - -const kernelReleaseMIB = "kern.osrelease" - -func KernelVersion() (string, error) { - version, err := unix.Sysctl(kernelReleaseMIB) - if err != nil { - return "", fmt.Errorf("failed to get kernel version: %w", err) - } - - return version, nil -} diff --git a/providers/freebsd/machineid_freebsd.go b/providers/freebsd/machineid_freebsd.go deleted file mode 100644 index 45d8bad8..00000000 --- a/providers/freebsd/machineid_freebsd.go +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd - -package freebsd - -import ( - "fmt" - - "golang.org/x/sys/unix" -) - -const kernelHostUUIDMIB = "kern.hostuuid" - -func MachineID() (string, error) { - uuid, err := unix.Sysctl(kernelHostUUIDMIB) - if err != nil { - return "", fmt.Errorf("failed to get machine id: %w", err) - } - - return uuid, nil -} diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/memory_freebsd_cgo.go index 328a56b2..89585ed3 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/memory_freebsd_cgo.go @@ -38,92 +38,6 @@ import ( "golang.org/x/sys/unix" ) -func totalPhysicalMem() (uint64, error) { - const mib = "hw.physmem" - - v, err := unix.SysctlUint64(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - return v, nil -} - -func pageSizeBytes() (uint32, error) { - const mib = "vm.stats.vm.v_page_size" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - - return v, nil -} - -func activePageCount() (uint32, error) { - const mib = "vm.stats.vm.v_active_count" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - return v, nil -} - -func wirePageCount() (uint32, error) { - const mib = "vm.stats.vm.v_wire_count" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - return v, nil -} - -func inactivePageCount() (uint32, error) { - const mib = "vm.stats.vm.v_inactive_count" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - - return v, nil -} - -func cachePageCount() (uint32, error) { - const mib = "vm.stats.vm.v_cache_count" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - - return v, nil -} - -func freePageCount() (uint32, error) { - const mib = "vm.stats.vm.v_free_count" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - - return v, nil -} - -// buffersUsedBytes returns the number memory bytes used as disk cache. -func buffersUsedBytes() (uint64, error) { - const mib = "vfs.bufspace" - - v, err := unix.SysctlUint64(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - - return v, nil -} - func kvmGetSwapInfo() (*kvmSwap, error) { var errstr *C.char kd := C.kvm_open(nil, nil, nil, unix.O_RDONLY, errstr) diff --git a/providers/freebsd/os_freebsd.go b/providers/freebsd/os_freebsd.go deleted file mode 100644 index d286eccb..00000000 --- a/providers/freebsd/os_freebsd.go +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd - -package freebsd - -import ( - "strconv" - "strings" - - "golang.org/x/sys/unix" - - "github.com/elastic/go-sysinfo/types" -) - -const ( - ostypeMIB = "kern.ostype" - osreleaseMIB = "kern.osrelease" -) - -func OperatingSystem() (*types.OSInfo, error) { - info := &types.OSInfo{ - Type: "freebsd", - Family: "freebsd", - Platform: "freebsd", - } - - ostype, err := unix.Sysctl(ostypeMIB) - if err != nil { - return info, err - } - info.Name = ostype - - // Example: 13.0-RELEASE-p11 - osrelease, err := unix.Sysctl(osreleaseMIB) - if err != nil { - return info, err - } - info.Version = osrelease - - releaseParts := strings.Split(osrelease, "-") - - majorMinor := strings.Split(releaseParts[0], ".") - if len(majorMinor) > 0 { - info.Major, _ = strconv.Atoi(majorMinor[0]) - } - if len(majorMinor) > 1 { - info.Minor, _ = strconv.Atoi(majorMinor[1]) - } - - if len(releaseParts) > 1 { - info.Build = releaseParts[1] - } - if len(releaseParts) > 2 { - info.Patch, _ = strconv.Atoi(strings.TrimPrefix(releaseParts[2], "p")) - } - - return info, nil -} diff --git a/providers/freebsd/sysctl_freebsd.go b/providers/freebsd/sysctl_freebsd.go new file mode 100644 index 00000000..4c6287d9 --- /dev/null +++ b/providers/freebsd/sysctl_freebsd.go @@ -0,0 +1,202 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build freebsd + +package freebsd + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/elastic/go-sysinfo/types" + + "golang.org/x/sys/unix" +) + +func activePageCount() (uint32, error) { + const mib = "vm.stats.vm.v_active_count" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + return v, nil +} + +func architecture() (string, error) { + const mib = "hw.machine" + + arch, err := unix.Sysctl(mib) + if err != nil { + return "", fmt.Errorf("failed to get architecture: %w", err) + } + + return arch, nil +} + +func bootTime() (time.Time, error) { + const mib = "kern.boottime" + + tv, err := unix.SysctlTimeval(mib) + if err != nil { + return time.Time{}, fmt.Errorf("failed to get host uptime: %w", err) + } + + bootTime := time.Unix(tv.Sec, tv.Usec*int64(time.Microsecond)) + return bootTime, nil +} + +// buffersUsedBytes returns the number memory bytes used as disk cache. +func buffersUsedBytes() (uint64, error) { + const mib = "vfs.bufspace" + + v, err := unix.SysctlUint64(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +} + +func cachePageCount() (uint32, error) { + const mib = "vm.stats.vm.v_cache_count" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +} + +func freePageCount() (uint32, error) { + const mib = "vm.stats.vm.v_free_count" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +} + +func inactivePageCount() (uint32, error) { + const mib = "vm.stats.vm.v_inactive_count" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +} + +func kernelVersion() (string, error) { + const mib = "kern.osrelease" + + version, err := unix.Sysctl(mib) + if err != nil { + return "", fmt.Errorf("failed to get kernel version: %w", err) + } + + return version, nil +} + +func machineID() (string, error) { + const mib = "kern.hostuuid" + + uuid, err := unix.Sysctl(mib) + if err != nil { + return "", fmt.Errorf("failed to get machine id: %w", err) + } + + return uuid, nil +} + +func operatingSystem() (*types.OSInfo, error) { + info := &types.OSInfo{ + Type: "freebsd", + Family: "freebsd", + Platform: "freebsd", + } + + osType, err := unix.Sysctl("kern.ostype") + if err != nil { + return info, err + } + info.Name = osType + + // Example: 13.0-RELEASE-p11 + osRelease, err := unix.Sysctl("kern.osrelease") + if err != nil { + return info, err + } + info.Version = osRelease + + releaseParts := strings.Split(osRelease, "-") + + majorMinor := strings.Split(releaseParts[0], ".") + if len(majorMinor) > 0 { + info.Major, _ = strconv.Atoi(majorMinor[0]) + } + if len(majorMinor) > 1 { + info.Minor, _ = strconv.Atoi(majorMinor[1]) + } + + if len(releaseParts) > 1 { + info.Build = releaseParts[1] + } + if len(releaseParts) > 2 { + info.Patch, _ = strconv.Atoi(strings.TrimPrefix(releaseParts[2], "p")) + } + + return info, nil +} + +func pageSizeBytes() (uint32, error) { + const mib = "vm.stats.vm.v_page_size" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +} + +func totalPhysicalMem() (uint64, error) { + const mib = "hw.physmem" + + v, err := unix.SysctlUint64(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + return v, nil +} + +func wirePageCount() (uint32, error) { + const mib = "vm.stats.vm.v_wire_count" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + return v, nil +} diff --git a/providers/freebsd/os_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go similarity index 68% rename from providers/freebsd/os_freebsd_test.go rename to providers/freebsd/sysctl_freebsd_test.go index 9797f31a..2ba1f916 100644 --- a/providers/freebsd/os_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -21,19 +21,54 @@ package freebsd import ( "testing" + "time" "github.com/stretchr/testify/assert" - - "github.com/elastic/go-sysinfo/types" ) +func TestArchitecture(t *testing.T) { + arch, err := architecture() + if err != nil { + t.Fatal(err) + } + + assert.NotEmpty(t, arch) +} + +func TestBootTime(t *testing.T) { + bootTime, err := bootTime() + if err != nil { + t.Fatal(err) + } + + // Apply a sanity check. This assumes the host has rebooted in the last year. + assert.WithinDuration(t, time.Now().UTC(), bootTime, 365*24*time.Hour) +} + +func TestKernelVersion(t *testing.T) { + kernel, err := kernelVersion() + if err != nil { + t.Fatal(err) + } + + assert.NotEmpty(t, kernel) +} + +func TestMachineID(t *testing.T) { + machineID, err := machineID() + if err != nil { + t.Fatal(err) + } + + assert.NotEmpty(t, machineID) +} + func TestOperatingSystem(t *testing.T) { t.Run("freebsd", func(t *testing.T) { - os, err := OperatingSystem() + os, err := operatingSystem() if err != nil { t.Fatal(err) } - assert.IsType(t, types.OSInfo{}, *os) assert.Equal(t, "freebsd", os.Type) assert.Equal(t, "freebsd", os.Family) assert.Equal(t, "freebsd", os.Platform) diff --git a/providers/freebsd/ztypes_freebsd.go b/providers/freebsd/ztypes_freebsd.go index 221b6739..14807b2c 100644 --- a/providers/freebsd/ztypes_freebsd.go +++ b/providers/freebsd/ztypes_freebsd.go @@ -21,24 +21,6 @@ package freebsd -type vmTotal struct { - Rq int16 - Dw int16 - Pw int16 - Sl int16 - _ int16 // cgo doesn't generate the same alignment as C does - Sw int16 - Vm int32 - Avm int32 - Rm int32 - Arm int32 - Vmshr int32 - Avmshr int32 - Rmshr int32 - Armshr int32 - Free int32 -} - type kvmSwap struct { Devname [32]int8 Used uint32 From 5b21c93e1b94dc8615f350fffbc2aec76ef5d665 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 10 Apr 2024 23:50:21 -0400 Subject: [PATCH 39/61] dead code - remove vmstat.go + related Remove vmstat.go and vmstat_test.go for now because these were unused. They can be added back later in a separate PR. --- providers/freebsd/util.go | 67 ------------ providers/freebsd/vmstat.go | 69 ------------- providers/freebsd/vmstat_test.go | 171 ------------------------------- 3 files changed, 307 deletions(-) delete mode 100644 providers/freebsd/util.go delete mode 100644 providers/freebsd/vmstat.go delete mode 100644 providers/freebsd/vmstat_test.go diff --git a/providers/freebsd/util.go b/providers/freebsd/util.go deleted file mode 100644 index e8f9fd11..00000000 --- a/providers/freebsd/util.go +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package freebsd - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "strconv" -) - -func parseKeyValue(content []byte, separator string, callback func(key, value []byte) error) error { - sc := bufio.NewScanner(bytes.NewReader(content)) - for sc.Scan() { - parts := bytes.SplitN(sc.Bytes(), []byte(separator), 2) - if len(parts) != 2 { - continue - } - - if err := callback(parts[0], bytes.TrimSpace(parts[1])); err != nil { - return err - } - } - - return sc.Err() -} - -func parseBytesOrNumber(data []byte) (uint64, error) { - parts := bytes.Fields(data) - - if len(parts) == 0 { - return 0, errors.New("empty value") - } - - num, err := strconv.ParseUint(string(parts[0]), 10, 64) - if err != nil { - return 0, fmt.Errorf("failed to parse value: %w", err) - } - - var multiplier uint64 = 1 - if len(parts) >= 2 { - switch string(parts[1]) { - case "kB": - multiplier = 1024 - default: - return 0, fmt.Errorf("unhandled unit %v", string(parts[1])) - } - } - - return num * multiplier, nil -} diff --git a/providers/freebsd/vmstat.go b/providers/freebsd/vmstat.go deleted file mode 100644 index f077267d..00000000 --- a/providers/freebsd/vmstat.go +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package freebsd - -import ( - "fmt" - "reflect" - - "github.com/elastic/go-sysinfo/types" -) - -// vmstatTagToFieldIndex contains a mapping of json struct tags to struct field indices. -var vmstatTagToFieldIndex = make(map[string]int) - -func init() { - var vmstat types.VMStatInfo - val := reflect.ValueOf(vmstat) - typ := reflect.TypeOf(vmstat) - - for i := 0; i < val.NumField(); i++ { - field := typ.Field(i) - if tag := field.Tag.Get("json"); tag != "" { - vmstatTagToFieldIndex[tag] = i - } - } -} - -// parseVMStat parses the contents of /proc/vmstat. -func parseVMStat(content []byte) (*types.VMStatInfo, error) { - var vmStat types.VMStatInfo - refValues := reflect.ValueOf(&vmStat).Elem() - - err := parseKeyValue(content, " ", func(key, value []byte) error { - // turn our []byte value into an int - val, err := parseBytesOrNumber(value) - if err != nil { - return fmt.Errorf("failed to parse %v value of %v: %w", string(key), string(value), err) - } - - idx, ok := vmstatTagToFieldIndex[string(key)] - if !ok { - return nil - } - - sval := refValues.Field(idx) - - if sval.CanSet() { - sval.SetUint(val) - } - return nil - }) - - return &vmStat, err -} diff --git a/providers/freebsd/vmstat_test.go b/providers/freebsd/vmstat_test.go deleted file mode 100644 index 8351d4fb..00000000 --- a/providers/freebsd/vmstat_test.go +++ /dev/null @@ -1,171 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package freebsd - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -var rawInput = ` -nr_free_pages 50545 -nr_zone_inactive_anon 66 -nr_zone_active_anon 26799 -nr_zone_inactive_file 31849 -nr_zone_active_file 94164 -nr_zone_unevictable 0 -nr_zone_write_pending 7 -nr_mlock 0 -nr_page_table_pages 1225 -nr_kernel_stack 2496 -nr_bounce 0 -nr_zspages 0 -nr_free_cma 0 -numa_hit 44470329 -numa_miss 0 -numa_foreign 0 -numa_interleave 16296 -numa_local 44470329 -numa_other 0 -nr_inactive_anon 66 -nr_active_anon 26799 -nr_inactive_file 31849 -nr_active_file 94164 -nr_unevictable 0 -nr_slab_reclaimable 31763 -nr_slab_unreclaimable 10329 -nr_isolated_anon 0 -nr_isolated_file 0 -workingset_refault 302914 -workingset_activate 108959 -workingset_nodereclaim 6422 -nr_anon_pages 26218 -nr_mapped 8641 -nr_file_pages 126182 -nr_dirty 7 -nr_writeback 0 -nr_writeback_temp 0 -nr_shmem 169 -nr_shmem_hugepages 0 -nr_shmem_pmdmapped 0 -nr_anon_transparent_hugepages 0 -nr_unstable 0 -nr_vmscan_write 35 -nr_vmscan_immediate_reclaim 9832 -nr_dirtied 7188920 -nr_written 6479005 -nr_dirty_threshold 31736 -nr_dirty_background_threshold 15848 -pgpgin 17010697 -pgpgout 27734292 -pswpin 0 -pswpout 0 -pgalloc_dma 241378 -pgalloc_dma32 45788683 -pgalloc_normal 0 -pgalloc_movable 0 -allocstall_dma 0 -allocstall_dma32 0 -allocstall_normal 5 -allocstall_movable 8 -pgskip_dma 0 -pgskip_dma32 0 -pgskip_normal 0 -pgskip_movable 0 -pgfree 46085578 -pgactivate 2475069 -pgdeactivate 636658 -pglazyfree 9426 -pgfault 46777498 -pgmajfault 19204 -pglazyfreed 0 -pgrefill 707817 -pgsteal_kswapd 3798890 -pgsteal_direct 1466 -pgscan_kswapd 3868525 -pgscan_direct 1483 -pgscan_direct_throttle 0 -zone_reclaim_failed 0 -pginodesteal 1710 -slabs_scanned 8348560 -kswapd_inodesteal 3142001 -kswapd_low_wmark_hit_quickly 541 -kswapd_high_wmark_hit_quickly 332 -pageoutrun 1492 -pgrotated 29725 -drop_pagecache 0 -drop_slab 0 -oom_kill 0 -numa_pte_updates 0 -numa_huge_pte_updates 0 -numa_hint_faults 0 -numa_hint_faults_local 0 -numa_pages_migrated 0 -pgmigrate_success 4539 -pgmigrate_fail 156 -compact_migrate_scanned 9331 -compact_free_scanned 136266 -compact_isolated 9407 -compact_stall 2 -compact_fail 0 -compact_success 2 -compact_daemon_wake 21 -compact_daemon_migrate_scanned 8311 -compact_daemon_free_scanned 107086 -htlb_buddy_alloc_success 0 -htlb_buddy_alloc_fail 0 -unevictable_pgs_culled 19 -unevictable_pgs_scanned 0 -unevictable_pgs_rescued 304 -unevictable_pgs_mlocked 304 -unevictable_pgs_munlocked 304 -unevictable_pgs_cleared 0 -unevictable_pgs_stranded 0 -thp_fault_alloc 2 -thp_fault_fallback 0 -thp_collapse_alloc 2 -thp_collapse_alloc_failed 0 -thp_file_alloc 0 -thp_file_mapped 0 -thp_split_page 0 -thp_split_page_failed 0 -thp_deferred_split_page 4 -thp_split_pmd 1 -thp_split_pud 0 -thp_zero_page_alloc 0 -thp_zero_page_alloc_failed 0 -thp_swpout 0 -thp_swpout_fallback 0 -balloon_inflate 0 -balloon_deflate 0 -balloon_migrate 0 -swap_ra 0 -swap_ra_hit 0 -` - -func TestVmStatParse(t *testing.T) { - data, err := parseVMStat([]byte(rawInput)) - if err != nil { - t.Fatal(err) - } - // Check a few values - assert.Equal(t, uint64(8348560), data.SlabsScanned) - assert.Equal(t, uint64(0), data.SwapRa) - assert.Equal(t, uint64(108959), data.WorkingsetActivate) -} From 5da5e609dbaf7b200f5bf44d5e6ec4f66b7b83a4 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 00:04:30 -0400 Subject: [PATCH 40/61] system_test.go - exercise process NetworkCounters() --- .../{memory_freebsd_cgo.go => kvm_freebsd_cgo.go} | 3 +++ system_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+) rename providers/freebsd/{memory_freebsd_cgo.go => kvm_freebsd_cgo.go} (89%) diff --git a/providers/freebsd/memory_freebsd_cgo.go b/providers/freebsd/kvm_freebsd_cgo.go similarity index 89% rename from providers/freebsd/memory_freebsd_cgo.go rename to providers/freebsd/kvm_freebsd_cgo.go index 89585ed3..3272287c 100644 --- a/providers/freebsd/memory_freebsd_cgo.go +++ b/providers/freebsd/kvm_freebsd_cgo.go @@ -38,7 +38,10 @@ import ( "golang.org/x/sys/unix" ) +// kvmGetSwapInfo returns swap summary statistics for the system. It accesses +// the kernel virtual memory (kvm) images by using libkvm. func kvmGetSwapInfo() (*kvmSwap, error) { + // Obtain a KVM file descriptor. var errstr *C.char kd := C.kvm_open(nil, nil, nil, unix.O_RDONLY, errstr) if errstr != nil { diff --git a/system_test.go b/system_test.go index 1d2b0677..ece2b138 100644 --- a/system_test.go +++ b/system_test.go @@ -258,6 +258,16 @@ func TestSelf(t *testing.T) { } } + if v, ok := process.(types.NetworkCounters); ok { + t.Log("Getting process NetworkCounters()") + + counters, err := v.NetworkCounters() + if assert.NoError(t, err) { + assert.NotZero(t, counters) + output["process.network_counters"] = counters + } + } + logAsJSON(t, output) } From 1eac8f6cce95f4fc34a67d4f2c6b9933dc3a261e Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 00:26:50 -0400 Subject: [PATCH 41/61] return error from host CPUTime() --- providers/freebsd/host_freebsd_cgo.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index e27044d0..8fc7fe1d 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -66,8 +66,7 @@ func (h *host) CPUTime() (types.CPUTimes, error) { cpu := types.CPUTimes{} r := &reader{} r.cpuTime(&cpu) - - return cpu, nil + return cpu, r.Err() } func (h *host) Memory() (*types.HostMemoryInfo, error) { @@ -122,7 +121,6 @@ func (r *reader) Err() error { func (r *reader) cpuTime(cpu *types.CPUTimes) { cptime, err := Cptime() - if r.addErr(err) { return } @@ -211,7 +209,6 @@ func (r *reader) bootTime(h *host) { if r.addErr(err) { return } - h.info.BootTime = v } From 46fd95b8839e3627f9ba35d0f42ab3f039dfba46 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 00:29:27 -0400 Subject: [PATCH 42/61] replace ioutil.ReadFile with os.ReadFile ioutil is deprecated. --- providers/freebsd/process_freebsd_cgo.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index c798ddfc..46c85d3a 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -76,7 +76,6 @@ import "C" import ( "errors" "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -311,9 +310,9 @@ func (p *process) User() (types.UserInfo, error) { }, nil } -// NetworkStats reports network stats for an individual PID. +// NetworkCounters reports network stats for the process. func (p *process) NetworkCounters() (*types.NetworkCountersInfo, error) { - snmpRaw, err := ioutil.ReadFile(p.path("net/snmp")) + snmpRaw, err := os.ReadFile(p.path("net/snmp")) if err != nil { return nil, err } @@ -322,7 +321,7 @@ func (p *process) NetworkCounters() (*types.NetworkCountersInfo, error) { return nil, err } - netstatRaw, err := ioutil.ReadFile(p.path("net/netstat")) + netstatRaw, err := os.ReadFile(p.path("net/netstat")) if err != nil { return nil, err } @@ -340,7 +339,6 @@ func (p *process) PID() int { func (p *process) OpenHandles() ([]string, error) { procstat := C.procstat_open_sysctl() - if procstat == nil { return nil, errors.New("failed to open procstat sysctl") } @@ -363,7 +361,6 @@ func (p *process) OpenHandles() ([]string, error) { func (p *process) OpenHandleCount() (int, error) { procstat := C.procstat_open_sysctl() - if procstat == nil { return 0, errors.New("failed to open procstat sysctl") } From 69bf19c2175adfba96f9fd9e4f5202c8ed5c921c Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 01:44:24 -0400 Subject: [PATCH 43/61] fix handling of kern.cp_time === RUN TestHost system_test.go:309: error parsing kern.cp_time: strconv.ParseUint: parsing "\xd8\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Q\x05\x00\x00\x00\x00\x00\x00\xce\x01\x00\x00\x00\x00\x00\x00k": invalid syntax --- providers/freebsd/host_freebsd_cgo.go | 9 +-- providers/freebsd/process_freebsd_cgo.go | 36 ---------- providers/freebsd/syscall_freebsd_cgo.go | 84 ------------------------ providers/freebsd/sysctl_freebsd.go | 46 +++++++++++++ providers/freebsd/sysctl_freebsd_test.go | 11 ++++ 5 files changed, 59 insertions(+), 127 deletions(-) delete mode 100644 providers/freebsd/syscall_freebsd_cgo.go diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index 8fc7fe1d..ad773e16 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -120,16 +120,11 @@ func (r *reader) Err() error { } func (r *reader) cpuTime(cpu *types.CPUTimes) { - cptime, err := Cptime() + times, err := cpuStateTimes() if r.addErr(err) { return } - - cpu.User = time.Duration(cptime["User"]) - cpu.System = time.Duration(cptime["System"]) - cpu.Idle = time.Duration(cptime["Idle"]) - cpu.Nice = time.Duration(cptime["Nice"]) - cpu.IRQ = time.Duration(cptime["IRQ"]) + *cpu = *times } func (r *reader) memInfo(m *types.HostMemoryInfo) { diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 46c85d3a..08885815 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -81,8 +81,6 @@ import ( "strings" "time" - "golang.org/x/sys/unix" - "github.com/elastic/go-sysinfo/types" ) @@ -398,40 +396,6 @@ func (s freebsdSystem) Self() (types.Process, error) { return s.Process(os.Getpid()) } -const ( - kernCptimeMIB = "kern.cp_time" - kernClockrateMIB = "kern.clockrate" -) - -func Cptime() (map[string]uint64, error) { - var clock clockInfo - - if err := sysctlByName(kernClockrateMIB, &clock); err != nil { - return make(map[string]uint64), fmt.Errorf("failed to get kern.clockrate: %w", err) - } - - cptime, err := unix.Sysctl(kernCptimeMIB) - if err != nil { - return make(map[string]uint64), fmt.Errorf("failed to get kern.cp_time: %w", err) - } - - cpMap := make(map[string]uint64) - - times := strings.Split(cptime, " ") - names := [5]string{"User", "Nice", "System", "IRQ", "Idle"} - - for index, time := range times { - i, err := strconv.ParseUint(time, 10, 64) - if err != nil { - return cpMap, fmt.Errorf("error parsing kern.cp_time: %w", err) - } - - cpMap[names[index]] = i * uint64(clock.Tick) * 1000 - } - - return cpMap, nil -} - func (p *process) Parent() (types.Process, error) { info, err := p.Info() if err != nil { diff --git a/providers/freebsd/syscall_freebsd_cgo.go b/providers/freebsd/syscall_freebsd_cgo.go deleted file mode 100644 index 8bd72a28..00000000 --- a/providers/freebsd/syscall_freebsd_cgo.go +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build freebsd && cgo - -package freebsd - -//#include -//#include -import "C" - -import ( - "bytes" - "encoding/binary" - "sync" - "unsafe" -) - -// Buffer Pool - -var bufferPool = sync.Pool{ - New: func() interface{} { - return &poolMem{ - buf: make([]byte, 512), - } - }, -} - -type poolMem struct { - buf []byte - pool *sync.Pool -} - -func getPoolMem() *poolMem { - pm := bufferPool.Get().(*poolMem) - pm.buf = pm.buf[0:cap(pm.buf)] - pm.pool = &bufferPool - return pm -} - -func (m *poolMem) Release() { m.pool.Put(m) } - -func sysctlbyname(name string, value interface{}) error { - mem := getPoolMem() - defer mem.Release() - - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - size := C.ulong(len(mem.buf)) - if n, err := C.sysctlbyname(cname, unsafe.Pointer(&mem.buf[0]), &size, nil, C.ulong(0)); n != 0 { - return err - } - - data := mem.buf[0:size] - - switch v := value.(type) { - case *[]byte: - out := make([]byte, len(data)) - copy(out, data) - *v = out - return nil - default: - return binary.Read(bytes.NewReader(data), binary.LittleEndian, v) - } -} - -func sysctlByName(name string, out interface{}) error { - return sysctlbyname(name, out) -} diff --git a/providers/freebsd/sysctl_freebsd.go b/providers/freebsd/sysctl_freebsd.go index 4c6287d9..bd829283 100644 --- a/providers/freebsd/sysctl_freebsd.go +++ b/providers/freebsd/sysctl_freebsd.go @@ -23,13 +23,25 @@ import ( "fmt" "strconv" "strings" + "sync" "time" + "unsafe" "github.com/elastic/go-sysinfo/types" "golang.org/x/sys/unix" ) +var tickDuration = sync.OnceValues(func() (time.Duration, error) { + const mib = "kern.clockrate" + + c, err := unix.SysctlClockinfo(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + return time.Duration(c.Tick) * time.Microsecond, nil +}) + func activePageCount() (uint32, error) { const mib = "vm.stats.vm.v_active_count" @@ -86,6 +98,40 @@ func cachePageCount() (uint32, error) { return v, nil } +const sizeOfUint64 = int(unsafe.Sizeof(uint64(0))) + +// cpuStateTimes uses sysctl kern.cp_time to get the amount of time spent in +// different CPU states. +func cpuStateTimes() (*types.CPUTimes, error) { + tickDuration, err := tickDuration() + if err != nil { + return nil, err + } + + const mib = "kern.cp_time" + buf, err := unix.SysctlRaw("kern.cp_time") + if err != nil { + return nil, fmt.Errorf("failed to get %s: %w", mib, err) + } + + var clockTicks [unix.CPUSTATES]uint64 + if len(buf) < len(clockTicks)*sizeOfUint64 { + return nil, fmt.Errorf("kern.cp_time data is too short (got %d bytes)", len(buf)) + } + for i := range clockTicks { + val := *(*uint64)(unsafe.Pointer(&buf[sizeOfUint64*i])) + clockTicks[i] = val + } + + return &types.CPUTimes{ + User: time.Duration(clockTicks[unix.CP_USER]) * tickDuration, + System: time.Duration(clockTicks[unix.CP_SYS]) * tickDuration, + Idle: time.Duration(clockTicks[unix.CP_IDLE]) * tickDuration, + IRQ: time.Duration(clockTicks[unix.CP_INTR]) * tickDuration, + Nice: time.Duration(clockTicks[unix.CP_NICE]) * tickDuration, + }, nil +} + func freePageCount() (uint32, error) { const mib = "vm.stats.vm.v_free_count" diff --git a/providers/freebsd/sysctl_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go index 2ba1f916..613371e4 100644 --- a/providers/freebsd/sysctl_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestArchitecture(t *testing.T) { @@ -45,6 +46,16 @@ func TestBootTime(t *testing.T) { assert.WithinDuration(t, time.Now().UTC(), bootTime, 365*24*time.Hour) } +func TestCPUStateTimes(t *testing.T) { + times, err := cpuStateTimes() + if err != nil { + t.Fatal(err) + } + + require.NotNil(t, times) + assert.NotZero(t, *times) +} + func TestKernelVersion(t *testing.T) { kernel, err := kernelVersion() if err != nil { From 8f0f7aa9e9d0984fafe8776185224047c7d5dfca Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 02:20:27 -0400 Subject: [PATCH 44/61] initialize process.fs to fix NetworkCounters system_test.go:265: Error Trace: system_test.go:265 Error: Received unexpected error: open 1620/net/snmp: no such file or directory Test: TestSelf --- providers/freebsd/process_freebsd_cgo.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 08885815..40ea4fb8 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -388,8 +388,10 @@ func (s freebsdSystem) Processes() ([]types.Process, error) { } func (s freebsdSystem) Process(pid int) (types.Process, error) { - p := process{pid: pid} - return &p, nil + return &process{ + pid: pid, + fs: s.procFS, + }, nil } func (s freebsdSystem) Self() (types.Process, error) { From 0f176f367d21b9094b556d1e05c2983e8b391902 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 02:54:13 -0400 Subject: [PATCH 45/61] remove NetworkCounters implementation The implementation was not tested and did not work. I don't think FreeBSD has /proc//net/{snmp,netstat} paths. --- README.md | 2 +- providers/freebsd/host_freebsd_cgo.go | 14 +-- providers/freebsd/host_freebsd_cgo_test.go | 30 ++--- providers/freebsd/process_freebsd_cgo.go | 33 +----- providers/freebsd/procnet.go | 127 --------------------- system_test.go | 1 - 6 files changed, 22 insertions(+), 185 deletions(-) delete mode 100644 providers/freebsd/procnet.go diff --git a/README.md b/README.md index e6cce66d..7e603282 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ that are implemented. | `OpenHandleCounter` | | x | | | x | | `Seccomp` | | x | | | | | `Capabilities` | | x | | | | -| `NetworkCounters` | | x | | | x | +| `NetworkCounters` | | x | | | | ### GOOS / GOARCH Pairs diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index ad773e16..b9fb9650 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -34,19 +34,13 @@ import ( ) func init() { - registry.Register(newFreeBSDSystem("")) + registry.Register(newFreeBSDSystem()) } -type freebsdSystem struct { - procFS procFS -} +type freebsdSystem struct{} -func newFreeBSDSystem(hostFS string) freebsdSystem { - mountPoint := filepath.Join(hostFS, procfs.DefaultMountPoint) - fs, _ := procfs.NewFS(mountPoint) - return freebsdSystem{ - procFS: procFS{FS: fs, mountPoint: mountPoint}, - } +func newFreeBSDSystem() freebsdSystem { + return freebsdSystem{} } func (s freebsdSystem) Host() (types.Host, error) { diff --git a/providers/freebsd/host_freebsd_cgo_test.go b/providers/freebsd/host_freebsd_cgo_test.go index 7a1ea746..4345cf01 100644 --- a/providers/freebsd/host_freebsd_cgo_test.go +++ b/providers/freebsd/host_freebsd_cgo_test.go @@ -29,23 +29,25 @@ import ( var _ registry.HostProvider = freebsdSystem{} func TestHost(t *testing.T) { - host, err := newFreeBSDSystem("").Host() + host, err := newFreeBSDSystem().Host() if err != nil { t.Fatal(err) } - info := host.Info() - data, _ := json.MarshalIndent(info, "", " ") - t.Log(string(data)) -} + t.Run("Info", func(t *testing.T) { + info := host.Info() -func TestHostMemoryInfo(t *testing.T) { - host, err := newFreeBSDSystem("").Host() - if err != nil { - t.Fatal(err) - } - _, err = host.Memory() - if err != nil { - t.Fatal(err) - } + data, _ := json.MarshalIndent(info, "", " ") + t.Log(string(data)) + }) + + t.Run("Memory", func(t *testing.T) { + mem, err := host.Memory() + if err != nil { + t.Fatal(err) + } + + data, _ := json.MarshalIndent(mem, "", " ") + t.Log(string(data)) + }) } diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 40ea4fb8..69ee736b 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -110,10 +110,6 @@ func getProcInfo(op, arg int) ([]process, error) { return procs, nil } -func (p *process) path(pa ...string) string { - return p.fs.path(append([]string{strconv.Itoa(p.PID())}, pa...)...) -} - func copyArray(from **C.char) []string { if from == nil { return nil @@ -226,7 +222,6 @@ func getProcCWD(p *process) (string, error) { type process struct { pid int - fs procFS kinfo C.struct_kinfo_proc } @@ -308,29 +303,6 @@ func (p *process) User() (types.UserInfo, error) { }, nil } -// NetworkCounters reports network stats for the process. -func (p *process) NetworkCounters() (*types.NetworkCountersInfo, error) { - snmpRaw, err := os.ReadFile(p.path("net/snmp")) - if err != nil { - return nil, err - } - snmp, err := getNetSnmpStats(snmpRaw) - if err != nil { - return nil, err - } - - netstatRaw, err := os.ReadFile(p.path("net/netstat")) - if err != nil { - return nil, err - } - netstat, err := getNetstatStats(netstatRaw) - if err != nil { - return nil, err - } - - return &types.NetworkCountersInfo{SNMP: snmp, Netstat: netstat}, nil -} - func (p *process) PID() int { return p.pid } @@ -388,10 +360,7 @@ func (s freebsdSystem) Processes() ([]types.Process, error) { } func (s freebsdSystem) Process(pid int) (types.Process, error) { - return &process{ - pid: pid, - fs: s.procFS, - }, nil + return &process{pid: pid}, nil } func (s freebsdSystem) Self() (types.Process, error) { diff --git a/providers/freebsd/procnet.go b/providers/freebsd/procnet.go deleted file mode 100644 index a676fb61..00000000 --- a/providers/freebsd/procnet.go +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package freebsd - -import ( - "errors" - "fmt" - "reflect" - "strconv" - "strings" - - "github.com/elastic/go-sysinfo/types" -) - -// getNetSnmpStats pulls snmp stats from /proc/net -func getNetSnmpStats(raw []byte) (types.SNMP, error) { - snmpData, err := parseNetFile(string(raw)) - if err != nil { - return types.SNMP{}, fmt.Errorf("error parsing SNMP: %w", err) - } - output := types.SNMP{} - fillStruct(&output, snmpData) - - return output, nil -} - -// fillStruct is some reflection work that can dynamically fill one of our tagged `netstat` structs with netstat data -func fillStruct(str interface{}, data map[string]map[string]uint64) { - val := reflect.ValueOf(str).Elem() - typ := reflect.TypeOf(str).Elem() - - for i := 0; i < typ.NumField(); i++ { - field := typ.Field(i) - if tag := field.Tag.Get("netstat"); tag != "" { - if values, ok := data[tag]; ok { - val.Field(i).Set(reflect.ValueOf(values)) - } - } - } -} - -// parseNetFile parses an entire file, and returns a 2D map, representing how files are sorted by protocol -func parseNetFile(body string) (map[string]map[string]uint64, error) { - fileMetrics := make(map[string]map[string]uint64) - bodySplit := strings.Split(strings.TrimSpace(body), "\n") - // There should be an even number of lines. If not, something is wrong. - if len(bodySplit)%2 != 0 { - return nil, fmt.Errorf("badly parsed body: %s", body) - } - // in the network counters, data is divided into two-line sections: a line of keys, and a line of values - // With each line - for index := 0; index < len(bodySplit); index += 2 { - keysSplit := strings.Split(bodySplit[index], ":") - valuesSplit := strings.Split(bodySplit[index+1], ":") - if len(keysSplit) != 2 || len(valuesSplit) != 2 { - return nil, fmt.Errorf("wrong number of keys: %#v", keysSplit) - } - valMap, err := parseEntry(keysSplit[1], valuesSplit[1]) - if err != nil { - return nil, fmt.Errorf("error parsing lines: %w", err) - } - fileMetrics[valuesSplit[0]] = valMap - } - return fileMetrics, nil -} - -// parseEntry parses two lines from the net files, the first line being keys, the second being values -func parseEntry(line1, line2 string) (map[string]uint64, error) { - keyArr := strings.Split(strings.TrimSpace(line1), " ") - valueArr := strings.Split(strings.TrimSpace(line2), " ") - - if len(keyArr) != len(valueArr) { - return nil, errors.New("key and value lines are mismatched") - } - - counters := make(map[string]uint64, len(valueArr)) - for iter, value := range valueArr { - - // This if-else block is to deal with the MaxConn value in SNMP, - // which is a signed value according to RFC2012. - // This library emulates the behavior of the kernel: store all values as a uint, then cast to a signed value for printing - // Users of this library need to be aware that this value should be printed as a signed int or hex value to make it useful. - var parsed uint64 - var err error - if strings.Contains(value, "-") { - signedParsed, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing string to int in line: %#v: %w", valueArr, err) - } - parsed = uint64(signedParsed) - } else { - parsed, err = strconv.ParseUint(value, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing string to int in line: %#v: %w", valueArr, err) - } - } - - counters[keyArr[iter]] = parsed - } - return counters, nil -} - -// getNetstatStats pulls netstat stats from /proc/net -func getNetstatStats(raw []byte) (types.Netstat, error) { - netstatData, err := parseNetFile(string(raw)) - if err != nil { - return types.Netstat{}, fmt.Errorf("error parsing netstat: %w", err) - } - output := types.Netstat{} - fillStruct(&output, netstatData) - return output, nil -} diff --git a/system_test.go b/system_test.go index ece2b138..e847f234 100644 --- a/system_test.go +++ b/system_test.go @@ -69,7 +69,6 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ Environment: true, OpenHandleEnumerator: true, OpenHandleCounter: true, - NetworkCounters: true, }, } From 07014473949f80dd4eaf026b9b57eab94e661abd Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 03:14:36 -0400 Subject: [PATCH 46/61] comment for HostMemroyInfo implementation --- providers/freebsd/host_freebsd_cgo.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index b9fb9650..48629085 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -122,6 +122,12 @@ func (r *reader) cpuTime(cpu *types.CPUTimes) { } func (r *reader) memInfo(m *types.HostMemoryInfo) { + // Memory counter calculations: + // total = physical memory + // used = active + wired + // free = free + // available = buffers + inactive + cache + free + ps, err := pageSizeBytes() if r.addErr(err) { return From 6f819576f477505301c5ee915e73094625617e24 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 03:32:11 -0400 Subject: [PATCH 47/61] add process.open_handle_count to test output --- system_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_test.go b/system_test.go index e847f234..5d64b9cd 100644 --- a/system_test.go +++ b/system_test.go @@ -233,7 +233,7 @@ func TestSelf(t *testing.T) { count, err := v.OpenHandleCount() if assert.NoError(t, err) { - t.Log("open handles count:", count) + output["process.open_handle_count"] = count } } From 60b935fd637c8097bd2c4dc6278fc9676d913ba6 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 03:49:03 -0400 Subject: [PATCH 48/61] multiple ki_rssize by page size --- providers/freebsd/process_freebsd_cgo.go | 10 +++++++--- providers/freebsd/sysctl_freebsd.go | 22 +++++++++++----------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 69ee736b..a87672b4 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -86,7 +86,6 @@ import ( func getProcInfo(op, arg int) ([]process, error) { procstat, err := C.procstat_open_sysctl() - if procstat == nil { return nil, fmt.Errorf("failed to open procstat sysctl: %w", err) } @@ -273,6 +272,11 @@ func (p *process) Info() (types.ProcessInfo, error) { } func (p *process) Memory() (types.MemoryInfo, error) { + pageSize, err := pageSizeBytes() + if err != nil { + return types.MemoryInfo{}, err + } + procs, err := getProcInfo(C.KERN_PROC_PID, p.PID()) if err != nil { return types.MemoryInfo{}, err @@ -280,8 +284,8 @@ func (p *process) Memory() (types.MemoryInfo, error) { p.kinfo = procs[0].kinfo return types.MemoryInfo{ - Resident: uint64(p.kinfo.ki_rssize), - Virtual: uint64(p.kinfo.ki_size), + Resident: uint64(p.kinfo.ki_rssize) * uint64(pageSize), + Virtual: uint64(p.kinfo.ki_size) * uint64(pageSize), }, nil } diff --git a/providers/freebsd/sysctl_freebsd.go b/providers/freebsd/sysctl_freebsd.go index bd829283..acb20e45 100644 --- a/providers/freebsd/sysctl_freebsd.go +++ b/providers/freebsd/sysctl_freebsd.go @@ -42,6 +42,17 @@ var tickDuration = sync.OnceValues(func() (time.Duration, error) { return time.Duration(c.Tick) * time.Microsecond, nil }) +var pageSizeBytes = sync.OnceValues(func() (uint32, error) { + const mib = "vm.stats.vm.v_page_size" + + v, err := unix.SysctlUint32(mib) + if err != nil { + return 0, fmt.Errorf("failed to get %s: %w", mib, err) + } + + return v, nil +}) + func activePageCount() (uint32, error) { const mib = "vm.stats.vm.v_active_count" @@ -216,17 +227,6 @@ func operatingSystem() (*types.OSInfo, error) { return info, nil } -func pageSizeBytes() (uint32, error) { - const mib = "vm.stats.vm.v_page_size" - - v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) - } - - return v, nil -} - func totalPhysicalMem() (uint64, error) { const mib = "hw.physmem" From 9ffae07818c38b251ab61f394742e8d82089f386 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 04:01:51 -0400 Subject: [PATCH 49/61] timevalToDuration - use time constants Based on the timeval definition, I think the conversion to time.Duration was incorrect. https://elixir.bootlin.com/freebsd/v14.0/source/sys/sys/_timeval.h#L47 --- providers/freebsd/process_freebsd_cgo.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index a87672b4..af0f9b90 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -225,7 +225,8 @@ type process struct { } func timevalToDuration(tm C.struct_timeval) time.Duration { - return (time.Duration(tm.tv_sec) * 1000000) + (time.Duration(tm.tv_usec) * 1000) + return time.Duration(tm.tv_sec)*time.Second + + time.Duration(tm.tv_usec)*time.Microsecond } func (p *process) CPUTime() (types.CPUTimes, error) { From d3ab680ddf6372b6867df1120537067b8b6f4862 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 11 Apr 2024 04:02:25 -0400 Subject: [PATCH 50/61] ki_size does not need multiplied by page size https://elixir.bootlin.com/freebsd/v14.0/source/sys/sys/user.h#L153 --- providers/freebsd/process_freebsd_cgo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index af0f9b90..42dba6a5 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -286,7 +286,7 @@ func (p *process) Memory() (types.MemoryInfo, error) { return types.MemoryInfo{ Resident: uint64(p.kinfo.ki_rssize) * uint64(pageSize), - Virtual: uint64(p.kinfo.ki_size) * uint64(pageSize), + Virtual: uint64(p.kinfo.ki_size), }, nil } From 799d7795e9bbd4ff1995247eab3a7024527ae3dd Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 16 May 2024 09:25:55 -0400 Subject: [PATCH 51/61] apply OpenHandles path from jurajlutter --- providers/freebsd/process_freebsd_cgo.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 42dba6a5..8c97b5e8 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -61,10 +61,10 @@ unsigned int countFileStats(struct filestat_list *head) { void copyFileStats(struct filestat_list *head, struct filestat *out, unsigned int size) { unsigned int index = 0; struct filestat *fst; + if (size == 0) { + return; + } STAILQ_FOREACH(fst, head, next) { - if (!size) { - break; - } memcpy(out, fst, sizeof(*fst)); ++out; --size; @@ -326,7 +326,7 @@ func (p *process) OpenHandles() ([]string, error) { names := make([]string, 0, len(files)) for _, file := range files { - if file.fs_uflags == 0 { + if file.fs_type == 1 { names = append(names, C.GoString(file.fs_path)) } } From 5c5317eb511539b1749ec50cd1572ea71774f7a6 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 16 May 2024 09:50:22 -0400 Subject: [PATCH 52/61] filter empty paths from OpenHandles --- providers/freebsd/process_freebsd_cgo.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 8c97b5e8..83cbf124 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -327,7 +327,9 @@ func (p *process) OpenHandles() ([]string, error) { for _, file := range files { if file.fs_type == 1 { - names = append(names, C.GoString(file.fs_path)) + if path := C.GoString(file.fs_path); path != "" { + names = append(names, path) + } } } From 534a478fcd19695256cdc83c3c3bdbc348f9d669 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Wed, 19 Jun 2024 12:23:59 -0400 Subject: [PATCH 53/61] TestBootTime() compare with system uptime binary output --- providers/freebsd/sysctl_freebsd_test.go | 39 +++++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/providers/freebsd/sysctl_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go index 613371e4..f2ba62b0 100644 --- a/providers/freebsd/sysctl_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -20,11 +20,12 @@ package freebsd import ( - "testing" - "time" - + "encoding/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "os/exec" + "testing" + "time" ) func TestArchitecture(t *testing.T) { @@ -42,8 +43,36 @@ func TestBootTime(t *testing.T) { t.Fatal(err) } - // Apply a sanity check. This assumes the host has rebooted in the last year. - assert.WithinDuration(t, time.Now().UTC(), bootTime, 365*24*time.Hour) + bootDiff := time.Since(bootTime) + // t.Logf("bootTime in seconds: %#v", int64(bootDiff.Seconds())) + + cmd := exec.Command("/usr/bin/uptime", "--libxo=json") + upcmd, err := cmd.Output() + + if err != nil { + t.Fatal(err) + } + + t.Logf(string(upcmd)) + + type UptimeOutput struct { + UptimeInformation struct { + Uptime int64 `json:"uptime"` + } `json:"uptime-information"` + } + + var upInfo UptimeOutput + err = json.Unmarshal(upcmd, &upInfo) + + if err != nil { + t.Fatal(err) + } + + upsec := upInfo.UptimeInformation.Uptime + uptime := time.Duration(upsec * int64(time.Second)) + // t.Logf("uptime in seconds: %#v", int64(uptime.Seconds())) + + assert.InDelta(t, uptime, bootDiff, float64(5*time.Second)) } func TestCPUStateTimes(t *testing.T) { From f9fda7545352e82a561e566d82d9b7b48480b162 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Wed, 19 Jun 2024 13:16:13 -0400 Subject: [PATCH 54/61] Moved sysctl value casts into helper methods --- providers/freebsd/host_freebsd_cgo.go | 19 +++++++++---------- providers/freebsd/process_freebsd_cgo.go | 2 +- providers/freebsd/sysctl_freebsd.go | 24 ++++++++++++------------ 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index 48629085..15fc9aed 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -128,11 +128,10 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { // free = free // available = buffers + inactive + cache + free - ps, err := pageSizeBytes() + pageSize, err := pageSizeBytes() if r.addErr(err) { return } - pageSize := uint64(ps) m.Total, err = totalPhysicalMem() if r.addErr(err) { @@ -144,31 +143,31 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { return } m.Metrics = make(map[string]uint64, 6) - m.Metrics["active_bytes"] = uint64(activePages) * pageSize + m.Metrics["active_bytes"] = activePages * pageSize wirePages, err := wirePageCount() if r.addErr(err) { return } - m.Metrics["wired_bytes"] = uint64(wirePages) * pageSize + m.Metrics["wired_bytes"] = wirePages * pageSize inactivePages, err := inactivePageCount() if r.addErr(err) { return } - m.Metrics["inactive_bytes"] = uint64(inactivePages) * pageSize + m.Metrics["inactive_bytes"] = inactivePages * pageSize cachePages, err := cachePageCount() if r.addErr(err) { return } - m.Metrics["cache_bytes"] = uint64(cachePages) * pageSize + m.Metrics["cache_bytes"] = cachePages * pageSize freePages, err := freePageCount() if r.addErr(err) { return } - m.Metrics["free_bytes"] = uint64(freePages) * pageSize + m.Metrics["free_bytes"] = freePages * pageSize buffers, err := buffersUsedBytes() if r.addErr(err) { @@ -176,9 +175,9 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { } m.Metrics["buffer_bytes"] = buffers - m.Used = uint64(activePages+wirePages) * pageSize - m.Free = uint64(freePages) * pageSize - m.Available = uint64(inactivePages+cachePages+freePages)*pageSize + buffers + m.Used = (activePages + wirePages) * pageSize + m.Free = freePages * pageSize + m.Available = (inactivePages+cachePages+freePages)*pageSize + buffers // Virtual (swap) Memory swap, err := kvmGetSwapInfo() diff --git a/providers/freebsd/process_freebsd_cgo.go b/providers/freebsd/process_freebsd_cgo.go index 83cbf124..ab6b506e 100644 --- a/providers/freebsd/process_freebsd_cgo.go +++ b/providers/freebsd/process_freebsd_cgo.go @@ -285,7 +285,7 @@ func (p *process) Memory() (types.MemoryInfo, error) { p.kinfo = procs[0].kinfo return types.MemoryInfo{ - Resident: uint64(p.kinfo.ki_rssize) * uint64(pageSize), + Resident: uint64(p.kinfo.ki_rssize) * pageSize, Virtual: uint64(p.kinfo.ki_size), }, nil } diff --git a/providers/freebsd/sysctl_freebsd.go b/providers/freebsd/sysctl_freebsd.go index acb20e45..60c91205 100644 --- a/providers/freebsd/sysctl_freebsd.go +++ b/providers/freebsd/sysctl_freebsd.go @@ -42,7 +42,7 @@ var tickDuration = sync.OnceValues(func() (time.Duration, error) { return time.Duration(c.Tick) * time.Microsecond, nil }) -var pageSizeBytes = sync.OnceValues(func() (uint32, error) { +var pageSizeBytes = sync.OnceValues(func() (uint64, error) { const mib = "vm.stats.vm.v_page_size" v, err := unix.SysctlUint32(mib) @@ -50,17 +50,17 @@ var pageSizeBytes = sync.OnceValues(func() (uint32, error) { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return v, nil + return uint64(v), nil }) -func activePageCount() (uint32, error) { +func activePageCount() (uint64, error) { const mib = "vm.stats.vm.v_active_count" v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return v, nil + return uint64(v), nil } func architecture() (string, error) { @@ -98,7 +98,7 @@ func buffersUsedBytes() (uint64, error) { return v, nil } -func cachePageCount() (uint32, error) { +func cachePageCount() (uint64, error) { const mib = "vm.stats.vm.v_cache_count" v, err := unix.SysctlUint32(mib) @@ -106,7 +106,7 @@ func cachePageCount() (uint32, error) { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return v, nil + return uint64(v), nil } const sizeOfUint64 = int(unsafe.Sizeof(uint64(0))) @@ -143,7 +143,7 @@ func cpuStateTimes() (*types.CPUTimes, error) { }, nil } -func freePageCount() (uint32, error) { +func freePageCount() (uint64, error) { const mib = "vm.stats.vm.v_free_count" v, err := unix.SysctlUint32(mib) @@ -151,10 +151,10 @@ func freePageCount() (uint32, error) { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return v, nil + return uint64(v), nil } -func inactivePageCount() (uint32, error) { +func inactivePageCount() (uint64, error) { const mib = "vm.stats.vm.v_inactive_count" v, err := unix.SysctlUint32(mib) @@ -162,7 +162,7 @@ func inactivePageCount() (uint32, error) { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return v, nil + return uint64(v), nil } func kernelVersion() (string, error) { @@ -237,12 +237,12 @@ func totalPhysicalMem() (uint64, error) { return v, nil } -func wirePageCount() (uint32, error) { +func wirePageCount() (uint64, error) { const mib = "vm.stats.vm.v_wire_count" v, err := unix.SysctlUint32(mib) if err != nil { return 0, fmt.Errorf("failed to get %s: %w", mib, err) } - return v, nil + return uint64(v), nil } From d3d45dfe19a278a4d801c991f9a6068046fe4c15 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Wed, 19 Jun 2024 13:17:54 -0400 Subject: [PATCH 55/61] Use mib const in sysctlRaw() --- providers/freebsd/sysctl_freebsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/freebsd/sysctl_freebsd.go b/providers/freebsd/sysctl_freebsd.go index 60c91205..52a51df4 100644 --- a/providers/freebsd/sysctl_freebsd.go +++ b/providers/freebsd/sysctl_freebsd.go @@ -120,7 +120,7 @@ func cpuStateTimes() (*types.CPUTimes, error) { } const mib = "kern.cp_time" - buf, err := unix.SysctlRaw("kern.cp_time") + buf, err := unix.SysctlRaw(mib) if err != nil { return nil, fmt.Errorf("failed to get %s: %w", mib, err) } From bbb75c4687b7190303dcbfeff01cdfab1320069e Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Wed, 19 Jun 2024 15:22:18 -0400 Subject: [PATCH 56/61] memInfo(): be less optimistic --- providers/freebsd/host_freebsd_cgo.go | 35 ++++------------- providers/freebsd/sysctl_freebsd.go | 56 +++++++++++++-------------- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index 15fc9aed..3b09fe39 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -133,46 +133,25 @@ func (r *reader) memInfo(m *types.HostMemoryInfo) { return } - m.Total, err = totalPhysicalMem() - if r.addErr(err) { - return - } + m.Total = totalPhysicalMem(r) + activePages := activePageCount(r) - activePages, err := activePageCount() - if r.addErr(err) { - return - } m.Metrics = make(map[string]uint64, 6) m.Metrics["active_bytes"] = activePages * pageSize - wirePages, err := wirePageCount() - if r.addErr(err) { - return - } + wirePages := wirePageCount(r) m.Metrics["wired_bytes"] = wirePages * pageSize - inactivePages, err := inactivePageCount() - if r.addErr(err) { - return - } + inactivePages := inactivePageCount(r) m.Metrics["inactive_bytes"] = inactivePages * pageSize - cachePages, err := cachePageCount() - if r.addErr(err) { - return - } + cachePages := cachePageCount(r) m.Metrics["cache_bytes"] = cachePages * pageSize - freePages, err := freePageCount() - if r.addErr(err) { - return - } + freePages := freePageCount(r) m.Metrics["free_bytes"] = freePages * pageSize - buffers, err := buffersUsedBytes() - if r.addErr(err) { - return - } + buffers := buffersUsedBytes(r) m.Metrics["buffer_bytes"] = buffers m.Used = (activePages + wirePages) * pageSize diff --git a/providers/freebsd/sysctl_freebsd.go b/providers/freebsd/sysctl_freebsd.go index 52a51df4..0e3e15a1 100644 --- a/providers/freebsd/sysctl_freebsd.go +++ b/providers/freebsd/sysctl_freebsd.go @@ -53,14 +53,14 @@ var pageSizeBytes = sync.OnceValues(func() (uint64, error) { return uint64(v), nil }) -func activePageCount() (uint64, error) { +func activePageCount(r *reader) uint64 { const mib = "vm.stats.vm.v_active_count" v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return uint64(v), nil + return uint64(v) } func architecture() (string, error) { @@ -87,26 +87,26 @@ func bootTime() (time.Time, error) { } // buffersUsedBytes returns the number memory bytes used as disk cache. -func buffersUsedBytes() (uint64, error) { +func buffersUsedBytes(r *reader) uint64 { const mib = "vfs.bufspace" v, err := unix.SysctlUint64(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return v, nil + return v } -func cachePageCount() (uint64, error) { +func cachePageCount(r *reader) uint64 { const mib = "vm.stats.vm.v_cache_count" v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return uint64(v), nil + return uint64(v) } const sizeOfUint64 = int(unsafe.Sizeof(uint64(0))) @@ -143,26 +143,26 @@ func cpuStateTimes() (*types.CPUTimes, error) { }, nil } -func freePageCount() (uint64, error) { +func freePageCount(r *reader) uint64 { const mib = "vm.stats.vm.v_free_count" v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return uint64(v), nil + return uint64(v) } -func inactivePageCount() (uint64, error) { +func inactivePageCount(r *reader) uint64 { const mib = "vm.stats.vm.v_inactive_count" v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return uint64(v), nil + return uint64(v) } func kernelVersion() (string, error) { @@ -227,22 +227,22 @@ func operatingSystem() (*types.OSInfo, error) { return info, nil } -func totalPhysicalMem() (uint64, error) { +func totalPhysicalMem(r *reader) uint64 { const mib = "hw.physmem" v, err := unix.SysctlUint64(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return v, nil + return v } -func wirePageCount() (uint64, error) { +func wirePageCount(r *reader) uint64 { const mib = "vm.stats.vm.v_wire_count" v, err := unix.SysctlUint32(mib) - if err != nil { - return 0, fmt.Errorf("failed to get %s: %w", mib, err) + if r.addErr(err) { + return 0 } - return uint64(v), nil + return uint64(v) } From be7058a03a083ca19a5fdee4d58dfa7ed51bc8d3 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Thu, 20 Jun 2024 10:36:24 -0400 Subject: [PATCH 57/61] Simplified reader.Err() --- providers/freebsd/host_freebsd_cgo.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/providers/freebsd/host_freebsd_cgo.go b/providers/freebsd/host_freebsd_cgo.go index 3b09fe39..52c9a285 100644 --- a/providers/freebsd/host_freebsd_cgo.go +++ b/providers/freebsd/host_freebsd_cgo.go @@ -107,10 +107,7 @@ func (r *reader) addErr(err error) bool { } func (r *reader) Err() error { - if len(r.errs) > 0 { - return errors.Join(r.errs...) - } - return nil + return errors.Join(r.errs...) } func (r *reader) cpuTime(cpu *types.CPUTimes) { From 1e82bbf1ac196f65faa0d420a95dd9df776b47dd Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Thu, 20 Jun 2024 13:04:13 -0400 Subject: [PATCH 58/61] TestArchitecture(): compare with known machine architecture (MACHINE_ARCH) values --- providers/freebsd/sysctl_freebsd_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/providers/freebsd/sysctl_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go index f2ba62b0..61a17301 100644 --- a/providers/freebsd/sysctl_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -35,6 +35,7 @@ func TestArchitecture(t *testing.T) { } assert.NotEmpty(t, arch) + assert.Regexp(t, `(amd64|i386|powerpc(64(le)?|spe)?|armv(6|7)|aarch64|riscv64*|mips(n)?(32|64)?(el)?|sparc64)`, arch) } func TestBootTime(t *testing.T) { From b119ea8bedd34dc05866720dc7bb018a6b2371f8 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Thu, 20 Jun 2024 13:13:41 -0400 Subject: [PATCH 59/61] TestMachineID(): validate host UUID --- providers/freebsd/sysctl_freebsd_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/providers/freebsd/sysctl_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go index 61a17301..434aa091 100644 --- a/providers/freebsd/sysctl_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -102,6 +102,7 @@ func TestMachineID(t *testing.T) { } assert.NotEmpty(t, machineID) + assert.Regexp(t, "^[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}$", machineID) } func TestOperatingSystem(t *testing.T) { From 144921732c7aceb657626ad443411df7c1401f01 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Thu, 20 Jun 2024 13:33:03 -0400 Subject: [PATCH 60/61] TestKernelVersion(): compare with currently running kernel version --- providers/freebsd/sysctl_freebsd_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/providers/freebsd/sysctl_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go index 434aa091..a40ade35 100644 --- a/providers/freebsd/sysctl_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "os/exec" + "strings" "testing" "time" ) @@ -92,7 +93,18 @@ func TestKernelVersion(t *testing.T) { t.Fatal(err) } + // Retrieve currently running kernel version + cmd := exec.Command("/bin/freebsd-version", "-r") + fbsdout, err := cmd.Output() + + if err != nil { + t.Fatal(err) + } + + fbsdver := strings.TrimSuffix(string(fbsdout), "\n") + assert.NotEmpty(t, kernel) + assert.EqualValues(t, kernel, fbsdver) } func TestMachineID(t *testing.T) { From 1106c15cd01752b5edd003b2bbdba130346041c1 Mon Sep 17 00:00:00 2001 From: "Saro G." Date: Thu, 20 Jun 2024 14:22:55 -0400 Subject: [PATCH 61/61] TestArchitecture(): revised architectures to compare against --- providers/freebsd/sysctl_freebsd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/freebsd/sysctl_freebsd_test.go b/providers/freebsd/sysctl_freebsd_test.go index a40ade35..2b125162 100644 --- a/providers/freebsd/sysctl_freebsd_test.go +++ b/providers/freebsd/sysctl_freebsd_test.go @@ -36,7 +36,7 @@ func TestArchitecture(t *testing.T) { } assert.NotEmpty(t, arch) - assert.Regexp(t, `(amd64|i386|powerpc(64(le)?|spe)?|armv(6|7)|aarch64|riscv64*|mips(n)?(32|64)?(el)?|sparc64)`, arch) + assert.Regexp(t, `(amd64|i386|powerpc|arm(64)?|riscv|mips|sparc64|pc98)`, arch) } func TestBootTime(t *testing.T) {