What would be an effective way to kill a process with Go code if you only know the process name? I see some functions provided by the os
package like:
func FindProcess(pid int) (*Process, error)
func (p *Process) Kill() error
func (p *Process) Signal(sig Signal) error
Is there a good/common practice to get the pid
without having to execute commands and then parse the output?
I have found a way to get back the pid using a command like the following:
echo $(ps cax | grep myapp | grep -o '^[ ]*[0-9]*')
and I have used it with exec.Command()
but I would like to avoid it if there is a better approach.
The SIGINT signal is sent to a process by its controlling terminal when a user wishes to interrupt the process. This is typically initiated by pressing Ctrl+C, but on some systems, the "delete" character or "break" key can be used.
The provided context is used to kill the process (by calling os. Process. Kill) if the context becomes done before the command completes on its own. After the Run() completes, you can inspect ctx.
The easiest way is to use the Magic SysRq key : Alt + SysRq + i . This will kill all processes except for init . Alt + SysRq + o will shut down the system (killing init also). Also note that on some modern keyboards, you have to use PrtSc rather than SysRq .
Running external commands is probably the best way to do this. However, the following code runs on Ubuntu at least as long as you are the owner of the process to kill.
// killprocess project main.go
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
)
// args holds the commandline args
var args []string
// findAndKillProcess walks iterative through the /process directory tree
// looking up the process name found in each /proc/<pid>/status file. If
// the name matches the name in the argument the process with the corresponding
// <pid> will be killed.
func findAndKillProcess(path string, info os.FileInfo, err error) error {
// We just return in case of errors, as they are likely due to insufficient
// privileges. We shouldn't get any errors for accessing the information we
// are interested in. Run as root (sudo) and log the error, in case you want
// this information.
if err != nil {
// log.Println(err)
return nil
}
// We are only interested in files with a path looking like /proc/<pid>/status.
if strings.Count(path, "/") == 3 {
if strings.Contains(path, "/status") {
// Let's extract the middle part of the path with the <pid> and
// convert the <pid> into an integer. Log an error if it fails.
pid, err := strconv.Atoi(path[6:strings.LastIndex(path, "/")])
if err != nil {
log.Println(err)
return nil
}
// The status file contains the name of the process in its first line.
// The line looks like "Name: theProcess".
// Log an error in case we cant read the file.
f, err := ioutil.ReadFile(path)
if err != nil {
log.Println(err)
return nil
}
// Extract the process name from within the first line in the buffer
name := string(f[6:bytes.IndexByte(f, '\n')])
if name == args[1] {
fmt.Printf("PID: %d, Name: %s will be killed.\n", pid, name)
proc, err := os.FindProcess(pid)
if err != nil {
log.Println(err)
}
// Kill the process
proc.Kill()
// Let's return a fake error to abort the walk through the
// rest of the /proc directory tree
return io.EOF
}
}
}
return nil
}
// main is the entry point of any go application
func main() {
args = os.Args
if len(args) != 2 {
log.Fatalln("Usage: killprocess <processname>")
}
fmt.Printf("trying to kill process \"%s\"\n", args[1])
err := filepath.Walk("/proc", findAndKillProcess)
if err != nil {
if err == io.EOF {
// Not an error, just a signal when we are done
err = nil
} else {
log.Fatal(err)
}
}
}
It's just an example that certainly can be improved. I wrote this for Linux and tested the code on Ubuntu 15.10. It will not run on Windows.
I finally used something like the following:
// `echo "sudo_password" | sudo -S [command]`
// is used in order to run the command with `sudo`
_, err := exec.Command("sh", "-c", "echo '"+ sudopassword +"' | sudo -S pkill -SIGINT my_app_name").Output()
if err != nil {
// ...
} else {
// ...
}
I used the SIGINT
signal to gracefully stop the app.
From wikipedia:
SIGINT
The SIGINT signal is sent to a process by its controlling terminal when a user wishes to interrupt the process. This is typically initiated by pressing Ctrl+C, but on some systems, the "delete" character or "break" key can be used.
SIGKILL
The SIGKILL signal is sent to a process to cause it to terminate immediately (kill). In contrast to SIGTERM and SIGINT, this signal cannot be caught or ignored, and the receiving process cannot perform any clean-up upon receiving this signal. The following exceptions apply:
I've implemented various solutions to do this for months now, and for some reason it took me that long to find gopsutil. It is a 3rd party library and that may or may not be a deal breaker for you, but it has worked flawlessly for our cross-platform projects. The following example will kill the first process with the matching name, but it can easily be adapted to kill all processes with the name.
import "github.com/shirou/gopsutil/v3/process"
func KillProcess(name string) error {
processes, err := process.Processes()
if err != nil {
return err
}
for _, p := range processes {
n, err := p.Name()
if err != nil {
return err
}
if n == name {
return p.Kill()
}
}
return fmt.Errorf("process not found")
}
As an added bonus, the library also supports context cancellation on all process related operations including process queries, and killing the process.
func KillAllProcessesCtx(ctx context.Context, name string) error {
processes, err := process.ProcessesWithContext(ctx)
if err != nil {
return err
}
for _, p := range processes {
n, err := p.NameWithContext(ctx)
if err != nil {
return err
}
if n == name {
err = p.KillWithContext(ctx)
if err != nil {
return err
}
}
}
return nil
}
The library also supports graceful termination by sending your own signal to the process.
// Do this
err = p.SendSignal(syscall.SIGINT)
// Instead of this
err = p.Kill()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With