// termSignalListener returns a signal chan that closes when either (a) the process receives a termination // signal: SIGTERM, SIGINT, or SIGHUP; or (b) the abort chan closes. func termSignalListener(abort <-chan struct{}) <-chan struct{} { shouldQuit := make(chan struct{}) sigCh := make(chan os.Signal, 1) signal.Notify(sigCh) go func() { defer close(shouldQuit) for { select { case <-abort: log.Infof("executor died, aborting") return case s, ok := <-sigCh: if !ok { return } switch s { case os.Interrupt, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP): log.Infof("received signal %q, aborting", s) return case os.Signal(syscall.SIGCHLD): // who cares? default: log.Errorf("unexpected signal: %T %#v", s, s) } } } }() return shouldQuit }
func (p *Proxy) closeOnSignal() { ch := make(chan os.Signal, 10) signal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP)) sig := <-ch p.ul.Close() switch sig { case os.Signal(syscall.SIGHUP): log.Printf("graceful shutdown from signal: %v\n", sig) default: log.Fatalf("exiting from signal: %v\n", sig) } }
func (h *shutdownHandler) wait() { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt, os.Signal(syscall.SIGTERM)) sig := <-ch grohl.Log(grohl.Data{"fn": "shutdown", "at": "start", "signal": fmt.Sprint(sig)}) h.shutdown(nil) }
func startServer(listenOn, dataDir string, pidFile string) { logger.Info("using version ", GITCOMMIT) logger.Info("starting server on ", listenOn) logger.Info("using dataDir ", dataDir) logger.Info("using pidFile", pidFile) if err := createPidFile(pidFile); err != nil { logger.Error(err) os.Exit(1) } defer removePidFile(pidFile) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c logger.Debug("Received signal '%v', exiting\n", sig) removePidFile(pidFile) os.Exit(0) }() if err := http.ListenAndServe(listenOn, NewHandler(dataDir)); err != nil { logger.Error(err.Error()) } }
// jobInitApi runs the remote api server `srv` as a daemon, // Only one api server can run at the same time - this is enforced by a pidfile. // The signals SIGINT, SIGKILL and SIGTERM are intercepted for cleanup. func jobInitApi(job *engine.Job) string { job.Logf("Creating server") srv, err := NewServer(job.Eng, ConfigFromJob(job)) if err != nil { return err.Error() } if srv.runtime.config.Pidfile != "" { job.Logf("Creating pidfile") if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil { log.Fatal(err) } } job.Logf("Setting up signal traps") c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) utils.RemovePidFile(srv.runtime.config.Pidfile) srv.Close() os.Exit(0) }() job.Eng.Hack_SetGlobalVar("httpapi.server", srv) if err := job.Eng.Register("create", srv.ContainerCreate); err != nil { return err.Error() } if err := job.Eng.Register("start", srv.ContainerStart); err != nil { return err.Error() } if err := job.Eng.Register("serveapi", srv.ListenAndServe); err != nil { return err.Error() } return "0" }
// Used for checking if a process exists in pidfile func processExistsAtPidString(pidStr string) bool { // Check if the process exists pid, err := strconv.Atoi(pidStr) if err != nil { return true // Convert error, can't determine. } process, err := os.FindProcess(pid) if err != nil { return true } err = process.Signal(os.Signal(syscall.Signal(0))) if err == nil { return true } errno, ok := err.(syscall.Errno) if !ok { return false } switch errno { case syscall.ESRCH: return false // Only here we could be sure the process does not exist. case syscall.EPERM: return true } return true }
func daemon(pidfile, addr string, port int, autoRestart bool) error { if addr != "127.0.0.1" { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } if err := createPidFile(pidfile); err != nil { log.Fatal(err) } defer removePidFile(pidfile) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) removePidFile(pidfile) os.Exit(0) }() server, err := docker.NewServer(autoRestart) if err != nil { return err } return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true) }
func (s *Service) stopService() error { s.stopTimer() s.mu.Lock() if !s.State.isRunState() { if s.State.canStop() { s.infof("stopped") } s.State = StateStopped s.mu.Unlock() return nil } prevState := s.State s.State = StateStopping s.infof("stopping") p := s.Cmd.Process s.mu.Unlock() if s != nil { stopped := isStoppedErr(p.Signal(os.Signal(syscall.SIGTERM))) if !stopped { select { case <-s.stopCh: stopped = true case <-time.After(10 * time.Second): stopped = isStoppedErr(p.Kill()) } if !stopped { select { case <-s.stopCh: case <-time.After(2 * time.Second): // sending signal 0 checks that the process is // alive and we're allowed to send the signal // without actually sending anything if isStoppedErr(p.Signal(syscall.Signal(0))) { break } s.mu.Lock() s.State = prevState s.mu.Unlock() err := fmt.Errorf("could not stop, probably stuck") s.errorf("%v", err) return err } } } } s.mu.Lock() s.State = StateStopped s.Restarts = 0 s.mu.Unlock() if s.Config.Log != nil { s.Config.Log.Close() } s.infof("stopped") return nil }
func (h *shutdownHandler) wait() { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt, os.Signal(syscall.SIGTERM)) sig := <-ch grohl.Log(grohl.Data{"fn": "shutdown", "at": "start", "signal": fmt.Sprint(sig)}) // signal exit handlers close(h.done) // wait for exit handlers to finish h.mtx.Lock() os.Exit(0) }
func handleSignals() { sigc := make(chan os.Signal, 1) signal.Notify(sigc) for s := range sigc { switch s { case os.Interrupt, os.Signal(syscall.SIGTERM): logger.Printf("Got signal %q; stopping all tasks.", s) for _, t := range GetTasks() { t.Stop() } logger.Printf("Tasks all stopped after %s; quitting.", s) os.Exit(0) case os.Signal(syscall.SIGCHLD): // Ignore. default: logger.Printf("unhandled signal: %T %#v", s, s) } } }
func handleSignals() { signals := make(chan os.Signal, 1) signal.Notify(signals) for s := range signals { switch s { case os.Interrupt, os.Signal(syscall.SIGTERM): logger.Printf("%q: stop everything", s) haproxy.Kill() ctemplate.Kill() os.Exit(0) case os.Signal(syscall.SIGHUP): fmt.Println("SIGHUP!") haproxy.Restart() case os.Signal(syscall.SIGCHLD): // Ignore. default: logger.Printf("WTF %T %#v", s, s) } } }
func daemon(config *docker.DaemonConfig) error { if err := createPidFile(config.Pidfile); err != nil { log.Fatal(err) } defer removePidFile(config.Pidfile) server, err := docker.NewServer(config) if err != nil { return err } defer server.Close() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) server.Close() removePidFile(config.Pidfile) os.Exit(0) }() chErrors := make(chan error, len(config.ProtoAddresses)) for _, protoAddr := range config.ProtoAddresses { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if protoAddrParts[0] == "unix" { syscall.Unlink(protoAddrParts[1]) } else if protoAddrParts[0] == "tcp" { if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } } else { server.Close() removePidFile(config.Pidfile) log.Fatal("Invalid protocol format.") } go func() { chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true) }() } for i := 0; i < len(config.ProtoAddresses); i += 1 { err := <-chErrors if err != nil { return err } } return nil }
func daemon(pidfile string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error { if err := createPidFile(pidfile); err != nil { log.Fatal(err) } defer removePidFile(pidfile) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) removePidFile(pidfile) os.Exit(0) }() var dns []string if flDns != "" { dns = []string{flDns} } server, err := docker.NewServer(autoRestart, enableCors, dns) if err != nil { return err } chErrors := make(chan error, len(protoAddrs)) for _, protoAddr := range protoAddrs { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if protoAddrParts[0] == "unix" { syscall.Unlink(protoAddrParts[1]) } else if protoAddrParts[0] == "tcp" { if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } } else { log.Fatal("Invalid protocol format.") os.Exit(-1) } go func() { chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true) }() } for i := 0; i < len(protoAddrs); i += 1 { err := <-chErrors if err != nil { return err } } return nil }
// Daemon runs the remote api server `srv` as a daemon, // Only one api server can run at the same time - this is enforced by a pidfile. // The signals SIGINT, SIGKILL and SIGTERM are intercepted for cleanup. func (srv *Server) Daemon() error { if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil { log.Fatal(err) } defer utils.RemovePidFile(srv.runtime.config.Pidfile) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) utils.RemovePidFile(srv.runtime.config.Pidfile) srv.Close() os.Exit(0) }() protoAddrs := srv.runtime.config.ProtoAddresses chErrors := make(chan error, len(protoAddrs)) for _, protoAddr := range protoAddrs { protoAddrParts := strings.SplitN(protoAddr, "://", 2) switch protoAddrParts[0] { case "unix": if err := syscall.Unlink(protoAddrParts[1]); err != nil && !os.IsNotExist(err) { log.Fatal(err) } case "tcp": if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } default: return fmt.Errorf("Invalid protocol format.") } go func() { chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, true) }() } for i := 0; i < len(protoAddrs); i += 1 { err := <-chErrors if err != nil { return err } } return nil }
func isProcessAlive(p *os.Process) error { err := p.Signal(os.Signal(syscall.Signal(0))) if err == nil { return nil } errno, ok := err.(syscall.Errno) if !ok { return ErrDeadOwner } switch errno { case syscall.ESRCH: return ErrDeadOwner case syscall.EPERM: return nil default: return err } }
// Who owns the lockfile? func (l Lockfile) GetOwner() (*os.Process, error) { name := string(l) // Ok, see, if we have a stale lockfile here content, err := ioutil.ReadFile(name) if err != nil { return nil, err } var pid int _, err = fmt.Sscanln(string(content), &pid) if err != nil { return nil, err } // try hard for pids. If no pid, the lockfile is junk anyway and we delete it. if pid > 0 { p, err := os.FindProcess(pid) if err != nil { return nil, err } err = p.Signal(os.Signal(syscall.Signal(0))) if err == nil { return p, nil } errno, ok := err.(syscall.Errno) if !ok { return nil, err } switch errno { case syscall.ESRCH: return nil, ErrDeadOwner case syscall.EPERM: return p, nil default: return nil, err } } else { return nil, ErrInvalidPid } panic("Not reached") }
func daemon(pidfile string) error { if err := createPidFile(pidfile); err != nil { log.Fatal(err) } defer removePidFile(pidfile) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) removePidFile(pidfile) os.Exit(0) }() service, err := docker.NewServer() if err != nil { return err } return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service) }
func IsActive(pid int) (bool, error) { if pid <= 0 { return false, errors.New("process id error.") } p, err := os.FindProcess(pid) if err != nil { if Debug { fmt.Printf("find process: %s\n", err.Error()) } return false, err } if err := p.Signal(os.Signal(syscall.Signal(0))); err != nil { if Debug { fmt.Printf("send signal [0]: %s\n", err.Error()) } return false, err } return true, nil }
func daemon(pidfile string, autoRestart bool) error { if err := createPidFile(pidfile); err != nil { log.Fatal(err) } defer removePidFile(pidfile) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) go func() { sig := <-c log.Printf("Received signal '%v', exiting\n", sig) removePidFile(pidfile) os.Exit(0) }() server, err := docker.NewServer(autoRestart) if err != nil { return err } return docker.ListenAndServe("0.0.0.0:4243", server, true) }
func isPidActive(pid int) (bool, error) { p, err := os.FindProcess(pid) if err != nil { return false, nil } err = p.Signal(os.Signal(syscall.Signal(0))) if err == nil { return true, nil } errno, ok := err.(syscall.Errno) if !ok { return false, nil // dead owner } switch errno { case syscall.ESRCH: return false, nil // dead owner case syscall.EPERM: return true, nil default: return false, err } }
func (m *Monitor) Run() { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Signal(syscall.SIGCHLD)) if m.set != nil { go m.set.Run() } loop: for { select { case <-ch: m.waitForExited() case <-m.quit.stop: m.quit.sendStopped() break loop } } signal.Stop(ch) close(ch) if m.set != nil { m.set.Stop() } }
func (h *signalsHandler) forward(p *process) int { pid1 := p.pid() for s := range h.signals { log.Debugf("signal: %q", s) switch s { case syscall.SIGWINCH: p.resizePty() case syscall.SIGCHLD: //child process died, dock will exit //sending sigterm to every remaining processes before calling wait4 if err := signalAllDescendants(syscall.SIGTERM); err != nil { log.Debugf("failed to send sigterm signal: %v", err) } go func() { <-time.After(killTimeout * time.Second) log.Debugf("kill timed out") if err := signalAllDescendants(syscall.SIGKILL); err != nil { log.Debugf("failed to send sigkill signal: %v", err) } }() //waiting for all processes to die log.Debug("reaping all children") exits, err := reap() log.Debug("children reaped") if err != nil { log.Error(err) } for _, e := range exits { if e.pid == pid1 { p.wait() return e.status } } case syscall.SIGINT: fallthrough case syscall.SIGTERM: fallthrough case syscall.SIGQUIT: //stopping signals sigToForward := s if h.authority { blocked, err := isSignalBlocked(pid1, s) if err != nil { log.Error(err) goto forward } ignored, err := isSignalIgnored(pid1, s) if err != nil { log.Error(err) goto forward } if blocked || ignored { sigToForward = os.Signal(syscall.SIGKILL) } } forward: if err := p.signal(sigToForward); err != nil { log.Error(err) } default: //simply forward the signal to the process if err := p.signal(s); err != nil { log.Error(err) } } } panic("-- this line should never been executed --") }
func main() { if err := globalFlags.Parse(os.Args[1:]); err != nil { Fatalln(err) } globalConfig.command = globalFlags.Args() globalConfig.source = "[commandline]" if verbose { printGlobals() } switch strings.ToLower(flagDecoration) { case "none": decoration = DecorationNone case "plain": decoration = DecorationPlain case "fancy": decoration = DecorationFancy default: Fatalln(fmt.Sprintf("Invalid decoration %s. Choices: none, plain, fancy.", flagDecoration)) } var configs []*Config if flagConf == "" { if flagSequential { Fatalln("Cannot set --sequential without --config (because you cannot specify multiple commands).") } configs = []*Config{globalConfig} } else { if anyNonGlobalsRegistered() { Fatalln("Cannot set other flags along with --config other than --sequential, --verbose, and --decoration.") } var err error configs, err = ReadConfigs(flagConf) if err != nil { Fatalln("Could not parse configs: ", err) } if len(configs) == 0 { Fatalln("No configurations found") } } for _, config := range configs { reflex, err := NewReflex(config) if err != nil { Fatalln("Could not make reflex for config:", err) } if verbose { fmt.Println(reflex) } reflexes = append(reflexes, reflex) } // Catch ctrl-c and make sure to kill off children. signals := make(chan os.Signal, 1) signal.Notify(signals, os.Interrupt) signal.Notify(signals, os.Signal(syscall.SIGTERM)) go func() { s := <-signals reason := fmt.Sprintf("Interrupted (%s). Cleaning up children...", s) cleanup(reason) }() defer cleanup("Cleaning up.") watcher, err := fsnotify.NewWatcher() if err != nil { Fatalln(err) } defer watcher.Close() changes := make(chan string) broadcastChanges := make([]chan string, len(reflexes)) done := make(chan error) for i := range reflexes { broadcastChanges[i] = make(chan string) } go watch(".", watcher, changes, done, reflexes) go broadcast(broadcastChanges, changes) go printOutput(stdout, os.Stdout) for i, reflex := range reflexes { reflex.Start(broadcastChanges[i]) } Fatalln(<-done) }
sync.Mutex server *graceful.Server // Interrupted is true if the application is in the process of shutting down. Interrupted bool // CancelOnShutdown tells whether existing requests should be canceled when shutdown is // triggered (true) or whether to wait until the requests complete (false). CancelOnShutdown bool } // InterruptSignals is the list of signals that initiate graceful shutdown. // Note that only SIGINT is supported on Windows so this list should be // overridden by the caller when running on that platform. var InterruptSignals = []os.Signal{ os.Signal(syscall.SIGINT), os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGQUIT), } // NewGraceful returns a goa application that uses a graceful shutdown server. func NewGraceful(name string, cancelOnShutdown bool) Service { app, _ := New(name).(*Application) return &GracefulApplication{Application: app, CancelOnShutdown: cancelOnShutdown} } // ListenAndServe starts the HTTP server and sets up a listener on the given host/port. func (gapp *GracefulApplication) ListenAndServe(addr string) error { gapp.setup(addr) gapp.Info("listen", "addr", addr) if err := gapp.server.ListenAndServe(); err != nil {
// WaitSIGHUP blocks until a SIGHUP signal is received. func WaitSIGHUP() { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Signal(syscall.SIGHUP)) <-ch }
func runRun(cmd *Command, args []string) { cols, err := term.Cols() if err != nil { log.Fatal(err) } lines, err := term.Lines() if err != nil { log.Fatal(err) } data := make(url.Values) if !detachedRun { data.Add("attach", "true") data.Add("ps_env[TERM]", os.Getenv("TERM")) data.Add("ps_env[COLUMNS]", strconv.Itoa(cols)) data.Add("ps_env[LINES]", strconv.Itoa(lines)) } data.Add("command", strings.Join(args, " ")) resp := struct { Url *string `json:"rendezvous_url,omitempty"` }{} must(Post(&v2{&resp}, "/apps/"+mustApp()+"/ps", data)) if detachedRun { return } u, err := url.Parse(*resp.Url) if err != nil { log.Fatal(err) } cn, err := tls.Dial("tcp", u.Host, nil) if err != nil { log.Fatal(err) } defer cn.Close() br := bufio.NewReader(cn) _, err = io.WriteString(cn, u.Path[1:]+"\r\n") if err != nil { log.Fatal(err) } for { _, pre, err := br.ReadLine() if err != nil { log.Fatal(err) } if !pre { break } } if term.IsTerminal(os.Stdin) && term.IsTerminal(os.Stdout) { err = term.MakeRaw(os.Stdin) if err != nil { log.Fatal(err) } defer term.Restore(os.Stdin) sig := make(chan os.Signal) signal.Notify(sig, os.Signal(syscall.SIGQUIT), os.Interrupt) go func() { defer term.Restore(os.Stdin) for sg := range sig { switch sg { case os.Interrupt: cn.Write([]byte{3}) case os.Signal(syscall.SIGQUIT): cn.Write([]byte{28}) default: panic("not reached") } } }() } errc := make(chan error) cp := func(a io.Writer, b io.Reader) { _, err := io.Copy(a, b) errc <- err } go cp(os.Stdout, br) go cp(cn, os.Stdin) if err = <-errc; err != nil { log.Fatal(err) } }
package main import ( "errors" "os" "os/exec" "os/signal" "syscall" ) // DeadlySignals lists signals, which lead to process termination by default // and can be caught by the process receiving them. var DeadlySignals = []os.Signal{ os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGINT), } // ReceiveDeadlySignals requests delivery of DeadlySignals on channel "on" func ReceiveDeadlySignals() (on chan os.Signal) { on = make(chan os.Signal, 1) signal.Notify(on, DeadlySignals...) return on } // IgnoreDeadlySignals stops delivery of DeadlySignals on channel "on" func IgnoreDeadlySignals(on chan os.Signal) { if on != nil { signal.Stop(on) } }
func main() { if err := globalFlags.Parse(os.Args[1:]); err != nil { Fatalln(err) } if verbose { printGlobals() } switch strings.ToLower(flagDecoration) { case "none": decoration = DecorationNone case "plain": decoration = DecorationPlain case "fancy": decoration = DecorationFancy default: Fatalln(fmt.Sprintf("Invalid decoration %s. Choices: none, plain, fancy.", flagDecoration)) } if flagConf == "" { reflex, err := NewReflex(globalConfig, globalFlags.Args()) if err != nil { Fatalln(err) } if verbose { reflex.PrintInfo("commandline") } reflexes = append(reflexes, reflex) if flagSequential { Fatalln("Cannot set --sequential without --config (because you cannot specify multiple commands).") } } else { if anyNonGlobalsRegistered() { Fatalln("Cannot set other flags along with --config other than --sequential, --verbose, and --decoration.") } // Now open the configuration file. // As a special case we read the config from stdin if --config is set to "-" var config io.ReadCloser if flagConf == "-" { config = os.Stdin } else { configFile, err := os.Open(flagConf) if err != nil { Fatalln(err) } config = configFile } scanner := bufio.NewScanner(config) lineNo := 0 for scanner.Scan() { lineNo++ errorMsg := fmt.Sprintf("Error on line %d of %s:", lineNo, flagConf) config := &Config{} flags := flag.NewFlagSet("", flag.ContinueOnError) registerFlags(flags, config) parts, err := shellquote.Split(scanner.Text()) if err != nil { Fatalln(errorMsg, err) } // Skip empty lines and comments (lines starting with #). if len(parts) == 0 || strings.HasPrefix(parts[0], "#") { continue } if err := flags.Parse(parts); err != nil { Fatalln(errorMsg, err) } reflex, err := NewReflex(config, flags.Args()) if err != nil { Fatalln(errorMsg, err) } if verbose { reflex.PrintInfo(fmt.Sprintf("%s, line %d", flagConf, lineNo)) } reflexes = append(reflexes, reflex) } if err := scanner.Err(); err != nil { Fatalln(err) } config.Close() } // Catch ctrl-c and make sure to kill off children. signals := make(chan os.Signal, 1) signal.Notify(signals, os.Interrupt) signal.Notify(signals, os.Signal(syscall.SIGTERM)) go func() { s := <-signals reason := fmt.Sprintf("Interrupted (%s). Cleaning up children...", s) cleanup(reason) }() defer cleanup("Cleaning up.") watcher, err := fsnotify.NewWatcher() if err != nil { Fatalln(err) } defer watcher.Close() rawChanges := make(chan string) allRawChanges := make([]chan<- string, len(reflexes)) done := make(chan error) for i, reflex := range reflexes { allRawChanges[i] = reflex.rawChanges } go watch(".", watcher, rawChanges, done) go broadcast(rawChanges, allRawChanges) go printOutput(stdout, os.Stdout) for _, reflex := range reflexes { go filterMatching(reflex.rawChanges, reflex.filtered, reflex) go batch(reflex.filtered, reflex.batched, reflex) go runEach(reflex.batched, reflex) if reflex.startService { // Easy hack to kick off the initial start. infoPrintln(reflex.id, "Starting service") runCommand(reflex, "", stdout) } } Fatalln(<-done) }
func runRun(cmd *Command, args []string) { if len(args) == 0 { cmd.PrintUsage() os.Exit(2) } appname := mustApp() cols, err := term.Cols() if err != nil { printFatal(err.Error()) } lines, err := term.Lines() if err != nil { printFatal(err.Error()) } attached := !detachedRun opts := heroku.DynoCreateOpts{Attach: &attached} if attached { env := map[string]string{ "COLUMNS": strconv.Itoa(cols), "LINES": strconv.Itoa(lines), "TERM": os.Getenv("TERM"), } opts.Env = &env } if dynoSize != "" { if !strings.HasSuffix(dynoSize, "X") { cmd.PrintUsage() os.Exit(2) } opts.Size = &dynoSize } command := strings.Join(args, " ") dyno, err := client.DynoCreate(appname, command, &opts) must(err) if detachedRun { log.Printf("Ran `%s` on %s as %s, detached.", dyno.Command, appname, dyno.Name) return } log.Printf("Running `%s` on %s as %s:", dyno.Command, appname, dyno.Name) u, err := url.Parse(*dyno.AttachURL) if err != nil { printFatal(err.Error()) } cn, err := tls.Dial("tcp", u.Host, nil) if err != nil { printFatal(err.Error()) } defer cn.Close() br := bufio.NewReader(cn) _, err = io.WriteString(cn, u.Path[1:]+"\r\n") if err != nil { printFatal(err.Error()) } for { _, pre, err := br.ReadLine() if err != nil { printFatal(err.Error()) } if !pre { break } } if term.IsTerminal(os.Stdin) && term.IsTerminal(os.Stdout) { err = term.MakeRaw(os.Stdin) if err != nil { printFatal(err.Error()) } defer term.Restore(os.Stdin) sig := make(chan os.Signal) signal.Notify(sig, os.Signal(syscall.SIGQUIT), os.Interrupt) go func() { defer term.Restore(os.Stdin) for sg := range sig { switch sg { case os.Interrupt: cn.Write([]byte{3}) case os.Signal(syscall.SIGQUIT): cn.Write([]byte{28}) default: panic("not reached") } } }() } errc := make(chan error) cp := func(a io.Writer, b io.Reader) { _, err := io.Copy(a, b) errc <- err } go cp(os.Stdout, br) go cp(cn, os.Stdin) if err = <-errc; err != nil { printFatal(err.Error()) } }
func (h *handler) wait() { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt, os.Signal(syscall.SIGTERM)) <-ch h.exit(nil, 0, nil) }