unix: add SysctlKinfoProc on darwin

This allows to get KinfoProc for a given process using
SysctlKinfoProcSlice("kern.proc.pid", pid) rather than having to query
all processes using SysctlKinfoProcSlice() and the extracting the
relevant KinfoProc.

Change-Id: I965ea5c77d6f3441592b4540c54ab56f6ac9e27d
Reviewed-on: https://go-review.googlesource.com/c/sys/+/359676
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/unix/syscall_darwin.go b/unix/syscall_darwin.go
index a8c1331..b799c5d 100644
--- a/unix/syscall_darwin.go
+++ b/unix/syscall_darwin.go
@@ -430,6 +430,23 @@
 	return x, err
 }
 
+func SysctlKinfoProc(name string, args ...int) (*KinfoProc, error) {
+	mib, err := sysctlmib(name, args...)
+	if err != nil {
+		return nil, err
+	}
+
+	var kinfo KinfoProc
+	n := uintptr(SizeofKinfoProc)
+	if err := sysctl(mib, (*byte)(unsafe.Pointer(&kinfo)), &n, nil, 0); err != nil {
+		return nil, err
+	}
+	if n != SizeofKinfoProc {
+		return nil, EIO
+	}
+	return &kinfo, nil
+}
+
 func SysctlKinfoProcSlice(name string) ([]KinfoProc, error) {
 	mib, err := sysctlmib(name)
 	if err != nil {
diff --git a/unix/syscall_darwin_test.go b/unix/syscall_darwin_test.go
index 731488f..9a68bfb 100644
--- a/unix/syscall_darwin_test.go
+++ b/unix/syscall_darwin_test.go
@@ -256,3 +256,14 @@
 		}
 	}
 }
+
+func TestSysctlKinfoProc(t *testing.T) {
+	pid := unix.Getpid()
+	kp, err := unix.SysctlKinfoProc("kern.proc.pid", pid)
+	if err != nil {
+		t.Fatalf("SysctlKinfoProc: %v", err)
+	}
+	if got, want := int(kp.Proc.P_pid), pid; got != want {
+		t.Errorf("got pid %d, want %d", got, want)
+	}
+}