linuxfan
linuxfan

Reputation: 1160

Read input from user while ignoring CTRL-C

I'd like to read user-input in go and ignore the user's attempts to kill my process by trapping CTRL-C. I spin a goroutine to trap CTRL-C and also write to a channel. At this stage, I expect control to return to case <-intr in the main goroutine, but that's not happening.

Here's output from a run:

$ go run read.go 
Enter text: hello
Entered:hello

$go run read.go 
Enter text: ^CGot signal interrupt
Exiting signal handler..

Invalid input2
Interrupted.. Continuing
Enter text: ^C
Invalid input2
Enter text: ^C
Invalid input2
Enter text: ^C^C^C^C
Invalid input2
Enter text: ^C
Invalid input2
Enter text: ^C
Invalid input2
Enter text: 

The first CTRL-C is trapped alright and soon after it exits, it appears that the main goroutine is executing r := bufio.NewReader(os.Stdin)

When I hit CTRL-C as input later, it is simply treated as text and the signal handler isn't invoked.

My code is below (goplay link: http://play.golang.org/p/LiKZMYGr00)

package main

import ("fmt"
    "bufio"
    "os"
    "os/signal"
    "syscall"
    "io"
    "strings"
)

func main() {
    c := make(chan os.Signal)
    intr := make(chan bool)

    signal.Notify(c,  syscall.SIGINT)
    go func() {
        s := <-c
        fmt.Println("Got signal", s)
        fmt.Println("Exiting signal handler..")
        intr <- true
        return
    }()

breakOutOfHere:
    for {
        select {
        case <-intr:
            fmt.Println("Interrupted.. Continuing")
            continue
        default:
            fmt.Printf("Enter text: ")
            r := bufio.NewReader(os.Stdin)
            text, ret := r.ReadString('\n')
            if ret == io.EOF {
                fmt.Println("Invalid input1")
                continue
            } else if ret == nil {
                text = strings.TrimSpace(text)
                if text == "" {
                    fmt.Println("Invalid input2")
                    continue
                } else {
                    fmt.Printf("Entered:%s\n", text)
                    break breakOutOfHere
                }
            }
        }
    }
}

Upvotes: 1

Views: 1823

Answers (1)

Parth Desai
Parth Desai

Reputation: 1839

signal.Notify sends signal information to the channel specified, every time process receives signal. But in your code go routine completes, after first signal. So, It is not able to trap the signal again.

One simple way to do this is, you need to have a infinite loop with select clause in the go routine.

go func(c chan os.Signal, quit chan bool) {
    for {
        select {
        case <-c:
            fmt.Println("Got interrupt signal")
            fmt.Println("Exiting signal handler..")
            intr <- true
        case <-quit:
            fmt.Println("quit")
            return
        }
    }

}(c, quit)

Here, Channel quit is used, to request go-routine to exit cleanly.

Upvotes: 4

Related Questions