From 5b15bc623c4abb0608ca53a0f5b7d556602e8bf8 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sat, 6 Jul 2019 20:42:36 +0200 Subject: [PATCH 1/3] [process][posix] Fix #607 check pid existence with a signal instead of listing every pids Reference https://github.com/nightlyone/lockfile/blob/0d6b91e916a248e6c4813532c8a9e1f31742190e/lockfile_unix.go --- process/process.go | 15 --------------- process/process_fallback.go | 15 +++++++++++++++ process/process_posix.go | 29 +++++++++++++++++++++++++++++ process/process_windows.go | 15 +++++++++++++++ 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/process/process.go b/process/process.go index a9d10e0..3866fae 100644 --- a/process/process.go +++ b/process/process.go @@ -139,21 +139,6 @@ func PidExists(pid int32) (bool, error) { return PidExistsWithContext(context.Background(), pid) } -func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { - pids, err := Pids() - if err != nil { - return false, err - } - - for _, i := range pids { - if i == pid { - return true, err - } - } - - return false, err -} - // Background returns true if the process is in background, false otherwise. func (p *Process) Background() (bool, error) { return p.BackgroundWithContext(context.Background()) diff --git a/process/process_fallback.go b/process/process_fallback.go index 99c6573..afdb03e 100644 --- a/process/process_fallback.go +++ b/process/process_fallback.go @@ -48,6 +48,21 @@ func NewProcess(pid int32) (*Process, error) { return nil, common.ErrNotImplementedError } +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + pids, err := PidsWithContext(ctx) + if err != nil { + return false, err + } + + for _, i := range pids { + if i == pid { + return true, err + } + } + + return false, err +} + func (p *Process) Ppid() (int32, error) { return p.PpidWithContext(context.Background()) } diff --git a/process/process_posix.go b/process/process_posix.go index 8ffb6b7..13f0308 100644 --- a/process/process_posix.go +++ b/process/process_posix.go @@ -4,6 +4,7 @@ package process import ( "context" + "fmt" "os" "os/user" "path/filepath" @@ -69,6 +70,34 @@ func getTerminalMap() (map[uint64]string, error) { return ret, nil } +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + if pid <= 0 { + return false, fmt.Errorf("invalid pid %v", pid) + } + proc, err := os.FindProcess(int(pid)) + if err != nil { + return false, err + } + err = proc.Signal(syscall.Signal(0)) + if err == nil { + return true, nil + } + if err.Error() == "os: process already finished" { + return false, nil + } + errno, ok := err.(syscall.Errno) + if !ok { + return false, err + } + switch errno { + case syscall.ESRCH: + return false, nil + case syscall.EPERM: + return true, nil + } + return false, err +} + // SendSignal sends a unix.Signal to the process. // Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported. func (p *Process) SendSignal(sig syscall.Signal) error { diff --git a/process/process_windows.go b/process/process_windows.go index 96c3b1f..7a6d6b0 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -185,6 +185,21 @@ func PidsWithContext(ctx context.Context) ([]int32, error) { } +func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { + pids, err := Pids() + if err != nil { + return false, err + } + + for _, i := range pids { + if i == pid { + return true, err + } + } + + return false, err +} + func (p *Process) Ppid() (int32, error) { return p.PpidWithContext(context.Background()) } From 4ad0300e1ea85f599a2325ca6a42e140bf4be8df Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sat, 6 Jul 2019 20:53:21 +0200 Subject: [PATCH 2/3] [process][windows] Remove magic numbers and useless constants --- process/process_windows.go | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index 7a6d6b0..c091615 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -19,11 +19,6 @@ import ( "golang.org/x/sys/windows" ) -const ( - NoMoreFiles = 0x12 - MaxPathLength = 260 -) - var ( modpsapi = windows.NewLazySystemDLL("psapi.dll") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") @@ -253,12 +248,11 @@ func (p *Process) Exe() (string, error) { } func (p *Process) ExeWithContext(ctx context.Context) (string, error) { - // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION - c, err := syscall.OpenProcess(0x1000, false, uint32(p.Pid)) + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(p.Pid)) if err != nil { return "", err } - defer syscall.CloseHandle(c) + defer windows.CloseHandle(c) buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(syscall.MAX_LONG_PATH) if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+ @@ -361,15 +355,14 @@ func (p *Process) Username() (string, error) { func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { pid := p.Pid - // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION - c, err := syscall.OpenProcess(0x1000, false, uint32(pid)) + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) if err != nil { return "", err } - defer syscall.CloseHandle(c) + defer windows.CloseHandle(c) var token syscall.Token - err = syscall.OpenProcessToken(c, syscall.TOKEN_QUERY, &token) + err = syscall.OpenProcessToken(syscall.Handle(c), syscall.TOKEN_QUERY, &token) if err != nil { return "", err } @@ -426,19 +419,18 @@ func (p *Process) Nice() (int32, error) { } func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { - // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION - c, err := syscall.OpenProcess(0x1000, false, uint32(p.Pid)) + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(p.Pid)) if err != nil { return 0, err } - defer syscall.CloseHandle(c) + defer windows.CloseHandle(c) ret, _, err := procGetPriorityClass.Call(uintptr(c)) if ret == 0 { return 0, err } priority, ok := priorityClasses[int(ret)] if !ok { - return 0, fmt.Errorf("unknown priority class %s", ret) + return 0, fmt.Errorf("unknown priority class %v", ret) } return priority, nil } @@ -803,8 +795,7 @@ func getRusage(pid int32) (*windows.Rusage, error) { func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { var mem PROCESS_MEMORY_COUNTERS - // PROCESS_QUERY_LIMITED_INFORMATION is 0x1000 - c, err := windows.OpenProcess(0x1000, false, uint32(pid)) + c, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) if err != nil { return mem, err } @@ -838,8 +829,7 @@ type SYSTEM_TIMES struct { func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { var times SYSTEM_TIMES - // PROCESS_QUERY_LIMITED_INFORMATION is 0x1000 - h, err := windows.OpenProcess(0x1000, false, uint32(pid)) + h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) if err != nil { return times, err } From 4a95469fc9c84a9b742ce582e09a871717827e3c Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 7 Jul 2019 16:15:45 +0200 Subject: [PATCH 3/3] [process][windows] Fix #607 check pid existence with OpenProcess+GetExitCodeProcess Reference https://stackoverflow.com/a/600217 --- process/process_windows.go | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index c091615..d47b9f4 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -181,18 +181,41 @@ func PidsWithContext(ctx context.Context) ([]int32, error) { } func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { - pids, err := Pids() - if err != nil { - return false, err + if pid == 0 { // special case for pid 0 System Idle Process + return true, nil } - - for _, i := range pids { - if i == pid { - return true, err + if pid < 0 { + return false, fmt.Errorf("invalid pid %v", pid) + } + if pid%4 != 0 { + // OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043 + // so we list every pid just to be sure and be future-proof + pids, err := PidsWithContext(ctx) + if err != nil { + return false, err + } + for _, i := range pids { + if i == pid { + return true, err + } } + return false, err } - - return false, err + const STILL_ACTIVE = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess + h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) + if err == windows.ERROR_ACCESS_DENIED { + return true, nil + } + if err == windows.ERROR_INVALID_PARAMETER { + return false, nil + } + if err != nil { + return false, err + } + defer syscall.CloseHandle(syscall.Handle(h)) + var exitCode uint32 + err = windows.GetExitCodeProcess(h, &exitCode) + return exitCode == STILL_ACTIVE, err } func (p *Process) Ppid() (int32, error) {