github.com/nycdavid/zeus@v0.0.0-20201208104106-9ba439429e03/go/zeusmaster/zeusmaster.go (about) 1 package zeusmaster 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "os/signal" 8 "strconv" 9 "syscall" 10 "time" 11 12 "github.com/burke/zeus/go/clienthandler" 13 "github.com/burke/zeus/go/config" 14 "github.com/burke/zeus/go/filemonitor" 15 "github.com/burke/zeus/go/processtree" 16 slog "github.com/burke/zeus/go/shinylog" 17 "github.com/burke/zeus/go/statuschart" 18 "github.com/burke/zeus/go/zerror" 19 "github.com/burke/zeus/go/zeusversion" 20 ) 21 22 const listenerPortVar = "ZEUS_NETWORK_FILE_MONITOR_PORT" 23 24 // man signal | grep 'terminate process' | awk '{print $2}' | xargs -I '{}' echo -n "syscall.{}, " 25 // Leaving out SIGPIPE as that is a signal the master receives if a client process is killed. 26 var terminatingSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGKILL, syscall.SIGALRM, syscall.SIGTERM, syscall.SIGXCPU, syscall.SIGXFSZ, syscall.SIGVTALRM, syscall.SIGPROF, syscall.SIGUSR1, syscall.SIGUSR2} 27 28 func Run(configFile string, fileChangeDelay time.Duration, simpleStatus bool) int { 29 slog.Colorized("{green}Starting {yellow}Z{red}e{blue}u{magenta}s{green} server v" + zeusversion.VERSION) 30 31 zerror.Init() 32 33 monitor, err := buildFileMonitor(fileChangeDelay) 34 if err != nil { 35 return 2 36 } 37 38 var tree = config.BuildProcessTree(configFile, monitor) 39 40 done := make(chan bool) 41 42 defer exit(processtree.StartSlaveMonitor(tree, monitor.Listen(), done), done) 43 defer exit(clienthandler.Start(tree, done), done) 44 defer monitor.Close() 45 defer slog.Suppress() 46 defer zerror.PrintFinalOutput() 47 defer exit(statuschart.Start(tree, done, simpleStatus), done) 48 49 c := make(chan os.Signal, 1) 50 signal.Notify(c, terminatingSignals...) 51 52 for { 53 select { 54 case sig := <-c: 55 if sig == syscall.SIGINT { 56 return 0 57 } else { 58 return 1 59 } 60 } 61 } 62 } 63 64 func exit(quit, done chan bool) { 65 // Signal the process to quit. 66 close(quit) 67 // Wait until the process signals it's done. 68 <-done 69 } 70 71 func buildFileMonitor(fileChangeDelay time.Duration) (filemonitor.FileMonitor, error) { 72 if portStr := os.Getenv(listenerPortVar); portStr != "" { 73 port, err := strconv.Atoi(portStr) 74 if err != nil { 75 return nil, fmt.Errorf("%s must be an integer or empty string: %v", listenerPortVar, err) 76 } 77 78 ln, err := net.ListenTCP("tcp", &net.TCPAddr{ 79 IP: net.ParseIP("127.0.0.1"), 80 Port: port, 81 }) 82 if err != nil { 83 return nil, err 84 } 85 86 return filemonitor.NewFileListener(fileChangeDelay, ln), nil 87 } 88 89 monitor, err := filemonitor.NewFileMonitor(fileChangeDelay) 90 if err != nil { 91 return nil, err 92 } 93 94 return monitor, nil 95 }