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