func RootStatus(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) { retentionMinutes := config.GetDataStoreConfig().RetentionMinutes smtpListener := fmt.Sprintf("%s:%d", config.GetSmtpConfig().Ip4address.String(), config.GetSmtpConfig().Ip4port) pop3Listener := fmt.Sprintf("%s:%d", config.GetPop3Config().Ip4address.String(), config.GetPop3Config().Ip4port) webListener := fmt.Sprintf("%s:%d", config.GetWebConfig().Ip4address.String(), config.GetWebConfig().Ip4port) return RenderTemplate("root/status.html", w, map[string]interface{}{ "ctx": ctx, "version": config.VERSION, "buildDate": config.BUILD_DATE, "retentionMinutes": retentionMinutes, "smtpListener": smtpListener, "pop3Listener": pop3Listener, "webListener": webListener, }) }
// Main listener loop func (s *Server) Start() { cfg := config.GetSmtpConfig() addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%v:%v", cfg.Ip4address, cfg.Ip4port)) if err != nil { log.LogError("Failed to build tcp4 address: %v", err) // TODO More graceful early-shutdown procedure panic(err) } log.LogInfo("SMTP listening on TCP4 %v", addr) s.listener, err = net.ListenTCP("tcp4", addr) if err != nil { log.LogError("SMTP failed to start tcp4 listener: %v", err) // TODO More graceful early-shutdown procedure panic(err) } if !s.storeMessages { log.LogInfo("Load test mode active, messages will not be stored") } else if s.domainNoStore != "" { log.LogInfo("Messages sent to domain '%v' will be discarded", s.domainNoStore) } // Start retention scanner StartRetentionScanner(s.dataStore) // Handle incoming connections var tempDelay time.Duration for sid := 1; ; sid++ { if conn, err := s.listener.Accept(); err != nil { if nerr, ok := err.(net.Error); ok && nerr.Temporary() { // Temporary error, sleep for a bit and try again if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } log.LogError("SMTP accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } else { if s.shutdown { log.LogTrace("SMTP listener shutting down on request") return } // TODO Implement a max error counter before shutdown? // or maybe attempt to restart smtpd panic(err) } } else { tempDelay = 0 expConnectsTotal.Add(1) s.waitgroup.Add(1) go s.startSession(sid, conn) } } }
func main() { config.VERSION = VERSION config.BUILD_DATE = BUILD_DATE flag.Parse() if *help { flag.Usage() return } // Load & Parse config if flag.NArg() != 1 { flag.Usage() os.Exit(1) } err := config.LoadConfig(flag.Arg(0)) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse config: %v\n", err) os.Exit(1) } // Setup signal handler sigChan := make(chan os.Signal) signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGTERM) go signalProcessor(sigChan) // Configure logging, close std* fds level, _ := config.Config.String("logging", "level") log.SetLogLevel(level) if *logfile != "stderr" { // stderr is the go logging default if *logfile == "stdout" { // set to stdout golog.SetOutput(os.Stdout) } else { err = openLogFile() if err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) } defer closeLogFile() // close std* streams os.Stdout.Close() os.Stderr.Close() // Warning: this will hide panic() output os.Stdin.Close() os.Stdout = logf os.Stderr = logf } } log.LogInfo("Inbucket %v (%v) starting...", config.VERSION, config.BUILD_DATE) // Write pidfile if requested // TODO: Probably supposed to remove pidfile during shutdown if *pidfile != "none" { pidf, err := os.Create(*pidfile) if err != nil { log.LogError("Failed to create %v: %v", *pidfile, err) os.Exit(1) } defer pidf.Close() fmt.Fprintf(pidf, "%v\n", os.Getpid()) } // Grab our datastore ds := smtpd.DefaultFileDataStore() // Start HTTP server web.Initialize(config.GetWebConfig(), ds) go web.Start() // Start POP3 server pop3Server = pop3d.New() go pop3Server.Start() // Startup SMTP server, block until it exits smtpServer = smtpd.NewSmtpServer(config.GetSmtpConfig(), ds) smtpServer.Start() // Wait for active connections to finish smtpServer.Drain() pop3Server.Drain() }