nbdkit-golang-plugin - writing nbdkit plugins in Go
nbdkit /path/to/plugin.so [arguments...]
This manual page describes how to write nbdkit plugins in compiled Golang code. Go plugins are compiled to *.so files (the same as plugins written in C) and are used in the same way.
For examples of plugins written in Go, see: https://gitlab.com/nbdkit/nbdkit/blob/master/plugins/golang/examples
The minimal/minimal.go example is the smallest possible plugin, implementing only the required callbacks (Open
, GetSize
and PRead
). Other examples show other nbdkit features.
Broadly speaking, Golang nbdkit plugins work like C ones, so you should read nbdkit-plugin(3) first.
To write a Golang nbdkit plugin, you must:
Set the package to main
Plugins should be main packages, because that's how Golang shared libraries work.
Import "C"
, "unsafe"
and "libguestfs.org/nbdkit"
Create plugin and connection structs
Conventionally these are named after the plugin, but you can call them whatever you want. They must derive from the nbdkit.Plugin
and nbdkit.Connection
structs respectively:
type MyPlugin struct {
nbdkit.Plugin
}
type MyConnection struct {
nbdkit.Connection
}
The plugin struct is used to define the global callbacks, like Load
, Config
and GetReady
. The connection struct represents a client connection and is used to define connection callbacks like GetSize
and PRead
. What connects the two is the Open
callback which is called when the client has connected and where you should return a new instance of your connection struct. For example:
func (p *MyPlugin) Load() {
// global callback used for initializing the plugin
}
func (p *MyPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
// new client has connected
return &MyConnection{}, nil
}
func (c *MyConnection) GetSize() (uint64, error) {
// called per-client
return virtual_size, nil
}
You can store per-client data in the connection struct. Global data could be stored as global variables or kept in the plugin struct as you wish.
Include the plugin_init
and main
boilerplate
See the example plugins for the correct boilerplate to include.
Compile the plugin to a shared library
go build -o nbdkit-mygolang-plugin.so -buildmode=c-shared
This builds the shared object (*.so) which is the plugin, and also a header file (*.h) which is irrelevant and you can delete.
Can*
callbacksImportant: If you implement PWrite
it will not be called unless you also implement a CanWrite
callback that returns true. (This is different from plugins written in C or other languages).
The same applies to Flush
(CanFlush
), Trim
(CanTrim
) and Zero
(CanZero
).
func (c *MyConnection) CanWrite() (bool, error) {
return true, nil
}
func (c *MyConnection) PWrite(buf []byte, offset uint64,
flags uint32) error {
// ...
}
The following callbacks are not yet implemented:
version
longname
description
magic_config_key
config_help
thread_model
At the moment the thread model is always NBDKIT_THREAD_MODEL_PARALLEL
.
can_fast_zero
can_extents
can_fua
can_cache
extents
cache
Golang plugins first appeared in nbdkit 1.20.
Richard W.M. Jones
Copyright Red Hat
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of Red Hat nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.