mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-04-20 16:40:29 +00:00
108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// getProcessArgs uses kern.procargs2 sysctl to get process arguments.
|
|
// This is the same mechanism ps(1) uses on macOS — no exec.Command needed.
|
|
//
|
|
// The kern.procargs2 buffer layout:
|
|
//
|
|
// [4 bytes: argc as int32]
|
|
// [executable path\0]
|
|
// [padding \0 bytes]
|
|
// [argv[0]\0][argv[1]\0]...[argv[argc-1]\0]
|
|
// [env vars...]
|
|
func getProcessArgs(pid int) ([]string, error) {
|
|
// kern.procargs2 MIB: CTL_KERN=1, KERN_PROCARGS2=49
|
|
mib := []int32{1, 49, int32(pid)} //nolint:mnd
|
|
|
|
// First call to get buffer size
|
|
n := uintptr(0)
|
|
if err := sysctl(mib, nil, &n, nil, 0); err != nil {
|
|
return nil, fmt.Errorf("sysctl size query for pid %d: %w", pid, err)
|
|
}
|
|
|
|
buf := make([]byte, n)
|
|
if err := sysctl(mib, &buf[0], &n, nil, 0); err != nil {
|
|
return nil, fmt.Errorf("sysctl read for pid %d: %w", pid, err)
|
|
}
|
|
buf = buf[:n]
|
|
|
|
if len(buf) < 4 {
|
|
return nil, fmt.Errorf("procargs2 buffer too short for pid %d", pid)
|
|
}
|
|
|
|
// First 4 bytes: argc
|
|
argc := int(binary.LittleEndian.Uint32(buf[:4]))
|
|
pos := 4
|
|
|
|
// Skip executable path (null-terminated)
|
|
end := bytes.IndexByte(buf[pos:], 0)
|
|
if end == -1 {
|
|
return nil, fmt.Errorf("no null terminator in exec path for pid %d", pid)
|
|
}
|
|
pos += end + 1
|
|
|
|
// Skip padding null bytes
|
|
for pos < len(buf) && buf[pos] == 0 {
|
|
pos++
|
|
}
|
|
|
|
// Read argc arguments
|
|
args := make([]string, 0, argc)
|
|
for i := 0; i < argc && pos < len(buf); i++ {
|
|
end := bytes.IndexByte(buf[pos:], 0)
|
|
if end == -1 {
|
|
args = append(args, string(buf[pos:]))
|
|
break
|
|
}
|
|
args = append(args, string(buf[pos:pos+end]))
|
|
pos += end + 1
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
return nil, fmt.Errorf("no args found for pid %d", pid)
|
|
}
|
|
|
|
return args, nil
|
|
}
|
|
|
|
// getParentPID uses kern.proc.pid sysctl to find the parent PID.
|
|
func getParentPID(pid int) (int, error) {
|
|
// kern.proc.pid MIB: CTL_KERN=1, KERN_PROC=14, KERN_PROC_PID=1
|
|
mib := []int32{1, 14, 1, int32(pid)} //nolint:mnd
|
|
|
|
var kinfo unix.KinfoProc
|
|
n := uintptr(unsafe.Sizeof(kinfo))
|
|
|
|
if err := sysctl(mib, (*byte)(unsafe.Pointer(&kinfo)), &n, nil, 0); err != nil {
|
|
return 0, fmt.Errorf("sysctl kern.proc.pid for pid %d: %w", pid, err)
|
|
}
|
|
|
|
return int(kinfo.Eproc.Ppid), nil
|
|
}
|
|
|
|
// sysctl is a thin wrapper around unix.Sysctl raw syscall.
|
|
func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error {
|
|
_, _, errno := unix.Syscall6(
|
|
unix.SYS___SYSCTL,
|
|
uintptr(unsafe.Pointer(&mib[0])),
|
|
uintptr(len(mib)),
|
|
uintptr(unsafe.Pointer(old)),
|
|
uintptr(unsafe.Pointer(oldlen)),
|
|
uintptr(unsafe.Pointer(new)),
|
|
newlen,
|
|
)
|
|
if errno != 0 {
|
|
return errno
|
|
}
|
|
return nil
|
|
}
|