1
0
mirror of https://github.com/containers/podman.git synced 2026-02-05 06:45:31 +01:00

Merge pull request #27872 from containers/renovate/github.com-crc-org-vfkit-0.x

Update module github.com/crc-org/vfkit to v0.6.3
This commit is contained in:
Paul Holzinger
2026-01-19 15:58:34 +01:00
committed by GitHub
22 changed files with 883 additions and 348 deletions

10
go.mod
View File

@@ -19,7 +19,7 @@ require (
github.com/containers/psgo v1.10.0
github.com/containers/winquit v1.1.0
github.com/coreos/go-systemd/v22 v22.6.0
github.com/crc-org/vfkit v0.6.1
github.com/crc-org/vfkit v0.6.3
github.com/cyphar/filepath-securejoin v0.6.1
github.com/digitalocean/go-qemu v0.0.0-20250212194115-ee9b0668d242
github.com/docker/distribution v2.8.3+incompatible
@@ -57,7 +57,7 @@ require (
github.com/opencontainers/selinux v1.13.1
github.com/openshift/imagebuilder v1.2.19
github.com/rootless-containers/rootlesskit/v2 v2.3.5
github.com/shirou/gopsutil/v4 v4.25.11
github.com/shirou/gopsutil/v4 v4.25.12
github.com/sirupsen/logrus v1.9.4-0.20251023124752-b61f268f75b6
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
@@ -70,7 +70,7 @@ require (
golang.org/x/crypto v0.46.0
golang.org/x/net v0.48.0
golang.org/x/sync v0.19.0
golang.org/x/sys v0.39.0
golang.org/x/sys v0.40.0
golang.org/x/term v0.38.0
google.golang.org/grpc v1.77.0
google.golang.org/protobuf v1.36.11
@@ -99,7 +99,7 @@ require (
github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containernetworking/cni v1.3.0 // indirect
github.com/containers/common v0.62.2 // indirect
github.com/containers/common v0.64.2 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957 // indirect
github.com/coreos/go-oidc/v3 v3.16.0 // indirect
@@ -182,7 +182,7 @@ require (
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.14.0 // indirect

20
go.sum
View File

@@ -59,8 +59,8 @@ github.com/containernetworking/plugins v1.8.0 h1:WjGbV/0UQyo8A4qBsAh6GaDAtu1hevx
github.com/containernetworking/plugins v1.8.0/go.mod h1:JG3BxoJifxxHBhG3hFyxyhid7JgRVBu/wtooGEvWf1c=
github.com/containers/buildah v1.42.0 h1:hS+/sq6g74wNNYvX6d5/jx4awkwqibBxUxJ/P2oOETk=
github.com/containers/buildah v1.42.0/go.mod h1:azIYkIUVSEiVVQi4hPm9ZsxuVNqb8HdSMlvaBzr8MtU=
github.com/containers/common v0.62.2 h1:xO45OOoeq17EZMIDZoSyRqg7GXGcRHa9sXlrr75zH+U=
github.com/containers/common v0.62.2/go.mod h1:veFiR9iq2j3CHXtB4YnPHuOkSRdhIQ3bAY8AFMP/5bE=
github.com/containers/common v0.64.2 h1:1xepE7QwQggUXxmyQ1Dbh6Cn0yd7ktk14sN3McSWf5I=
github.com/containers/common v0.64.2/go.mod h1:o29GfYy4tefUuShm8mOn2AiL5Mpzdio+viHI7n24KJ4=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/gvisor-tap-vsock v0.8.7 h1:mFMMU5CIXO9sbtsgECc90loUHx15km3AN6Zuhg3X4qM=
@@ -84,8 +84,8 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/crc-org/vfkit v0.6.1 h1:JepqPrgzKBuM/jximOcwA5ZnQPMcdarPRCrF4jHFau8=
github.com/crc-org/vfkit v0.6.1/go.mod h1:M3UMhDHg4d9/KzcslwG4Zay8EUMe7Pv6vxG/++vJ//4=
github.com/crc-org/vfkit v0.6.3 h1:X6pHxDWx0jQRV6TpzCmI4tfPyW/2R03HEEiS5vNnsBU=
github.com/crc-org/vfkit v0.6.3/go.mod h1:jp0CkOQW5BAvSdJSV7OHUuTGRUo7+/fwaFHd+92pqR8=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
@@ -345,8 +345,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/sigstore/fulcio v1.8.1 h1:PmoQv3XmhjR2BWFWw5LcMUXJPmhyizOIL7HeYnpio58=
github.com/sigstore/fulcio v1.8.1/go.mod h1:7tP3KW9eCGlPYRj5N4MSuUOat7CkeIHuXZ2jAUQ+Rwc=
github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY=
@@ -477,8 +477,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -531,8 +531,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View File

@@ -16,7 +16,6 @@ import (
"math"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
@@ -32,6 +31,7 @@ type VirtualMachine struct {
Devices []VirtioDevice `json:"devices,omitempty"`
Timesync *TimeSync `json:"timesync,omitempty"`
Ignition *Ignition `json:"ignition,omitempty"`
Nested bool `json:"nested,omitempty"`
}
// TimeSync enables synchronization of the host time to the linux guest after the host was suspended.
@@ -42,7 +42,8 @@ type TimeSync struct {
type Ignition struct {
ConfigPath string `json:"configPath"`
SocketPath string `json:"socketPath"`
SocketPath string `json:"socketPath,omitempty"`
VsockPort uint32 `json:"-"`
}
// The VMComponent interface represents a VM element (device, bootloader, ...)
@@ -53,8 +54,9 @@ type VMComponent interface {
}
const (
ignitionVsockPort uint = 1024
ignitionSocketName string = "ignition.sock"
// the ignition vsock port is hardcoded to 1024 in ignition source code:
// https://github.com/coreos/ignition/blob/d4ff84b2c28a28ad828b974befe3575563faacdd/internal/providers/applehv/applehv.go#L59-L68
ignitionVsockPort uint32 = 1024
)
// NewVirtualMachine creates a new VirtualMachine instance. The virtual machine
@@ -112,6 +114,10 @@ func (vm *VirtualMachine) ToCmdLine() ([]string, error) {
args = append(args, "--ignition", vm.Ignition.ConfigPath)
}
if vm.Nested {
args = append(args, "--nested")
}
return args, nil
}
@@ -156,26 +162,30 @@ func (vm *VirtualMachine) AddDevicesFromCmdLine(cmdlineOpts []string) error {
return nil
}
func (vm *VirtualMachine) VirtioGPUDevices() []*VirtioGPU {
gpuDevs := []*VirtioGPU{}
func FilterDevices[V VMComponent](vm *VirtualMachine) []V {
devs := []V{}
for _, dev := range vm.Devices {
if gpuDev, isVirtioGPU := dev.(*VirtioGPU); isVirtioGPU {
gpuDevs = append(gpuDevs, gpuDev)
if dev, isV := dev.(V); isV {
devs = append(devs, dev)
}
}
return devs
}
return gpuDevs
func (vm *VirtualMachine) VirtioGPUDevices() []*VirtioGPU {
return FilterDevices[*VirtioGPU](vm)
}
func (vm *VirtualMachine) VirtioVsockDevices() []*VirtioVsock {
vsockDevs := []*VirtioVsock{}
for _, dev := range vm.Devices {
if vsockDev, isVirtioVsock := dev.(*VirtioVsock); isVirtioVsock {
vsockDevs = append(vsockDevs, vsockDev)
}
}
return FilterDevices[*VirtioVsock](vm)
}
return vsockDevs
func (vm *VirtualMachine) VirtioInputDevices() []*VirtioInput {
return FilterDevices[*VirtioInput](vm)
}
func (vm *VirtualMachine) VirtioNetDevices() []*VirtioNet {
return FilterDevices[*VirtioNet](vm)
}
func (vm *VirtualMachine) NetworkBlockDevice(deviceID string) *NetworkBlockDevice {
@@ -217,13 +227,13 @@ func (vm *VirtualMachine) TimeSync() *TimeSync {
return vm.Timesync
}
func IgnitionNew(configPath string, socketPath string) (*Ignition, error) {
if configPath == "" || socketPath == "" {
return nil, fmt.Errorf("config path and socket path cannot be empty")
func IgnitionNew(configPath string, _ string) (*Ignition, error) {
if configPath == "" {
return nil, fmt.Errorf("config path cannot be empty")
}
return &Ignition{
ConfigPath: configPath,
SocketPath: socketPath,
VsockPort: ignitionVsockPort,
}, nil
}
@@ -233,16 +243,10 @@ func (vm *VirtualMachine) AddIgnitionFileFromCmdLine(cmdlineOpts string) error {
}
opts := strings.Split(cmdlineOpts, ",")
if len(opts) != 1 {
return fmt.Errorf("ignition only accept one option in command line argument")
return fmt.Errorf("ignition only accepts one option in command line argument")
}
socketPath := filepath.Join(os.TempDir(), ignitionSocketName)
dev, err := VirtioVsockNew(ignitionVsockPort, socketPath, true)
if err != nil {
return err
}
vm.Devices = append(vm.Devices, dev)
ignition, err := IgnitionNew(opts[0], socketPath)
ignition, err := IgnitionNew(opts[0], "")
if err != nil {
return err
}
@@ -256,7 +260,7 @@ func TimeSyncNew(vsockPort uint) (VMComponent, error) {
return nil, fmt.Errorf("invalid vsock port: %d", vsockPort)
}
return &TimeSync{
VsockPort: uint32(vsockPort), //#nosec G115 -- was compared to math.MaxUint32
VsockPort: uint32(vsockPort),
}, nil
}
@@ -276,7 +280,7 @@ func (ts *TimeSync) FromOptions(options []option) error {
if err != nil {
return err
}
ts.VsockPort = uint32(vsockPort) //#nosec G115 -- ParseUint(_, _, 32) guarantees no overflow
ts.VsockPort = uint32(vsockPort)
default:
return fmt.Errorf("unknown option for timesync parameter: %s", option.key)
}

View File

@@ -110,6 +110,9 @@ func unmarshalIgnition(rawMsg json.RawMessage) (Ignition, error) {
func unmarshalVirtioNet(rawMsg json.RawMessage) (*VirtioNet, error) {
var dev virtioNetForMarshalling
// defaults to true for backwards compatibility with vfkit versions which did not have this field
dev.VfkitMagic = true
err := json.Unmarshal(rawMsg, &dev)
if err != nil {
return nil, err
@@ -121,6 +124,10 @@ func unmarshalVirtioNet(rawMsg json.RawMessage) (*VirtioNet, error) {
}
dev.VirtioNet.MacAddress = macAddr
}
// vfkitMagic is only useful in combination with unixSocketPath
if dev.UnixSocketPath == "" {
dev.VfkitMagic = false
}
return &dev.VirtioNet, nil
}

View File

@@ -1,10 +1,12 @@
package config
import (
"bytes"
"fmt"
"math"
"net"
"os"
"slices"
"strconv"
"strings"
"time"
@@ -25,6 +27,7 @@ const (
// Default VirtioGPU Resolution
defaultVirtioGPUResolutionWidth = 800
defaultVirtioGPUResolutionHeight = 600
qcow2Header = "QFI\xfb"
)
// VirtioInput configures an input device, such as a keyboard or pointing device
@@ -101,12 +104,17 @@ type VirtioNet struct {
Socket *os.File `json:"socket,omitempty"`
UnixSocketPath string `json:"unixSocketPath,omitempty"`
VfkitMagic bool `json:"vfkitMagic,omitempty"`
}
// VirtioSerial configures the virtual machine serial ports.
type VirtioSerial struct {
LogFile string `json:"logFile,omitempty"`
UsesStdio bool `json:"usesStdio,omitempty"`
UsesPty bool `json:"usesPty,omitempty"`
// PtyName must not be set when creating the VM, from a user perspective, it's read-only,
// vfkit will set it during VM startup.
PtyName string `json:"ptyName,omitempty"`
}
type NBDSynchronizationMode string
@@ -230,12 +238,24 @@ func VirtioSerialNewStdio() (VirtioDevice, error) {
}, nil
}
func VirtioSerialNewPty() (VirtioDevice, error) {
return &VirtioSerial{
UsesPty: true,
}, nil
}
func (dev *VirtioSerial) validate() error {
if dev.LogFile != "" && dev.UsesStdio {
return fmt.Errorf("'logFilePath' and 'stdio' cannot be set at the same time")
}
if dev.LogFile == "" && !dev.UsesStdio {
return fmt.Errorf("one of 'logFilePath' or 'stdio' must be set")
if dev.LogFile != "" && dev.UsesPty {
return fmt.Errorf("'logFilePath' and 'pty' cannot be set at the same time")
}
if dev.UsesStdio && dev.UsesPty {
return fmt.Errorf("'stdio' and 'pty' cannot be set at the same time")
}
if dev.LogFile == "" && !dev.UsesStdio && !dev.UsesPty {
return fmt.Errorf("one of 'logFilePath', 'stdio' or 'pty' must be set")
}
return nil
@@ -245,11 +265,16 @@ func (dev *VirtioSerial) ToCmdLine() ([]string, error) {
if err := dev.validate(); err != nil {
return nil, err
}
if dev.UsesStdio {
switch {
case dev.UsesStdio:
return []string{"--device", "virtio-serial,stdio"}, nil
case dev.UsesPty:
return []string{"--device", "virtio-serial,pty"}, nil
case dev.LogFile != "":
fallthrough
default:
return []string{"--device", fmt.Sprintf("virtio-serial,logFilePath=%s", dev.LogFile)}, nil
}
return []string{"--device", fmt.Sprintf("virtio-serial,logFilePath=%s", dev.LogFile)}, nil
}
func (dev *VirtioSerial) FromOptions(options []option) error {
@@ -262,6 +287,8 @@ func (dev *VirtioSerial) FromOptions(options []option) error {
return fmt.Errorf("unexpected value for virtio-serial 'stdio' option: %s", option.value)
}
dev.UsesStdio = true
case "pty":
dev.UsesPty = true
default:
return fmt.Errorf("unknown option for virtio-serial devices: %s", option.key)
}
@@ -350,14 +377,14 @@ func (dev *VirtioGPU) FromOptions(options []option) error {
case VirtioGPUResolutionHeight:
height, err := strconv.Atoi(option.value)
if err != nil || height < 1 {
return fmt.Errorf("Invalid value for virtio-gpu %s: %s", option.key, option.value)
return fmt.Errorf("invalid value for virtio-gpu %s: %s", option.key, option.value)
}
dev.Height = height
case VirtioGPUResolutionWidth:
width, err := strconv.Atoi(option.value)
if err != nil || width < 1 {
return fmt.Errorf("Invalid value for virtio-gpu %s: %s", option.key, option.value)
return fmt.Errorf("invalid value for virtio-gpu %s: %s", option.key, option.value)
}
dev.Width = width
@@ -405,6 +432,7 @@ func (dev *VirtioNet) SetSocket(file *os.File) {
func (dev *VirtioNet) SetUnixSocketPath(path string) {
dev.UnixSocketPath = path
dev.Nat = false
dev.VfkitMagic = true // Enable vfkit magic by default for unix sockets
}
func (dev *VirtioNet) validate() error {
@@ -435,19 +463,40 @@ func (dev *VirtioNet) ToCmdLine() ([]string, error) {
case dev.Nat:
builder.WriteString(",nat")
case dev.UnixSocketPath != "":
fmt.Fprintf(&builder, ",unixSocketPath=%s", dev.UnixSocketPath)
if dev.VfkitMagic {
// Use the old commandline syntax for backwards compatibility
// The pkg/config code is used by other projects as a go module to
// generate the command line to start vfkit. There is no guarantee
// that the `vfkit` binary these projects are using is the latest
// one with support for the new syntax.
// https://github.com/containers/podman/issues/27873
fmt.Fprintf(&builder, ",unixSocketPath=%s", dev.UnixSocketPath)
} else {
builder.WriteString(",type=unixgram")
fmt.Fprintf(&builder, ",path=%s", dev.UnixSocketPath)
builder.WriteString(",vfkitMagic=off")
}
default:
fmt.Fprintf(&builder, ",fd=%d", dev.Socket.Fd())
}
if len(dev.MacAddress) != 0 {
builder.WriteString(fmt.Sprintf(",mac=%s", dev.MacAddress))
fmt.Fprintf(&builder, ",mac=%s", dev.MacAddress)
}
return []string{"--device", builder.String()}, nil
}
func (dev *VirtioNet) FromOptions(options []option) error {
var hasType bool
var typeOnlyOptions []string // Options that require type to be specified
if slices.ContainsFunc(options, func(opt option) bool {
return opt.key == "path" || opt.key == "unixSocketPath"
}) {
dev.VfkitMagic = true
}
for _, option := range options {
switch option.key {
case "nat":
@@ -469,11 +518,38 @@ func (dev *VirtioNet) FromOptions(options []option) error {
dev.Socket = os.NewFile(uintptr(fd), "vfkit virtio-net socket")
case "unixSocketPath":
dev.UnixSocketPath = option.value
case "type":
if option.value != "unixgram" {
return fmt.Errorf("unsupported virtio-net type: %s (only 'unixgram' is supported)", option.value)
}
hasType = true
case "path":
dev.UnixSocketPath = option.value
typeOnlyOptions = append(typeOnlyOptions, option.key)
case "vfkitMagic":
if option.value != "on" && option.value != "off" {
return fmt.Errorf("invalid value for vfkitMagic: %s (expected on/off)", option.value)
}
dev.VfkitMagic = option.value == "on"
case "offloading":
if option.value != "off" {
return fmt.Errorf("invalid value for offloading: %s (only 'off' is supported)", option.value)
}
typeOnlyOptions = append(typeOnlyOptions, option.key)
default:
return fmt.Errorf("unknown option for virtio-net devices: %s", option.key)
}
}
// Validate type+path dependency and type-only options
if hasType && dev.UnixSocketPath == "" {
return fmt.Errorf("'type' option requires 'path' to be specified")
}
if !hasType && len(typeOnlyOptions) > 0 {
return fmt.Errorf("'%s' option requires 'type' to be specified", typeOnlyOptions[0])
}
return dev.validate()
}
@@ -548,7 +624,10 @@ func (dev *VirtioBlk) FromOptions(options []option) error {
}
}
return dev.DiskStorageConfig.FromOptions(unhandledOpts)
if err := dev.DiskStorageConfig.FromOptions(unhandledOpts); err != nil {
return err
}
return dev.validate()
}
func (dev *VirtioBlk) ToCmdLine() ([]string, error) {
@@ -565,6 +644,24 @@ func (dev *VirtioBlk) ToCmdLine() ([]string, error) {
return cmdLine, nil
}
func (dev *VirtioBlk) validate() error {
imgPath := dev.ImagePath
file, err := os.Open(imgPath)
if err != nil {
return fmt.Errorf("failed to open file %s: %v", imgPath, err)
}
defer file.Close()
header := make([]byte, 4)
_, err = file.Read(header)
if err != nil {
return fmt.Errorf("failed to read the header of file %s: %v", imgPath, err)
}
if bytes.Equal(header, []byte(qcow2Header)) {
return fmt.Errorf("vfkit does not support qcow2 image format")
}
return nil
}
// VirtioVsockNew creates a new virtio-vsock device for 2-way communication
// between the host and the virtual machine. The communication will happen on
// vsock port, and on the host it will use the unix socket at socketURL.
@@ -575,7 +672,7 @@ func VirtioVsockNew(port uint, socketURL string, listen bool) (VirtioDevice, err
return nil, fmt.Errorf("invalid vsock port: %d", port)
}
return &VirtioVsock{
Port: uint32(port), //#nosec G115 -- was compared to math.MaxUint32
Port: uint32(port),
SocketURL: socketURL,
Listen: listen,
}, nil
@@ -606,7 +703,7 @@ func (dev *VirtioVsock) FromOptions(options []option) error {
if err != nil {
return err
}
dev.Port = uint32(port) //#nosec G115 -- ParseUint(_, _, 32) guarantees no overflow
dev.Port = uint32(port)
case "listen":
dev.Listen = true
case "connect":
@@ -802,7 +899,7 @@ func USBMassStorageNew(imagePath string) (*USBMassStorage, error) {
}
func (dev *USBMassStorage) SetReadOnly(readOnly bool) {
dev.StorageConfig.ReadOnly = readOnly
dev.ReadOnly = readOnly
}
// StorageConfig configures a disk device.
@@ -811,9 +908,32 @@ type StorageConfig struct {
ReadOnly bool `json:"readOnly,omitempty"`
}
type DiskBackendType string
const (
/// Normal disk images, like .img
DiskBackendImage DiskBackendType = "image"
/// Real block devices, like /dev/disk1s1
DiskBackendBlockDevice DiskBackendType = "dev"
/// If the value is empty, it defaults to image
DiskBackendDefault DiskBackendType = ""
)
func (typ DiskBackendType) IsValid() bool {
switch typ {
case DiskBackendImage, DiskBackendBlockDevice, DiskBackendDefault:
return true
default:
return false
}
}
type DiskStorageConfig struct {
StorageConfig
ImagePath string `json:"imagePath,omitempty"`
ImagePath string `json:"imagePath,omitempty"`
Type DiskBackendType `json:"type,omitempty"`
}
type NetworkBlockStorageConfig struct {
@@ -828,6 +948,10 @@ func (config *DiskStorageConfig) ToCmdLine() ([]string, error) {
value := fmt.Sprintf("%s,path=%s", config.DevName, config.ImagePath)
if config.Type != DiskBackendDefault {
value += fmt.Sprintf(",type=%s", string(config.Type))
}
if config.ReadOnly {
value += ",readonly"
}
@@ -839,6 +963,12 @@ func (config *DiskStorageConfig) FromOptions(options []option) error {
switch option.key {
case "path":
config.ImagePath = option.value
case "type":
typ := DiskBackendType(option.value)
if !typ.IsValid() {
return fmt.Errorf("unexpected value for disk 'type' option: %s", option.value)
}
config.Type = typ
case "readonly":
if option.value != "" {
return fmt.Errorf("unexpected value for virtio-blk 'readonly' option: %s", option.value)

View File

@@ -28,22 +28,24 @@ func RegisterExitHandler(handler func()) {
// SetupExitSignalHandling sets up a signal channel to listen for termination or interruption signals.
// When one of these signals is received, all the registered exit handlers will be invoked, just
// before terminating the program.
func SetupExitSignalHandling() {
setupExitSignalHandling(true)
func SetupExitSignalHandling(shutdownFunc func()) {
setupExitSignalHandling(shutdownFunc)
}
// setupExitSignalHandling sets up a signal channel to listen for termination or interruption signals.
// When one of these signals is received, all the registered exit handlers will be invoked.
// It is possible to prevent the program from exiting by setting the doExit param to false (used for testing)
func setupExitSignalHandling(doExit bool) {
func setupExitSignalHandling(shutdownFunc func()) {
sigChan := make(chan os.Signal, 2)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
go func() {
defer func() {
signal.Stop(sigChan)
}()
for sig := range sigChan {
log.Printf("captured %v, calling exit handlers and exiting..", sig)
ExecuteExitHandlers()
if doExit {
os.Exit(1)
if shutdownFunc != nil {
shutdownFunc()
}
}
}()

View File

@@ -61,17 +61,17 @@ func Times(percpu bool) ([]TimesStat, error) {
}
func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) {
lib, err := common.NewLibrary(common.System)
sys, err := common.NewSystemLib()
if err != nil {
return nil, err
}
defer lib.Close()
defer sys.Close()
if percpu {
return perCPUTimes(lib)
return perCPUTimes(sys)
}
return allCPUTimes(lib)
return allCPUTimes(sys)
}
// Returns only one CPUInfoStat on FreeBSD
@@ -138,16 +138,12 @@ func CountsWithContext(_ context.Context, logical bool) (int, error) {
return int(count), nil
}
func perCPUTimes(machLib *common.Library) ([]TimesStat, error) {
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
machTaskSelf := common.GetFunc[common.MachTaskSelfFunc](machLib, common.MachTaskSelfSym)
hostProcessorInfo := common.GetFunc[common.HostProcessorInfoFunc](machLib, common.HostProcessorInfoSym)
vmDeallocate := common.GetFunc[common.VMDeallocateFunc](machLib, common.VMDeallocateSym)
func perCPUTimes(sys *common.SystemLib) ([]TimesStat, error) {
var count, ncpu uint32
var cpuload *hostCpuLoadInfoData
status := hostProcessorInfo(machHostSelf(), processorCpuLoadInfo, &ncpu, uintptr(unsafe.Pointer(&cpuload)), &count)
status := sys.HostProcessorInfo(sys.MachHostSelf(), processorCpuLoadInfo,
&ncpu, uintptr(unsafe.Pointer(&cpuload)), &count)
if status != common.KERN_SUCCESS {
return nil, fmt.Errorf("host_processor_info error=%d", status)
@@ -157,7 +153,7 @@ func perCPUTimes(machLib *common.Library) ([]TimesStat, error) {
return nil, errors.New("host_processor_info returned nil cpuload")
}
defer vmDeallocate(machTaskSelf(), uintptr(unsafe.Pointer(cpuload)), uintptr(ncpu))
defer sys.VMDeallocate(sys.MachTaskSelf(), uintptr(unsafe.Pointer(cpuload)), uintptr(ncpu))
ret := []TimesStat{}
loads := unsafe.Slice(cpuload, ncpu)
@@ -170,21 +166,17 @@ func perCPUTimes(machLib *common.Library) ([]TimesStat, error) {
Nice: float64(loads[i].cpuTicks[cpuStateNice]) / ClocksPerSec,
Idle: float64(loads[i].cpuTicks[cpuStateIdle]) / ClocksPerSec,
}
ret = append(ret, c)
}
return ret, nil
}
func allCPUTimes(machLib *common.Library) ([]TimesStat, error) {
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym)
func allCPUTimes(sys *common.SystemLib) ([]TimesStat, error) {
var cpuload hostCpuLoadInfoData
count := uint32(cpuStateMax)
status := hostStatistics(machHostSelf(), common.HOST_CPU_LOAD_INFO,
status := sys.HostStatistics(sys.MachHostSelf(), common.HOST_CPU_LOAD_INFO,
uintptr(unsafe.Pointer(&cpuload)), &count)
if status != common.KERN_SUCCESS {

View File

@@ -13,55 +13,43 @@ import (
// https://github.com/shoenig/go-m1cpu/blob/v0.1.6/cpu.go
func getFrequency() (float64, error) {
ioKit, err := common.NewLibrary(common.IOKit)
iokit, err := common.NewIOKitLib()
if err != nil {
return 0, err
}
defer ioKit.Close()
defer iokit.Close()
coreFoundation, err := common.NewLibrary(common.CoreFoundation)
corefoundation, err := common.NewCoreFoundationLib()
if err != nil {
return 0, err
}
defer coreFoundation.Close()
defer corefoundation.Close()
ioServiceMatching := common.GetFunc[common.IOServiceMatchingFunc](ioKit, common.IOServiceMatchingSym)
ioServiceGetMatchingServices := common.GetFunc[common.IOServiceGetMatchingServicesFunc](ioKit, common.IOServiceGetMatchingServicesSym)
ioIteratorNext := common.GetFunc[common.IOIteratorNextFunc](ioKit, common.IOIteratorNextSym)
ioRegistryEntryGetName := common.GetFunc[common.IORegistryEntryGetNameFunc](ioKit, common.IORegistryEntryGetNameSym)
ioRegistryEntryCreateCFProperty := common.GetFunc[common.IORegistryEntryCreateCFPropertyFunc](ioKit, common.IORegistryEntryCreateCFPropertySym)
ioObjectRelease := common.GetFunc[common.IOObjectReleaseFunc](ioKit, common.IOObjectReleaseSym)
cfStringCreateWithCString := common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym)
cfDataGetLength := common.GetFunc[common.CFDataGetLengthFunc](coreFoundation, common.CFDataGetLengthSym)
cfDataGetBytePtr := common.GetFunc[common.CFDataGetBytePtrFunc](coreFoundation, common.CFDataGetBytePtrSym)
cfRelease := common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym)
matching := ioServiceMatching("AppleARMIODevice")
matching := iokit.IOServiceMatching("AppleARMIODevice")
var iterator uint32
if status := ioServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(matching), &iterator); status != common.KERN_SUCCESS {
if status := iokit.IOServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(matching), &iterator); status != common.KERN_SUCCESS {
return 0.0, fmt.Errorf("IOServiceGetMatchingServices error=%d", status)
}
defer ioObjectRelease(iterator)
defer iokit.IOObjectRelease(iterator)
pCorekey := cfStringCreateWithCString(common.KCFAllocatorDefault, "voltage-states5-sram", common.KCFStringEncodingUTF8)
defer cfRelease(uintptr(pCorekey))
pCorekey := corefoundation.CFStringCreateWithCString(common.KCFAllocatorDefault, "voltage-states5-sram", common.KCFStringEncodingUTF8)
defer corefoundation.CFRelease(uintptr(pCorekey))
var pCoreHz uint32
for {
service := ioIteratorNext(iterator)
service := iokit.IOIteratorNext(iterator)
if service <= 0 {
break
}
buf := common.NewCStr(512)
ioRegistryEntryGetName(service, buf)
iokit.IORegistryEntryGetName(service, buf)
if buf.GoString() == "pmgr" {
pCoreRef := ioRegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions)
length := cfDataGetLength(uintptr(pCoreRef))
data := cfDataGetBytePtr(uintptr(pCoreRef))
pCoreRef := iokit.IORegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions)
length := corefoundation.CFDataGetLength(uintptr(pCoreRef))
data := corefoundation.CFDataGetBytePtr(uintptr(pCoreRef))
// composite uint32 from the byte array
buf := unsafe.Slice((*byte)(data), length)
@@ -69,11 +57,12 @@ func getFrequency() (float64, error) {
// combine the bytes into a uint32 value
b := buf[length-8 : length-4]
pCoreHz = binary.LittleEndian.Uint32(b)
ioObjectRelease(service)
corefoundation.CFRelease(uintptr(pCoreRef))
iokit.IOObjectRelease(service)
break
}
ioObjectRelease(service)
iokit.IOObjectRelease(service)
}
return float64(pCoreHz / 1_000_000), nil

View File

@@ -195,7 +195,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c := InfoStat{CPU: -1, Cores: 1}
for _, line := range lines {
fields := strings.Split(line, ":")
fields := strings.SplitN(line, ":", 2)
if len(fields) < 2 {
continue
}
@@ -221,6 +221,25 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
if strings.Contains(value, "S390") {
processorName = "S390"
}
case "mvendorid":
if !strings.HasPrefix(value, "0x") {
continue
}
if v, err := strconv.ParseUint(value[2:], 16, 32); err == nil {
switch v {
case 0x31e:
c.VendorID = "Andes"
case 0x029:
c.VendorID = "Microchip"
case 0x127:
c.VendorID = "MIPS"
case 0x489:
c.VendorID = "SiFive"
case 0x5b7:
c.VendorID = "T-Head"
}
}
case "CPU implementer":
if v, err := strconv.ParseUint(value, 0, 8); err == nil {
switch v {
@@ -256,9 +275,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.VendorID = "Ampere"
}
}
case "cpu family":
case "cpu family", "marchid":
c.Family = value
case "model", "CPU part":
case "model", "CPU part", "mimpid":
c.Model = value
// if CPU is arm based, model name is found via model number. refer to: arch/arm64/kernel/cpuinfo.c
if c.VendorID == "ARM" {
@@ -271,7 +290,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
}
}
}
case "Model Name", "model name", "cpu":
case "Model Name", "model name", "cpu", "uarch":
c.ModelName = value
if strings.Contains(value, "POWER") {
c.Model = strings.Split(value, " ")[0]
@@ -305,7 +324,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
return ret, err
}
c.CacheSize = int32(t)
case "physical id":
case "physical id", "hart":
c.PhysicalID = value
case "core id":
c.CoreID = value
@@ -313,6 +332,11 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
c.Flags = strings.FieldsFunc(value, func(r rune) bool {
return r == ',' || r == ' '
})
case "isa", "hart isa":
if len(c.Flags) != 0 || !strings.HasPrefix(value, "rv64") {
continue
}
c.Flags = riscvISAParse(value)
case "microcode":
c.Microcode = value
}
@@ -476,7 +500,7 @@ func CountsWithContext(ctx context.Context, logical bool) (int, error) {
currentInfo = make(map[string]int)
continue
}
fields := strings.Split(line, ":")
fields := strings.SplitN(line, ":", 2)
if len(fields) < 2 {
continue
}
@@ -495,3 +519,13 @@ func CountsWithContext(ctx context.Context, logical bool) (int, error) {
}
return ret, nil
}
func riscvISAParse(s string) []string {
ext := strings.Split(s, "_")
if len(ext[0]) <= 4 {
return nil
}
// the base extensions must "rv64" prefix
base := strings.Split(ext[0][4:], "")
return append(base, ext[1:]...)
}

View File

@@ -9,90 +9,314 @@ import (
"unsafe"
"github.com/ebitengine/purego"
"golang.org/x/sys/unix"
)
func CallSyscall(mib []int32) ([]byte, uint64, error) {
miblen := uint64(len(mib))
// get required buffer size
length := uint64(0)
_, _, err := unix.Syscall6(
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
uintptr(unsafe.Pointer(&mib[0])),
uintptr(miblen),
0,
uintptr(unsafe.Pointer(&length)),
0,
0)
if err != 0 {
var b []byte
return b, length, err
}
if length == 0 {
var b []byte
return b, length, err
}
// get proc info itself
buf := make([]byte, length)
_, _, err = unix.Syscall6(
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
uintptr(unsafe.Pointer(&mib[0])),
uintptr(miblen),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(unsafe.Pointer(&length)),
0,
0)
if err != 0 {
return buf, length, err
}
return buf, length, nil
}
// Library represents a dynamic library loaded by purego.
type Library struct {
addr uintptr
path string
close func()
type library struct {
handle uintptr
fnMap map[string]any
}
// library paths
const (
IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"
CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"
System = "/usr/lib/libSystem.B.dylib"
IOKitLibPath = "/System/Library/Frameworks/IOKit.framework/IOKit"
CoreFoundationLibPath = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"
SystemLibPath = "/usr/lib/libSystem.B.dylib"
)
func NewLibrary(path string) (*Library, error) {
func newLibrary(path string) (*library, error) {
lib, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err != nil {
return nil, err
}
closeFunc := func() {
purego.Dlclose(lib)
}
return &Library{
addr: lib,
path: path,
close: closeFunc,
return &library{
handle: lib,
fnMap: make(map[string]any),
}, nil
}
func (lib *Library) Dlsym(symbol string) (uintptr, error) {
return purego.Dlsym(lib.addr, symbol)
func (lib *library) Dlsym(symbol string) (uintptr, error) {
return purego.Dlsym(lib.handle, symbol)
}
func GetFunc[T any](lib *Library, symbol string) T {
var fptr T
purego.RegisterLibFunc(&fptr, lib.addr, symbol)
return fptr
func getFunc[T any](lib *library, symbol string) T {
var dlfun *dlFunc[T]
if f, ok := lib.fnMap[symbol].(*dlFunc[T]); ok {
dlfun = f
} else {
dlfun = newDlfunc[T](symbol)
dlfun.init(lib.handle)
lib.fnMap[symbol] = dlfun
}
return dlfun.fn
}
func (lib *Library) Close() {
lib.close()
func (lib *library) Close() {
purego.Dlclose(lib.handle)
}
type dlFunc[T any] struct {
sym string
fn T
}
func (d *dlFunc[T]) init(handle uintptr) {
purego.RegisterLibFunc(&d.fn, handle, d.sym)
}
func newDlfunc[T any](sym string) *dlFunc[T] {
return &dlFunc[T]{sym: sym}
}
type CoreFoundationLib struct {
*library
}
func NewCoreFoundationLib() (*CoreFoundationLib, error) {
library, err := newLibrary(CoreFoundationLibPath)
if err != nil {
return nil, err
}
return &CoreFoundationLib{library}, nil
}
func (c *CoreFoundationLib) CFGetTypeID(cf uintptr) int32 {
fn := getFunc[CFGetTypeIDFunc](c.library, "CFGetTypeID")
return fn(cf)
}
func (c *CoreFoundationLib) CFNumberCreate(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer {
fn := getFunc[CFNumberCreateFunc](c.library, "CFNumberCreate")
return fn(allocator, theType, valuePtr)
}
func (c *CoreFoundationLib) CFNumberGetValue(num uintptr, theType int32, valuePtr uintptr) bool {
fn := getFunc[CFNumberGetValueFunc](c.library, "CFNumberGetValue")
return fn(num, theType, valuePtr)
}
func (c *CoreFoundationLib) CFDictionaryCreate(allocator uintptr, keys, values *unsafe.Pointer, numValues int32,
keyCallBacks, valueCallBacks uintptr,
) unsafe.Pointer {
fn := getFunc[CFDictionaryCreateFunc](c.library, "CFDictionaryCreate")
return fn(allocator, keys, values, numValues, keyCallBacks, valueCallBacks)
}
func (c *CoreFoundationLib) CFDictionaryAddValue(theDict, key, value uintptr) {
fn := getFunc[CFDictionaryAddValueFunc](c.library, "CFDictionaryAddValue")
fn(theDict, key, value)
}
func (c *CoreFoundationLib) CFDictionaryGetValue(theDict, key uintptr) unsafe.Pointer {
fn := getFunc[CFDictionaryGetValueFunc](c.library, "CFDictionaryGetValue")
return fn(theDict, key)
}
func (c *CoreFoundationLib) CFArrayGetCount(theArray uintptr) int32 {
fn := getFunc[CFArrayGetCountFunc](c.library, "CFArrayGetCount")
return fn(theArray)
}
func (c *CoreFoundationLib) CFArrayGetValueAtIndex(theArray uintptr, index int32) unsafe.Pointer {
fn := getFunc[CFArrayGetValueAtIndexFunc](c.library, "CFArrayGetValueAtIndex")
return fn(theArray, index)
}
func (c *CoreFoundationLib) CFStringCreateMutable(alloc uintptr, maxLength int32) unsafe.Pointer {
fn := getFunc[CFStringCreateMutableFunc](c.library, "CFStringCreateMutable")
return fn(alloc, maxLength)
}
func (c *CoreFoundationLib) CFStringGetLength(theString uintptr) int32 {
fn := getFunc[CFStringGetLengthFunc](c.library, "CFStringGetLength")
return fn(theString)
}
func (c *CoreFoundationLib) CFStringGetCString(theString uintptr, buffer CStr, bufferSize int32, encoding uint32) {
fn := getFunc[CFStringGetCStringFunc](c.library, "CFStringGetCString")
fn(theString, buffer, bufferSize, encoding)
}
func (c *CoreFoundationLib) CFStringCreateWithCString(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer {
fn := getFunc[CFStringCreateWithCStringFunc](c.library, "CFStringCreateWithCString")
return fn(alloc, cStr, encoding)
}
func (c *CoreFoundationLib) CFDataGetLength(theData uintptr) int32 {
fn := getFunc[CFDataGetLengthFunc](c.library, "CFDataGetLength")
return fn(theData)
}
func (c *CoreFoundationLib) CFDataGetBytePtr(theData uintptr) unsafe.Pointer {
fn := getFunc[CFDataGetBytePtrFunc](c.library, "CFDataGetBytePtr")
return fn(theData)
}
func (c *CoreFoundationLib) CFRelease(cf uintptr) {
fn := getFunc[CFReleaseFunc](c.library, "CFRelease")
fn(cf)
}
type IOKitLib struct {
*library
}
func NewIOKitLib() (*IOKitLib, error) {
library, err := newLibrary(IOKitLibPath)
if err != nil {
return nil, err
}
return &IOKitLib{library}, nil
}
func (l *IOKitLib) IOServiceGetMatchingService(mainPort uint32, matching uintptr) uint32 {
fn := getFunc[IOServiceGetMatchingServiceFunc](l.library, "IOServiceGetMatchingService")
return fn(mainPort, matching)
}
func (l *IOKitLib) IOServiceGetMatchingServices(mainPort uint32, matching uintptr, existing *uint32) int {
fn := getFunc[IOServiceGetMatchingServicesFunc](l.library, "IOServiceGetMatchingServices")
return fn(mainPort, matching, existing)
}
func (l *IOKitLib) IOServiceMatching(name string) unsafe.Pointer {
fn := getFunc[IOServiceMatchingFunc](l.library, "IOServiceMatching")
return fn(name)
}
func (l *IOKitLib) IOServiceOpen(service, owningTask, connType uint32, connect *uint32) int {
fn := getFunc[IOServiceOpenFunc](l.library, "IOServiceOpen")
return fn(service, owningTask, connType, connect)
}
func (l *IOKitLib) IOServiceClose(connect uint32) int {
fn := getFunc[IOServiceCloseFunc](l.library, "IOServiceClose")
return fn(connect)
}
func (l *IOKitLib) IOIteratorNext(iterator uint32) uint32 {
fn := getFunc[IOIteratorNextFunc](l.library, "IOIteratorNext")
return fn(iterator)
}
func (l *IOKitLib) IORegistryEntryGetName(entry uint32, name CStr) int {
fn := getFunc[IORegistryEntryGetNameFunc](l.library, "IORegistryEntryGetName")
return fn(entry, name)
}
func (l *IOKitLib) IORegistryEntryGetParentEntry(entry uint32, plane string, parent *uint32) int {
fn := getFunc[IORegistryEntryGetParentEntryFunc](l.library, "IORegistryEntryGetParentEntry")
return fn(entry, plane, parent)
}
func (l *IOKitLib) IORegistryEntryCreateCFProperty(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer {
fn := getFunc[IORegistryEntryCreateCFPropertyFunc](l.library, "IORegistryEntryCreateCFProperty")
return fn(entry, key, allocator, options)
}
func (l *IOKitLib) IORegistryEntryCreateCFProperties(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int {
fn := getFunc[IORegistryEntryCreateCFPropertiesFunc](l.library, "IORegistryEntryCreateCFProperties")
return fn(entry, properties, allocator, options)
}
func (l *IOKitLib) IOObjectConformsTo(object uint32, className string) bool {
fn := getFunc[IOObjectConformsToFunc](l.library, "IOObjectConformsTo")
return fn(object, className)
}
func (l *IOKitLib) IOObjectRelease(object uint32) int {
fn := getFunc[IOObjectReleaseFunc](l.library, "IOObjectRelease")
return fn(object)
}
func (l *IOKitLib) IOConnectCallStructMethod(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int {
fn := getFunc[IOConnectCallStructMethodFunc](l.library, "IOConnectCallStructMethod")
return fn(connection, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt)
}
func (l *IOKitLib) IOHIDEventSystemClientCreate(allocator uintptr) unsafe.Pointer {
fn := getFunc[IOHIDEventSystemClientCreateFunc](l.library, "IOHIDEventSystemClientCreate")
return fn(allocator)
}
func (l *IOKitLib) IOHIDEventSystemClientSetMatching(client, match uintptr) int {
fn := getFunc[IOHIDEventSystemClientSetMatchingFunc](l.library, "IOHIDEventSystemClientSetMatching")
return fn(client, match)
}
func (l *IOKitLib) IOHIDServiceClientCopyEvent(service uintptr, eventType int64, options int32, timeout int64) unsafe.Pointer {
fn := getFunc[IOHIDServiceClientCopyEventFunc](l.library, "IOHIDServiceClientCopyEvent")
return fn(service, eventType, options, timeout)
}
func (l *IOKitLib) IOHIDServiceClientCopyProperty(service, property uintptr) unsafe.Pointer {
fn := getFunc[IOHIDServiceClientCopyPropertyFunc](l.library, "IOHIDServiceClientCopyProperty")
return fn(service, property)
}
func (l *IOKitLib) IOHIDEventGetFloatValue(event uintptr, field int32) float64 {
fn := getFunc[IOHIDEventGetFloatValueFunc](l.library, "IOHIDEventGetFloatValue")
return fn(event, field)
}
func (l *IOKitLib) IOHIDEventSystemClientCopyServices(client uintptr) unsafe.Pointer {
fn := getFunc[IOHIDEventSystemClientCopyServicesFunc](l.library, "IOHIDEventSystemClientCopyServices")
return fn(client)
}
type SystemLib struct {
*library
}
func NewSystemLib() (*SystemLib, error) {
library, err := newLibrary(SystemLibPath)
if err != nil {
return nil, err
}
return &SystemLib{library}, nil
}
func (s *SystemLib) HostProcessorInfo(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr,
outProcessorInfoCnt *uint32,
) int {
fn := getFunc[HostProcessorInfoFunc](s.library, "host_processor_info")
return fn(host, flavor, outProcessorCount, outProcessorInfo, outProcessorInfoCnt)
}
func (s *SystemLib) HostStatistics(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int {
fn := getFunc[HostStatisticsFunc](s.library, "host_statistics")
return fn(host, flavor, hostInfoOut, hostInfoOutCnt)
}
func (s *SystemLib) MachHostSelf() uint32 {
fn := getFunc[MachHostSelfFunc](s.library, "mach_host_self")
return fn()
}
func (s *SystemLib) MachTaskSelf() uint32 {
fn := getFunc[MachTaskSelfFunc](s.library, "mach_task_self")
return fn()
}
func (s *SystemLib) MachTimeBaseInfo(info uintptr) int {
fn := getFunc[MachTimeBaseInfoFunc](s.library, "mach_timebase_info")
return fn(info)
}
func (s *SystemLib) VMDeallocate(targetTask uint32, vmAddress, vmSize uintptr) int {
fn := getFunc[VMDeallocateFunc](s.library, "vm_deallocate")
return fn(targetTask, vmAddress, vmSize)
}
func (s *SystemLib) ProcPidPath(pid int32, buffer uintptr, bufferSize uint32) int32 {
fn := getFunc[ProcPidPathFunc](s.library, "proc_pidpath")
return fn(pid, buffer, bufferSize)
}
func (s *SystemLib) ProcPidInfo(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32 {
fn := getFunc[ProcPidInfoFunc](s.library, "proc_pidinfo")
return fn(pid, flavor, arg, buffer, bufferSize)
}
// status codes
@@ -100,7 +324,7 @@ const (
KERN_SUCCESS = 0
)
// IOKit functions and symbols.
// IOKit types and constants.
type (
IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32
IOServiceGetMatchingServicesFunc func(mainPort uint32, matching uintptr, existing *uint32) int
@@ -125,29 +349,6 @@ type (
IOHIDEventSystemClientCopyServicesFunc func(client uintptr) unsafe.Pointer
)
const (
IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService"
IOServiceGetMatchingServicesSym = "IOServiceGetMatchingServices"
IOServiceMatchingSym = "IOServiceMatching"
IOServiceOpenSym = "IOServiceOpen"
IOServiceCloseSym = "IOServiceClose"
IOIteratorNextSym = "IOIteratorNext"
IORegistryEntryGetNameSym = "IORegistryEntryGetName"
IORegistryEntryGetParentEntrySym = "IORegistryEntryGetParentEntry"
IORegistryEntryCreateCFPropertySym = "IORegistryEntryCreateCFProperty"
IORegistryEntryCreateCFPropertiesSym = "IORegistryEntryCreateCFProperties"
IOObjectConformsToSym = "IOObjectConformsTo"
IOObjectReleaseSym = "IOObjectRelease"
IOConnectCallStructMethodSym = "IOConnectCallStructMethod"
IOHIDEventSystemClientCreateSym = "IOHIDEventSystemClientCreate"
IOHIDEventSystemClientSetMatchingSym = "IOHIDEventSystemClientSetMatching"
IOHIDServiceClientCopyEventSym = "IOHIDServiceClientCopyEvent"
IOHIDServiceClientCopyPropertySym = "IOHIDServiceClientCopyProperty"
IOHIDEventGetFloatValueSym = "IOHIDEventGetFloatValue"
IOHIDEventSystemClientCopyServicesSym = "IOHIDEventSystemClientCopyServices"
)
const (
KIOMainPortDefault = 0
@@ -161,7 +362,7 @@ const (
KIOServicePlane = "IOService"
)
// CoreFoundation functions and symbols.
// CoreFoundation types and constants.
type (
CFGetTypeIDFunc func(cf uintptr) int32
CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer
@@ -181,24 +382,6 @@ type (
CFReleaseFunc func(cf uintptr)
)
const (
CFGetTypeIDSym = "CFGetTypeID"
CFNumberCreateSym = "CFNumberCreate"
CFNumberGetValueSym = "CFNumberGetValue"
CFDictionaryCreateSym = "CFDictionaryCreate"
CFDictionaryAddValueSym = "CFDictionaryAddValue"
CFDictionaryGetValueSym = "CFDictionaryGetValue"
CFArrayGetCountSym = "CFArrayGetCount"
CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex"
CFStringCreateMutableSym = "CFStringCreateMutable"
CFStringGetLengthSym = "CFStringGetLength"
CFStringGetCStringSym = "CFStringGetCString"
CFStringCreateWithCStringSym = "CFStringCreateWithCString"
CFDataGetLengthSym = "CFDataGetLength"
CFDataGetBytePtrSym = "CFDataGetBytePtr"
CFReleaseSym = "CFRelease"
)
const (
KCFStringEncodingUTF8 = 0x08000100
KCFNumberSInt64Type = 4
@@ -206,7 +389,7 @@ const (
KCFAllocatorDefault = 0
)
// Kernel functions and symbols.
// libSystem types and constants.
type MachTimeBaseInfo struct {
Numer uint32
Denom uint32
@@ -232,17 +415,12 @@ const (
)
const (
CTL_KERN = 1
KERN_ARGMAX = 8
KERN_PROCARGS2 = 49
HOST_VM_INFO = 2
HOST_CPU_LOAD_INFO = 3
HOST_VM_INFO_COUNT = 0xf
)
// System functions and symbols.
type (
ProcPidPathFunc func(pid int32, buffer uintptr, bufferSize uint32) int32
ProcPidInfoFunc func(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32
@@ -256,6 +434,7 @@ const (
const (
MAXPATHLEN = 1024
PROC_PIDLISTFDS = 1
PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
PROC_PIDTASKINFO = 4
PROC_PIDVNODEPATHINFO = 9
@@ -263,9 +442,8 @@ const (
// SMC represents a SMC instance.
type SMC struct {
lib *Library
conn uint32
callStruct IOConnectCallStructMethodFunc
lib *IOKitLib
conn uint32
}
const ioServiceSMC = "AppleSMC"
@@ -287,47 +465,39 @@ const (
KSMCKeyNotFound = 132
)
func NewSMC(ioKit *Library) (*SMC, error) {
if ioKit.path != IOKit {
return nil, errors.New("library is not IOKit")
func NewSMC() (*SMC, error) {
iokit, err := NewIOKitLib()
if err != nil {
return nil, err
}
ioServiceGetMatchingService := GetFunc[IOServiceGetMatchingServiceFunc](ioKit, IOServiceGetMatchingServiceSym)
ioServiceMatching := GetFunc[IOServiceMatchingFunc](ioKit, IOServiceMatchingSym)
ioServiceOpen := GetFunc[IOServiceOpenFunc](ioKit, IOServiceOpenSym)
ioObjectRelease := GetFunc[IOObjectReleaseFunc](ioKit, IOObjectReleaseSym)
machTaskSelf := GetFunc[MachTaskSelfFunc](ioKit, MachTaskSelfSym)
ioConnectCallStructMethod := GetFunc[IOConnectCallStructMethodFunc](ioKit, IOConnectCallStructMethodSym)
service := ioServiceGetMatchingService(0, uintptr(ioServiceMatching(ioServiceSMC)))
service := iokit.IOServiceGetMatchingService(0, uintptr(iokit.IOServiceMatching(ioServiceSMC)))
if service == 0 {
return nil, fmt.Errorf("ERROR: %s NOT FOUND", ioServiceSMC)
}
var conn uint32
if result := ioServiceOpen(service, machTaskSelf(), 0, &conn); result != 0 {
machTaskSelf := getFunc[MachTaskSelfFunc](iokit.library, "mach_task_self")
if result := iokit.IOServiceOpen(service, machTaskSelf(), 0, &conn); result != 0 {
return nil, errors.New("ERROR: IOServiceOpen failed")
}
ioObjectRelease(service)
iokit.IOObjectRelease(service)
return &SMC{
lib: ioKit,
conn: conn,
callStruct: ioConnectCallStructMethod,
lib: iokit,
conn: conn,
}, nil
}
func (s *SMC) CallStruct(selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int {
return s.callStruct(s.conn, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt)
return s.lib.IOConnectCallStructMethod(s.conn, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt)
}
func (s *SMC) Close() error {
ioServiceClose := GetFunc[IOServiceCloseFunc](s.lib, IOServiceCloseSym)
if result := ioServiceClose(s.conn); result != 0 {
if result := s.lib.IOServiceClose(s.conn); result != 0 {
return errors.New("ERROR: IOServiceClose failed")
}
s.lib.Close()
return nil
}

View File

@@ -14,6 +14,7 @@ type ExVirtualMemory struct {
ActiveAnon uint64 `json:"activeanon"`
InactiveAnon uint64 `json:"inactiveanon"`
Unevictable uint64 `json:"unevictable"`
Percpu uint64 `json:"percpu"`
}
func (v ExVirtualMemory) String() string {

View File

@@ -48,10 +48,11 @@ type VirtualMemoryStat struct {
Laundry uint64 `json:"laundry"`
// Linux specific numbers
// https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-meminfo
// https://blogs.oracle.com/linux/understanding-linux-kernel-memory-statistics
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
// https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
// https://www.kernel.org/doc/Documentation/vm/transhuge.txt
//
Buffers uint64 `json:"buffers"`
Cached uint64 `json:"cached"`
WriteBack uint64 `json:"writeBack"`

View File

@@ -85,26 +85,23 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
}
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
machLib, err := common.NewLibrary(common.System)
sys, err := common.NewSystemLib()
if err != nil {
return nil, err
}
defer machLib.Close()
hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym)
machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym)
defer sys.Close()
count := uint32(common.HOST_VM_INFO_COUNT)
var vmstat vmStatisticsData
status := hostStatistics(machHostSelf(), common.HOST_VM_INFO,
status := sys.HostStatistics(sys.MachHostSelf(), common.HOST_VM_INFO,
uintptr(unsafe.Pointer(&vmstat)), &count)
if status != common.KERN_SUCCESS {
return nil, fmt.Errorf("host_statistics error=%d", status)
}
pageSizeAddr, _ := machLib.Dlsym("vm_kernel_page_size")
pageSizeAddr, _ := sys.Dlsym("vm_kernel_page_size")
pageSize := **(**uint64)(unsafe.Pointer(&pageSizeAddr))
total, err := getHwMemsize()
if err != nil {

View File

@@ -138,6 +138,12 @@ func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *ExVir
return ret, retEx, err
}
retEx.Unevictable = t * 1024
case "Percpu":
t, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return ret, retEx, err
}
retEx.Percpu = t * 1024
case "Writeback":
t, err := strconv.ParseUint(value, 10, 64)
if err != nil {

View File

@@ -36,10 +36,6 @@ func (*Process) NumCtxSwitchesWithContext(_ context.Context) (*NumCtxSwitchesSta
return nil, common.ErrNotImplementedError
}
func (*Process) NumFDsWithContext(_ context.Context) (int32, error) {
return 0, common.ErrNotImplementedError
}
func (*Process) CPUAffinityWithContext(_ context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError
}

View File

@@ -280,31 +280,21 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption,
}
type dlFuncs struct {
lib *common.Library
procPidPath common.ProcPidPathFunc
procPidInfo common.ProcPidInfoFunc
machTimeBaseInfo common.MachTimeBaseInfoFunc
lib *common.SystemLib
}
func loadProcFuncs() (*dlFuncs, error) {
lib, err := common.NewLibrary(common.System)
lib, err := common.NewSystemLib()
if err != nil {
return nil, err
}
return &dlFuncs{
lib: lib,
procPidPath: common.GetFunc[common.ProcPidPathFunc](lib, common.ProcPidPathSym),
procPidInfo: common.GetFunc[common.ProcPidInfoFunc](lib, common.ProcPidInfoSym),
machTimeBaseInfo: common.GetFunc[common.MachTimeBaseInfoFunc](lib, common.MachTimeBaseInfoSym),
}, nil
return &dlFuncs{lib}, err
}
func (f *dlFuncs) getTimeScaleToNanoSeconds() float64 {
var timeBaseInfo common.MachTimeBaseInfo
f.machTimeBaseInfo(uintptr(unsafe.Pointer(&timeBaseInfo)))
f.lib.MachTimeBaseInfo(uintptr(unsafe.Pointer(&timeBaseInfo)))
return float64(timeBaseInfo.Numer) / float64(timeBaseInfo.Denom)
}
@@ -321,7 +311,7 @@ func (p *Process) ExeWithContext(_ context.Context) (string, error) {
defer funcs.Close()
buf := common.NewCStr(common.PROC_PIDPATHINFO_MAXSIZE)
ret := funcs.procPidPath(p.Pid, buf.Addr(), common.PROC_PIDPATHINFO_MAXSIZE)
ret := funcs.lib.ProcPidPath(p.Pid, buf.Addr(), common.PROC_PIDPATHINFO_MAXSIZE)
if ret <= 0 {
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
@@ -330,13 +320,6 @@ func (p *Process) ExeWithContext(_ context.Context) (string, error) {
return buf.GoString(), nil
}
// sys/proc_info.h
type vnodePathInfo struct {
_ [152]byte
vipPath [common.MAXPATHLEN]byte
_ [1176]byte
}
// CwdWithContext retrieves the Current Working Directory for the given process.
// It uses the proc_pidinfo from libproc and will only work for processes the
// EUID can access. Otherwise "operation not permitted" will be returned as the
@@ -355,7 +338,7 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) {
var vpi vnodePathInfo
const vpiSize = int32(unsafe.Sizeof(vpi))
ret := funcs.procPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, uintptr(unsafe.Pointer(&vpi)), vpiSize)
ret := funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, uintptr(unsafe.Pointer(&vpi)), vpiSize)
errno, _ := funcs.lib.Dlsym("errno")
err = *(**unix.Errno)(unsafe.Pointer(&errno))
if errors.Is(err, unix.EPERM) {
@@ -369,11 +352,11 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) {
if ret != vpiSize {
return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret)
}
return common.GoString(&vpi.vipPath[0]), nil
return common.GoString((*byte)(unsafe.Pointer(&vpi.Cdir.Path[0]))), nil
}
func procArgs(pid int32) ([]byte, int, error) {
procargs, _, err := common.CallSyscall([]int32{common.CTL_KERN, common.KERN_PROCARGS2, pid})
procargs, err := unix.SysctlRaw("kern.procargs2", int(pid))
if err != nil {
return nil, 0, err
}
@@ -447,7 +430,7 @@ func (p *Process) NumThreadsWithContext(_ context.Context) (int32, error) {
defer funcs.Close()
var ti ProcTaskInfo
funcs.procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
return int32(ti.Threadnum), nil
}
@@ -460,7 +443,7 @@ func (p *Process) TimesWithContext(_ context.Context) (*cpu.TimesStat, error) {
defer funcs.Close()
var ti ProcTaskInfo
funcs.procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
timescaleToNanoSeconds := funcs.getTimeScaleToNanoSeconds()
ret := &cpu.TimesStat{
@@ -479,7 +462,7 @@ func (p *Process) MemoryInfoWithContext(_ context.Context) (*MemoryInfoStat, err
defer funcs.Close()
var ti ProcTaskInfo
funcs.procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
ret := &MemoryInfoStat{
RSS: uint64(ti.Resident_size),
@@ -488,3 +471,54 @@ func (p *Process) MemoryInfoWithContext(_ context.Context) (*MemoryInfoStat, err
}
return ret, nil
}
// procFDInfo represents a file descriptor entry from sys/proc_info.h
type procFDInfo struct {
ProcFd int32
ProcFdtype uint32
}
// NumFDsWithContext returns the number of file descriptors used by the process.
// It uses proc_pidinfo with PROC_PIDLISTFDS to query the kernel for the count
// of open file descriptors. The method makes a single syscall and calculates
// the count from the buffer size returned by the kernel.
func (p *Process) NumFDsWithContext(_ context.Context) (int32, error) {
funcs, err := loadProcFuncs()
if err != nil {
return 0, err
}
defer funcs.Close()
// First call: get required buffer size
bufferSize := funcs.lib.ProcPidInfo(
p.Pid,
common.PROC_PIDLISTFDS,
0,
0, // NULL buffer
0, // 0 size
)
if bufferSize <= 0 {
return 0, fmt.Errorf("unknown error: proc_pidinfo returned %d", bufferSize)
}
// Allocate buffer of the required size
const sizeofProcFDInfo = int32(unsafe.Sizeof(procFDInfo{}))
numEntries := bufferSize / sizeofProcFDInfo
buf := make([]procFDInfo, numEntries)
// Second call: get actual data
ret := funcs.lib.ProcPidInfo(
p.Pid,
common.PROC_PIDLISTFDS,
0,
uintptr(unsafe.Pointer(&buf[0])), // Real buffer
bufferSize, // Size from first call
)
if ret <= 0 {
return 0, fmt.Errorf("unknown error: proc_pidinfo returned %d", ret)
}
// Calculate actual number of FDs returned
numFDs := ret / sizeofProcFDInfo
return numFDs, nil
}

View File

@@ -233,6 +233,51 @@ type ProcTaskInfo struct {
Priority int32
}
type vinfoStat struct {
Dev uint32
Mode uint16
Nlink uint16
Ino uint64
Uid uint32
Gid uint32
Atime int64
Atimensec int64
Mtime int64
Mtimensec int64
Ctime int64
Ctimensec int64
Birthtime int64
Birthtimensec int64
Size int64
Blocks int64
Blksize int32
Flags uint32
Gen uint32
Rdev uint32
Qspare [2]int64
}
type fsid struct {
Val [2]int32
}
type vnodeInfo struct {
Stat vinfoStat
Type int32
Pad int32
Fsid fsid
}
type vnodeInfoPath struct {
Vi vnodeInfo
Path [1024]int8
}
type vnodePathInfo struct {
Cdir vnodeInfoPath
Rdir vnodeInfoPath
}
type AuditinfoAddr struct {
Auid uint32
Mask AuMask

View File

@@ -211,6 +211,51 @@ type ProcTaskInfo struct {
Priority int32
}
type vinfoStat struct {
Dev uint32
Mode uint16
Nlink uint16
Ino uint64
Uid uint32
Gid uint32
Atime int64
Atimensec int64
Mtime int64
Mtimensec int64
Ctime int64
Ctimensec int64
Birthtime int64
Birthtimensec int64
Size int64
Blocks int64
Blksize int32
Flags uint32
Gen uint32
Rdev uint32
Qspare [2]int64
}
type fsid struct {
Val [2]int32
}
type vnodeInfo struct {
Stat vinfoStat
Type int32
Pad int32
Fsid fsid
}
type vnodeInfoPath struct {
Vi vnodeInfo
Path [1024]int8
}
type vnodePathInfo struct {
Cdir vnodeInfoPath
Rdir vnodeInfoPath
}
type AuditinfoAddr struct {
Auid uint32
Mask AuMask

View File

@@ -344,6 +344,10 @@ func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
return results, nil
}
func (*Process) NumFDsWithContext(_ context.Context) (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) getKProc() (*KinfoProc, error) {
mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}

View File

@@ -342,6 +342,10 @@ func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
return results, nil
}
func (*Process) NumFDsWithContext(_ context.Context) (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) getKProc() (*KinfoProc, error) {
buf, length, err := callKernProcSyscall(KernProcPID, p.Pid)
if err != nil {

View File

@@ -64,6 +64,80 @@ func initOptions() {
func archInit() {
// From internal/cpu
const (
// eax bits
cpuid_AVXVNNI = 1 << 4
// ecx bits
cpuid_SSE3 = 1 << 0
cpuid_PCLMULQDQ = 1 << 1
cpuid_AVX512VBMI = 1 << 1
cpuid_AVX512VBMI2 = 1 << 6
cpuid_SSSE3 = 1 << 9
cpuid_AVX512GFNI = 1 << 8
cpuid_AVX512VAES = 1 << 9
cpuid_AVX512VNNI = 1 << 11
cpuid_AVX512BITALG = 1 << 12
cpuid_FMA = 1 << 12
cpuid_AVX512VPOPCNTDQ = 1 << 14
cpuid_SSE41 = 1 << 19
cpuid_SSE42 = 1 << 20
cpuid_POPCNT = 1 << 23
cpuid_AES = 1 << 25
cpuid_OSXSAVE = 1 << 27
cpuid_AVX = 1 << 28
// "Extended Feature Flag" bits returned in EBX for CPUID EAX=0x7 ECX=0x0
cpuid_BMI1 = 1 << 3
cpuid_AVX2 = 1 << 5
cpuid_BMI2 = 1 << 8
cpuid_ERMS = 1 << 9
cpuid_AVX512F = 1 << 16
cpuid_AVX512DQ = 1 << 17
cpuid_ADX = 1 << 19
cpuid_AVX512CD = 1 << 28
cpuid_SHA = 1 << 29
cpuid_AVX512BW = 1 << 30
cpuid_AVX512VL = 1 << 31
// "Extended Feature Flag" bits returned in ECX for CPUID EAX=0x7 ECX=0x0
cpuid_AVX512_VBMI = 1 << 1
cpuid_AVX512_VBMI2 = 1 << 6
cpuid_GFNI = 1 << 8
cpuid_AVX512VPCLMULQDQ = 1 << 10
cpuid_AVX512_BITALG = 1 << 12
// edx bits
cpuid_FSRM = 1 << 4
// edx bits for CPUID 0x80000001
cpuid_RDTSCP = 1 << 27
)
// Additional constants not in internal/cpu
const (
// eax=1: edx
cpuid_SSE2 = 1 << 26
// eax=1: ecx
cpuid_CX16 = 1 << 13
cpuid_RDRAND = 1 << 30
// eax=7,ecx=0: ebx
cpuid_RDSEED = 1 << 18
cpuid_AVX512IFMA = 1 << 21
cpuid_AVX512PF = 1 << 26
cpuid_AVX512ER = 1 << 27
// eax=7,ecx=0: edx
cpuid_AVX5124VNNIW = 1 << 2
cpuid_AVX5124FMAPS = 1 << 3
cpuid_AMXBF16 = 1 << 22
cpuid_AMXTile = 1 << 24
cpuid_AMXInt8 = 1 << 25
// eax=7,ecx=1: eax
cpuid_AVX512BF16 = 1 << 5
cpuid_AVXIFMA = 1 << 23
// eax=7,ecx=1: edx
cpuid_AVXVNNIInt8 = 1 << 4
)
Initialized = true
maxID, _, _, _ := cpuid(0, 0)
@@ -73,90 +147,90 @@ func archInit() {
}
_, _, ecx1, edx1 := cpuid(1, 0)
X86.HasSSE2 = isSet(26, edx1)
X86.HasSSE2 = isSet(edx1, cpuid_SSE2)
X86.HasSSE3 = isSet(0, ecx1)
X86.HasPCLMULQDQ = isSet(1, ecx1)
X86.HasSSSE3 = isSet(9, ecx1)
X86.HasFMA = isSet(12, ecx1)
X86.HasCX16 = isSet(13, ecx1)
X86.HasSSE41 = isSet(19, ecx1)
X86.HasSSE42 = isSet(20, ecx1)
X86.HasPOPCNT = isSet(23, ecx1)
X86.HasAES = isSet(25, ecx1)
X86.HasOSXSAVE = isSet(27, ecx1)
X86.HasRDRAND = isSet(30, ecx1)
X86.HasSSE3 = isSet(ecx1, cpuid_SSE3)
X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ)
X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3)
X86.HasFMA = isSet(ecx1, cpuid_FMA)
X86.HasCX16 = isSet(ecx1, cpuid_CX16)
X86.HasSSE41 = isSet(ecx1, cpuid_SSE41)
X86.HasSSE42 = isSet(ecx1, cpuid_SSE42)
X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT)
X86.HasAES = isSet(ecx1, cpuid_AES)
X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE)
X86.HasRDRAND = isSet(ecx1, cpuid_RDRAND)
var osSupportsAVX, osSupportsAVX512 bool
// For XGETBV, OSXSAVE bit is required and sufficient.
if X86.HasOSXSAVE {
eax, _ := xgetbv()
// Check if XMM and YMM registers have OS support.
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2)
if runtime.GOOS == "darwin" {
// Darwin requires special AVX512 checks, see cpu_darwin_x86.go
osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512()
} else {
// Check if OPMASK and ZMM registers have OS support.
osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax)
osSupportsAVX512 = osSupportsAVX && isSet(eax, 1<<5) && isSet(eax, 1<<6) && isSet(eax, 1<<7)
}
}
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX
X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX
if maxID < 7 {
return
}
eax7, ebx7, ecx7, edx7 := cpuid(7, 0)
X86.HasBMI1 = isSet(3, ebx7)
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX
X86.HasBMI2 = isSet(8, ebx7)
X86.HasERMS = isSet(9, ebx7)
X86.HasRDSEED = isSet(18, ebx7)
X86.HasADX = isSet(19, ebx7)
X86.HasBMI1 = isSet(ebx7, cpuid_BMI1)
X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX
X86.HasBMI2 = isSet(ebx7, cpuid_BMI2)
X86.HasERMS = isSet(ebx7, cpuid_ERMS)
X86.HasRDSEED = isSet(ebx7, cpuid_RDSEED)
X86.HasADX = isSet(ebx7, cpuid_ADX)
X86.HasAVX512 = isSet(16, ebx7) && osSupportsAVX512 // Because avx-512 foundation is the core required extension
X86.HasAVX512 = isSet(ebx7, cpuid_AVX512F) && osSupportsAVX512 // Because avx-512 foundation is the core required extension
if X86.HasAVX512 {
X86.HasAVX512F = true
X86.HasAVX512CD = isSet(28, ebx7)
X86.HasAVX512ER = isSet(27, ebx7)
X86.HasAVX512PF = isSet(26, ebx7)
X86.HasAVX512VL = isSet(31, ebx7)
X86.HasAVX512BW = isSet(30, ebx7)
X86.HasAVX512DQ = isSet(17, ebx7)
X86.HasAVX512IFMA = isSet(21, ebx7)
X86.HasAVX512VBMI = isSet(1, ecx7)
X86.HasAVX5124VNNIW = isSet(2, edx7)
X86.HasAVX5124FMAPS = isSet(3, edx7)
X86.HasAVX512VPOPCNTDQ = isSet(14, ecx7)
X86.HasAVX512VPCLMULQDQ = isSet(10, ecx7)
X86.HasAVX512VNNI = isSet(11, ecx7)
X86.HasAVX512GFNI = isSet(8, ecx7)
X86.HasAVX512VAES = isSet(9, ecx7)
X86.HasAVX512VBMI2 = isSet(6, ecx7)
X86.HasAVX512BITALG = isSet(12, ecx7)
X86.HasAVX512CD = isSet(ebx7, cpuid_AVX512CD)
X86.HasAVX512ER = isSet(ebx7, cpuid_AVX512ER)
X86.HasAVX512PF = isSet(ebx7, cpuid_AVX512PF)
X86.HasAVX512VL = isSet(ebx7, cpuid_AVX512VL)
X86.HasAVX512BW = isSet(ebx7, cpuid_AVX512BW)
X86.HasAVX512DQ = isSet(ebx7, cpuid_AVX512DQ)
X86.HasAVX512IFMA = isSet(ebx7, cpuid_AVX512IFMA)
X86.HasAVX512VBMI = isSet(ecx7, cpuid_AVX512_VBMI)
X86.HasAVX5124VNNIW = isSet(edx7, cpuid_AVX5124VNNIW)
X86.HasAVX5124FMAPS = isSet(edx7, cpuid_AVX5124FMAPS)
X86.HasAVX512VPOPCNTDQ = isSet(ecx7, cpuid_AVX512VPOPCNTDQ)
X86.HasAVX512VPCLMULQDQ = isSet(ecx7, cpuid_AVX512VPCLMULQDQ)
X86.HasAVX512VNNI = isSet(ecx7, cpuid_AVX512VNNI)
X86.HasAVX512GFNI = isSet(ecx7, cpuid_AVX512GFNI)
X86.HasAVX512VAES = isSet(ecx7, cpuid_AVX512VAES)
X86.HasAVX512VBMI2 = isSet(ecx7, cpuid_AVX512VBMI2)
X86.HasAVX512BITALG = isSet(ecx7, cpuid_AVX512BITALG)
}
X86.HasAMXTile = isSet(24, edx7)
X86.HasAMXInt8 = isSet(25, edx7)
X86.HasAMXBF16 = isSet(22, edx7)
X86.HasAMXTile = isSet(edx7, cpuid_AMXTile)
X86.HasAMXInt8 = isSet(edx7, cpuid_AMXInt8)
X86.HasAMXBF16 = isSet(edx7, cpuid_AMXBF16)
// These features depend on the second level of extended features.
if eax7 >= 1 {
eax71, _, _, edx71 := cpuid(7, 1)
if X86.HasAVX512 {
X86.HasAVX512BF16 = isSet(5, eax71)
X86.HasAVX512BF16 = isSet(eax71, cpuid_AVX512BF16)
}
if X86.HasAVX {
X86.HasAVXIFMA = isSet(23, eax71)
X86.HasAVXVNNI = isSet(4, eax71)
X86.HasAVXVNNIInt8 = isSet(4, edx71)
X86.HasAVXIFMA = isSet(eax71, cpuid_AVXIFMA)
X86.HasAVXVNNI = isSet(eax71, cpuid_AVXVNNI)
X86.HasAVXVNNIInt8 = isSet(edx71, cpuid_AVXVNNIInt8)
}
}
}
func isSet(bitpos uint, value uint32) bool {
return value&(1<<bitpos) != 0
func isSet(hwc uint32, value uint32) bool {
return hwc&value != 0
}

14
vendor/modules.txt vendored
View File

@@ -124,8 +124,8 @@ github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/pkg/volumes
github.com/containers/buildah/util
# github.com/containers/common v0.62.2
## explicit; go 1.22.8
# github.com/containers/common v0.64.2
## explicit; go 1.23.3
github.com/containers/common/pkg/strongunits
# github.com/containers/conmon v2.0.20+incompatible
## explicit
@@ -189,8 +189,8 @@ github.com/coreos/go-systemd/v22/dbus
github.com/coreos/go-systemd/v22/internal/dlopen
github.com/coreos/go-systemd/v22/journal
github.com/coreos/go-systemd/v22/sdjournal
# github.com/crc-org/vfkit v0.6.1
## explicit; go 1.23.0
# github.com/crc-org/vfkit v0.6.3
## explicit; go 1.24.2
github.com/crc-org/vfkit/pkg/config
github.com/crc-org/vfkit/pkg/rest/define
github.com/crc-org/vfkit/pkg/util
@@ -644,7 +644,7 @@ github.com/seccomp/libseccomp-golang
# github.com/secure-systems-lab/go-securesystemslib v0.9.1
## explicit; go 1.23.0
github.com/secure-systems-lab/go-securesystemslib/encrypted
# github.com/shirou/gopsutil/v4 v4.25.11
# github.com/shirou/gopsutil/v4 v4.25.12
## explicit; go 1.24.0
github.com/shirou/gopsutil/v4/common
github.com/shirou/gopsutil/v4/cpu
@@ -1000,7 +1000,7 @@ golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
golang.org/x/crypto/ssh/knownhosts
golang.org/x/crypto/twofish
golang.org/x/crypto/xts
# golang.org/x/mod v0.30.0
# golang.org/x/mod v0.31.0
## explicit; go 1.24.0
golang.org/x/mod/semver
# golang.org/x/net v0.48.0
@@ -1026,7 +1026,7 @@ golang.org/x/oauth2/internal
## explicit; go 1.24.0
golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
# golang.org/x/sys v0.39.0
# golang.org/x/sys v0.40.0
## explicit; go 1.24.0
golang.org/x/sys/cpu
golang.org/x/sys/plan9