Reputation: 2745
I want to implement a "process wrapper" in Go. Basically what it will do, is launch a process (lets say a node server) and monitor it (catch signals like SIGKILL, SIGTERM ...)
I think the way to do is to launch the node server in a go routine using syscall.Exec
:
func launchCmd(path string, args []string) {
err := syscall.Exec(path, args, os.Environ())
if err != nil {
panic(err)
}
}
Then I'd like to catch every possible signals generated by the command executed by syscall
. I'm pretty new to Go, any help would be appreciated.
Upvotes: 44
Views: 55564
Reputation: 623
Do NOT test your code with handling exit signals by:
go run main.go
build and run it independently:
go build main.go
./main
Upvotes: 1
Reputation: 41
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSEGV)
for {
s := <-c
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
return
case syscall.SIGHUP:
case syscall.SIGSEGV:
default:
return
}
}
}
Upvotes: 4
Reputation: 14158
There are three ways of executing a program in Go:
syscall
package with syscall.Exec, syscall.ForkExec, syscall.StartProcessos
package with os.StartProcessos/exec
package with exec.Commandsyscall.StartProcess is low level. It returns a uintptr
as a handle.
os.StartProcess
gives you a nice os.Process
struct that you can call Signal on. os/exec
gives you io.ReaderWriter
to use on a pipe. Both use syscall
internally.
Reading signals sent from a process other than your own seems a bit tricky. If it was possible, syscall
would be able to do it. I don't see anything obvious in the higher level packages.
To receive a signal you can use signal.Notify like this:
sigc := make(chan os.Signal, 1)
signal.Notify(sigc,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
s := <-sigc
// ... do something ...
}()
You just need to change the signals you're interested in listening to. If you don't specify a signal, it'll catch all the signals that can be captured.
You would use syscall.Kill or Process.Signal to map the signal. You can get the pid from Process.Pid
or as a result from syscall.StartProcess.
Upvotes: 76
Reputation: 95161
You can use signal.Notify :
import (
"os"
"os/signal"
"syscall"
)
func main() {
signalChannel := make(chan os.Signal, 2)
signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
go func() {
sig := <-signalChannel
switch sig {
case os.Interrupt:
//handle SIGINT
case syscall.SIGTERM:
//handle SIGTERM
}
}()
// ...
}
Upvotes: 41