1
0
mirror of https://github.com/shirou/gopsutil.git synced 2025-04-24 13:48:56 +08:00

[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
This commit is contained in:
Lomanic 2024-09-07 03:10:57 +02:00
parent 74cb403730
commit 76ccf0d220
6 changed files with 57 additions and 51 deletions

View File

@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
}
return ret, nil
}
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
out, err := invoke.CommandWithContext(ctx, "pgrep", "-P", strconv.Itoa(int(pid)))
if err != nil {
return []int32{}, err
}
lines := strings.Split(string(out), "\n")
ret := make([]int32, 0, len(lines))
for _, l := range lines {
if len(l) == 0 {
continue
}
i, err := strconv.ParseInt(l, 10, 32)
if err != nil {
continue
}
ret = append(ret, int32(i))
}
return ret, nil
}

View File

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

View File

@ -7,6 +7,7 @@ import (
"context"
"fmt"
"path/filepath"
"sort"
"strconv"
"strings"
@ -233,18 +234,21 @@ func convertCPUTimes(s string) (ret float64, err error) {
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
procs, err := ProcessesWithContext(ctx)
if err != nil {
return nil, err
return nil, nil
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(procs))
for _, proc := range procs {
ppid, err := proc.PpidWithContext(ctx)
if err != nil {
return nil, err
continue
}
if ppid == p.Pid {
ret = append(ret, proc)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}

View File

@ -8,6 +8,7 @@ import (
"context"
"errors"
"path/filepath"
"sort"
"strconv"
"strings"
@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
procs, err := ProcessesWithContext(ctx)
if err != nil {
return nil, err
return nil, nil
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(procs))
for _, proc := range procs {
ppid, err := proc.PpidWithContext(ctx)
if err != nil {
return nil, err
continue
}
if ppid == p.Pid {
ret = append(ret, proc)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}

View File

@ -12,6 +12,7 @@ import (
"math"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
statFiles, err := filepath.Glob(common.HostProcWithContext(ctx, "[0-9]*/stat"))
if err != nil {
return nil, err
}
if len(pids) == 0 {
return nil, ErrorNoChildren
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(statFiles))
for _, statFile := range statFiles {
statContents, err := os.ReadFile(statFile)
if err != nil {
return nil, err
continue
}
fields := splitProcStat(statContents)
pid, err := strconv.ParseInt(fields[1], 10, 32)
if err != nil {
continue
}
ppid, err := strconv.ParseInt(fields[4], 10, 32)
if err != nil {
continue
}
if int32(ppid) == p.Pid {
np, err := NewProcessWithContext(ctx, int32(pid))
if err != nil {
continue
}
ret = append(ret, np)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}

View File

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"path/filepath"
"sort"
"strconv"
"strings"
"unsafe"
@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
procs, err := ProcessesWithContext(ctx)
if err != nil {
return nil, err
return nil, nil
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
ret := make([]*Process, 0, len(procs))
for _, proc := range procs {
ppid, err := proc.PpidWithContext(ctx)
if err != nil {
return nil, err
continue
}
if ppid == p.Pid {
ret = append(ret, proc)
}
ret = append(ret, np)
}
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
return ret, nil
}