make c, cpp and rust plugins as well as their builders (thank you claude). Also add support for multi lang plugins so that we can actually fucking unload them unlike shitty golang (issue has been opened since 2015 and there is no due date for the issue...)
This commit is contained in:
@@ -45,6 +45,11 @@ static void so_call_onunload(void* fn) {
|
||||
typedef void (*fn_t)(void);
|
||||
((fn_t)fn)();
|
||||
}
|
||||
|
||||
static const char* so_call_getruntime(void* fn) {
|
||||
typedef const char* (*fn_t)(void);
|
||||
return ((fn_t)fn)();
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -125,21 +130,30 @@ func loadNativePlugin(data []byte) (NativePlugin, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginRT := "go"
|
||||
if getRuntimeFn, err := resolve("PluginGetRuntime"); err == nil {
|
||||
if cs := C.so_call_getruntime(getRuntimeFn); cs != nil {
|
||||
pluginRT = C.GoString(cs)
|
||||
}
|
||||
}
|
||||
|
||||
return &soPlugin{
|
||||
handle: handle,
|
||||
onLoadFn: onLoad,
|
||||
onEventFn: onEvent,
|
||||
onUnloadFn: onUnload,
|
||||
handle: handle,
|
||||
onLoadFn: onLoad,
|
||||
onEventFn: onEvent,
|
||||
onUnloadFn: onUnload,
|
||||
pluginRuntime: pluginRT,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type soPlugin struct {
|
||||
handle unsafe.Pointer // dlopen handle
|
||||
onLoadFn unsafe.Pointer
|
||||
onEventFn unsafe.Pointer
|
||||
onUnloadFn unsafe.Pointer
|
||||
cbHandle cgo.Handle
|
||||
mu sync.Mutex
|
||||
handle unsafe.Pointer
|
||||
onLoadFn unsafe.Pointer
|
||||
onEventFn unsafe.Pointer
|
||||
onUnloadFn unsafe.Pointer
|
||||
cbHandle cgo.Handle
|
||||
pluginRuntime string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (p *soPlugin) Load(send func(string, []byte), hostInfo []byte) error {
|
||||
@@ -200,8 +214,14 @@ func (p *soPlugin) Close() error {
|
||||
p.cbHandle = 0
|
||||
}
|
||||
if p.handle != nil {
|
||||
C.so_dlclose(p.handle)
|
||||
if p.pluginRuntime != "go" {
|
||||
C.so_dlclose(p.handle)
|
||||
}
|
||||
p.handle = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *soPlugin) Runtime() string {
|
||||
return p.pluginRuntime
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ static void so_call_onunload(void* fn) {
|
||||
typedef void (*fn_t)(void);
|
||||
((fn_t)fn)();
|
||||
}
|
||||
|
||||
static const char* so_call_getruntime(void* fn) {
|
||||
typedef const char* (*fn_t)(void);
|
||||
return ((fn_t)fn)();
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -145,21 +150,30 @@ func loadNativePlugin(data []byte) (NativePlugin, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginRT := "go"
|
||||
if getRuntimeFn, err := resolve("PluginGetRuntime"); err == nil {
|
||||
if cs := C.so_call_getruntime(getRuntimeFn); cs != nil {
|
||||
pluginRT = C.GoString(cs)
|
||||
}
|
||||
}
|
||||
|
||||
return &soPlugin{
|
||||
handle: handle,
|
||||
onLoadFn: onLoad,
|
||||
onEventFn: onEvent,
|
||||
onUnloadFn: onUnload,
|
||||
handle: handle,
|
||||
onLoadFn: onLoad,
|
||||
onEventFn: onEvent,
|
||||
onUnloadFn: onUnload,
|
||||
pluginRuntime: pluginRT,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type soPlugin struct {
|
||||
handle unsafe.Pointer
|
||||
onLoadFn unsafe.Pointer
|
||||
onEventFn unsafe.Pointer
|
||||
onUnloadFn unsafe.Pointer
|
||||
cbHandle cgo.Handle
|
||||
mu sync.Mutex
|
||||
handle unsafe.Pointer
|
||||
onLoadFn unsafe.Pointer
|
||||
onEventFn unsafe.Pointer
|
||||
onUnloadFn unsafe.Pointer
|
||||
cbHandle cgo.Handle
|
||||
pluginRuntime string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (p *soPlugin) Load(send func(string, []byte), hostInfo []byte) error {
|
||||
@@ -220,8 +234,14 @@ func (p *soPlugin) Close() error {
|
||||
p.cbHandle = 0
|
||||
}
|
||||
if p.handle != nil {
|
||||
C.so_dlclose(p.handle)
|
||||
if p.pluginRuntime != "go" {
|
||||
C.so_dlclose(p.handle)
|
||||
}
|
||||
p.handle = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *soPlugin) Runtime() string {
|
||||
return p.pluginRuntime
|
||||
}
|
||||
|
||||
@@ -43,12 +43,29 @@ func loadNativePlugin(data []byte) (NativePlugin, error) {
|
||||
|
||||
setCallback, _ := mm.GetExport("PluginSetCallback")
|
||||
|
||||
runtime := "go"
|
||||
if getRuntimeAddr, err := mm.GetExport("PluginGetRuntime"); err == nil {
|
||||
ret, _, _ := syscall.SyscallN(getRuntimeAddr)
|
||||
if ret != 0 {
|
||||
var buf [32]byte
|
||||
for i := range buf {
|
||||
b := *(*byte)(unsafe.Pointer(ret + uintptr(i)))
|
||||
if b == 0 {
|
||||
runtime = string(buf[:i])
|
||||
break
|
||||
}
|
||||
buf[i] = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dp := &dllPlugin{
|
||||
mem: mm,
|
||||
onLoadAddr: onLoad,
|
||||
onEventAddr: onEvent,
|
||||
onUnloadAddr: onUnload,
|
||||
setCallbackAddr: setCallback,
|
||||
pluginRuntime: runtime,
|
||||
}
|
||||
return dp, nil
|
||||
}
|
||||
@@ -60,6 +77,7 @@ type dllPlugin struct {
|
||||
onUnloadAddr uintptr
|
||||
setCallbackAddr uintptr
|
||||
callbackHandle uintptr // prevent GC of the callback closure
|
||||
pluginRuntime string // "go", "c", "cpp", etc.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -130,8 +148,17 @@ func (p *dllPlugin) Unload() {
|
||||
|
||||
func (p *dllPlugin) Close() error {
|
||||
p.Unload()
|
||||
// https://github.com/golang/go/issues/11100 as of right now you can't
|
||||
// unload a go c-shared DLL safely
|
||||
p.mem = nil
|
||||
if p.pluginRuntime != "go" {
|
||||
if p.mem != nil {
|
||||
p.mem.Free()
|
||||
p.mem = nil
|
||||
}
|
||||
} else {
|
||||
p.mem = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *dllPlugin) Runtime() string {
|
||||
return p.pluginRuntime
|
||||
}
|
||||
|
||||
@@ -184,7 +184,9 @@ func (m *Manager) Load(ctx context.Context, manifest PluginManifest, binary []by
|
||||
m.plugins[pluginID] = pi
|
||||
m.mu.Unlock()
|
||||
|
||||
log.Printf("[plugin] loaded %s (native)", pluginID)
|
||||
rt := np.Runtime()
|
||||
freeable := rt != "go"
|
||||
log.Printf("[plugin] loaded %s (native, runtime=%s, freeable=%v)", pluginID, rt, freeable)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -207,8 +209,14 @@ func (m *Manager) Close() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for id, pi := range m.plugins {
|
||||
rt := pi.native.Runtime()
|
||||
pi.native.Close()
|
||||
delete(m.plugins, id)
|
||||
if rt != "go" {
|
||||
log.Printf("[plugin] unloaded %s (runtime=%s, memory freed)", id, rt)
|
||||
} else {
|
||||
log.Printf("[plugin] unloaded %s (runtime=go, memory leaked — see golang/go#11100)", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +224,13 @@ func (m *Manager) Unload(pluginId string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if pi, ok := m.plugins[pluginId]; ok {
|
||||
rt := pi.native.Runtime()
|
||||
pi.native.Close()
|
||||
delete(m.plugins, pluginId)
|
||||
if rt != "go" {
|
||||
log.Printf("[plugin] unloaded %s (runtime=%s, memory freed)", pluginId, rt)
|
||||
} else {
|
||||
log.Printf("[plugin] unloaded %s (runtime=go, memory leaked — see golang/go#11100)", pluginId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ type NativePlugin interface {
|
||||
Unload()
|
||||
|
||||
Close() error
|
||||
|
||||
Runtime() string
|
||||
}
|
||||
|
||||
func ManifestFromMap(m map[string]interface{}) (PluginManifest, error) {
|
||||
|
||||
Reference in New Issue
Block a user