202 lines
5.0 KiB
Go
202 lines
5.0 KiB
Go
/*
|
|
* Copyright (C) 2020-2022, IrineSistiana
|
|
*
|
|
* This file is part of mosdns.
|
|
*
|
|
* mosdns is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* mosdns is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package coremain
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/IrineSistiana/mosdns/v5/pkg/utils"
|
|
"github.com/go-chi/chi/v5"
|
|
"go.uber.org/zap"
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
// NewPluginArgsFunc represents a func that creates a new args object.
|
|
type NewPluginArgsFunc func() any
|
|
|
|
// NewPluginFunc represents a func that can init a Plugin.
|
|
// args is the object created by NewPluginArgsFunc.
|
|
type NewPluginFunc func(bp *BP, args any) (p any, err error)
|
|
|
|
type PluginTypeInfo struct {
|
|
NewPlugin NewPluginFunc
|
|
NewArgs NewPluginArgsFunc
|
|
}
|
|
|
|
var (
|
|
// pluginTypeRegister stores init funcs for certain plugin types
|
|
pluginTypeRegister struct {
|
|
sync.RWMutex
|
|
m map[string]PluginTypeInfo
|
|
}
|
|
)
|
|
|
|
// RegNewPluginFunc registers the type.
|
|
// If the type has been registered. RegNewPluginFunc will panic.
|
|
func RegNewPluginFunc(typ string, initFunc NewPluginFunc, argsType NewPluginArgsFunc) {
|
|
pluginTypeRegister.Lock()
|
|
defer pluginTypeRegister.Unlock()
|
|
|
|
if pluginTypeRegister.m == nil {
|
|
pluginTypeRegister.m = make(map[string]PluginTypeInfo)
|
|
}
|
|
|
|
_, ok := pluginTypeRegister.m[typ]
|
|
if ok {
|
|
panic(fmt.Sprintf("duplicate plugin type [%s]", typ))
|
|
}
|
|
|
|
pluginTypeRegister.m[typ] = PluginTypeInfo{
|
|
NewPlugin: initFunc,
|
|
NewArgs: argsType,
|
|
}
|
|
}
|
|
|
|
// DelPluginType deletes the init func for this plugin type.
|
|
// It is a noop if pluginType is not registered.
|
|
func DelPluginType(typ string) {
|
|
pluginTypeRegister.Lock()
|
|
defer pluginTypeRegister.Unlock()
|
|
delete(pluginTypeRegister.m, typ)
|
|
}
|
|
|
|
// GetPluginType gets the registered type init func.
|
|
func GetPluginType(typ string) (PluginTypeInfo, bool) {
|
|
pluginTypeRegister.RLock()
|
|
defer pluginTypeRegister.RUnlock()
|
|
|
|
info, ok := pluginTypeRegister.m[typ]
|
|
return info, ok
|
|
}
|
|
|
|
// newPlugin initializes a Plugin from c and adds it to mosdns.
|
|
func (m *Mosdns) newPlugin(c PluginConfig) error {
|
|
if len(c.Tag) == 0 {
|
|
c.Tag = fmt.Sprintf("anonymouse_%s_%d", c.Type, len(m.plugins))
|
|
}
|
|
|
|
if _, dup := m.plugins[c.Tag]; dup {
|
|
return fmt.Errorf("duplicated plugin tag %s", c.Tag)
|
|
}
|
|
|
|
typeInfo, ok := GetPluginType(c.Type)
|
|
if !ok {
|
|
return fmt.Errorf("plugin type %s not defined", c.Type)
|
|
}
|
|
|
|
args := typeInfo.NewArgs()
|
|
if reflect.TypeOf(c.Args) == reflect.TypeOf(args) { // Same type, no need to parse.
|
|
args = c.Args
|
|
} else {
|
|
if err := utils.WeakDecode(c.Args, args); err != nil {
|
|
return fmt.Errorf("unable to decode plugin args: %w", err)
|
|
}
|
|
}
|
|
|
|
m.logger.Info("loading plugin", zap.String("tag", c.Tag), zap.String("type", c.Type))
|
|
p, err := typeInfo.NewPlugin(NewBP(c.Tag, m), args)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to init plugin: %w", err)
|
|
}
|
|
m.plugins[c.Tag] = p
|
|
return nil
|
|
}
|
|
|
|
// GetAllPluginTypes returns all plugin types which are configurable.
|
|
func GetAllPluginTypes() []string {
|
|
pluginTypeRegister.RLock()
|
|
defer pluginTypeRegister.RUnlock()
|
|
|
|
var t []string
|
|
for typ := range pluginTypeRegister.m {
|
|
t = append(t, typ)
|
|
}
|
|
return t
|
|
}
|
|
|
|
type NewPersetPluginFunc func(bp *BP) (any, error)
|
|
|
|
var presetPluginFuncReg struct {
|
|
sync.Mutex
|
|
m map[string]NewPersetPluginFunc
|
|
}
|
|
|
|
func RegNewPersetPluginFunc(tag string, f NewPersetPluginFunc) {
|
|
presetPluginFuncReg.Lock()
|
|
defer presetPluginFuncReg.Unlock()
|
|
|
|
if presetPluginFuncReg.m == nil {
|
|
presetPluginFuncReg.m = make(map[string]NewPersetPluginFunc)
|
|
}
|
|
if _, ok := presetPluginFuncReg.m[tag]; ok {
|
|
panic(fmt.Sprintf("preset plugin %s has already been registered", tag))
|
|
}
|
|
|
|
presetPluginFuncReg.m[tag] = f
|
|
}
|
|
|
|
func LoadNewPersetPluginFuncs() map[string]NewPersetPluginFunc {
|
|
presetPluginFuncReg.Lock()
|
|
defer presetPluginFuncReg.Unlock()
|
|
m := make(map[string]NewPersetPluginFunc)
|
|
for tag, pluginFunc := range presetPluginFuncReg.m {
|
|
m[tag] = pluginFunc
|
|
}
|
|
return m
|
|
}
|
|
|
|
type BP struct {
|
|
tag string
|
|
m *Mosdns
|
|
l *zap.Logger
|
|
}
|
|
|
|
// NewBP creates a new BP. m MUST NOT nil.
|
|
func NewBP(tag string, m *Mosdns) *BP {
|
|
return &BP{
|
|
tag: tag,
|
|
l: m.Logger().Named(tag),
|
|
m: m,
|
|
}
|
|
}
|
|
|
|
// L returns a non-nil logger.
|
|
func (p *BP) L() *zap.Logger {
|
|
return p.l
|
|
}
|
|
|
|
// M returns a non-nil Mosdns.
|
|
func (p *BP) M() *Mosdns {
|
|
return p.m
|
|
}
|
|
|
|
// Tag returns the plugin tag.
|
|
// This tag should be unique globally unless it's in
|
|
// a test environment.
|
|
func (p *BP) Tag() string {
|
|
return p.tag
|
|
}
|
|
|
|
// RegAPI mounts mux to mosdns api. Note: Plugins MUST NOT call RegAPI twice.
|
|
// Since mounting same path to root chi.Mux causes runtime panic.
|
|
func (p *BP) RegAPI(mux *chi.Mux) {
|
|
p.m.RegPluginAPI(p.tag, mux)
|
|
}
|