func CheckFileLimit(c *AppConfig) (bool, int, uint64) {
	fileLimit, err := getFileLimit()
	maxClients := 0
	if err != nil {
		return true, maxClients, fileLimit
	}
	if c.Servers == nil {
		maxClients = defaultMaxClients
	} else {
		for _, s := range c.Servers {
			maxClients += s.MaxClients
		}
	}
	if uint64(maxClients) > fileLimit {
		return false, maxClients, fileLimit
	}
	return true, maxClients, fileLimit
}
CheckFileLimit checks the number of files we can open (works on OS'es that support the ulimit command)
package guerrilla
import (
	"errors"
	"fmt"
	"os"
	"sync"
	"sync/atomic"
	"github.com/flashmob/go-guerrilla/backends"
	"github.com/flashmob/go-guerrilla/log"
)
const (
	daemonStateNew = iota
	daemonStateStarted
	daemonStateStopped
)
type Errors []error
func (e Errors) Error() string {
	if len(e) == 1 {
		return e[0].Error()
	}
	msg := ""
	for _, err := range e {
		msg += "\n" + err.Error()
	}
	return msg
}
type Guerrilla interface {
	Start() error
	Shutdown()
	Subscribe(topic Event, fn interface{}) error
	Publish(topic Event, args ...interface{})
	Unsubscribe(topic Event, handler interface{}) error
	SetLogger(log.Logger)
}
type guerrilla struct {
	Config  AppConfig
	servers map[string]*server
	guard sync.Mutex
	state int8
	EventHandler
	logStore
	backendStore
}
type logStore struct {
	atomic.Value
}
type backendStore struct {
	atomic.Value
}
type daemonEvent func(c *AppConfig)
type serverEvent func(sc *ServerConfig)
func (ls *logStore) mainlog() log.Logger {
	if v, ok := ls.Load().(log.Logger); ok {
		return v
	}
	l, _ := log.GetLogger(log.OutputStderr.String(), log.InfoLevel.String())
	return l
}
func (ls *logStore) setMainlog(log log.Logger) {
	ls.Store(log)
}
func New(ac *AppConfig, b backends.Backend, l log.Logger) (Guerrilla, error) {
	g := &guerrilla{
		Config:  *ac,  
		servers: make(map[string]*server, len(ac.Servers)),
	}
	g.backendStore.Store(b)
	g.setMainlog(l)
	if ac.LogLevel != "" {
		if h, ok := l.(*log.HookedLogger); ok {
			if h, err := log.GetLogger(h.GetLogDest(), ac.LogLevel); err == nil {
				g.setMainlog(h)
			}
		}
	}
	_ = g.writePid()
	g.state = daemonStateNew
	err := g.makeServers()
	if err != nil {
		return g, err
	}
	err = g.backend().Start()
	if err != nil {
		return g, err
	}
	g.subscribeEvents()
	return g, err
}
func (g *guerrilla) makeServers() error {
	g.mainlog().Debug("making servers")
	var errs Errors
	for _, sc := range g.Config.Servers {
		if _, ok := g.servers[sc.ListenInterface]; ok {
			continue
		}
		if err := sc.Validate(); err != nil {
			g.mainlog().WithError(errs).Errorf("Failed to create server [%s]", sc.ListenInterface)
			errs = append(errs, err)
			continue
		} else {
			sc := sc  
			server, err := newServer(&sc, g.backend(), g.mainlog())
			if err != nil {
				g.mainlog().WithError(err).Errorf("Failed to create server [%s]", sc.ListenInterface)
				errs = append(errs, err)
			}
			if server != nil {
				g.servers[sc.ListenInterface] = server
				server.setAllowedHosts(g.Config.AllowedHosts)
			}
		}
	}
	if len(g.servers) == 0 {
		errs = append(errs, errors.New("there are no servers that can start, please check your config"))
	}
	if len(errs) == 0 {
		return nil
	}
	return errs
}
func (g *guerrilla) findServer(iface string) (*server, error) {
	g.guard.Lock()
	defer g.guard.Unlock()
	if server, ok := g.servers[iface]; ok {
		return server, nil
	}
	return nil, errors.New("server not found in g.servers")
}
func (g *guerrilla) removeServer(iface string) {
	g.guard.Lock()
	defer g.guard.Unlock()
	delete(g.servers, iface)
}
func (g *guerrilla) setConfig(c *AppConfig) {
	g.guard.Lock()
	defer g.guard.Unlock()
	g.Config = *c
}
func (g *guerrilla) setServerConfig(sc *ServerConfig) {
	g.guard.Lock()
	defer g.guard.Unlock()
	if _, ok := g.servers[sc.ListenInterface]; ok {
		g.servers[sc.ListenInterface].setConfig(sc)
	}
}
func (g *guerrilla) mapServers(callback func(*server)) map[string]*server {
	defer g.guard.Unlock()
	g.guard.Lock()
	for _, server := range g.servers {
		callback(server)
	}
	return g.servers
}
func (g *guerrilla) subscribeEvents() {
	events := map[Event]interface{}{}
	events[EventConfigNewConfig] = daemonEvent(func(c *AppConfig) {
		g.setConfig(c)
	})
	events[EventConfigAllowedHosts] = daemonEvent(func(c *AppConfig) {
		g.mapServers(func(server *server) {
			server.setAllowedHosts(c.AllowedHosts)
		})
		g.mainlog().Infof("allowed_hosts config changed, a new list was set")
	})
	events[EventConfigLogFile] = daemonEvent(func(c *AppConfig) {
		var err error
		var l log.Logger
		if l, err = log.GetLogger(c.LogFile, c.LogLevel); err == nil {
			g.setMainlog(l)
			g.mapServers(func(server *server) {
				server.mainlogStore.Store(l)
			})
			g.mainlog().Infof("main log for new clients changed to [%s]", c.LogFile)
		} else {
			g.mainlog().WithError(err).Errorf("main logging change failed [%s]", c.LogFile)
		}
	})
	events[EventConfigLogReopen] = daemonEvent(func(c *AppConfig) {
		err := g.mainlog().Reopen()
		if err != nil {
			g.mainlog().WithError(err).Errorf("main log file [%s] failed to re-open", c.LogFile)
			return
		}
		g.mainlog().Infof("re-opened main log file [%s]", c.LogFile)
	})
	events[EventConfigLogLevel] = daemonEvent(func(c *AppConfig) {
		l, err := log.GetLogger(g.mainlog().GetLogDest(), c.LogLevel)
		if err == nil {
			g.logStore.Store(l)
			g.mapServers(func(server *server) {
				server.logStore.Store(l)
			})
			g.mainlog().Infof("log level changed to [%s]", c.LogLevel)
		}
	})
	events[EventConfigPidFile] = daemonEvent(func(ac *AppConfig) {
		_ = g.writePid()
	})
	events[EventConfigServerConfig] = serverEvent(func(sc *ServerConfig) {
		g.setServerConfig(sc)
		g.mainlog().Infof("server %s config change event, a new config has been saved", sc.ListenInterface)
	})
	events[EventConfigServerNew] = serverEvent(func(sc *ServerConfig) {
		g.mainlog().Debugf("event fired [%s] %s", EventConfigServerNew, sc.ListenInterface)
		if _, err := g.findServer(sc.ListenInterface); err != nil {
			if err := g.makeServers(); err != nil {
				g.mainlog().WithError(err).Errorf("cannot add server [%s]", sc.ListenInterface)
				return
			}
			g.mainlog().Infof("New server added [%s]", sc.ListenInterface)
			if g.state == daemonStateStarted {
				err := g.Start()
				if err != nil {
					g.mainlog().WithError(err).Info("Event server_change:new_server returned errors when starting")
				}
			}
		} else {
			g.mainlog().Debugf("new event, but server already fund")
		}
	})
	events[EventConfigServerStart] = serverEvent(func(sc *ServerConfig) {
		if server, err := g.findServer(sc.ListenInterface); err == nil {
			if server.state == ServerStateStopped || server.state == ServerStateNew {
				g.mainlog().Infof("Starting server [%s]", server.listenInterface)
				err := g.Start()
				if err != nil {
					g.mainlog().WithError(err).Info("Event server_change:start_server returned errors when starting")
				}
			}
		}
	})
	events[EventConfigServerStop] = serverEvent(func(sc *ServerConfig) {
		if server, err := g.findServer(sc.ListenInterface); err == nil {
			if server.state == ServerStateRunning {
				server.Shutdown()
				g.mainlog().Infof("Server [%s] stopped.", sc.ListenInterface)
			}
		}
	})
	events[EventConfigServerRemove] = serverEvent(func(sc *ServerConfig) {
		if server, err := g.findServer(sc.ListenInterface); err == nil {
			server.Shutdown()
			g.removeServer(sc.ListenInterface)
			g.mainlog().Infof("Server [%s] removed from config, stopped it.", sc.ListenInterface)
		}
	})
	events[EventConfigServerTLSConfig] = serverEvent(func(sc *ServerConfig) {
		if server, err := g.findServer(sc.ListenInterface); err == nil {
			if err := server.configureTLS(); err == nil {
				g.mainlog().Infof("Server [%s] new TLS configuration loaded", sc.ListenInterface)
			} else {
				g.mainlog().WithError(err).Errorf("Server [%s] failed to load the new TLS configuration", sc.ListenInterface)
			}
		}
	})
	events[EventConfigServerTimeout] = serverEvent(func(sc *ServerConfig) {
		g.mapServers(func(server *server) {
			server.setTimeout(sc.Timeout)
		})
	})
	events[EventConfigServerMaxClients] = serverEvent(func(sc *ServerConfig) {
		g.mapServers(func(server *server) {
		})
	})
	events[EventConfigServerLogFile] = serverEvent(func(sc *ServerConfig) {
		if server, err := g.findServer(sc.ListenInterface); err == nil {
			var err error
			var l log.Logger
			level := g.mainlog().GetLevel()
			if l, err = log.GetLogger(sc.LogFile, level); err == nil {
				g.setMainlog(l)
				backends.Svc.SetMainlog(l)
				server.logStore.Store(l)
				g.mainlog().Infof("Server [%s] changed, new clients will log to: [%s]",
					sc.ListenInterface,
					sc.LogFile,
				)
			} else {
				g.mainlog().WithError(err).Errorf(
					"Server [%s] log change failed to: [%s]",
					sc.ListenInterface,
					sc.LogFile,
				)
			}
		}
	})
	events[EventConfigServerLogReopen] = serverEvent(func(sc *ServerConfig) {
		if server, err := g.findServer(sc.ListenInterface); err == nil {
			if err = server.log().Reopen(); err != nil {
				g.mainlog().WithError(err).Errorf("server [%s] log file [%s] failed to re-open", sc.ListenInterface, sc.LogFile)
				return
			}
			g.mainlog().Infof("Server [%s] re-opened log file [%s]", sc.ListenInterface, sc.LogFile)
		}
	})
	events[EventConfigBackendConfig] = daemonEvent(func(appConfig *AppConfig) {
		logger, _ := log.GetLogger(appConfig.LogFile, appConfig.LogLevel)
		var err error
		if err = g.backend().Shutdown(); err != nil {
			logger.WithError(err).Warn("Backend failed to shutdown")
			return
		}
		if newBackend, newErr := backends.New(appConfig.BackendConfig, logger); newErr != nil {
			logger.WithError(newErr).Error("Error while loading the backend")
			err = g.backend().Reinitialize()
			if err != nil {
				logger.WithError(err).Fatal("failed to revert to old backend config")
				return
			}
			err = g.backend().Start()
			if err != nil {
				logger.WithError(err).Fatal("failed to start backend with old config")
				return
			}
			logger.Info("reverted to old backend config")
		} else {
			if err := newBackend.Start(); err != nil {
				logger.WithError(err).Error("backend could not start")
			}
			logger.Info("new backend started")
			g.storeBackend(newBackend)
		}
	})
	var err error
	for topic, fn := range events {
		switch f := fn.(type) {
		case daemonEvent:
			err = g.Subscribe(topic, f)
		case serverEvent:
			err = g.Subscribe(topic, f)
		}
		if err != nil {
			g.mainlog().WithError(err).Errorf("failed to subscribe on topic [%s]", topic)
			break
		}
	}
}
func (g *guerrilla) storeBackend(b backends.Backend) {
	g.backendStore.Store(b)
	g.mapServers(func(server *server) {
		server.setBackend(b)
	})
}
func (g *guerrilla) backend() backends.Backend {
	if b, ok := g.backendStore.Load().(backends.Backend); ok {
		return b
	}
	return nil
}
func (g *guerrilla) Start() error {
	var startErrors Errors
	g.guard.Lock()
	defer func() {
		g.state = daemonStateStarted
		g.guard.Unlock()
	}()
	if len(g.servers) == 0 {
		return append(startErrors, errors.New("no servers to start, please check the config"))
	}
	if g.state == daemonStateStopped {
		if err := g.backend().Reinitialize(); err != nil {
			startErrors = append(startErrors, err)
		}
		if err := g.backend().Start(); err != nil {
			startErrors = append(startErrors, err)
		}
	}
	errs := make(chan error, len(g.servers))
	var startWG sync.WaitGroup
	for ListenInterface := range g.servers {
		if !g.servers[ListenInterface].isEnabled() {
			continue
		}
		if g.servers[ListenInterface].state != ServerStateNew &&
			g.servers[ListenInterface].state != ServerStateStopped {
			continue
		}
		startWG.Add(1)
		go func(s *server) {
			g.mainlog().Infof("Starting: %s", s.listenInterface)
			if err := s.Start(&startWG); err != nil {
				errs <- err
			}
		}(g.servers[ListenInterface])
	}
	startWG.Wait()
	close(errs)
	for err := range errs {
		if err != nil {
			startErrors = append(startErrors, err)
		}
	}
	if len(startErrors) > 0 {
		return startErrors
	}
	return nil
}
func (g *guerrilla) Shutdown() {
	g.mapServers(func(s *server) {
		if s.state == ServerStateRunning {
			s.Shutdown()
			g.mainlog().Infof("shutdown completed for [%s]", s.listenInterface)
		}
	})
	g.guard.Lock()
	defer func() {
		g.state = daemonStateStopped
		defer g.guard.Unlock()
	}()
	if err := g.backend().Shutdown(); err != nil {
		g.mainlog().WithError(err).Warn("Backend failed to shutdown")
	} else {
		g.mainlog().Infof("Backend shutdown completed")
	}
}
func (g *guerrilla) SetLogger(l log.Logger) {
	g.setMainlog(l)
	backends.Svc.SetMainlog(l)
}
func (g *guerrilla) writePid() (err error) {
	var f *os.File
	defer func() {
		if f != nil {
			if closeErr := f.Close(); closeErr != nil {
				err = closeErr
			}
		}
		if err != nil {
			g.mainlog().WithError(err).Errorf("error while writing pidFile (%s)", g.Config.PidFile)
		}
	}()
	if len(g.Config.PidFile) > 0 {
		if f, err = os.Create(g.Config.PidFile); err != nil {
			return err
		}
		pid := os.Getpid()
		if _, err := f.WriteString(fmt.Sprintf("%d", pid)); err != nil {
			return err
		}
		if err = f.Sync(); err != nil {
			return err
		}
		g.mainlog().Infof("pid_file (%s) written with pid:%v", g.Config.PidFile, pid)
	}
	return nil
}