// Serve starts the devd server. The callback is called with the serving URL // just before service starts. func (dd *Devd) Serve(address string, port int, certFile string, logger termlog.TermLog, callback func(string)) error { templates, err := ricetemp.MakeTemplates(rice.MustFindBox("templates")) if err != nil { return fmt.Errorf("Error loading templates: %s", err) } mux, err := dd.Router(logger, templates) if err != nil { return err } var tlsConfig *tls.Config var tlsEnabled bool if certFile != "" { tlsConfig, err = getTLSConfig(certFile) if err != nil { return fmt.Errorf("Could not load certs: %s", err) } tlsEnabled = true } var hl net.Listener if port > 0 { hl, err = net.Listen("tcp", fmt.Sprintf("%v:%d", address, port)) } else { hl, err = pickPort(address, portLow, portHigh, tlsEnabled) } if err != nil { return err } if tlsConfig != nil { hl = tls.NewListener(hl, tlsConfig) } hl = slowdown.NewSlowListener(hl, dd.UpKbps*1024, dd.DownKbps*1024) url := formatURL(tlsEnabled, address, hl.Addr().(*net.TCPAddr).Port) logger.Say("Listening on %s (%s)", url, hl.Addr().String()) server := &http.Server{Addr: hl.Addr().String(), Handler: mux} callback(url) if dd.HasLivereload() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGHUP) go func() { for { <-c logger.Say("Received signal - reloading") dd.lrserver.Reload([]string{"*"}) } }() } err = server.Serve(hl) logger.Shout("Server stopped: %v", err) return nil }
// Returns a (continue, error) tuple. If continue is true, execution of the // remainder of the block should proceed. If error is not nil, modd should // exit. func prepsAndNotify(b conf.Block, vars map[string]string, lmod *watch.Mod, log termlog.TermLog) (bool, error) { err := modd.RunPreps(b, vars, lmod, log) if pe, ok := err.(modd.ProcError); ok { if *beep { fmt.Print("\a") } if *doNotify { n := notify.NewNotifier() if n == nil { log.Shout("Could not find a desktop notifier") } else { n.Push("modd error", pe.Output, "") } } return false, nil } else if err != nil { return false, err } return true, nil }
// Start starts set of daemons, each specified by a command func (dp *DaemonPen) Start(daemons []conf.Daemon, vars map[string]string, log termlog.TermLog) { dp.Lock() defer dp.Unlock() d := make([]daemon, len(daemons)) for i, dmn := range daemons { vcmd := varcmd.VarCmd{Block: nil, Mod: nil, Vars: vars} finalcmd, err := vcmd.Render(dmn.Command) if err != nil { log.Shout("%s", err) continue } dmn.Command = finalcmd d[i] = daemon{ conf: dmn, log: log.Stream( niceHeader("daemon: ", dmn.Command), ), } go d[i].Run() } dp.daemons = &d }
// Gives control of chan to caller func runOnChan(modchan chan *watch.Mod, readyCallback func(), log termlog.TermLog, cnf *conf.Config, watchconf string, notifiers []notify.Notifier) (*conf.Config, error) { err := PrepOnly(log, cnf, notifiers) if err != nil { return nil, err } dworld, err := NewDaemonWorld(cnf, log) if err != nil { return nil, err } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) defer signal.Reset(os.Interrupt, os.Kill) defer dworld.Shutdown(os.Kill) go func() { dworld.Shutdown(<-c) os.Exit(0) }() dworld.Start() watchpaths := cnf.WatchPatterns() if watchconf != "" { watchpaths = append(watchpaths, filepath.Dir(watchconf)) } // FIXME: This takes a long time. We could start it in parallel with the // first process run in a goroutine watcher, err := watch.Watch(watchpaths, lullTime, modchan) if err != nil { return nil, fmt.Errorf("Error watching: %s", err) } defer watcher.Stop() go readyCallback() for mod := range modchan { if mod == nil { break } if watchconf != "" && mod.Has(watchconf) { ret, err := ioutil.ReadFile(watchconf) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } newcnf, err := conf.Parse(watchconf, string(ret)) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } log.Notice("Reloading config %s", watchconf) return newcnf, nil } log.SayAs("debug", "Delta: \n%s", mod.String()) for i, b := range cnf.Blocks { lmod, err := mod.Filter(b.Include, b.Exclude) if err != nil { log.Shout("Error filtering events: %s", err) continue } if lmod.Empty() { continue } err = RunPreps(b, cnf.GetVariables(), lmod, log, notifiers) if err != nil { if _, ok := err.(ProcError); ok { continue } else { return nil, err } } dworld.DaemonPens[i].Restart() } } return nil, nil }
func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config { modchan := make(chan *watch.Mod, 1024) if *ignores { for _, patt := range watch.CommonExcludes { fmt.Println(patt) } os.Exit(0) } daemonPens := make([]*modd.DaemonPen, len(cnf.Blocks)) for i, b := range cnf.Blocks { if !b.NoCommonFilter { b.Exclude = append(b.Exclude, watch.CommonExcludes...) } cnf.Blocks[i] = b _, err := prepsAndNotify(b, cnf.GetVariables(), nil, log) if err != nil { log.Shout("%s", err) return nil } d := modd.DaemonPen{} c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) go func() { d.Shutdown(<-c) os.Exit(0) }() if !*prep { d.Start(b.Daemons, cnf.GetVariables(), log) } daemonPens[i] = &d } if *prep { os.Exit(0) } watchpaths := cnf.WatchPaths() if watchconf != "" { watchpaths = append(watchpaths, watchconf) } // FIXME: This takes a long time. We could start it in parallel with the // first process run in a goroutine watcher, err := watch.Watch(watchpaths, lullTime, modchan) defer watcher.Stop() if err != nil { kingpin.Fatalf("Fatal error: %s", err) } for mod := range modchan { if watchconf != "" && mod.Has(watchconf) { ret, err := ioutil.ReadFile(watchconf) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } newcnf, err := conf.Parse(*file, string(ret)) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } log.Notice("Reloading config %s", watchconf) return newcnf } if mod == nil { break } log.SayAs("debug", "Delta: \n%s", mod.String()) for i, b := range cnf.Blocks { lmod, err := mod.Filter(b.Include, b.Exclude) if err != nil { log.Shout("Error filtering events: %s", err) continue } if lmod.Empty() { continue } proceed, err := prepsAndNotify(b, cnf.GetVariables(), lmod, log) if err != nil { log.Shout("%s", err) return nil } if !proceed { continue } daemonPens[i].Restart() } } return nil }