ThePiachu
ThePiachu

Reputation: 9185

How to send an interrupt signal

I'm trying to implement a function that would call an interrupt signal in Go. I know how to intercept interrupt signals from the console, by using signal.Notify(interruptChannel, os.Interrupt), however, I can't find a way to actually send the interrupt signals around. I've found that you can send a signal to a process, but I'm not sure if this can be used to send a top-level interrupt signal.

Is there a way to send an interrupt signal from within a Go function that could be captured by anything that is listening for system interrupt signals, or is that something that's not supported in Go?

Upvotes: 24

Views: 50903

Answers (3)

NotX
NotX

Reputation: 2415

What I did (to implement a graceful shutdown logic with timeout and "resend" the catched Signal):

import (
    "io"
    "os"
    "os/exec"
    "os/signal"
    "runtime"
    "syscall"
)

func RegisterGracefulShutdown(closeHandler func() error) {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
    go func() {
        var err error
        sig := <-sigs
        signal.Stop(sigs)
        close(sigs)
        // simplified, you should wrap this call into a timeout
        closeHandler()
        syscallSig, ok := sig.(syscall.Signal)
        if ok && (runtime.GOOS != "windows" || syscallSig != syscall.SIGINT) {
            err = syscall.Kill(syscall.Getpid(), syscallSig)
        } else {
            if runtime.GOOS == "windows" {
                err = syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
            } else {
                err = syscall.Kill(syscall.Getpid(), syscall.SIGINT)
            }
        }
        if err != nil {
            // panic to ensure the process shutdown won't be pending forever b/c I couldn't re-propagate the signal
            panic(errUtil.ExplainIfErrorf(err, "emit a new signal"))
        }
    }()
}

Upvotes: 0

Till Scholz
Till Scholz

Reputation: 11

For the windows case you may use the following method:

func SendInterrupt() error {
    d, e := syscall.LoadDLL("kernel32.dll")
    if e != nil {
        return fmt.Errorf("LoadDLL: %v", e)
    }
    p, e := d.FindProc("GenerateConsoleCtrlEvent")
    if e != nil {
        return fmt.Errorf("FindProc: %v", e)
    }
    r, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(syscall.Getpid()))
    if r == 0 {
        return fmt.Errorf("GenerateConsoleCtrlEvent: %v", e)
    }
    return nil
}

Upvotes: 1

Debasish Mitra
Debasish Mitra

Reputation: 1535

Assuming you are using something like this for capturing interrupt signal

var stopChan = make(chan os.Signal, 2)
signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)

<-stopChan // wait for SIGINT

Use below from anywhere in your code to send interrupt signal to above wait part.

syscall.Kill(syscall.Getpid(), syscall.SIGINT)

Or if you are in the same package where where stopChan variable is defined. Thus making it accessible. You can do this.

stopChan <- syscall.SIGINT

Or you can define stopChan as a global variable (making the first letter in Capital letter will achieve the same), then you can send interrupt signal from a different package too.

Stopchan <- syscall.SIGINT

Upvotes: 73

Related Questions