mosdns/coremain/plugin.go
dengxiongjian cd761e8145
Some checks are pending
Test mosdns / build (push) Waiting to run
新增Mikrotik API 插入解析ip
2025-07-31 11:28:55 +08:00

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)
}