serve.go 4.3 KB

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