From 0881c11a9ac7b7da403b4571ebe4329fb020a90e Mon Sep 17 00:00:00 2001 From: Lomanic Date: Thu, 18 Mar 2021 23:32:09 +0100 Subject: [PATCH] [process][posix] Fix #1049 check if procfs is mounted before checking if pid exists there Benchmark before this change (process.NewProcess() calls process.PidExistsWithContext() internally) go test -bench=BenchmarkNewProcess github.com/shirou/gopsutil/process goos: linux goarch: amd64 pkg: github.com/shirou/gopsutil/process BenchmarkNewProcess-4 14722 78751 ns/op PASS ok github.com/shirou/gopsutil/process 3.685s Benchmark with this change applied go test -bench=BenchmarkNewProcess github.com/shirou/gopsutil/process goos: linux goarch: amd64 pkg: github.com/shirou/gopsutil/process BenchmarkNewProcess-4 14835 80180 ns/op PASS ok github.com/shirou/gopsutil/process 3.761s --- process/process_posix.go | 28 +++++++++++++++++++++------- v3/process/process_posix.go | 28 +++++++++++++++++++++------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/process/process_posix.go b/process/process_posix.go index 0074c6b..6f04129 100644 --- a/process/process_posix.go +++ b/process/process_posix.go @@ -71,6 +71,25 @@ func getTerminalMap() (map[uint64]string, error) { return ret, nil } +// isMount is a port of python's os.path.ismount() +// https://github.com/python/cpython/blob/08ff4369afca84587b1c82034af4e9f64caddbf2/Lib/posixpath.py#L186-L216 +// https://docs.python.org/3/library/os.path.html#os.path.ismount +func isMount(path string) bool { + var stat1 unix.Stat_t + if err := unix.Lstat(path, &stat1); err != nil { + return false + } + if stat1.Mode == unix.DT_LNK { + return false + } + parent := filepath.Join(path, "..") + var stat2 unix.Stat_t + if err := unix.Lstat(parent, &stat2); err != nil { + return false + } + return stat1.Dev != stat2.Dev || stat1.Ino == stat2.Ino +} + func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { if pid <= 0 { return false, fmt.Errorf("invalid pid %v", pid) @@ -79,11 +98,7 @@ func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { if err != nil { return false, err } - - if _, err := os.Stat(common.HostProc()); err == nil { //Means that proc filesystem exist - // Checking PID existence based on existence of //proc/ folder - // This covers the case when running inside container with a different process namespace (by default) - + if isMount(common.HostProc()) { // if //proc exists and is mounted, check if //proc/ folder exists _, err := os.Stat(common.HostProc(strconv.Itoa(int(pid)))) if os.IsNotExist(err) { return false, nil @@ -91,8 +106,7 @@ func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { return err == nil, err } - //'/proc' filesystem is not exist, checking of PID existence is done via signalling the process - //Make sense only if we run in the same process namespace + // procfs does not exist or is not mounted, check PID existence by signalling the pid err = proc.Signal(syscall.Signal(0)) if err == nil { return true, nil diff --git a/v3/process/process_posix.go b/v3/process/process_posix.go index 2b9997d..130788f 100644 --- a/v3/process/process_posix.go +++ b/v3/process/process_posix.go @@ -71,6 +71,25 @@ func getTerminalMap() (map[uint64]string, error) { return ret, nil } +// isMount is a port of python's os.path.ismount() +// https://github.com/python/cpython/blob/08ff4369afca84587b1c82034af4e9f64caddbf2/Lib/posixpath.py#L186-L216 +// https://docs.python.org/3/library/os.path.html#os.path.ismount +func isMount(path string) bool { + var stat1 unix.Stat_t + if err := unix.Lstat(path, &stat1); err != nil { + return false + } + if stat1.Mode == unix.DT_LNK { + return false + } + parent := filepath.Join(path, "..") + var stat2 unix.Stat_t + if err := unix.Lstat(parent, &stat2); err != nil { + return false + } + return stat1.Dev != stat2.Dev || stat1.Ino == stat2.Ino +} + func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { if pid <= 0 { return false, fmt.Errorf("invalid pid %v", pid) @@ -80,10 +99,7 @@ func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { return false, err } - if _, err := os.Stat(common.HostProc()); err == nil { //Means that proc filesystem exist - // Checking PID existence based on existence of //proc/ folder - // This covers the case when running inside container with a different process namespace (by default) - + if isMount(common.HostProc()) { // if //proc exists and is mounted, check if //proc/ folder exists _, err := os.Stat(common.HostProc(strconv.Itoa(int(pid)))) if os.IsNotExist(err) { return false, nil @@ -91,8 +107,7 @@ func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { return err == nil, err } - //'/proc' filesystem is not exist, checking of PID existence is done via signalling the process - //Make sense only if we run in the same process namespace + // procfs does not exist or is not mounted, check PID existence by signalling the pid err = proc.Signal(syscall.Signal(0)) if err == nil { return true, nil @@ -158,4 +173,3 @@ func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { } return "", nil } -