1// +build openbsd
2
3package process
4
5import (
6	"C"
7	"bytes"
8	"context"
9	"encoding/binary"
10	"strings"
11	"unsafe"
12
13	cpu "github.com/shirou/gopsutil/cpu"
14	"github.com/shirou/gopsutil/internal/common"
15	mem "github.com/shirou/gopsutil/mem"
16	net "github.com/shirou/gopsutil/net"
17	"golang.org/x/sys/unix"
18)
19
20// MemoryInfoExStat is different between OSes
21type MemoryInfoExStat struct {
22}
23
24type MemoryMapsStat struct {
25}
26
27func Pids() ([]int32, error) {
28	return PidsWithContext(context.Background())
29}
30
31func PidsWithContext(ctx context.Context) ([]int32, error) {
32	var ret []int32
33	procs, err := Processes()
34	if err != nil {
35		return ret, nil
36	}
37
38	for _, p := range procs {
39		ret = append(ret, p.Pid)
40	}
41
42	return ret, nil
43}
44
45func (p *Process) Ppid() (int32, error) {
46	return p.PpidWithContext(context.Background())
47}
48
49func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
50	k, err := p.getKProc()
51	if err != nil {
52		return 0, err
53	}
54
55	return k.Ppid, nil
56}
57func (p *Process) Name() (string, error) {
58	return p.NameWithContext(context.Background())
59}
60
61func (p *Process) NameWithContext(ctx context.Context) (string, error) {
62	k, err := p.getKProc()
63	if err != nil {
64		return "", err
65	}
66
67	return common.IntToString(k.Comm[:]), nil
68}
69func (p *Process) Tgid() (int32, error) {
70	return 0, common.ErrNotImplementedError
71}
72func (p *Process) Exe() (string, error) {
73	return p.ExeWithContext(context.Background())
74}
75
76func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
77	return "", common.ErrNotImplementedError
78}
79
80func (p *Process) CmdlineSlice() ([]string, error) {
81	return p.CmdlineSliceWithContext(context.Background())
82}
83
84func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
85	mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv}
86	buf, _, err := common.CallSyscall(mib)
87
88	if err != nil {
89		return nil, err
90	}
91
92	argc := 0
93	argvp := unsafe.Pointer(&buf[0])
94	argv := *(**C.char)(unsafe.Pointer(argvp))
95	size := unsafe.Sizeof(argv)
96	var strParts []string
97
98	for argv != nil {
99		strParts = append(strParts, C.GoString(argv))
100
101		argc++
102		argv = *(**C.char)(unsafe.Pointer(uintptr(argvp) + uintptr(argc)*size))
103	}
104	return strParts, nil
105}
106
107func (p *Process) Cmdline() (string, error) {
108	return p.CmdlineWithContext(context.Background())
109}
110
111func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
112	argv, err := p.CmdlineSlice()
113	if err != nil {
114		return "", err
115	}
116	return strings.Join(argv, " "), nil
117}
118
119func (p *Process) CreateTime() (int64, error) {
120	return p.CreateTimeWithContext(context.Background())
121}
122
123func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
124	return 0, common.ErrNotImplementedError
125}
126func (p *Process) Cwd() (string, error) {
127	return p.CwdWithContext(context.Background())
128}
129
130func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
131	return "", common.ErrNotImplementedError
132}
133func (p *Process) Parent() (*Process, error) {
134	return p.ParentWithContext(context.Background())
135}
136
137func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
138	return p, common.ErrNotImplementedError
139}
140func (p *Process) Status() (string, error) {
141	return p.StatusWithContext(context.Background())
142}
143
144func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
145	k, err := p.getKProc()
146	if err != nil {
147		return "", err
148	}
149	var s string
150	switch k.Stat {
151	case SIDL:
152	case SRUN:
153	case SONPROC:
154		s = "R"
155	case SSLEEP:
156		s = "S"
157	case SSTOP:
158		s = "T"
159	case SDEAD:
160		s = "Z"
161	}
162
163	return s, nil
164}
165func (p *Process) Foreground() (bool, error) {
166	return p.ForegroundWithContext(context.Background())
167}
168
169func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
170	// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
171	pid := p.Pid
172	ps, err := exec.LookPath("ps")
173	if err != nil {
174		return false, err
175	}
176	out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid)))
177	if err != nil {
178		return false, err
179	}
180	return strings.IndexByte(string(out), '+') != -1, nil
181}
182func (p *Process) Uids() ([]int32, error) {
183	return p.UidsWithContext(context.Background())
184}
185
186func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
187	k, err := p.getKProc()
188	if err != nil {
189		return nil, err
190	}
191
192	uids := make([]int32, 0, 3)
193
194	uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid))
195
196	return uids, nil
197}
198func (p *Process) Gids() ([]int32, error) {
199	return p.GidsWithContext(context.Background())
200}
201
202func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
203	k, err := p.getKProc()
204	if err != nil {
205		return nil, err
206	}
207
208	gids := make([]int32, 0, 3)
209	gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid))
210
211	return gids, nil
212}
213func (p *Process) Terminal() (string, error) {
214	return p.TerminalWithContext(context.Background())
215}
216
217func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
218	k, err := p.getKProc()
219	if err != nil {
220		return "", err
221	}
222
223	ttyNr := uint64(k.Tdev)
224
225	termmap, err := getTerminalMap()
226	if err != nil {
227		return "", err
228	}
229
230	return termmap[ttyNr], nil
231}
232func (p *Process) Nice() (int32, error) {
233	return p.NiceWithContext(context.Background())
234}
235
236func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
237	k, err := p.getKProc()
238	if err != nil {
239		return 0, err
240	}
241	return int32(k.Nice), nil
242}
243func (p *Process) IOnice() (int32, error) {
244	return p.IOniceWithContext(context.Background())
245}
246
247func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
248	return 0, common.ErrNotImplementedError
249}
250func (p *Process) Rlimit() ([]RlimitStat, error) {
251	return p.RlimitWithContext(context.Background())
252}
253
254func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
255	var rlimit []RlimitStat
256	return rlimit, common.ErrNotImplementedError
257}
258func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
259	return p.RlimitUsageWithContext(context.Background(), gatherUsed)
260}
261
262func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
263	var rlimit []RlimitStat
264	return rlimit, common.ErrNotImplementedError
265}
266func (p *Process) IOCounters() (*IOCountersStat, error) {
267	return p.IOCountersWithContext(context.Background())
268}
269
270func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
271	k, err := p.getKProc()
272	if err != nil {
273		return nil, err
274	}
275	return &IOCountersStat{
276		ReadCount:  uint64(k.Uru_inblock),
277		WriteCount: uint64(k.Uru_oublock),
278	}, nil
279}
280func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
281	return p.NumCtxSwitchesWithContext(context.Background())
282}
283
284func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
285	return nil, common.ErrNotImplementedError
286}
287func (p *Process) NumFDs() (int32, error) {
288	return p.NumFDsWithContext(context.Background())
289}
290
291func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
292	return 0, common.ErrNotImplementedError
293}
294func (p *Process) NumThreads() (int32, error) {
295	return p.NumThreadsWithContext(context.Background())
296}
297
298func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
299	/* not supported, just return 1 */
300	return 1, nil
301}
302func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
303	return p.ThreadsWithContext(context.Background())
304}
305
306func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
307	ret := make(map[int32]*cpu.TimesStat)
308	return ret, common.ErrNotImplementedError
309}
310func (p *Process) Times() (*cpu.TimesStat, error) {
311	return p.TimesWithContext(context.Background())
312}
313
314func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
315	k, err := p.getKProc()
316	if err != nil {
317		return nil, err
318	}
319	return &cpu.TimesStat{
320		CPU:    "cpu",
321		User:   float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000,
322		System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000,
323	}, nil
324}
325func (p *Process) CPUAffinity() ([]int32, error) {
326	return p.CPUAffinityWithContext(context.Background())
327}
328
329func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
330	return nil, common.ErrNotImplementedError
331}
332func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
333	return p.MemoryInfoWithContext(context.Background())
334}
335
336func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
337	k, err := p.getKProc()
338	if err != nil {
339		return nil, err
340	}
341	pageSize, err := mem.GetPageSize()
342	if err != nil {
343		return nil, err
344	}
345
346	return &MemoryInfoStat{
347		RSS: uint64(k.Vm_rssize) * pageSize,
348		VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) +
349			uint64(k.Vm_ssize),
350	}, nil
351}
352func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
353	return p.MemoryInfoExWithContext(context.Background())
354}
355
356func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
357	return nil, common.ErrNotImplementedError
358}
359
360func (p *Process) Children() ([]*Process, error) {
361	return p.ChildrenWithContext(context.Background())
362}
363
364func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
365	pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
366	if err != nil {
367		return nil, err
368	}
369	ret := make([]*Process, 0, len(pids))
370	for _, pid := range pids {
371		np, err := NewProcess(pid)
372		if err != nil {
373			return nil, err
374		}
375		ret = append(ret, np)
376	}
377	return ret, nil
378}
379
380func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
381	return p.OpenFilesWithContext(context.Background())
382}
383
384func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
385	return nil, common.ErrNotImplementedError
386}
387
388func (p *Process) Connections() ([]net.ConnectionStat, error) {
389	return p.ConnectionsWithContext(context.Background())
390}
391
392func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
393	return nil, common.ErrNotImplementedError
394}
395
396func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
397	return p.NetIOCountersWithContext(context.Background(), pernic)
398}
399
400func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
401	return nil, common.ErrNotImplementedError
402}
403
404func (p *Process) IsRunning() (bool, error) {
405	return p.IsRunningWithContext(context.Background())
406}
407
408func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
409	return true, common.ErrNotImplementedError
410}
411func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
412	return p.MemoryMapsWithContext(context.Background(), grouped)
413}
414
415func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
416	var ret []MemoryMapsStat
417	return &ret, common.ErrNotImplementedError
418}
419
420func Processes() ([]*Process, error) {
421	return ProcessesWithContext(context.Background())
422}
423
424func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
425	results := []*Process{}
426
427	buf, length, err := CallKernProcSyscall(KernProcAll, 0)
428
429	if err != nil {
430		return results, err
431	}
432
433	// get kinfo_proc size
434	count := int(length / uint64(sizeOfKinfoProc))
435
436	// parse buf to procs
437	for i := 0; i < count; i++ {
438		b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc]
439		k, err := parseKinfoProc(b)
440		if err != nil {
441			continue
442		}
443		p, err := NewProcess(int32(k.Pid))
444		if err != nil {
445			continue
446		}
447
448		results = append(results, p)
449	}
450
451	return results, nil
452}
453
454func parseKinfoProc(buf []byte) (KinfoProc, error) {
455	var k KinfoProc
456	br := bytes.NewReader(buf)
457	err := common.Read(br, binary.LittleEndian, &k)
458	return k, err
459}
460
461func (p *Process) getKProc() (*KinfoProc, error) {
462	return p.getKProcWithContext(context.Background())
463}
464
465func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) {
466	buf, length, err := CallKernProcSyscall(KernProcPID, p.Pid)
467	if err != nil {
468		return nil, err
469	}
470	if length != sizeOfKinfoProc {
471		return nil, err
472	}
473
474	k, err := parseKinfoProc(buf)
475	if err != nil {
476		return nil, err
477	}
478	return &k, nil
479}
480
481func NewProcess(pid int32) (*Process, error) {
482	p := &Process{Pid: pid}
483
484	return p, nil
485}
486
487func CallKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) {
488	return CallKernProcSyscallWithContext(context.Background(), op, arg)
489}
490
491func CallKernProcSyscallWithContext(ctx context.Context, op int32, arg int32) ([]byte, uint64, error) {
492	mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0}
493	mibptr := unsafe.Pointer(&mib[0])
494	miblen := uint64(len(mib))
495	length := uint64(0)
496	_, _, err := unix.Syscall6(
497		unix.SYS___SYSCTL,
498		uintptr(mibptr),
499		uintptr(miblen),
500		0,
501		uintptr(unsafe.Pointer(&length)),
502		0,
503		0)
504	if err != 0 {
505		return nil, length, err
506	}
507
508	count := int32(length / uint64(sizeOfKinfoProc))
509	mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count}
510	mibptr = unsafe.Pointer(&mib[0])
511	miblen = uint64(len(mib))
512	// get proc info itself
513	buf := make([]byte, length)
514	_, _, err = unix.Syscall6(
515		unix.SYS___SYSCTL,
516		uintptr(mibptr),
517		uintptr(miblen),
518		uintptr(unsafe.Pointer(&buf[0])),
519		uintptr(unsafe.Pointer(&length)),
520		0,
521		0)
522	if err != 0 {
523		return buf, length, err
524	}
525
526	return buf, length, nil
527}
528