maddy/maddy.go
fox.cpp 35c3b1c792
Restructure code tree
Root package now contains only initialization code and 'dummy' module.

Each module now got its own package. Module packages are grouped by
their main purpose (storage/, target/, auth/, etc). Shared code is
placed in these "group" packages.

Parser for module references in config is moved into config/module.

Code shared by tests (mock modules, etc) is placed in testutils.
2019-09-08 16:06:38 +03:00

120 lines
3.5 KiB
Go

package maddy
import (
"io"
"os"
"os/signal"
"syscall"
"github.com/foxcpp/maddy/config"
"github.com/foxcpp/maddy/endpoint"
"github.com/foxcpp/maddy/log"
"github.com/foxcpp/maddy/module"
// Import packages for side-effect of module registration.
_ "github.com/foxcpp/maddy/auth/external"
_ "github.com/foxcpp/maddy/check/dns"
_ "github.com/foxcpp/maddy/endpoint/imap"
_ "github.com/foxcpp/maddy/endpoint/smtp"
_ "github.com/foxcpp/maddy/storage/sql"
_ "github.com/foxcpp/maddy/target/queue"
_ "github.com/foxcpp/maddy/target/remote"
)
type modInfo struct {
instance module.Module
cfg config.Node
}
func Start(cfg []config.Node) error {
instances := make(map[string]modInfo)
globals := config.NewMap(nil, &config.Node{Children: cfg})
globals.String("hostname", false, false, "", nil)
globals.String("autogenerated_msg_domain", false, false, "", nil)
globals.String("statedir", false, false, "", nil)
globals.String("libexecdir", false, false, "", nil)
globals.Custom("tls", false, false, nil, endpoint.TLSDirective, nil)
globals.Bool("auth_perdomain", false, nil)
globals.StringList("auth_domains", false, false, nil, nil)
globals.Custom("log", false, false, defaultLogOutput, logOutput, &log.DefaultLogger.Out)
globals.Bool("debug", false, &log.DefaultLogger.Debug)
globals.AllowUnknown()
unmatched, err := globals.Process()
if err != nil {
return err
}
for _, block := range unmatched {
var instName string
var modAliases []string
if len(block.Args) == 0 {
instName = block.Name
} else {
instName = block.Args[0]
modAliases = block.Args[1:]
}
modName := block.Name
factory := module.Get(modName)
if factory == nil {
return config.NodeErr(&block, "unknown module: %s", modName)
}
if module.HasInstance(instName) {
return config.NodeErr(&block, "config block named %s already exists", instName)
}
log.Debugln("module create", modName, instName)
inst, err := factory(modName, instName, modAliases)
if err != nil {
return err
}
block := block
module.RegisterInstance(inst, config.NewMap(globals.Values, &block))
for _, alias := range modAliases {
if module.HasInstance(alias) {
return config.NodeErr(&block, "config block named %s already exists", alias)
}
module.RegisterAlias(alias, instName)
log.Debugln("module alias", alias, "->", instName)
}
instances[instName] = modInfo{instance: inst, cfg: block}
}
for _, inst := range instances {
if module.Initialized[inst.instance.InstanceName()] {
log.Debugln("module init", inst.instance.Name(), inst.instance.InstanceName(), "skipped because it was lazily initialized before")
continue
}
module.Initialized[inst.instance.InstanceName()] = true
log.Debugln("module init", inst.instance.Name(), inst.instance.InstanceName())
if err := inst.instance.Init(config.NewMap(globals.Values, &inst.cfg)); err != nil {
return err
}
}
sig := make(chan os.Signal, 5)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGINT)
s := <-sig
log.Printf("signal received (%v), next signal will force immediate shutdown.", s)
go func() {
s := <-sig
log.Printf("forced shutdown due to signal (%v)!", s)
os.Exit(1)
}()
for _, inst := range instances {
if closer, ok := inst.instance.(io.Closer); ok {
log.Debugln("clean-up for module", inst.instance.Name(), inst.instance.InstanceName())
if err := closer.Close(); err != nil {
log.Printf("module %s (%s) close failed: %v", inst.instance.Name(), inst.instance.InstanceName(), err)
}
}
}
return nil
}