serve.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package main
  2. import (
  3. "fmt"
  4. "github.com/flashmob/go-guerrilla"
  5. "github.com/flashmob/go-guerrilla/log"
  6. "github.com/spf13/cobra"
  7. "os"
  8. "os/exec"
  9. "os/signal"
  10. "strconv"
  11. "strings"
  12. "syscall"
  13. "time"
  14. )
  15. const (
  16. defaultPidFile = "/var/run/go-guerrilla.pid"
  17. )
  18. var (
  19. configPath string
  20. pidFile string
  21. serveCmd = &cobra.Command{
  22. Use: "serve",
  23. Short: "start the daemon and start all available servers",
  24. Run: serve,
  25. }
  26. signalChannel = make(chan os.Signal, 1) // for trapping SIGHUP and friends
  27. mainlog log.Logger
  28. d guerrilla.Daemon
  29. )
  30. func init() {
  31. // log to stderr on startup
  32. var err error
  33. mainlog, err = log.GetLogger(log.OutputStderr.String(), log.InfoLevel.String())
  34. if err != nil {
  35. mainlog.WithError(err).Errorf("Failed creating a logger to %s", log.OutputStderr)
  36. }
  37. cfgFile := "goguerrilla.conf" // deprecated default name
  38. if _, err := os.Stat(cfgFile); err != nil {
  39. cfgFile = "goguerrilla.conf.json" // use the new name
  40. }
  41. serveCmd.PersistentFlags().StringVarP(&configPath, "config", "c",
  42. cfgFile, "Path to the configuration file")
  43. // intentionally didn't specify default pidFile; value from config is used if flag is empty
  44. serveCmd.PersistentFlags().StringVarP(&pidFile, "pidFile", "p",
  45. "", "Path to the pid file")
  46. rootCmd.AddCommand(serveCmd)
  47. }
  48. func sigHandler() {
  49. signal.Notify(signalChannel,
  50. syscall.SIGHUP,
  51. syscall.SIGTERM,
  52. syscall.SIGQUIT,
  53. syscall.SIGINT,
  54. syscall.SIGKILL,
  55. syscall.SIGUSR1,
  56. )
  57. for sig := range signalChannel {
  58. if sig == syscall.SIGHUP {
  59. if ac, err := readConfig(configPath, pidFile); err == nil {
  60. d.ReloadConfig(*ac)
  61. } else {
  62. mainlog.WithError(err).Error("Could not reload config")
  63. }
  64. } else if sig == syscall.SIGUSR1 {
  65. d.ReopenLogs()
  66. } else if sig == syscall.SIGTERM || sig == syscall.SIGQUIT || sig == syscall.SIGINT {
  67. mainlog.Infof("Shutdown signal caught")
  68. go func() {
  69. select {
  70. // exit if graceful shutdown not finished in 60 sec.
  71. case <-time.After(time.Second * 60):
  72. mainlog.Error("graceful shutdown timed out")
  73. os.Exit(1)
  74. }
  75. }()
  76. d.Shutdown()
  77. mainlog.Infof("Shutdown completed, exiting.")
  78. return
  79. } else {
  80. mainlog.Infof("Shutdown, unknown signal caught")
  81. return
  82. }
  83. }
  84. }
  85. func serve(cmd *cobra.Command, args []string) {
  86. logVersion()
  87. d = guerrilla.Daemon{Logger: mainlog}
  88. ac, err := readConfig(configPath, pidFile)
  89. if err != nil {
  90. mainlog.WithError(err).Fatal("Error while reading config")
  91. }
  92. d.SetConfig(*ac)
  93. // Check that max clients is not greater than system open file limit.
  94. fileLimit := getFileLimit()
  95. if fileLimit > 0 {
  96. maxClients := 0
  97. for _, s := range ac.Servers {
  98. maxClients += s.MaxClients
  99. }
  100. if maxClients > fileLimit {
  101. mainlog.Fatalf("Combined max clients for all servers (%d) is greater than open file limit (%d). "+
  102. "Please increase your open file limit or decrease max clients.", maxClients, fileLimit)
  103. }
  104. }
  105. err = d.Start()
  106. if err != nil {
  107. mainlog.WithError(err).Error("Error(s) when creating new server(s)")
  108. os.Exit(1)
  109. }
  110. sigHandler()
  111. }
  112. // ReadConfig is called at startup, or when a SIG_HUP is caught
  113. func readConfig(path string, pidFile string) (*guerrilla.AppConfig, error) {
  114. // Load in the config.
  115. // Note here is the only place we can make an exception to the
  116. // "treat config values as immutable". For example, here the
  117. // command line flags can override config values
  118. appConfig, err := d.LoadConfig(path)
  119. if err != nil {
  120. return &appConfig, fmt.Errorf("Could not read config file: %s", err.Error())
  121. }
  122. // override config pidFile with with flag from the command line
  123. if len(pidFile) > 0 {
  124. appConfig.PidFile = pidFile
  125. } else if len(appConfig.PidFile) == 0 {
  126. appConfig.PidFile = defaultPidFile
  127. }
  128. if verbose {
  129. appConfig.LogLevel = "debug"
  130. }
  131. return &appConfig, nil
  132. }
  133. func getFileLimit() int {
  134. cmd := exec.Command("ulimit", "-n")
  135. out, err := cmd.Output()
  136. if err != nil {
  137. return -1
  138. }
  139. limit, err := strconv.Atoi(strings.TrimSpace(string(out)))
  140. if err != nil {
  141. return -1
  142. }
  143. return limit
  144. }