From 234508376ae6efe7b4180598ea43aa72906a3871 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 25 Jul 2013 23:27:13 -0700 Subject: [PATCH] ctrl-c closes stdin for plugins so that they are unblocked --- packer.go | 3 +++ signal.go | 2 +- stdin.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 stdin.go diff --git a/packer.go b/packer.go index c2a3422cf..590e7ea3c 100644 --- a/packer.go +++ b/packer.go @@ -46,6 +46,9 @@ func main() { packer.Version, packer.VersionPrerelease, packer.GitCommit) log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH) + // Prepare stdin for plugin usage by switching it to a pipe + setupStdin() + config, err := loadConfig() if err != nil { fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err) diff --git a/signal.go b/signal.go index c7b4deab1..2808bdf54 100644 --- a/signal.go +++ b/signal.go @@ -18,7 +18,7 @@ func setupSignalHandlers(env packer.Environment) { // First interrupt. We mostly ignore this because it allows the // plugins time to cleanup. <-ch - log.Println("First interrupt. Ignoring, but closing stdin...") + log.Println("First interrupt. Ignoring to allow plugins to clean up.") // Second interrupt. Go down hard. <-ch diff --git a/stdin.go b/stdin.go new file mode 100644 index 000000000..2df4153f5 --- /dev/null +++ b/stdin.go @@ -0,0 +1,37 @@ +package main + +import ( + "io" + "log" + "os" + "os/signal" +) + +// setupStdin switches out stdin for a pipe. We do this so that we can +// close the writer end of the pipe when we receive an interrupt so plugins +// blocked on reading from stdin are unblocked. +func setupStdin() { + // Create the pipe and swap stdin for the reader end + r, w, _ := os.Pipe() + originalStdin := os.Stdin + os.Stdin = r + + // Create a goroutine that copies data from the original stdin + // into the writer end of the pipe forever. + go func() { + defer w.Close() + io.Copy(w, originalStdin) + }() + + // Register a signal handler for interrupt in order to close the + // writer end of our pipe so that readers get EOF downstream. + ch := make(chan os.Signal, 1) + signal.Notify(ch, os.Interrupt) + + go func() { + defer signal.Stop(ch) + defer w.Close() + <-ch + log.Println("Closing stdin because interrupt received.") + }() +}