[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
pull/1051/head
Lomanic 4 years ago
parent 59c1f43d3e
commit 0881c11a9a

@ -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 /<HOST_PROC>/proc/<PID> folder
// This covers the case when running inside container with a different process namespace (by default)
if isMount(common.HostProc()) { // if /<HOST_PROC>/proc exists and is mounted, check if /<HOST_PROC>/proc/<PID> 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

@ -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 /<HOST_PROC>/proc/<PID> folder
// This covers the case when running inside container with a different process namespace (by default)
if isMount(common.HostProc()) { // if /<HOST_PROC>/proc exists and is mounted, check if /<HOST_PROC>/proc/<PID> 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
}

Loading…
Cancel
Save