Skip to content

Commit 76ccf0d

Browse files
committed
[process][darwin][freebsd][linux][openbsd] Make process.Children not reliant on pgrep
pgrep -P $PID exits with status of 1 (and nothing in stdout nor stderr) both if a process doesn't exist or it doesn't have child processes, so we don't use it anymore on these OSes. We sort PIDs as pgrep did. Also deprecate the ErrorNoChildren error when there are no child processes, this is erroneous (simply check for the length of the returned slice, plus this is not an error per se), this was only returned on linux anyway. Fixes #1698
1 parent 74cb403 commit 76ccf0d

File tree

6 files changed

+57
-51
lines changed

6 files changed

+57
-51
lines changed

internal/common/common_unix.go

-20
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
4040
}
4141
return ret, nil
4242
}
43-
44-
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
45-
out, err := invoke.CommandWithContext(ctx, "pgrep", "-P", strconv.Itoa(int(pid)))
46-
if err != nil {
47-
return []int32{}, err
48-
}
49-
lines := strings.Split(string(out), "\n")
50-
ret := make([]int32, 0, len(lines))
51-
for _, l := range lines {
52-
if len(l) == 0 {
53-
continue
54-
}
55-
i, err := strconv.ParseInt(l, 10, 32)
56-
if err != nil {
57-
continue
58-
}
59-
ret = append(ret, int32(i))
60-
}
61-
return ret, nil
62-
}

process/process.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
var (
2020
invoke common.Invoker = common.Invoke{}
21-
ErrorNoChildren = errors.New("process does not have children")
21+
ErrorNoChildren = errors.New("process does not have children") // Deprecated: ErrorNoChildren is never returned by process.Children(), check its returned []*Process slice length instead
2222
ErrorProcessNotRunning = errors.New("process does not exist")
2323
ErrorNotPermitted = errors.New("operation not permitted")
2424
)

process/process_darwin.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"fmt"
99
"path/filepath"
10+
"sort"
1011
"strconv"
1112
"strings"
1213

@@ -233,18 +234,21 @@ func convertCPUTimes(s string) (ret float64, err error) {
233234
}
234235

235236
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
236-
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
237+
procs, err := ProcessesWithContext(ctx)
237238
if err != nil {
238-
return nil, err
239+
return nil, nil
239240
}
240-
ret := make([]*Process, 0, len(pids))
241-
for _, pid := range pids {
242-
np, err := NewProcessWithContext(ctx, pid)
241+
ret := make([]*Process, 0, len(procs))
242+
for _, proc := range procs {
243+
ppid, err := proc.PpidWithContext(ctx)
243244
if err != nil {
244-
return nil, err
245+
continue
246+
}
247+
if ppid == p.Pid {
248+
ret = append(ret, proc)
245249
}
246-
ret = append(ret, np)
247250
}
251+
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
248252
return ret, nil
249253
}
250254

process/process_freebsd.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"context"
99
"errors"
1010
"path/filepath"
11+
"sort"
1112
"strconv"
1213
"strings"
1314

@@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
269270
}
270271

271272
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
272-
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
273+
procs, err := ProcessesWithContext(ctx)
273274
if err != nil {
274-
return nil, err
275+
return nil, nil
275276
}
276-
ret := make([]*Process, 0, len(pids))
277-
for _, pid := range pids {
278-
np, err := NewProcessWithContext(ctx, pid)
277+
ret := make([]*Process, 0, len(procs))
278+
for _, proc := range procs {
279+
ppid, err := proc.PpidWithContext(ctx)
279280
if err != nil {
280-
return nil, err
281+
continue
282+
}
283+
if ppid == p.Pid {
284+
ret = append(ret, proc)
281285
}
282-
ret = append(ret, np)
283286
}
287+
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
284288
return ret, nil
285289
}
286290

process/process_linux.go

+23-9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"math"
1313
"os"
1414
"path/filepath"
15+
"sort"
1516
"strconv"
1617
"strings"
1718

@@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
338339
}
339340

340341
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
341-
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
342+
statFiles, err := filepath.Glob(common.HostProcWithContext(ctx, "[0-9]*/stat"))
342343
if err != nil {
343344
return nil, err
344345
}
345-
if len(pids) == 0 {
346-
return nil, ErrorNoChildren
347-
}
348-
ret := make([]*Process, 0, len(pids))
349-
for _, pid := range pids {
350-
np, err := NewProcessWithContext(ctx, pid)
346+
ret := make([]*Process, 0, len(statFiles))
347+
for _, statFile := range statFiles {
348+
statContents, err := os.ReadFile(statFile)
351349
if err != nil {
352-
return nil, err
350+
continue
351+
}
352+
fields := splitProcStat(statContents)
353+
pid, err := strconv.ParseInt(fields[1], 10, 32)
354+
if err != nil {
355+
continue
356+
}
357+
ppid, err := strconv.ParseInt(fields[4], 10, 32)
358+
if err != nil {
359+
continue
360+
}
361+
if int32(ppid) == p.Pid {
362+
np, err := NewProcessWithContext(ctx, int32(pid))
363+
if err != nil {
364+
continue
365+
}
366+
ret = append(ret, np)
353367
}
354-
ret = append(ret, np)
355368
}
369+
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
356370
return ret, nil
357371
}
358372

process/process_openbsd.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"io"
1313
"path/filepath"
14+
"sort"
1415
"strconv"
1516
"strings"
1617
"unsafe"
@@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
286287
}
287288

288289
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
289-
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
290+
procs, err := ProcessesWithContext(ctx)
290291
if err != nil {
291-
return nil, err
292+
return nil, nil
292293
}
293-
ret := make([]*Process, 0, len(pids))
294-
for _, pid := range pids {
295-
np, err := NewProcessWithContext(ctx, pid)
294+
ret := make([]*Process, 0, len(procs))
295+
for _, proc := range procs {
296+
ppid, err := proc.PpidWithContext(ctx)
296297
if err != nil {
297-
return nil, err
298+
continue
299+
}
300+
if ppid == p.Pid {
301+
ret = append(ret, proc)
298302
}
299-
ret = append(ret, np)
300303
}
304+
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
301305
return ret, nil
302306
}
303307

0 commit comments

Comments
 (0)