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  }