You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gopsutil/internal/common/common_darwin.go

404 lines
12 KiB
Go

// SPDX-License-Identifier: BSD-3-Clause
//go:build darwin
package common
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"unsafe"
"github.com/ebitengine/purego"
"golang.org/x/sys/unix"
)
func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) {
cmd := exec.CommandContext(ctx, "sysctl", "-n", mib)
cmd.Env = getSysctrlEnv(os.Environ())
out, err := cmd.Output()
if err != nil {
return []string{}, err
}
v := strings.Replace(string(out), "{ ", "", 1)
v = strings.Replace(string(v), " }", "", 1)
values := strings.Fields(string(v))
return values, nil
}
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()
}
// library paths
const (
IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"
CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"
System = "/usr/lib/libSystem.B.dylib"
)
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,
}, nil
}
func (lib *Library) Dlsym(symbol string) (uintptr, error) {
return purego.Dlsym(lib.addr, symbol)
}
func GetFunc[T any](lib *Library, symbol string) T {
var fptr T
purego.RegisterLibFunc(&fptr, lib.addr, symbol)
return fptr
}
func (lib *Library) Close() {
lib.close()
}
// status codes
const (
KERN_SUCCESS = 0
)
// IOKit functions and symbols.
type (
IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32
IOServiceGetMatchingServicesFunc func(mainPort uint32, matching uintptr, existing *uint32) int
IOServiceMatchingFunc func(name string) unsafe.Pointer
IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int
IOServiceCloseFunc func(connect uint32) int
IOIteratorNextFunc func(iterator uint32) uint32
IORegistryEntryGetNameFunc func(entry uint32, name CStr) int
IORegistryEntryGetParentEntryFunc func(entry uint32, plane string, parent *uint32) int
IORegistryEntryCreateCFPropertyFunc func(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer
IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int
IOObjectConformsToFunc func(object uint32, className string) bool
IOObjectReleaseFunc func(object uint32) int
IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int
IOHIDEventSystemClientCreateFunc func(allocator uintptr) unsafe.Pointer
IOHIDEventSystemClientSetMatchingFunc func(client, match uintptr) int
IOHIDServiceClientCopyEventFunc func(service uintptr, eventType int64,
options int32, timeout int64) unsafe.Pointer
IOHIDServiceClientCopyPropertyFunc func(service, property uintptr) unsafe.Pointer
IOHIDEventGetFloatValueFunc func(event uintptr, field int32) float64
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
KIOHIDEventTypeTemperature = 15
KNilOptions = 0
)
const (
KIOMediaWholeKey = "Media"
KIOServicePlane = "IOService"
)
// CoreFoundation functions and symbols.
type (
CFGetTypeIDFunc func(cf uintptr) int32
CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer
CFNumberGetValueFunc func(num uintptr, theType int32, valuePtr uintptr) bool
CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int32,
keyCallBacks, valueCallBacks uintptr) unsafe.Pointer
CFDictionaryAddValueFunc func(theDict, key, value uintptr)
CFDictionaryGetValueFunc func(theDict, key uintptr) unsafe.Pointer
CFArrayGetCountFunc func(theArray uintptr) int32
CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer
CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer
CFStringGetLengthFunc func(theString uintptr) int32
CFStringGetCStringFunc func(theString uintptr, buffer CStr, bufferSize int32, encoding uint32)
CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer
CFDataGetLengthFunc func(theData uintptr) int32
CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer
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
KCFNumberIntType = 9
KCFAllocatorDefault = 0
)
// Kernel functions and symbols.
type MachTimeBaseInfo struct {
Numer uint32
Denom uint32
}
type (
HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr,
outProcessorInfoCnt *uint32) int
HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int
MachHostSelfFunc func() uint32
MachTaskSelfFunc func() uint32
MachTimeBaseInfoFunc func(info uintptr) int
VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int
)
const (
HostProcessorInfoSym = "host_processor_info"
HostStatisticsSym = "host_statistics"
MachHostSelfSym = "mach_host_self"
MachTaskSelfSym = "mach_task_self"
MachTimeBaseInfoSym = "mach_timebase_info"
VMDeallocateSym = "vm_deallocate"
)
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
)
const (
SysctlSym = "sysctl"
ProcPidPathSym = "proc_pidpath"
ProcPidInfoSym = "proc_pidinfo"
)
const (
MAXPATHLEN = 1024
PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
PROC_PIDTASKINFO = 4
PROC_PIDVNODEPATHINFO = 9
)
// SMC represents a SMC instance.
type SMC struct {
lib *Library
conn uint32
callStruct IOConnectCallStructMethodFunc
}
const ioServiceSMC = "AppleSMC"
const (
KSMCUserClientOpen = 0
KSMCUserClientClose = 1
KSMCHandleYPCEvent = 2
KSMCReadKey = 5
KSMCWriteKey = 6
KSMCGetKeyCount = 7
KSMCGetKeyFromIndex = 8
KSMCGetKeyInfo = 9
)
const (
KSMCSuccess = 0
KSMCError = 1
KSMCKeyNotFound = 132
)
func NewSMC(ioKit *Library) (*SMC, error) {
if ioKit.path != IOKit {
return nil, errors.New("library is not IOKit")
}
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)))
if service == 0 {
return nil, fmt.Errorf("ERROR: %s NOT FOUND", ioServiceSMC)
}
var conn uint32
if result := ioServiceOpen(service, machTaskSelf(), 0, &conn); result != 0 {
return nil, errors.New("ERROR: IOServiceOpen failed")
}
ioObjectRelease(service)
return &SMC{
lib: ioKit,
conn: conn,
callStruct: ioConnectCallStructMethod,
}, nil
}
func (s *SMC) CallStruct(selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int {
return s.callStruct(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 {
return errors.New("ERROR: IOServiceClose failed")
}
return nil
}
type CStr []byte
func NewCStr(length int32) CStr {
return make(CStr, length)
}
func (s CStr) Length() int32 {
// Include null terminator to make CFStringGetCString properly functions
return int32(len(s)) + 1
}
func (s CStr) Ptr() *byte {
if len(s) < 1 {
return nil
}
return &s[0]
}
func (s CStr) Addr() uintptr {
return uintptr(unsafe.Pointer(s.Ptr()))
}
func (s CStr) GoString() string {
if s == nil {
return ""
}
var length int
for _, char := range s {
if char == '\x00' {
break
}
length++
}
return string(s[:length])
}
// https://github.com/ebitengine/purego/blob/main/internal/strings/strings.go#L26
func GoString(cStr *byte) string {
if cStr == nil {
return ""
}
var length int
for {
if *(*byte)(unsafe.Add(unsafe.Pointer(cStr), uintptr(length))) == '\x00' {
break
}
length++
}
return string(unsafe.Slice(cStr, length))
}