golang exec a command on a running binary / process - go

If you look at Nginx it calls "nginx reload" to reload itself. Is there any way to send a signal from the command line to a running process? Even if the main process starts child processes how can I send commands to the main to notify its children?
ex:
myapp start -debug // starts a server
myapp reload -gracefull // stops the app gracefully
Now i need to send os signals to notify my server to perform a graceful shutdown
kill -QUIT pid
kill -USR2 pid
I hope my question is clear enough
Thnx

Receive signals
Take a look at the os/signal package.
Package signal implements access to incoming signals.
There is even an example in the documentation :
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal
// if we're not ready to receive when the signal is sent.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
// Block until a signal is received.
s := <-c
fmt.Println("Got signal:", s)
Send signals
To see how to send signals take a look at signal_test.go, it uses syscall. For example :
// Send this process a SIGHUP
t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
waitSig(t, c, syscall.SIGHUP)

I figured out that in go i we can pass the environment to syscall.Exec
err := syscall.Exec(argv0. os.Args. os.Environ())
simply copies the current env to the child process.

Related

Graceful shutdowns on Cloud Run

I'm referring to this article: Graceful Shutdowns on Cloud Run
The example outlines how to do this in Node.js.
How would one do this in Golang? Any issues with simply adding this to the func init() method?
func shutdownGracefully() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
// Do some cleanup stuff here...
os.Exit(0)
}()
}
We have sample Go code for proper signal handling at https://github.com/GoogleCloudPlatform/golang-samples/pull/1902. It's not merged yet but essentially highlights how to do this properly on Go.
Beware that when running locally, sending Ctrl+C to your application is a SIGINT, however on Cloud Run you will be getting SIGTERM. It is also important to pass cancellations properly and handling server shutdown gracefully while not dropping the ongoing requests (though, net/http.Server handles a decent portion of this for you as you’ll see in sample code).
How would one do this in Golang?
An idiomatic way to handle graceful shutdowns in go is having a select statement blocking the main goroutine listening for any signal. From there you can trigger all the proper shutdowns when necessary.
For instance:
select {
case err := <-anyOtherError:
// ...
case signal := <- c:
// Handle shutdown e.g. like http Server Shutdown
}
I like to set it inside a run function with other startup tasks like starting the server, configs, etc.

Gracefully terminate a process on Windows

I'm writing a server application in Go, and using a wrapper to run it as a Windows service.
There's a need to gracefully shut down the server (to close resources and connections properly), and in UNIX it would be handled through the SIGTERM signal. No big deal.
Though on Windows things seem very different. I see on this guide that signals actually exist on windows (?), and the SIGTERM is defined, though other pages indicate they don't, or to use other mechanisms like WM_CLOSE.
What is the preferable way to tell a headless process to gracefully terminate? How should it be implemented in Go?
The server is designed to be multiplatform, so the most standard way of doing it is preferable.
The go way to initiate canceling a task/service, is to use the context.Context package.
So if you want a signal handler to trigger the closing of a task's context.Context:
func registerSigHandler() context.Context {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
rootCtx := context.Background()
taskCtx, cancelFn := context.WithCancel(rootCtx)
go func() {
sig := <-sigCh
log.Println("received signal:", sig)
// let sub-task know to wrap up: cancel taskCtx
cancelFn()
}()
return taskCtx
}
and then pass the returned taskCtx on to your worker task for it to listen on.
select {
case <-taskCtx.Done():
// context was canceled
default: // poll - rather than block in this example
}
Playground source-code.
Output:
2019/03/10 19:18:51 Worker PID: 33186
2019/03/10 19:18:51 Will terminate on these signals: interrupt terminated
2019/03/10 19:18:51 2019-03-10 19:18:51.018962 -0400 EDT m=+0.001727305
2019/03/10 19:18:52 2019-03-10 19:18:52.022782 -0400 EDT m=+1.005517010
2019/03/10 19:18:53 2019-03-10 19:18:53.019925 -0400 EDT m=+2.002630457
$ kill -INT 33186
2019/03/10 19:18:53 received signal: interrupt
2019/03/10 19:18:53 task context terminated reason: context canceled
2019/03/10 19:18:53 wrapping up task...
2019/03/10 19:18:53 workerTask() exited cleanly
2019/03/10 19:18:53 main exited cleanly
EDIT:
I tested this on Windows 10 and the clean-up is triggered when a Ctrl-C is issued from the same console.
Not sure how to send signals externally on Windows - which may be the OP's original issue. Using say killtask /F /PID 33186 would indeed kill the process without any signal handler being triggered.
Signaling is implemented on Windows but Unix signals are unavailable.
There is an example in the signal package of golang for Windows to send a Ctrl-Break. It is refactored for interactive use here.
So, after a while, I just wanted to share how I solved it. It's ugly, but the only real way I found. Windows golang programs do listen to a CTRL+C, which is not a signal or anything like it, but it does trigger a graceful shutdown in the go program using the same signal UNIX has.
Here's the relevant code:
// Create shutdown channel
exitChan := make(chan int)
interruptChan := make(chan os.Signal, 1)
signal.Notify(interruptChan, os.Interrupt)
select {
case exitCode := <-exitChan:
os.Exit(exitCode)
case <-interruptChan:
// This works in Windows if ctrl-c is invoked. It sucks though
svr.Stop()
os.Exit(-1)
}
To trigger the CTRL+C given the process, I use a small program called windows-kill.exe
// Windows is "special" and we need to use a library for killing processes gracefully
// Unfortunately setting up the C++ library with go is a hassle, so we resort to a CLI tool
ex, _ := os.Executable()
cmdPath := filepath.Join(filepath.Dir(ex), "windows-kill.exe")
cmd := exec.CommandContext(context.Background(), cmdPath, "-SIGINT", strconv.Itoa(l.proc.Pid))
err := cmd.Start()
if err != nil {
panic(err)
}
Here's the repo of the library and tool: https://github.com/alirdn/windows-kill
The idea of using *nix constructs and functions to Windows is generally bad and prone to weird tricks that may or may not work.
The proper way to cleanup on shutdown a GUI application is the handling of WM_QUERYENDSESSION and WM_ENDSESSION.
Also, you have flexibility on generic shut-down mechanism, check here.
The proper way to get notified if you are a service is the service handler of your console application (SERVICE_STOPPED). For more, see Writing a ServiceMain.
If you are running some form of web-services, may be better to use the web-service itself to initiate a shutdown, as tracking PIDS for signals can get messy.
To stop a http web-service, simply add a route like this:
http.HandleFunc("/shutdown",
func(w http.ResponseWriter, r *http.Request) {
// stops server - may want to add admin credentials check here!
srv.Shutdown(context.Background())
})
Playground source-code example.
2019/03/10 16:58:15 Listening on: :8080
$ curl 'localhost:8080/shutdown'
2019/03/10 17:04:17 Listening on: :8080
2019/03/10 17:04:19 exited cleanly

How to listen for graceful server termination in grpc

I want to listen for graceful server termination in grpc in my handler. When the server is being stopped gracefully, I want to add some logic in my code to close open ports, files, flush results etc. How can I do that?
How is it different in case of unary and streaming handler?
You can have shutdown hook by listening to the signals something like this
In your main function or where you start your server create channel for signals that you want to listen to
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
// call your cleanup method with this channel as a routine
go cleanup(c)
In your clean up method
func cleanup(c chan os.Signal) {
// Wait for termination
<- c
// Do your cleanups here
}
Create the signal channel and call the cleanup function as a go routine before you start the gRPC server. Whenever the application (gRPC server) stopped or interrupted this channel will get the signal in the cleanup function where you can do the necessary cleanups
For grpc servers u can do this
func waitForGracefulShutdown(srv *grpc.Server) {
fmt.Println("Grpc messaging server started ...")
interruptChan := make(chan os.Signal, 1)
signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
// Block until we receive our signal.
<-interruptChan
// Create a deadline to wait for.
_, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
srv.GracefulStop()
publisher, messagingError := messaging.GetPublisherInstance()
if messagingError.Error == nil {
publisher.Close()
}
log.Println("Shutting down grpc messaging server.")
os.Exit(0)
}
Currently there's no mechanism to signal a service handler about Graceful stop. Cleanups and other such global functions that must happen upon server exiting wouldn't usually happen inside of a service handler.
That said, if you think your design is better off with such cleanups happening inside of the service handler and a signal from graceful close is critical, we would love to hear more about your use case. Perhaps open an issue on our github repo and we can discuss it there.
Best,
Mak

Golang Channel Won't Receive Messages

I try to explore go channel, i create channel buffer max 10, with gomaxprocess is 2, but i wonder why this code won't receive message
runtime.GOMAXPROCS(2)
messages := make(chan int, 9)
go func() {
for {
i := <-messages
fmt.Println("Receive data:", i)
}
}()
for i := 0; i <= 9; i++ {
fmt.Println("Send data ", i)
messages <- i
}
Your case works like this, though it may appear to work certain times, but it's not guaranteed to always.
Just to add some context, in an unbuffered channel, the sending go routine is blocked as it tries to send a value and a receive is guaranteed to occur before the sending go routine is awakened (in this case the main), so it may seem like a viable option in such cases. But the sending go routine may still exit before the print statement in the receiving go routine is executed. So basically you need to use a synchronization mechanism such that the sending go routine exits only after the work in the receiver is completed.
Here's how you can use a synchronization mechanism, have annotated it so that you can make better sense out of it. This will work for both buffered and unbuffered channels. Another option is to have the receive in the main thread itself so that it doesn't exit before receive processing is done, this way you won't need a separate synchronization mechanism. Hope this helps.
You created a channel which has 9 buffer space, which means main routine (r1) will not blocked until the 10th element was ready to send to messages.
In your go func() (r2), it most probably starts running when r1 almost finished for r2 is a new routine and system takes time to create stacks etc.
so, r2 doesn't print anything, for r1 is done and program exits while r2 has just begin running.

Signal other than SIGKILL not terminating process on Windows

I am launching a simple Java application through Go, with the goal of proving that Go can send a signal like SIGQUIT or SIGTERM, and the Java can catch that and handle it appropriately (i.e. shutdown gracefully). When I run the Java program on the command line and send it a CTRL+C, the Java program correctly catches the signal.
However, when I launch the Java program through Go and attempt to send the process a signal, the Java process is neither terminated nor does it handle the signal. The only one that works is SIGKILL, which of course is not caught, and simply kills the process.
Here are the relevant parts of my Go code:
Start:
startChan := make(chan bool)
go func(startChan chan<- bool) {
a.Cmd = exec.Command("java", "-jar", "C:\\Code\\sigterm\\TestApp.jar")
a.Cmd.Stdout = os.Stdout
a.Cmd.Stderr = os.Stderr
launchErr := a.Cmd.Start()
if launchErr != nil {
fmt.Println("Unable to start app:" + launchErr.Error())
}
startChan <- true
}(startChan)
Stop:
func (a *App) Stop(timeout time.Duration) {
a.Cmd.Process.Signal(syscall.SIGQUIT) //Does not work
a.Cmd.Process.Signal(syscall.SIGTERM) //Does not work
a.Cmd.Process.Signal(syscall.SIGKILL) //Works
}
This is on Windows, by the way. I tried it by launching another program (notepad) and got the same results; that is, only SIGKILL worked. I have confirmed that Go is getting the correct PID and so forth, so the signal should be going to the right place.
Any ideas about how I can get this to work?
This does not seem to be supported as stated in the documentation:
https://godoc.org/os#Process.Signal
Sending Interrupt on Windows is not implemented.
Also, see this issue discussion to get more context on why it could not be done:
https://github.com/golang/go/issues/6720
syscall package is tailored to the OS (https://golang.org/pkg/syscall/#pkg-overview).
Although interrupt is not implemented on Windows, signaling is usable.
There is an example in the signal package itself for Windows.
An interactive version (go run) is available here.

Resources