Fatih Arslan
Fatih Arslan

Reputation: 17117

Check if a process exists in go way

If I have the PID of a process, is os.FindProcess enough to test for the existing of the process? I mean if it returns err can I assume that it's terminated (or killed)?

Edit:

I've just wrote a wrapper function around kill -s 0 (old-style bash process testing). This works without any problem, but I'm still happy if there is other solutions (done with go libraries) to this problem.:

func checkPid(pid int) bool {
    out, err := exec.Command("kill", "-s", "0", strconv.Itoa(pid)).CombinedOutput()
    if err != nil {
        log.Println(err)
    }

    if string(out) == "" {
        return true // pid exist
    }
    return false
}

Upvotes: 23

Views: 39543

Answers (8)

majidarif
majidarif

Reputation: 19985

Here is one way of how to check if a process exists/running with Golang on Windows.

We execute the command:

TASKLIST /V /NH /FI "PID eq 23232"

Which can return either:

INFO: No tasks are running which match the specified criteria.

Or if found:

Image Name                     PID Session Name        Session#    Mem Usage Status          User Name                                              CPU Time Window Title                                                         
========================= ======== ================ =========== ============ =============== ================================================== ============ ========================================================================
chrome.exe                   23232 Console                    1     42,472 K Unknown         THANOS\MARVEL                                       0:00:00 N/A                                         

Here is a function that takes advantage of this information.

func isProcessRunning(pid int) bool {
    cmd := exec.Command("TASKLIST", "/FI", fmt.Sprintf("PID eq %d", pid))
    result, err := cmd.Output()
    if err != nil {
        return false
    }
    return !bytes.Contains(result, []byte("No tasks are running"))
}

The best thing about this is you can find the process by other parameters too:

ImageName   eq, ne                  Image Name String
PID         eq, ne, gt, lt, ge, le  Process ID, A Positive integer.
Session     eq, ne, gt, lt, ge, le  Any valid session number.
SessionName eq, ne                  String
Status      eq, ne                  RUNNING | NOT RESPONDING | UNKNOWN
CPUTime     eq, ne, gt, lt, ge, le  Time hh:mm:ss
MemUsage    eq, ne, gt, lt, ge, le  Memory usage in KB, specify a valid integer.
Username    eq, ne                  User name ([Domain\]User).
Services    eq, ne                  Service Name String
Windowtitle eq, ne                  Window Title String
Modules     eq, ne                  DLL Name String

Upvotes: 2

Guido Perre
Guido Perre

Reputation: 19

After searching for a few hours, the correct answer to know if a process is running on Windows is the following:

func CheckProcessLife(pid int){
    cmd,_ := exec.Command("tasklist","/FI", "PID eq " + strconv.Itoa(pid)).Output()
    output := string(cmd[:])
    splitOutp := strings.Split(output, " ")
    if !(splitOutp[1] == "no") {
        time.Sleep(500 * time.Millisecond)
        fmt.Println("Process is running...")
        CheckProcessLife(pid)
    }else{
        fmt.Println("Process is no longer running.")
    }
}

You can check if the process is running with his PID or directly with his name, only change this line:

cmd,_ := exec.Command("tasklist","/FI", "IMAGENAME eq yourprocessname.exe").Output()

Upvotes: -1

Paul
Paul

Reputation: 605

All the answers so far are incomplete implementations. See https://github.com/shirou/gopsutil/blob/c141152a7b8f59b63e060fa8450f5cd5e7196dfb/process/process_posix.go#L73 for a more complete implementation (copied inline)

    func PidExists(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
    }

Upvotes: 4

Kerrmiter
Kerrmiter

Reputation: 676

On Windows checking the result of os.FindProcess() seems to be enough to check if process is running.

func isProcessRunning(pid int) bool {
    _, err = os.FindProcess(pid)
    if err != nil {
        return false
    }
    if runtime.GOOS == "windows" {
        return true
    }
    return false // further checking for other systems then Windows is not supported here
}

Upvotes: 2

Artur Sapek
Artur Sapek

Reputation: 2497

You can also just use syscall.Kill. It amounts to less code.

killErr := syscall.Kill(pid, syscall.Signal(0))
procExists := killErr == nil

Upvotes: 5

hyde
hyde

Reputation: 62777

If a previously known pid is not found in the system (not sure of go functions), it means process has definitely terminated and has been joined (on Unix, with wait call) too.

But other way around is not necessarily true. Just because a pid exists, it does not quarantee it is same process as before. There are only 65535 valid pids in standard Linux for example, and they can get re-used when there is a wrap-around. However, if you check reasonably often, for practical purposes you don't need to care about this (as long as pid of wrong new process being found is not a security vulnerability or something else critical, which somebody might try to trigger intentionally for malicious purposes).

Related links (and Related questions on their right columns):

Upvotes: 2

Nick Craig-Wood
Nick Craig-Wood

Reputation: 54079

Here is the traditional unix way to see if a process is alive - send it a signal of 0 (like you did with your bash example).

From kill(2):

   If  sig  is 0, then no signal is sent, but error checking is still per‐
   formed; this can be used to check for the existence of a process ID  or
   process group ID.

And translated into Go

package main

import (
    "fmt"
    "log"
    "os"
    "strconv"
    "syscall"
)

func main() {
    for _, p := range os.Args[1:] {
        pid, err := strconv.ParseInt(p, 10, 64)
        if err != nil {
            log.Fatal(err)
        }
        process, err := os.FindProcess(int(pid))
        if err != nil {
            fmt.Printf("Failed to find process: %s\n", err)
        } else {
            err := process.Signal(syscall.Signal(0))
            fmt.Printf("process.Signal on pid %d returned: %v\n", pid, err)
        }

    }
}

When you run it you get this, showing that process 123 is dead, process 1 is alive but not owned by you and process 12606 is alive and owned by you.

$ ./kill 1 $$ 123
process.Signal on pid 1 returned: operation not permitted
process.Signal on pid 12606 returned: <nil>
process.Signal on pid 123 returned: no such process

Upvotes: 48

Stephen Weinberg
Stephen Weinberg

Reputation: 53398

On unix like systems (linux, freebsd, etc) os.FindProcess will never return an error. I don't know what happens on Windows. This means you won't know if the PID is correct until you try to use the *os.Process for something.

You can look at the code here.

Upvotes: 6

Related Questions