Open joonas-fi opened 4 years ago
package main import ( "fmt" "log" "os" "os/exec" "os/signal" "syscall" "time" "golang.org/x/sys/windows" ) func main() { if err := logic(); err != nil { panic(err) } } func logic() error { switch { case len(os.Args) == 1: return parent() case len(os.Args) == 2 && os.Args[1] == "child": return child() default: return fmt.Errorf(`usage: %s ["child"]`, os.Args[0]) } } func child() error { sigs := catchAllSignals() log.Println("[CHILD] started") select { case <-time.After(5 * time.Second): log.Println("[CHILD] reached timeout, bye") return nil case sig := <-sigs: log.Printf("[CHILD] got %s", sig) return nil } } func parent() error { go func() { log.Printf("[PARENT] got UNEXPECTED %s - exiting", <-catchAllSignals()) time.Sleep(1 * time.Second) // to log our "child exited" msg os.Exit(1) }() childCmd := exec.Command(os.Args[0], "child") childCmd.SysProcAttr = &syscall.SysProcAttr{ CreationFlags: windows.CREATE_NEW_PROCESS_GROUP, } childCmd.Stdout = os.Stdout childCmd.Stderr = os.Stderr if err := childCmd.Start(); err != nil { return err } clientExit := make(chan error, 1) go func() { clientExit <- childCmd.Wait() }() // make sure child gets time to set up signal handlers time.Sleep(1 * time.Second) /* Summary: CTRL_CLOSE_EVENT works for different process group: NO CTRL_C_EVENT works for different process group: NO CTRL_BREAK_EVENT works for different process group: YES */ // if err := windows.GenerateConsoleCtrlEvent(windows.CTRL_C_EVENT, uint32(client.Process.Pid)); err != nil { // if err := windows.GenerateConsoleCtrlEvent(windows.CTRL_CLOSE_EVENT, uint32(client.Process.Pid)); err != nil { if err := windows.GenerateConsoleCtrlEvent(windows.CTRL_BREAK_EVENT, uint32(childCmd.Process.Pid)); err != nil { return err } log.Printf("[PARENT] child exited: %v", <-clientExit) return nil } func catchAllSignals() <-chan os.Signal { sigs := make(chan os.Signal, 1) signal.Notify(sigs) // = all signals return sigs }