maddy/internal/module/instances.go
fox.cpp ea40a8fbc5
Implement global callbacks for "shutdown" and "reload"
This allows modules (and any other code) to implement arbitrary actions
on server shutdown and "reload". Later will be used to address issues
like #160. There are no plans to implement complete configuration
reloading, however, since it adds a lot of problems to the modules
framework and it is believed to be "not worth it" at the moment.

Closes #130.
2020-01-02 12:26:32 +03:00

87 lines
2 KiB
Go

package module
import (
"fmt"
"io"
"github.com/foxcpp/maddy/internal/config"
"github.com/foxcpp/maddy/internal/hooks"
"github.com/foxcpp/maddy/internal/log"
)
var (
instances = make(map[string]struct {
mod Module
cfg *config.Map
})
aliases = make(map[string]string)
Initialized = make(map[string]bool)
)
// RegisterInstance adds module instance to the global registry.
//
// Instance name must be unique. Second RegisterInstance with same instance
// name will replace previous.
func RegisterInstance(inst Module, cfg *config.Map) {
instances[inst.InstanceName()] = struct {
mod Module
cfg *config.Map
}{inst, cfg}
}
// RegisterAlias creates an association between a certain name and instance name.
//
// After RegisterAlias, module.GetInstance(aliasName) will return the same
// result as module.GetInstance(instName).
func RegisterAlias(aliasName, instName string) {
aliases[aliasName] = instName
}
func HasInstance(name string) bool {
aliasedName := aliases[name]
if aliasedName != "" {
name = aliasedName
}
_, ok := instances[name]
return ok
}
// GetInstance returns module instance from global registry, initializing it if
// necessary.
//
// Error is returned if module initialization fails or module instance does not
// exists.
func GetInstance(name string) (Module, error) {
aliasedName := aliases[name]
if aliasedName != "" {
name = aliasedName
}
mod, ok := instances[name]
if !ok {
return nil, fmt.Errorf("unknown config block: %s", name)
}
// Break circular dependencies.
if Initialized[name] {
return mod.mod, nil
}
Initialized[name] = true
if err := mod.mod.Init(mod.cfg); err != nil {
return mod.mod, err
}
if closer, ok := mod.mod.(io.Closer); ok {
hooks.AddHook(hooks.EventShutdown, func() {
log.Debugf("close %s (%s)", mod.mod.Name(), mod.mod.InstanceName())
if err := closer.Close(); err != nil {
log.Printf("module %s (%s) close failed: %v", mod.mod.Name(), mod.mod.InstanceName(), err)
}
})
}
return mod.mod, nil
}