| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "os/signal" |
| "syscall" |
| "time" |
| |
| "github.com/flashmob/go-guerrilla" |
| "github.com/flashmob/go-guerrilla/backends" |
| "github.com/flashmob/go-guerrilla/log" |
| |
| // Choose iconv or mail/encoding package which uses golang.org/x/net/html/charset |
| //_ "github.com/flashmob/go-guerrilla/mail/iconv" |
| _ "github.com/flashmob/go-guerrilla/mail/encoding" |
| |
| "github.com/spf13/cobra" |
| |
| _ "github.com/go-sql-driver/mysql" |
| |
| "gomodules.avm99963.com/hichip2mqtt/mqtt_processor" |
| ) |
| |
| const ( |
| defaultPidFile = "/var/run/hichipbridge.pid" |
| ) |
| |
| var ( |
| configPath string |
| pidFile string |
| |
| serveCmd = &cobra.Command{ |
| Use: "serve", |
| Short: "start the daemon and start all available SMTP servers", |
| Run: serve, |
| } |
| |
| signalChannel = make(chan os.Signal, 1) // for trapping SIGHUP and friends |
| mainlog log.Logger |
| |
| d guerrilla.Daemon |
| ) |
| |
| func init() { |
| // log to stderr on startup |
| var err error |
| mainlog, err = log.GetLogger(log.OutputStderr.String(), log.InfoLevel.String()) |
| if err != nil && mainlog != nil { |
| mainlog.WithError(err).Errorf("Failed creating a logger to %s", log.OutputStderr) |
| } |
| cfgFile := "config.json" |
| serveCmd.PersistentFlags().StringVarP(&configPath, "config", "c", |
| cfgFile, "Path to the configuration file") |
| // intentionally didn't specify default pidFile; value from config is used if flag is empty |
| serveCmd.PersistentFlags().StringVarP(&pidFile, "pidFile", "p", |
| "", "Path to the pid file") |
| rootCmd.AddCommand(serveCmd) |
| } |
| |
| func sigHandler() { |
| signal.Notify(signalChannel, |
| syscall.SIGHUP, |
| syscall.SIGTERM, |
| syscall.SIGQUIT, |
| syscall.SIGINT, |
| syscall.SIGKILL, |
| syscall.SIGUSR1, |
| os.Kill, |
| ) |
| for sig := range signalChannel { |
| if sig == syscall.SIGHUP { |
| if ac, err := readConfig(configPath, pidFile); err == nil { |
| _ = d.ReloadConfig(*ac) |
| } else { |
| mainlog.WithError(err).Error("Could not reload config") |
| } |
| } else if sig == syscall.SIGUSR1 { |
| if err := d.ReopenLogs(); err != nil { |
| mainlog.WithError(err).Error("reopening logs failed") |
| } |
| } else if sig == syscall.SIGTERM || sig == syscall.SIGQUIT || sig == syscall.SIGINT || sig == os.Kill { |
| mainlog.Infof("Shutdown signal caught") |
| go func() { |
| select { |
| // exit if graceful shutdown not finished in 60 sec. |
| case <-time.After(time.Second * 60): |
| mainlog.Error("graceful shutdown timed out") |
| os.Exit(1) |
| } |
| }() |
| d.Shutdown() |
| mainlog.Infof("Shutdown completed, exiting.") |
| return |
| } else { |
| mainlog.Infof("Shutdown, unknown signal caught") |
| return |
| } |
| } |
| } |
| |
| func serve(cmd *cobra.Command, args []string) { |
| logVersion() |
| d = guerrilla.Daemon{Logger: mainlog} |
| d.AddProcessor("MQTT", mqtt_processor.Processor) |
| |
| c, err := readConfig(configPath, pidFile) |
| if err != nil { |
| mainlog.WithError(err).Fatal("Error while reading config") |
| } |
| _ = d.SetConfig(*c) |
| |
| // Check that max clients is not greater than system open file limit. |
| if ok, maxClients, fileLimit := guerrilla.CheckFileLimit(c); !ok { |
| mainlog.Fatalf("Combined max clients for all servers (%d) is greater than open file limit (%d). "+ |
| "Please increase your open file limit or decrease max clients.", maxClients, fileLimit) |
| } |
| |
| err = d.Start() |
| if err != nil { |
| mainlog.WithError(err).Error("Error(s) when creating new server(s)") |
| os.Exit(1) |
| } |
| sigHandler() |
| |
| } |
| |
| func lookupPrefixedEnv(coreName string) (string, error) { |
| name := "HICHIPBRIDGE_" + coreName |
| v, ok := os.LookupEnv(name) |
| if !ok { |
| return v, fmt.Errorf("Environment variable %s isn't set", name) |
| } |
| |
| return v, nil |
| } |
| |
| // ReadConfig is called at startup, or when a SIG_HUP is caught |
| func readConfig(path string, pidFile string) (*guerrilla.AppConfig, error) { |
| // Environment variables where some settings are configured (without the prefix "HICHIPBRIDGE_"): |
| envNames := []string{ |
| "SMTP_HOST", |
| "MQTT_BROKER", |
| "MQTT_CLIENTID", |
| "MQTT_USERNAME", |
| "MQTT_PASSWORD", |
| "MQTT_TOPIC", |
| "EMAIL_TOKEN", |
| } |
| |
| // Set default configuration (overridable) |
| defaultAppConfig := guerrilla.AppConfig{ |
| BackendConfig: backends.BackendConfig{ |
| "save_workers_size": 1, |
| "log_received_mails": false, |
| }, |
| } |
| |
| d.SetConfig(defaultAppConfig) |
| |
| // Load in the config. |
| appConfig, err := d.LoadConfig(path) |
| if err != nil { |
| return &appConfig, fmt.Errorf("could not read config file: %s", err.Error()) |
| } |
| |
| // override config pidFile with with flag from the command line |
| if len(pidFile) > 0 { |
| appConfig.PidFile = pidFile |
| } else if len(appConfig.PidFile) == 0 { |
| appConfig.PidFile = defaultPidFile |
| } |
| if verbose { |
| appConfig.LogLevel = "debug" |
| } |
| |
| // Override compulsory settings |
| var envs = make(map[string]string) |
| for _, env := range envNames { |
| v, err := lookupPrefixedEnv(env) |
| if err != nil { |
| return &appConfig, err |
| } |
| envs[env] = v |
| } |
| |
| appConfig.AllowedHosts = []string{envs["SMTP_HOST"]} |
| appConfig.BackendConfig = backends.BackendConfig{ |
| "save_process": "HeadersParser|Hasher|Debugger|MQTT", |
| "validate_process": "", |
| "primary_mail_host": envs["SMTP_HOST"], |
| "mqtt_broker": envs["MQTT_BROKER"], |
| "mqtt_clientid": envs["MQTT_CLIENTID"], |
| "mqtt_username": envs["MQTT_USERNAME"], |
| "mqtt_password": envs["MQTT_PASSWORD"], |
| "mqtt_topic": envs["MQTT_TOPIC"], |
| "mqtt_email_token": envs["EMAIL_TOKEN"], |
| } |
| |
| for i, _ := range appConfig.Servers { |
| appConfig.Servers[i].Hostname = envs["SMTP_HOST"] |
| } |
| |
| return &appConfig, nil |
| } |