From 1e7281bb9240e0767b30963eab3d3acab5d0ea12 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 5 Jan 2024 23:51:41 +0100 Subject: [PATCH 1/3] Use errors.Is(err, fs.ErrPermission) instead of os.IsPermission Per the os.IsPermission godoc. --- process_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/process_test.go b/process_test.go index 9d88eda..a053145 100644 --- a/process_test.go +++ b/process_test.go @@ -6,6 +6,8 @@ package ps_test import ( "bytes" + "errors" + "io/fs" "os" "path/filepath" "runtime" @@ -160,7 +162,7 @@ func TestFindProcessInit(t *testing.T) { } proc, err := ps.FindProcess(initPID) - if os.IsPermission(err) { + if errors.Is(err, fs.ErrPermission) { t.Skipf("no permission to read init process") } else if err != nil { t.Fatalf("FindProcess(%d): %v", initPID, err) From e20af04fa9401e74041737da1cdd43511b90b8c3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 5 Jan 2024 23:52:19 +0100 Subject: [PATCH 2/3] Get own process by PID in TestProcesses There might be multiple processes running with the same executable name. Use the PID to uniquely identify the test process. --- process_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/process_test.go b/process_test.go index a053145..f852ce2 100644 --- a/process_test.go +++ b/process_test.go @@ -105,8 +105,9 @@ func TestProcesses(t *testing.T) { t.Errorf("no processes returned") } + pid := os.Getpid() for _, p := range procs { - if p.Command() == getExeName() { + if p.PID() == pid { checkOwnProcess(t, p) return } From bbbb2de4265c4723bca9f3323417c7c5ad2e6faf Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 5 Jan 2024 23:55:25 +0100 Subject: [PATCH 3/3] More robust parsing of /proc//stat Also return more meaningful errors. --- process_procfs_stat.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/process_procfs_stat.go b/process_procfs_stat.go index 342bacb..1f861b4 100644 --- a/process_procfs_stat.go +++ b/process_procfs_stat.go @@ -28,7 +28,9 @@ func newUnixProcess(pid int) (Process, error) { } else if err != nil { return nil, fmt.Errorf("failed to lstat %s: %w", procDir, err) } - b, err := os.ReadFile(filepath.Join(procDir, "stat")) + + procStat := filepath.Join(procDir, "stat") + b, err := os.ReadFile(procStat) if err != nil { return nil, err } @@ -37,22 +39,24 @@ func newUnixProcess(pid int) (Process, error) { // format commStart := bytes.IndexByte(b, '(') commEnd := bytes.LastIndexByte(b[commStart:], ')') + commStart - pidS := b[:bytes.IndexByte(b, ' ')] - comm := b[commStart+1 : commEnd] - fields := append([][]byte{pidS, comm}, bytes.Fields(b[commEnd+2:])...) // +2 for '( ' + comm := string(b[commStart+1 : commEnd]) + fields := bytes.Fields(b[commEnd+2:]) // +2 for '( ' + if len(fields) < 2 { + return nil, fmt.Errorf("invalid process status format in %s", procStat) + } + ppid, err := strconv.Atoi(string(fields[1])) + if err != nil { + return nil, fmt.Errorf("invalid ppid format %s: %w", fields[1], err) + } p := &unixProcess{ pid: pid, + ppid: ppid, uid: int(st.Uid), gid: int(st.Gid), + command: comm, creationTime: time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec)), } - - p.ppid, err = strconv.Atoi(string(fields[3])) - if err != nil { - return nil, err - } - p.command = string(comm) p.executablePath, _ = filepath.EvalSymlinks(filepath.Join(procDir, "exe")) b, err = os.ReadFile(filepath.Join(procDir, "cmdline"))