Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

os/signal: Notify doesn't catch signals when connecting os.Stdin to a subcommand #20159

Closed
mbertschler opened this issue Apr 28, 2017 · 4 comments

Comments

@mbertschler
Copy link

In some cases node Docker containers do not exit when you press ^C. I am writing a tool that starts these Docker containers, and want to catch the ^C and kill the program manually. The problem is just that once I connect the subcommands stdin with cmd.Stdin = os.Stdin, I do not get notified of ^C anymore.

I tried to reproduce the Docker behavior with a small Go program by catching signals and ignoring them, but that attempt was unsuccessful. This is why the reproducer is using Docker.

What version of Go are you using (go version)?

go version go1.8.1 darwin/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/mbertschler/go:/Users/mbertschler/wcd/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.8.1/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.8.1/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/d0/zv0zzgf90r9bds00x6rm_qbr0000gn/T/go-build584631438=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

What did you do?

https://play.golang.org/p/GQ1YHd2Af-

package main

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

func main() {
	ForceQuitter()
	Run("docker", "run", "--rm", "-it", "node:latest")
}

func ForceQuitter() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		fmt.Println("[SIGINT force quit]")
		os.Exit(0)
	}()
}

func Run(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

What did you expect to see?

$ signalkiller
> [SIGINT force quit]

What did you see instead?

To exit the node prompt you have to press ^C twice, but with this code it should exit immediately after the first one.

$ signalkiller
> 
(To exit, press ^C again or type .exit)
> 
@matipan
Copy link

matipan commented Apr 28, 2017

If I understand correctly, what you want to do here is to capture the interrupt request of the program.
The reason(I think) why you can't capture it is the -it argument. When you use that, the docker process needs to "steal away" all std inputs and outputs to be interactive, so your Go program is not in control anymore.

This isn't an issue with the os/signal package. To prove that, let me show you an example.
Imagine you have this go program that only says hi every 20 milliseconds

// Suppose we build this into a binary "hi"
package main

import (
	"fmt"
	"time"
)

func main() {
	for {
		fmt.Println("HI!")
		time.Sleep(20 * time.Millisecond)
	}
}

Then we modify your program a bit to be:

package main

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

func main() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		fmt.Println("[SIGINT force quit]")
		os.Exit(0)
	}()
	err := Run()
	fmt.Println(err)
}

func Run() error {
	cmd := exec.Command("./hi")
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

If you run this, you'll see that os/signal is able to read the interrupt signal and print [SIGINT] force quit] so the package is successfully catching signals when the std inputs and outputs are connected to a subcommand.

In conclusion, the issue seems to be with how the docker run -it command works. What do you think? :)

@odeke-em
Copy link
Member

/cc Mr. Signals @ianlancetaylor

@ianlancetaylor
Copy link
Contributor

Let's not all start calling me Mr. Signals....

@ianlancetaylor
Copy link
Contributor

I don't know much about docker, but @matipan 's comment looks plausible to me. I'm going to close this issue, since it doesn't seem that there is anything we can do to fix it in Go. @mbertschler please comment if you disagree.

@golang golang locked and limited conversation to collaborators May 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants