[Libguestfs] [PATCH nbdkit v2] Add the ability to write plugins in golang.
Daniel P. Berrangé
berrange at redhat.com
Tue Apr 21 16:07:43 UTC 2020
On Tue, Apr 21, 2020 at 11:44:59AM +0100, Richard W.M. Jones wrote:
> Thanks: Dan Berrangé
>
> XXX UNFINISHED:
>
> - Is using uintptr for the handle a good idea? Plugins must return
> something != 0. In other languages we would allow plugins to
> return an arbitrary object here, but this is not possible in golang
> because of lack of GC roots.
Yeah, this is the bit that looks wierd to me when thinking about this
from a Go dev POV. You asked me on IRC whether we should have a separate
interface for a connection object, and this makes me think that we should
indeed do that.
Of course we still have the problem that C code is forbidden to keep
a pointer to a Go object across method calls.
IIUC, the C layuer in nbdkit treats the "void *" as a completely
opaque value, so we don't actually have to store any valid pointer
as the handle at all. It can be literally any value that fits in
a 32-bit pointer. Thus we can keep a global variable mapping the
GO objects to fake C "pointers"
So it would look like this:
// The plugin interface.
type PluginInterface interface {
// Open, GetSize and PRead are required for all plugins.
// Other methods are optional.
Config(key string, value string) error
ConfigComplete() error
Open(readonly bool) (PluginConnectionInterface, error)
}
type PluginConnectionInterface interface {
// Open, GetSize and PRead are required for all plugins.
// Other methods are optional.
Close()
GetSize() (uint64, error)
PRead(buf []byte, offset uint64, flags uint32) error
}
Now, we need todo some mapping
var connectionID uint
var connectionMap map[uint]PluginConnectionInterface
//export implOpen
func implOpen(c_readonly C.int) unsafe.Pointer {
readonly := false
if c_readonly != 0 {
readonly = true
}
h, err := pluginImpl.Open(readonly)
if err != nil {
set_error(err)
return nil
}
if h == 0 {
panic("Open method: handle must be != 0")
}
id := connectionID++
connectionMap[id] = h
return unsafe.Pointer(uintptr(id))
}
func getConn(handle unsafe.Pointer) PluginConnectionInterface {
id := uint(uintptr(handle))
conn, ok = connectionMap[id]
if !ok {
panic("Connection %d was not open", id)
}
}
//export implClose
func implClose(handle unsafe.Pointer) {
conn := getConn(handle)
conn.Close()
delete(connectionMap, id)
}
//export implGetSize
func implGetSize(handle unsafe.Pointer) C.int64_t {
conn := getConn(handle)
size, err := conn.GetSize()
if err != nil {
set_error(err)
return -1
}
return C.int64_t(size)
}
> diff --git a/plugins/golang/test/test.go b/plugins/golang/test/test.go
> new file mode 100644
> index 00000000..f5b1a33b
> --- /dev/null
> +++ b/plugins/golang/test/test.go
type TestPlugin struct {
nbdkit.Plugin
}
type TestPluginConnection struct {
nbdkit.PluginConnection
...other per-connection fields...
}
var pluginName = "test"
var size uint64
var size_set = false
func (p TestPlugin) Config(key string, value string) error {
if key == "size" {
var err error
size, err = strconv.ParseUint(value, 0, 64)
if err != nil {
return err
}
size_set = true
return nil
} else {
return nbdkit.PluginError{Errmsg: "unknown parameter"}
}
}
func (p TestPlugin) ConfigComplete() error {
if !size_set {
>+ return nbdkit.PluginError{Errmsg: "size parameter is required"}
}
return nil
}
func (p TestPlugin) Open(readonly bool) (nbdkit.PluginConnectionInterface, error) {
nbdkit.Debug("golang code running in the .open callback")
return &TestPluginConnection{}, nil
}
func (p TestPluginConnection) GetSize() (uint64, error) {
nbdkit.Debug("golang code running in the .get_size callback")
return size, nil
}
func (p TestPluginConnection) PRead(buf []byte, offset uint64, flags uint32) error {
nbdkit.Debug("golang code running in the .pread callback")
for i := 0; i < len(buf); i++ {
buf[i] = 0
}
return nil
}
Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
More information about the Libguestfs
mailing list