GO capture ctrl+c signal & run cleanup function? [SOLVED]


GOLANG Solutions, GO

Author: Tuan Nguyen
Reviewer: Deepak Prasad

Introduction

In this tutorial, we will answer the question "Is it possible to capture a Ctrl+C signal (SIGINT) and run a cleanup function, in a "defer" fashion" in Golang.

The answer is yes, with the help of the built-in package os/signal, we can handle incoming signals. A way for processes to communicate is through signals. When a process receives a signal, it interrupts itself in order to run a signal handler. The type of signal received typically determines how the software operates.

SIGINT is the signal sent when we press Ctrl+C. The default action is to terminate the process. However, some programs override this action and handle it differently. One common example is the bash interpreter. When we press Ctrl+C it doesn’t quit, instead, it prints a new and empty prompt line.

On Windows Control-C or (Control-Break) normally cause the program to exit. If Notify is called for os.Interrupt, Ctrl+C or BREAK will cause os.Interrupt to be sent on the channel, and the program will not exit. If Reset is called, or Stop is called on all channels passed to Notify, then the default behavior will be restored.

 

Example 1: Capture a Ctrl+C trap signal using os/signal package

In this example, we will use the Notify() function to capture the os.Interrupt signal (Ctrl+C).

func Notify(c chan<- os.Signal, sig ...os.Signal): Notify causes package signal to relay incoming signals to c. If no signals are provided, all incoming signals will be relayed to c. Otherwise, just the provided signals will. Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate. For a channel used for notification of just one signal value, a buffer of size 1 is sufficient.

package main

import (
	"fmt"
	"os"
	"os/signal"
	"time"
)

func main() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			fmt.Println(sig)
		}
	}()

	temp := 0
	for temp < 10 {
		fmt.Println("sleeping...")
		time.Sleep(2 * time.Second)
		temp += 1
	}
}

Output:

Firstly, I created a goroutine to handle the signals from keyboard. Whenever the os.I<code class="language-go">nterrupt is captured, this goroutine will print out a message to the console. While in the main goroutine, we have a for loop to keep it running.

 

Example 2: Another example to capture a Ctrl+C signal

We can apply another technic with channel to catch the interrupt signal and do some operations before exiting:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"time"
)

func main() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		fmt.Println("Program killed!")
		cleanup()
		os.Exit(1)
	}()

	for {
		fmt.Println("sleeping...")
		time.Sleep(2 * time.Second)
	}
}

func cleanup() {
	fmt.Println("This is the clean up function")
}

Output:

sleeping...
Program killed!
This is the clean up function
exit status 1

To slightly expand on the previous answers, syscall can be used to intercept SIGTERM, the standard signal sent by the kill command. Instead of os.Interrupt, use SIGTERM. Be aware that the syscall interface depends on the OS and may not always function (e.g. on windows). But catching both works well:

signal.Notify(c, os.Interrupt, syscall.SIGTERM)

 

Example 3: Block the main goroutine until SIGINT signal

In this example, we will create 2 channel, 1 for handle the signals and 1 for block the main goroutine. When we run this program it will block waiting for a signal. By typing ctrl-C (which the terminal shows as ^C) we can send a SIGINT signal, causing the program to print interrupt and then exit.

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	c := make(chan os.Signal, 1)
	check := make(chan bool, 1)

	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		sig := <-c
		fmt.Println()
		fmt.Println(sig)
		check <- true

		fmt.Println("This is the clean up message")
	}()

	fmt.Println("waiting for Ctrl+C signal")

	<-check
	fmt.Println("Done!!")
}

Output:

 

Summary

In some cases, we need to do extra work if users press Ctrl+C to terminate the program. This post shows how to capture Ctrl+C event and run the handler in Go. We can easily use the built-in package os/signal to catch and handle this signal.

 

References

https://pkg.go.dev/os/signal

 

Views: 179
Tuan Nguyen

Tuan Nguyen

He is proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment