1// +build dragonfly
2
3package ps
4
5import (
6	"bytes"
7	"encoding/binary"
8	"fmt"
9	"syscall"
10	"unsafe"
11)
12
13// copied from sys/sysctl.h
14const (
15	CTL_KERN           = 1  // "high kernel": proc, limits
16	KERN_PROC          = 14 // struct: process entries
17	KERN_PROC_PID      = 1  // by process id
18	KERN_PROC_PROC     = 8  // only return procs
19	KERN_PROC_PATHNAME = 12 // path to executable
20)
21
22// copied from sys/user.h
23type Kinfo_proc struct {
24	Ki_structsize   int32
25	Ki_layout       int32
26	Ki_args         int64
27	Ki_paddr        int64
28	Ki_addr         int64
29	Ki_tracep       int64
30	Ki_textvp       int64
31	Ki_fd           int64
32	Ki_vmspace      int64
33	Ki_wchan        int64
34	Ki_pid          int32
35	Ki_ppid         int32
36	Ki_pgid         int32
37	Ki_tpgid        int32
38	Ki_sid          int32
39	Ki_tsid         int32
40	Ki_jobc         [2]byte
41	Ki_spare_short1 [2]byte
42	Ki_tdev         int32
43	Ki_siglist      [16]byte
44	Ki_sigmask      [16]byte
45	Ki_sigignore    [16]byte
46	Ki_sigcatch     [16]byte
47	Ki_uid          int32
48	Ki_ruid         int32
49	Ki_svuid        int32
50	Ki_rgid         int32
51	Ki_svgid        int32
52	Ki_ngroups      [2]byte
53	Ki_spare_short2 [2]byte
54	Ki_groups       [64]byte
55	Ki_size         int64
56	Ki_rssize       int64
57	Ki_swrss        int64
58	Ki_tsize        int64
59	Ki_dsize        int64
60	Ki_ssize        int64
61	Ki_xstat        [2]byte
62	Ki_acflag       [2]byte
63	Ki_pctcpu       int32
64	Ki_estcpu       int32
65	Ki_slptime      int32
66	Ki_swtime       int32
67	Ki_cow          int32
68	Ki_runtime      int64
69	Ki_start        [16]byte
70	Ki_childtime    [16]byte
71	Ki_flag         int64
72	Ki_kiflag       int64
73	Ki_traceflag    int32
74	Ki_stat         [1]byte
75	Ki_nice         [1]byte
76	Ki_lock         [1]byte
77	Ki_rqindex      [1]byte
78	Ki_oncpu        [1]byte
79	Ki_lastcpu      [1]byte
80	Ki_ocomm        [17]byte
81	Ki_wmesg        [9]byte
82	Ki_login        [18]byte
83	Ki_lockname     [9]byte
84	Ki_comm         [20]byte
85	Ki_emul         [17]byte
86	Ki_sparestrings [68]byte
87	Ki_spareints    [36]byte
88	Ki_cr_flags     int32
89	Ki_jid          int32
90	Ki_numthreads   int32
91	Ki_tid          int32
92	Ki_pri          int32
93	Ki_rusage       [144]byte
94	Ki_rusage_ch    [144]byte
95	Ki_pcb          int64
96	Ki_kstack       int64
97	Ki_udata        int64
98	Ki_tdaddr       int64
99	Ki_spareptrs    [48]byte
100	Ki_spareint64s  [96]byte
101	Ki_sflag        int64
102	Ki_tdflags      int64
103}
104
105// UnixProcess is an implementation of Process that contains Unix-specific
106// fields and information.
107type UnixProcess struct {
108	pid   int
109	ppid  int
110	state rune
111	pgrp  int
112	sid   int
113
114	binary string
115}
116
117// Pid returns process id
118func (p *UnixProcess) Pid() int {
119	return p.pid
120}
121
122// PPid returns parent process id
123func (p *UnixProcess) PPid() int {
124	return p.ppid
125}
126
127// Executable returns process executable name
128func (p *UnixProcess) Executable() string {
129	return p.binary
130}
131
132// Path returns path to process executable
133func (p *UnixProcess) Path() (string, error) {
134	return "", fmt.Errorf("Unsupported")
135}
136
137// Refresh reloads all the data associated with this process.
138func (p *UnixProcess) Refresh() error {
139
140	mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(p.pid)}
141
142	buf, length, err := call_syscall(mib)
143	if err != nil {
144		return err
145	}
146	proc_k := Kinfo_proc{}
147	if length != uint64(unsafe.Sizeof(proc_k)) {
148		return err
149	}
150
151	k, err := parse_kinfo_proc(buf)
152	if err != nil {
153		return err
154	}
155
156	p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
157	return nil
158}
159
160func copy_params(k *Kinfo_proc) (int, int, int, string) {
161	n := -1
162	for i, b := range k.Ki_comm {
163		if b == 0 {
164			break
165		}
166		n = i + 1
167	}
168	comm := string(k.Ki_comm[:n])
169
170	return int(k.Ki_ppid), int(k.Ki_pgid), int(k.Ki_sid), comm
171}
172
173func findProcess(pid int) (Process, error) {
174	mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, int32(pid)}
175
176	_, _, err := call_syscall(mib)
177	if err != nil {
178		return nil, err
179	}
180
181	return newUnixProcess(pid)
182}
183
184func processes() ([]Process, error) {
185	results := make([]Process, 0, 50)
186
187	mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0}
188	buf, length, err := call_syscall(mib)
189	if err != nil {
190		return results, err
191	}
192
193	// get kinfo_proc size
194	k := Kinfo_proc{}
195	procinfo_len := int(unsafe.Sizeof(k))
196	count := int(length / uint64(procinfo_len))
197
198	// parse buf to procs
199	for i := 0; i < count; i++ {
200		b := buf[i*procinfo_len : i*procinfo_len+procinfo_len]
201		k, err := parse_kinfo_proc(b)
202		if err != nil {
203			continue
204		}
205		p, err := newUnixProcess(int(k.Ki_pid))
206		if err != nil {
207			continue
208		}
209		p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
210
211		results = append(results, p)
212	}
213
214	return results, nil
215}
216
217func parse_kinfo_proc(buf []byte) (Kinfo_proc, error) {
218	var k Kinfo_proc
219	br := bytes.NewReader(buf)
220	err := binary.Read(br, binary.LittleEndian, &k)
221	if err != nil {
222		return k, err
223	}
224
225	return k, nil
226}
227
228func call_syscall(mib []int32) ([]byte, uint64, error) {
229	miblen := uint64(len(mib))
230
231	// get required buffer size
232	length := uint64(0)
233	_, _, err := syscall.RawSyscall6(
234		syscall.SYS___SYSCTL,
235		uintptr(unsafe.Pointer(&mib[0])),
236		uintptr(miblen),
237		0,
238		uintptr(unsafe.Pointer(&length)),
239		0,
240		0)
241	if err != 0 {
242		b := make([]byte, 0)
243		return b, length, err
244	}
245	if length == 0 {
246		b := make([]byte, 0)
247		return b, length, err
248	}
249	// get proc info itself
250	buf := make([]byte, length)
251	_, _, err = syscall.RawSyscall6(
252		syscall.SYS___SYSCTL,
253		uintptr(unsafe.Pointer(&mib[0])),
254		uintptr(miblen),
255		uintptr(unsafe.Pointer(&buf[0])),
256		uintptr(unsafe.Pointer(&length)),
257		0,
258		0)
259	if err != 0 {
260		return buf, length, err
261	}
262
263	return buf, length, nil
264}
265
266func newUnixProcess(pid int) (*UnixProcess, error) {
267	p := &UnixProcess{pid: pid}
268	return p, p.Refresh()
269}
270