Skip to content

Commit 9c11ca4

Browse files
qbitgabriel
authored andcommitted
Add support for OpenBSD (mitchellh#7)
1 parent a56a1a8 commit 9c11ca4

File tree

2 files changed

+330
-0
lines changed

2 files changed

+330
-0
lines changed

process_openbsd.go

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
// +build openbsd,amd64
2+
3+
package ps
4+
5+
import (
6+
"bytes"
7+
"encoding/binary"
8+
"fmt"
9+
"syscall"
10+
"unsafe"
11+
)
12+
13+
// copied from sys/sysctl.h
14+
const (
15+
CTL_KERN = 1
16+
KERN_PROC = 66
17+
KERN_PROC_PID = 1
18+
KERN_PROC_ARGS = 55
19+
KERN_PROC_ARGV = 1
20+
KERN_PROC_ALL = 0
21+
)
22+
23+
/* Generated via cgo:
24+
25+
$ cat /tmp/gen_defs.go
26+
// +build ignore
27+
package ps
28+
// #include <sys/types.h>
29+
// #include <sys/sysctl.h>
30+
import "C"
31+
32+
type Kinfo_proc C.struct_kinfo_proc
33+
34+
$ go tool cgo -godefs temp.go
35+
36+
*/
37+
38+
type Kinfo_proc struct {
39+
Ki_forw uint64
40+
Ki_back uint64
41+
Ki_paddr uint64
42+
Ki_addr uint64
43+
Ki_fd uint64
44+
Ki_stats uint64
45+
Ki_limit uint64
46+
Ki_vmspace uint64
47+
Ki_sigacts uint64
48+
Ki_sess uint64
49+
Ki_tsess uint64
50+
Ki_ru uint64
51+
Ki_eflag int32
52+
Ki_exitsig int32
53+
Ki_flag int32
54+
Ki_pid int32
55+
Ki_ppid int32
56+
Ki_sid int32
57+
Ki_x_pgid int32
58+
Ki_tpgid int32
59+
Ki_uid uint32
60+
Ki_ruid uint32
61+
Ki_gid uint32
62+
Ki_rgid uint32
63+
Ki_groups [16]uint32
64+
Ki_ngroups int16
65+
Ki_jobc int16
66+
Ki_tdev uint32
67+
Ki_estcpu uint32
68+
Ki_rtime_sec uint32
69+
Ki_rtime_usec uint32
70+
Ki_cpticks int32
71+
Ki_pctcpu uint32
72+
Ki_swtime uint32
73+
Ki_slptime uint32
74+
Ki_schedflags int32
75+
Ki_uticks uint64
76+
Ki_sticks uint64
77+
Ki_iticks uint64
78+
Ki_tracep uint64
79+
Ki_traceflag int32
80+
Ki_holdcnt int32
81+
Ki_siglist int32
82+
Ki_sigmask uint32
83+
Ki_sigignore uint32
84+
Ki_sigcatch uint32
85+
Ki_stat int8
86+
Ki_priority uint8
87+
Ki_usrpri uint8
88+
Ki_nice uint8
89+
Ki_xstat uint16
90+
Ki_acflag uint16
91+
//Ki_comm [24]int8
92+
Ki_comm [20]byte
93+
Ki_wmesg [8]int8
94+
Ki_wchan uint64
95+
Ki_login [32]int8
96+
Ki_vm_rssize int32
97+
Ki_vm_tsize int32
98+
Ki_vm_dsize int32
99+
Ki_vm_ssize int32
100+
Ki_uvalid int64
101+
Ki_ustart_sec uint64
102+
Ki_ustart_usec uint32
103+
Ki_uutime_sec uint32
104+
Ki_uutime_usec uint32
105+
Ki_ustime_sec uint32
106+
Ki_ustime_usec uint32
107+
Ki_pad_cgo_0 [4]byte
108+
Ki_uru_maxrss uint64
109+
Ki_uru_ixrss uint64
110+
Ki_uru_idrss uint64
111+
Ki_uru_isrss uint64
112+
Ki_uru_minflt uint64
113+
Ki_uru_majflt uint64
114+
Ki_uru_nswap uint64
115+
Ki_uru_inblock uint64
116+
Ki_uru_oublock uint64
117+
Ki_uru_msgsnd uint64
118+
Ki_uru_msgrcv uint64
119+
Ki_uru_nsignals uint64
120+
Ki_uru_nvcsw uint64
121+
Ki_uru_nivcsw uint64
122+
Ki_uctime_sec uint32
123+
Ki_uctime_usec uint32
124+
Ki_psflags int32
125+
Ki_spare int32
126+
Ki_svuid uint32
127+
Ki_svgid uint32
128+
Ki_emul [8]int8
129+
Ki_rlim_rss_cur uint64
130+
Ki_cpuid uint64
131+
Ki_vm_map_size uint64
132+
Ki_tid int32
133+
Ki_rtableid uint32
134+
}
135+
136+
var proc_k_size = unsafe.Sizeof(Kinfo_proc{})
137+
138+
// UnixProcess is an implementation of Process that contains Unix-specific
139+
// fields and information.
140+
type UnixProcess struct {
141+
pid int
142+
ppid int
143+
state rune
144+
pgrp int
145+
sid int
146+
147+
binary string
148+
}
149+
150+
// Pid returns process id
151+
func (p *UnixProcess) Pid() int {
152+
return p.pid
153+
}
154+
155+
// PPid returns parent process id
156+
func (p *UnixProcess) PPid() int {
157+
return p.ppid
158+
}
159+
160+
// Executable returns process executable name
161+
func (p *UnixProcess) Executable() string {
162+
return p.binary
163+
}
164+
165+
// Path returns path to process executable
166+
func (p *UnixProcess) Path() (string, error) {
167+
// On OpenBSD we don't have the actual path of a binary, the next
168+
// best thing we can do is walk $PATH to hopefully find the binary.
169+
// More info here: https://github.com/kardianos/osext/commit/b4814f465fb1f92d46e37f7ef84d732ece7c3e3a
170+
return "", fmt.Errorf("Unsupported")
171+
}
172+
173+
// Refresh reloads all the data associated with this process.
174+
func (p *UnixProcess) Refresh() error {
175+
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(p.pid), int32(proc_k_size), 1}
176+
177+
buf, length, err := call_syscall(mib)
178+
if err != nil {
179+
return err
180+
}
181+
if length != uint64(proc_k_size) {
182+
return err
183+
}
184+
185+
k, err := parse_kinfo_proc(buf)
186+
if err != nil {
187+
return err
188+
}
189+
190+
p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
191+
return nil
192+
}
193+
194+
func copy_params(k *Kinfo_proc) (int, int, int, string) {
195+
n := -1
196+
for i, b := range k.Ki_comm {
197+
if b == 0 {
198+
break
199+
}
200+
n = i + 1
201+
}
202+
comm := string(k.Ki_comm[:n])
203+
204+
return int(k.Ki_ppid), int(k.Ki_x_pgid), int(k.Ki_sid), comm
205+
}
206+
207+
func findProcess(pid int) (Process, error) {
208+
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(pid), int32(proc_k_size), 1}
209+
210+
_, _, err := call_syscall(mib)
211+
if err != nil {
212+
return nil, err
213+
}
214+
215+
return newUnixProcess(pid)
216+
}
217+
218+
func processes() ([]Process, error) {
219+
results := make([]Process, 0, 50)
220+
221+
mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, int32(proc_k_size), 400}
222+
buf, length, err := call_syscall(mib)
223+
if err != nil {
224+
return results, err
225+
}
226+
227+
// get kinfo_proc size
228+
procinfo_len := int(proc_k_size)
229+
count := int(length / uint64(proc_k_size))
230+
231+
// parse buf to procs
232+
for i := 0; i < count; i++ {
233+
b := buf[i*procinfo_len : i*procinfo_len+procinfo_len]
234+
k, err := parse_kinfo_proc(b)
235+
if err != nil {
236+
continue
237+
}
238+
p, err := newUnixProcess(int(k.Ki_pid))
239+
if err != nil {
240+
continue
241+
}
242+
p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
243+
244+
results = append(results, p)
245+
}
246+
247+
return results, nil
248+
}
249+
250+
func parse_kinfo_proc(buf []byte) (Kinfo_proc, error) {
251+
var k Kinfo_proc
252+
br := bytes.NewReader(buf)
253+
err := binary.Read(br, binary.LittleEndian, &k)
254+
if err != nil {
255+
return k, err
256+
}
257+
258+
return k, nil
259+
}
260+
261+
func call_syscall(mib []int32) ([]byte, uint64, error) {
262+
miblen := uint64(len(mib))
263+
264+
// get required buffer size
265+
length := uint64(0)
266+
_, _, err := syscall.RawSyscall6(
267+
syscall.SYS___SYSCTL,
268+
uintptr(unsafe.Pointer(&mib[0])),
269+
uintptr(miblen),
270+
0,
271+
uintptr(unsafe.Pointer(&length)),
272+
0,
273+
0)
274+
if err != 0 {
275+
b := make([]byte, 0)
276+
return b, length, err
277+
}
278+
if length == 0 {
279+
b := make([]byte, 0)
280+
return b, length, err
281+
}
282+
// get proc info itself
283+
buf := make([]byte, length)
284+
_, _, err = syscall.RawSyscall6(
285+
syscall.SYS___SYSCTL,
286+
uintptr(unsafe.Pointer(&mib[0])),
287+
uintptr(miblen),
288+
uintptr(unsafe.Pointer(&buf[0])),
289+
uintptr(unsafe.Pointer(&length)),
290+
0,
291+
0)
292+
if err != 0 {
293+
return buf, length, err
294+
}
295+
296+
return buf, length, nil
297+
}
298+
299+
func newUnixProcess(pid int) (*UnixProcess, error) {
300+
p := &UnixProcess{pid: pid}
301+
return p, p.Refresh()
302+
}

process_openbsd_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// +build openbsd
2+
3+
package ps
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestFindProcessOpenBSD(t *testing.T) {
12+
proc := testFindProcess(t, "go-ps.test")
13+
assert.True(t, proc.PPid() > 0)
14+
}
15+
16+
func TestProcessesOpenBSD(t *testing.T) {
17+
testProcesses(t, "go")
18+
}
19+
20+
/*
21+
// Currently querying for -1 will return -1 :P
22+
23+
func TestProcessesOpenBSDError(t *testing.T) {
24+
proc, err := findProcess(-1)
25+
assert.Nil(t, proc)
26+
assert.Nil(t, err)
27+
}
28+
*/

0 commit comments

Comments
 (0)