Skip to content

Commit

Permalink
Add support for OpenBSD (mitchellh#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
qbit authored and gabriel committed Aug 9, 2016
1 parent a56a1a8 commit 9c11ca4
Show file tree
Hide file tree
Showing 2 changed files with 330 additions and 0 deletions.
302 changes: 302 additions & 0 deletions process_openbsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
// +build openbsd,amd64

package ps

import (
"bytes"
"encoding/binary"
"fmt"
"syscall"
"unsafe"
)

// copied from sys/sysctl.h
const (
CTL_KERN = 1
KERN_PROC = 66
KERN_PROC_PID = 1
KERN_PROC_ARGS = 55
KERN_PROC_ARGV = 1
KERN_PROC_ALL = 0
)

/* Generated via cgo:
$ cat /tmp/gen_defs.go
// +build ignore
package ps
// #include <sys/types.h>
// #include <sys/sysctl.h>
import "C"
type Kinfo_proc C.struct_kinfo_proc
$ go tool cgo -godefs temp.go
*/

type Kinfo_proc struct {
Ki_forw uint64
Ki_back uint64
Ki_paddr uint64
Ki_addr uint64
Ki_fd uint64
Ki_stats uint64
Ki_limit uint64
Ki_vmspace uint64
Ki_sigacts uint64
Ki_sess uint64
Ki_tsess uint64
Ki_ru uint64
Ki_eflag int32
Ki_exitsig int32
Ki_flag int32
Ki_pid int32
Ki_ppid int32
Ki_sid int32
Ki_x_pgid int32
Ki_tpgid int32
Ki_uid uint32
Ki_ruid uint32
Ki_gid uint32
Ki_rgid uint32
Ki_groups [16]uint32
Ki_ngroups int16
Ki_jobc int16
Ki_tdev uint32
Ki_estcpu uint32
Ki_rtime_sec uint32
Ki_rtime_usec uint32
Ki_cpticks int32
Ki_pctcpu uint32
Ki_swtime uint32
Ki_slptime uint32
Ki_schedflags int32
Ki_uticks uint64
Ki_sticks uint64
Ki_iticks uint64
Ki_tracep uint64
Ki_traceflag int32
Ki_holdcnt int32
Ki_siglist int32
Ki_sigmask uint32
Ki_sigignore uint32
Ki_sigcatch uint32
Ki_stat int8
Ki_priority uint8
Ki_usrpri uint8
Ki_nice uint8
Ki_xstat uint16
Ki_acflag uint16
//Ki_comm [24]int8
Ki_comm [20]byte
Ki_wmesg [8]int8
Ki_wchan uint64
Ki_login [32]int8
Ki_vm_rssize int32
Ki_vm_tsize int32
Ki_vm_dsize int32
Ki_vm_ssize int32
Ki_uvalid int64
Ki_ustart_sec uint64
Ki_ustart_usec uint32
Ki_uutime_sec uint32
Ki_uutime_usec uint32
Ki_ustime_sec uint32
Ki_ustime_usec uint32
Ki_pad_cgo_0 [4]byte
Ki_uru_maxrss uint64
Ki_uru_ixrss uint64
Ki_uru_idrss uint64
Ki_uru_isrss uint64
Ki_uru_minflt uint64
Ki_uru_majflt uint64
Ki_uru_nswap uint64
Ki_uru_inblock uint64
Ki_uru_oublock uint64
Ki_uru_msgsnd uint64
Ki_uru_msgrcv uint64
Ki_uru_nsignals uint64
Ki_uru_nvcsw uint64
Ki_uru_nivcsw uint64
Ki_uctime_sec uint32
Ki_uctime_usec uint32
Ki_psflags int32
Ki_spare int32
Ki_svuid uint32
Ki_svgid uint32
Ki_emul [8]int8
Ki_rlim_rss_cur uint64
Ki_cpuid uint64
Ki_vm_map_size uint64
Ki_tid int32
Ki_rtableid uint32
}

var proc_k_size = unsafe.Sizeof(Kinfo_proc{})

// UnixProcess is an implementation of Process that contains Unix-specific
// fields and information.
type UnixProcess struct {
pid int
ppid int
state rune
pgrp int
sid int

binary string
}

// Pid returns process id
func (p *UnixProcess) Pid() int {
return p.pid
}

// PPid returns parent process id
func (p *UnixProcess) PPid() int {
return p.ppid
}

// Executable returns process executable name
func (p *UnixProcess) Executable() string {
return p.binary
}

// Path returns path to process executable
func (p *UnixProcess) Path() (string, error) {
// On OpenBSD we don't have the actual path of a binary, the next
// best thing we can do is walk $PATH to hopefully find the binary.
// More info here: https://github.com/kardianos/osext/commit/b4814f465fb1f92d46e37f7ef84d732ece7c3e3a
return "", fmt.Errorf("Unsupported")
}

// Refresh reloads all the data associated with this process.
func (p *UnixProcess) Refresh() error {
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(p.pid), int32(proc_k_size), 1}

buf, length, err := call_syscall(mib)
if err != nil {
return err
}
if length != uint64(proc_k_size) {
return err
}

k, err := parse_kinfo_proc(buf)
if err != nil {
return err
}

p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
return nil
}

func copy_params(k *Kinfo_proc) (int, int, int, string) {
n := -1
for i, b := range k.Ki_comm {
if b == 0 {
break
}
n = i + 1
}
comm := string(k.Ki_comm[:n])

return int(k.Ki_ppid), int(k.Ki_x_pgid), int(k.Ki_sid), comm
}

func findProcess(pid int) (Process, error) {
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(pid), int32(proc_k_size), 1}

_, _, err := call_syscall(mib)
if err != nil {
return nil, err
}

return newUnixProcess(pid)
}

func processes() ([]Process, error) {
results := make([]Process, 0, 50)

mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, int32(proc_k_size), 400}
buf, length, err := call_syscall(mib)
if err != nil {
return results, err
}

// get kinfo_proc size
procinfo_len := int(proc_k_size)
count := int(length / uint64(proc_k_size))

// parse buf to procs
for i := 0; i < count; i++ {
b := buf[i*procinfo_len : i*procinfo_len+procinfo_len]
k, err := parse_kinfo_proc(b)
if err != nil {
continue
}
p, err := newUnixProcess(int(k.Ki_pid))
if err != nil {
continue
}
p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)

results = append(results, p)
}

return results, nil
}

func parse_kinfo_proc(buf []byte) (Kinfo_proc, error) {
var k Kinfo_proc
br := bytes.NewReader(buf)
err := binary.Read(br, binary.LittleEndian, &k)
if err != nil {
return k, err
}

return k, nil
}

func call_syscall(mib []int32) ([]byte, uint64, error) {
miblen := uint64(len(mib))

// get required buffer size
length := uint64(0)
_, _, err := syscall.RawSyscall6(
syscall.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])),
uintptr(miblen),
0,
uintptr(unsafe.Pointer(&length)),
0,
0)
if err != 0 {
b := make([]byte, 0)
return b, length, err
}
if length == 0 {
b := make([]byte, 0)
return b, length, err
}
// get proc info itself
buf := make([]byte, length)
_, _, err = syscall.RawSyscall6(
syscall.SYS___SYSCTL,
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
}

func newUnixProcess(pid int) (*UnixProcess, error) {
p := &UnixProcess{pid: pid}
return p, p.Refresh()
}
28 changes: 28 additions & 0 deletions process_openbsd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// +build openbsd

package ps

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestFindProcessOpenBSD(t *testing.T) {
proc := testFindProcess(t, "go-ps.test")
assert.True(t, proc.PPid() > 0)
}

func TestProcessesOpenBSD(t *testing.T) {
testProcesses(t, "go")
}

/*
// Currently querying for -1 will return -1 :P
func TestProcessesOpenBSDError(t *testing.T) {
proc, err := findProcess(-1)
assert.Nil(t, proc)
assert.Nil(t, err)
}
*/

0 comments on commit 9c11ca4

Please sign in to comment.