git » gofer » main » tree

[main] / gofer.go

package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"blitiri.com.ar/go/gofer/config"
	"blitiri.com.ar/go/gofer/debug"
	"blitiri.com.ar/go/gofer/ratelimit"
	"blitiri.com.ar/go/gofer/reqlog"
	"blitiri.com.ar/go/gofer/server"
	"blitiri.com.ar/go/log"
)

// Flags.
var (
	configFile  = flag.String("configfile", "gofer.yaml", "Configuration file")
	configCheck = flag.Bool("configcheck", false,
		"Check the configuration and exit afterwards")
	configPrint = flag.Bool("configprint", false,
		"Check the configuration, print it, and exit afterwards")
)

func main() {
	flag.Parse()
	log.Init()
	log.Infof("gofer starting (%s, %s)",
		debug.Version,
		debug.SourceDate.Format("2006-01-02 15:04:05 -0700"))

	conf, err := config.Load(*configFile)
	if err != nil {
		log.Fatalf("error reading config file: %v", err)
	}

	if errs := conf.Check(); len(errs) > 0 {
		for _, err := range errs {
			log.Errorf("%v", err)
		}
		log.Fatalf("invalid configuration")
	}
	if *configPrint {
		fmt.Print(conf.String())
		return
	}
	if *configCheck {
		log.Infof("config ok")
		return
	}

	go signalHandler()

	for name, rlog := range conf.ReqLog {
		err := reqlog.FromConfig(name, rlog)
		if err != nil {
			log.Fatalf(err.Error())
		}
	}

	for name, rl := range conf.RateLimit {
		ratelimit.FromConfig(name, rl)
	}

	servers := []runnerFunc{}

	for addr, https := range conf.HTTPS {
		addr := addr
		https := https
		servers = append(servers, func() error {
			return server.HTTPS(addr, https)
		})
	}

	for addr, http := range conf.HTTP {
		addr := addr
		http := http
		servers = append(servers, func() error {
			return server.HTTP(addr, http)
		})
	}

	for addr, raw := range conf.Raw {
		addr := addr
		raw := raw
		servers = append(servers, func() error {
			return server.Raw(addr, raw)
		})
	}

	if conf.ControlAddr != "" {
		servers = append(servers, func() error {
			return debug.ServeDebugging(conf.ControlAddr, conf)
		})
	}

	err = runMany(servers...)
	log.Fatalf(err.Error())
}

func signalHandler() {
	var err error

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)

	for {
		switch sig := <-signals; sig {
		case syscall.SIGHUP:
			// SIGHUP triggers a reopen of the log files. This is used for log
			// rotation.
			err = log.Default.Reopen()
			if err != nil {
				log.Fatalf("Error reopening log: %v", err)
			}

			reqlog.ReopenAll()
		case syscall.SIGTERM, syscall.SIGINT:
			log.Fatalf("Got signal to exit: %v", sig)
		default:
			log.Errorf("Unexpected signal: %v", sig)
		}
	}
}

type runnerFunc func() error

func runMany(fs ...runnerFunc) error {
	var err error
	mu := &sync.Mutex{}
	cond := sync.NewCond(mu)

	mu.Lock()

	for _, f := range fs {
		go func(f runnerFunc) {
			e := f()
			mu.Lock()
			err = e
			cond.Broadcast()
			mu.Unlock()
		}(f)
	}

	cond.Wait()

	return err
}