1// +build openbsd
2
3package process
4
5import (
6	"C"
7	"context"
8	"os/exec"
9	"path/filepath"
10	"strconv"
11	"strings"
12	"unsafe"
13
14	cpu "github.com/shirou/gopsutil/v3/cpu"
15	"github.com/shirou/gopsutil/v3/internal/common"
16	mem "github.com/shirou/gopsutil/v3/mem"
17	net "github.com/shirou/gopsutil/v3/net"
18	"golang.org/x/sys/unix"
19)
20
21func pidsWithContext(ctx context.Context) ([]int32, error) {
22	var ret []int32
23	procs, err := ProcessesWithContext(ctx)
24	if err != nil {
25		return ret, nil
26	}
27
28	for _, p := range procs {
29		ret = append(ret, p.Pid)
30	}
31
32	return ret, nil
33}
34
35func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
36	k, err := p.getKProc()
37	if err != nil {
38		return 0, err
39	}
40
41	return k.Ppid, nil
42}
43
44func (p *Process) NameWithContext(ctx context.Context) (string, error) {
45	k, err := p.getKProc()
46	if err != nil {
47		return "", err
48	}
49	name := common.IntToString(k.Comm[:])
50
51	if len(name) >= 15 {
52		cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
53		if err != nil {
54			return "", err
55		}
56		if len(cmdlineSlice) > 0 {
57			extendedName := filepath.Base(cmdlineSlice[0])
58			if strings.HasPrefix(extendedName, p.name) {
59				name = extendedName
60			} else {
61				name = cmdlineSlice[0]
62			}
63		}
64	}
65
66	return name, nil
67}
68
69func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
70	return "", common.ErrNotImplementedError
71}
72
73func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
74	mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv}
75	buf, _, err := common.CallSyscall(mib)
76
77	if err != nil {
78		return nil, err
79	}
80
81	argc := 0
82	argvp := unsafe.Pointer(&buf[0])
83	argv := *(**C.char)(unsafe.Pointer(argvp))
84	size := unsafe.Sizeof(argv)
85	var strParts []string
86
87	for argv != nil {
88		strParts = append(strParts, C.GoString(argv))
89
90		argc++
91		argv = *(**C.char)(unsafe.Pointer(uintptr(argvp) + uintptr(argc)*size))
92	}
93	return strParts, nil
94}
95
96func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
97	argv, err := p.CmdlineSliceWithContext(ctx)
98	if err != nil {
99		return "", err
100	}
101	return strings.Join(argv, " "), nil
102}
103
104func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
105	return 0, common.ErrNotImplementedError
106}
107
108func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
109	return nil, common.ErrNotImplementedError
110}
111
112func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
113	k, err := p.getKProc()
114	if err != nil {
115		return []string{""}, err
116	}
117	var s string
118	switch k.Stat {
119	case SIDL:
120	case SRUN:
121	case SONPROC:
122		s = Running
123	case SSLEEP:
124		s = Sleep
125	case SSTOP:
126		s = Stop
127	case SDEAD:
128		s = Zombie
129	}
130
131	return []string{s}, nil
132}
133
134func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
135	// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
136	pid := p.Pid
137	ps, err := exec.LookPath("ps")
138	if err != nil {
139		return false, err
140	}
141	out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid)))
142	if err != nil {
143		return false, err
144	}
145	return strings.IndexByte(string(out), '+') != -1, nil
146}
147
148func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
149	k, err := p.getKProc()
150	if err != nil {
151		return nil, err
152	}
153
154	uids := make([]int32, 0, 3)
155
156	uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid))
157
158	return uids, nil
159}
160
161func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
162	k, err := p.getKProc()
163	if err != nil {
164		return nil, err
165	}
166
167	gids := make([]int32, 0, 3)
168	gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid))
169
170	return gids, nil
171}
172
173func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
174	k, err := p.getKProc()
175	if err != nil {
176		return nil, err
177	}
178
179	groups := make([]int32, k.Ngroups)
180	for i := int16(0); i < k.Ngroups; i++ {
181		groups[i] = int32(k.Groups[i])
182	}
183
184	return groups, nil
185}
186
187func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
188	k, err := p.getKProc()
189	if err != nil {
190		return "", err
191	}
192
193	ttyNr := uint64(k.Tdev)
194
195	termmap, err := getTerminalMap()
196	if err != nil {
197		return "", err
198	}
199
200	return termmap[ttyNr], nil
201}
202
203func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
204	k, err := p.getKProc()
205	if err != nil {
206		return 0, err
207	}
208	return int32(k.Nice), nil
209}
210
211func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
212	k, err := p.getKProc()
213	if err != nil {
214		return nil, err
215	}
216	return &IOCountersStat{
217		ReadCount:  uint64(k.Uru_inblock),
218		WriteCount: uint64(k.Uru_oublock),
219	}, nil
220}
221
222func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
223	/* not supported, just return 1 */
224	return 1, nil
225}
226
227func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
228	k, err := p.getKProc()
229	if err != nil {
230		return nil, err
231	}
232	return &cpu.TimesStat{
233		CPU:    "cpu",
234		User:   float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000,
235		System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000,
236	}, nil
237}
238
239func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
240	k, err := p.getKProc()
241	if err != nil {
242		return nil, err
243	}
244	pageSize, err := mem.GetPageSizeWithContext(ctx)
245	if err != nil {
246		return nil, err
247	}
248
249	return &MemoryInfoStat{
250		RSS: uint64(k.Vm_rssize) * pageSize,
251		VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) +
252			uint64(k.Vm_ssize),
253	}, nil
254}
255
256func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
257	pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
258	if err != nil {
259		return nil, err
260	}
261	ret := make([]*Process, 0, len(pids))
262	for _, pid := range pids {
263		np, err := NewProcessWithContext(ctx, pid)
264		if err != nil {
265			return nil, err
266		}
267		ret = append(ret, np)
268	}
269	return ret, nil
270}
271
272func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
273	return nil, common.ErrNotImplementedError
274}
275
276func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
277	return nil, common.ErrNotImplementedError
278}
279
280func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
281	results := []*Process{}
282
283	buf, length, err := callKernProcSyscall(KernProcAll, 0)
284
285	if err != nil {
286		return results, err
287	}
288
289	// get kinfo_proc size
290	count := int(length / uint64(sizeOfKinfoProc))
291
292	// parse buf to procs
293	for i := 0; i < count; i++ {
294		b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc]
295		k, err := parseKinfoProc(b)
296		if err != nil {
297			continue
298		}
299		p, err := NewProcessWithContext(ctx, int32(k.Pid))
300		if err != nil {
301			continue
302		}
303
304		results = append(results, p)
305	}
306
307	return results, nil
308}
309
310func (p *Process) getKProc() (*KinfoProc, error) {
311	buf, length, err := callKernProcSyscall(KernProcPID, p.Pid)
312	if err != nil {
313		return nil, err
314	}
315	if length != sizeOfKinfoProc {
316		return nil, err
317	}
318
319	k, err := parseKinfoProc(buf)
320	if err != nil {
321		return nil, err
322	}
323	return &k, nil
324}
325
326func callKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) {
327	mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0}
328	mibptr := unsafe.Pointer(&mib[0])
329	miblen := uint64(len(mib))
330	length := uint64(0)
331	_, _, err := unix.Syscall6(
332		unix.SYS___SYSCTL,
333		uintptr(mibptr),
334		uintptr(miblen),
335		0,
336		uintptr(unsafe.Pointer(&length)),
337		0,
338		0)
339	if err != 0 {
340		return nil, length, err
341	}
342
343	count := int32(length / uint64(sizeOfKinfoProc))
344	mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count}
345	mibptr = unsafe.Pointer(&mib[0])
346	miblen = uint64(len(mib))
347	// get proc info itself
348	buf := make([]byte, length)
349	_, _, err = unix.Syscall6(
350		unix.SYS___SYSCTL,
351		uintptr(mibptr),
352		uintptr(miblen),
353		uintptr(unsafe.Pointer(&buf[0])),
354		uintptr(unsafe.Pointer(&length)),
355		0,
356		0)
357	if err != 0 {
358		return buf, length, err
359	}
360
361	return buf, length, nil
362}
363