✏️ 分离 dyloader

This commit is contained in:
fumiama 2021-10-14 16:26:01 +08:00
parent d2bcd0bc9f
commit cccea70db1
7 changed files with 2 additions and 458 deletions

View File

@ -1,16 +0,0 @@
package dyloader
import zero "github.com/wdvxdr1123/ZeroBot"
func sendGroupMessage(ctx *zero.Ctx, groupID int64, message interface{}) int64 {
return ctx.SendGroupMessage(groupID, message)
}
func sendPrivateMessage(ctx *zero.Ctx, userID int64, message interface{}) int64 {
return ctx.SendPrivateMessage(userID, message)
}
func getMessage(ctx *zero.Ctx, messageID int64) zero.Message {
return ctx.GetMessage(messageID)
}
func parse(ctx *zero.Ctx, model interface{}) (err error) {
return ctx.Parse(model)
}

View File

@ -1,57 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux,cgo darwin,cgo freebsd,cgo
// +build linux,cgo darwin,cgo freebsd,cgo
package plugin
/*
#cgo linux LDFLAGS: -ldl
#include <dlfcn.h>
#include <limits.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
static int pluginClose(void* handle, char** err) {
int res = dlclose(handle);
if (res != 0) {
*err = (char*)dlerror();
}
return res;
}
*/
import "C"
import (
"errors"
"sync"
"unsafe"
)
func unload(pg *Plugin) error {
filepath := pg.pluginpath
pluginsMu.Lock()
p := plugins[filepath]
if p != nil {
delete(plugins, filepath)
}
pluginsMu.Unlock()
if p != nil {
var cErr *C.char
res := C.pluginClose(unsafe.Pointer(p), &cErr)
if res != 0 {
return errors.New(`plugin.Close("` + filepath + `"): ` + C.GoString(cErr))
}
}
return nil
}
//go:linkname pluginsMu plugin.pluginsMu
//go:linkname plugins plugin.plugins
var (
pluginsMu sync.Mutex
plugins map[string]*Plugin
)

View File

@ -1,22 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !linux,!freebsd,!darwin !cgo
// +build !linux,!freebsd,!darwin !cgo
package plugin
import "errors"
func lookup(p *Plugin, symName string) (Symbol, error) {
return nil, errors.New("plugin: not implemented")
}
func open(name string) (*Plugin, error) {
return nil, errors.New("plugin: not implemented")
}
func unload(name string) error {
return nil, errors.New("plugin: not implemented")
}

View File

@ -1,89 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package plugin implements loading and symbol resolution of Go plugins.
//
// A plugin is a Go main package with exported functions and variables that
// has been built with:
//
// go build -buildmode=plugin
//
// When a plugin is first opened, the init functions of all packages not
// already part of the program are called. The main function is not run.
// A plugin is only initialized once, and cannot be closed.
//
// Currently plugins are only supported on Linux, FreeBSD, and macOS.
// Please report any issues.
//go:build linux,cgo darwin,cgo freebsd,cgo
// +build linux,cgo darwin,cgo freebsd,cgo
package plugin
import (
pl "plugin"
"unsafe"
)
// Plugin is a loaded Go plugin.
type Plugin struct {
pluginpath string
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
syms map[string]interface{}
}
// Open opens a Go plugin.
// If a path has already been opened, then the existing *Plugin is returned.
// It is safe for concurrent use by multiple goroutines.
func Open(path string) (*Plugin, error) {
p, err := pl.Open(path)
return (*Plugin)(unsafe.Pointer(p)), err
}
// Lookup searches for a symbol named symName in plugin p.
// A symbol is any exported variable or function.
// It reports an error if the symbol is not found.
// It is safe for concurrent use by multiple goroutines.
func (p *Plugin) Lookup(symName string) (Symbol, error) {
return (*pl.Plugin)(unsafe.Pointer(p)).Lookup(symName)
}
// Close closes a Go plugin.
// If a path is noth opened, it is ignored.
// It is safe for concurrent use by multiple goroutines.
func Close(p *Plugin) error {
return unload(p)
}
// A Symbol is a pointer to a variable or function.
//
// For example, a plugin defined as
//
// package main
//
// import "fmt"
//
// var V int
//
// func F() { fmt.Printf("Hello, number %d\n", V) }
//
// may be loaded with the Open function and then the exported package
// symbols V and F can be accessed
//
// p, err := plugin.Open("plugin_name.so")
// if err != nil {
// panic(err)
// }
// v, err := p.Lookup("V")
// if err != nil {
// panic(err)
// }
// f, err := p.Lookup("F")
// if err != nil {
// panic(err)
// }
// *v.(*int) = 7
// f.(func())() // prints "Hello, number 7"
type Symbol interface{}

View File

@ -1,83 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package plugin implements loading and symbol resolution of Go plugins.
//
// A plugin is a Go main package with exported functions and variables that
// has been built with:
//
// go build -buildmode=plugin
//
// When a plugin is first opened, the init functions of all packages not
// already part of the program are called. The main function is not run.
// A plugin is only initialized once, and cannot be closed.
//
// Currently plugins are only supported on Linux, FreeBSD, and macOS.
// Please report any issues.
//go:build windows,cgo
// +build windows,cgo
package plugin
// Plugin is a loaded Go plugin.
type Plugin struct {
pluginpath string
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
syms map[string]interface{}
}
// Open opens a Go plugin.
// If a path has already been opened, then the existing *Plugin is returned.
// It is safe for concurrent use by multiple goroutines.
func Open(path string) (*Plugin, error) {
return open(path)
}
// Lookup searches for a symbol named symName in plugin p.
// A symbol is any exported variable or function.
// It reports an error if the symbol is not found.
// It is safe for concurrent use by multiple goroutines.
func (p *Plugin) Lookup(symName string) (Symbol, error) {
return lookup(p, symName)
}
// Close closes a Go plugin.
// If a path is noth opened, it is ignored.
// It is safe for concurrent use by multiple goroutines.
func Close(p *Plugin) error {
return unload(p)
}
// A Symbol is a pointer to a variable or function.
//
// For example, a plugin defined as
//
// package main
//
// import "fmt"
//
// var V int
//
// func F() { fmt.Printf("Hello, number %d\n", V) }
//
// may be loaded with the Open function and then the exported package
// symbols V and F can be accessed
//
// p, err := plugin.Open("plugin_name.so")
// if err != nil {
// panic(err)
// }
// v, err := p.Lookup("V")
// if err != nil {
// panic(err)
// }
// f, err := p.Lookup("F")
// if err != nil {
// panic(err)
// }
// *v.(*int) = 7
// f.(func())() // prints "Hello, number 7"
type Symbol interface{}

View File

@ -1,189 +0,0 @@
// Package dyloader 动态插件加载器
package dyloader
import (
"io/fs"
"path/filepath"
"strings"
"sync"
"time"
_ "unsafe"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/dyloader/plugin"
)
var typeIsSo bool
var visited bool
//go:linkname matcherList github.com/wdvxdr1123/ZeroBot.matcherList
//go:linkname matcherLock github.com/wdvxdr1123/ZeroBot.matcherLock
//go:linkname defaultEngine github.com/wdvxdr1123/ZeroBot.defaultEngine
var (
// matcherList 所有主匹配器列表
matcherList []*zero.Matcher
// Matcher 修改读写锁
matcherLock sync.RWMutex
// defaultEngine zero 的默认 engine
defaultEngine *zero.Engine
)
var (
pluginsMu sync.Mutex
plugins = make(map[string]*plugin.Plugin)
)
func init() {
zero.OnCommand("刷新插件", zero.SuperUserPermission).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
err := scan()
if err != nil {
ctx.SendChain(message.Text("Error: " + err.Error()))
} else {
ctx.SendChain(message.Text("成功!"))
}
})
zero.OnCommand("加载插件", zero.SuperUserPermission).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
_, ok := control.Lookup(model.Args)
if !ok {
t := ".dll"
if typeIsSo {
t = ".so"
}
target := model.Args + t
logrus.Debugln("[dyloader]target:", target)
path := "plugins/" + target
err := open(path, target)
if err != nil {
ctx.SendChain(message.Text("Error: " + err.Error()))
} else {
ctx.SendChain(message.Text("成功!"))
}
}
})
zero.OnCommand("卸载插件", zero.SuperUserPermission).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
_, ok := control.Lookup(model.Args)
if ok {
t := ".dll"
if typeIsSo {
t = ".so"
}
target := model.Args + t
logrus.Debugln("[dyloader]target:", target)
pluginsMu.Lock()
p, ok := plugins[target]
pluginsMu.Unlock()
if ok {
err := plugin.Close(p)
control.Delete(model.Args)
pluginsMu.Lock()
delete(plugins, target)
pluginsMu.Unlock()
if err != nil {
ctx.SendChain(message.Text("Error: " + err.Error()))
} else {
ctx.SendChain(message.Text("成功!"))
}
} else {
ctx.SendChain(message.Text("没有这个插件!"))
}
}
})
go func() {
time.Sleep(time.Second * 2)
_ = scan()
}()
}
func scan() error {
return filepath.WalkDir("plugins/", load)
}
func open(path, target string) error {
pluginsMu.Lock()
_, ok := plugins[target]
pluginsMu.Unlock()
if !ok {
p, err := plugin.Open(path)
var initfunc, hookfunc, ishooked plugin.Symbol
if err == nil {
ishooked, err = p.Lookup("IsHooked")
if err == nil {
if !*ishooked.(*bool) {
hookfunc, err = p.Lookup("Hook")
if err == nil {
logrus.Debugf("[dyloader]reg: %x, del: %x\n", control.Register, control.Delete)
logrus.Debugf("[dyloader]matlist: %p, matlock: %p\n", &matcherList, &matcherLock)
hookfunc.(func(interface{}, interface{}, interface{},
interface{}, interface{}, interface{},
interface{}, interface{},
interface{}, interface{}, interface{},
interface{},
interface{}, interface{}, interface{},
))(
&zero.BotConfig, &zero.APICallers, zero.New,
&matcherList, &matcherLock, defaultEngine,
control.Register, control.Delete,
sendGroupMessage, sendPrivateMessage, getMessage,
parse,
message.CustomNode, message.ParseMessage, message.ParseMessageFromArray,
)
} else {
_ = plugin.Close(p)
return err
}
}
initfunc, err = p.Lookup("Inita")
if err == nil {
initfunc.(func())()
logrus.Infoln("[dyloader]加载插件", path, "成功")
pluginsMu.Lock()
plugins[target] = p
pluginsMu.Unlock()
return nil
}
}
_ = plugin.Close(p)
}
if err != nil {
logrus.Errorln("[dyloader]加载插件", path, "错误:", err)
}
return err
}
return nil
}
func load(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
n := d.Name()
if !visited {
if strings.HasSuffix(n, ".so") {
typeIsSo = true
visited = true
} else if strings.HasSuffix(n, ".dll") {
visited = true
}
}
if strings.HasSuffix(n, ".so") || strings.HasSuffix(n, ".dll") {
target := path[strings.LastIndex(path, "/")+1:]
logrus.Debugln("[dyloader]target:", target)
return open(path, target)
}
return nil
}

View File

@ -48,7 +48,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
// 以下为内置依赖,勿动
// _ "github.com/FloatTech/ZeroBot-Plugin/dyloader"
// _ "github.com/FloatTech/ZeroBot-Plugin-Dynamic/dyloader"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/driver"
@ -57,7 +57,7 @@ import (
var (
contents = []string{
"* OneBot + ZeroBot + Golang",
"* Version 1.1.6 - 2021-10-09 12:50:23 +0800 CST",
"* Version 1.1.7 - 2021-10-09 12:50:23 +0800 CST",
"* Copyright © 2020 - 2021 Kanri, DawnNights, Fumiama, Suika",
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
}