diff --git a/process_darwin.go b/process_darwin.go index 5ee87fb..f9238fc 100644 --- a/process_darwin.go +++ b/process_darwin.go @@ -2,9 +2,16 @@ package ps +// #include +// #include +// #include +import "C" + import ( "bytes" "encoding/binary" + "fmt" + "path/filepath" "syscall" "unsafe" ) @@ -27,6 +34,34 @@ func (p *DarwinProcess) Executable() string { return p.binary } +// bufSize references the constant that the implementation +// of proc_pidpath uses under the hood to make sure that +// no overflows happen. +// +// See https://opensource.apple.com/source/xnu/xnu-2782.40.9/libsyscall/wrappers/libproc/libproc.c +const bufSize = C.PROC_PIDPATHINFO_MAXSIZE + +func getExePathFromPid(pid int) (path string, err error) { + // Allocate in the C heap a string (char* terminated + // with `/0`) of size `bufSize` and then make sure + // that we free that memory that gets allocated + // in C (see the `defer` below). + buf := C.CString(string(make([]byte, bufSize))) + defer C.free(unsafe.Pointer(buf)) + + // Call the C function `proc_pidpath` from the included + // header file (libproc.h). + ret, err := C.proc_pidpath(C.int(pid), unsafe.Pointer(buf), bufSize) + if ret <= 0 { + err = fmt.Errorf("failed to retrieve pid path: %v", err) + return + } + + // Convert the C string back to a Go string. + path = C.GoString(buf) + return +} + func findProcess(pid int) (Process, error) { ps, err := processes() if err != nil { @@ -63,10 +98,20 @@ func processes() ([]Process, error) { darwinProcs := make([]Process, len(procs)) for i, p := range procs { + comm, err := getExePathFromPid(int(p.Pid)) + + if err != nil { + // Falls back to the kinfo_proc->kp_proc.p_comm if no string for the pid was found + comm = darwinCstring(p.Comm) + } else { + // returns the last element of the process execution path + comm = filepath.Base(comm) + } + darwinProcs[i] = &DarwinProcess{ pid: int(p.Pid), ppid: int(p.PPid), - binary: darwinCstring(p.Comm), + binary: comm, } }