NAME

nbdkit-golang-plugin - writing nbdkit plugins in Go

SYNOPSIS

 nbdkit /path/to/plugin.so [arguments...]

DESCRIPTION

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.

IMPORTANT NOTE

Go plugins do not support nbdkit forking into the background (ie. the default behaviour if -f is not used; or if --run is used). Therefore you must use the -f option (or options which imply it, see nbdkit(1)).

Fixing this would require changes to the Go runtime. For details see https://gitlab.com/nbdkit/nbdkit/-/issues/29#note_2370463738

WRITING A GOLANG NBDKIT PLUGIN

For examples of plugins written in Go, see: https://gitlab.com/nbdkit/nbdkit/blob/master/plugins/golang/examples

minimal/minimal.go 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.

Most Golang nbdkit plugins should start with:

 package main

 import (
        "C"
        "libguestfs.org/nbdkit"
        "unsafe"
 )

Plugins have to be main packages, because that's how Golang shared libraries work.

 var pluginName = "myname"

 type MyNamePlugin struct {
        nbdkit.Plugin
 }

The plugin struct is used to define the global callbacks, like Load, Config and GetReady.

 type MyNameConnection struct {
        nbdkit.Connection
 }

The connection struct represents a client connection and is used to define connection callbacks like GetSize and PRead.

What connects Plugin and Connection 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 *MyNamePlugin) Load() {
         // global callback used for initializing the plugin
 }

 func (p *MyNamePlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
         // new client has connected
         return &MyNameConnection{}, nil
 }

 func (c *MyNameConnection) GetSize() (uint64, error) {
         // called per-connection
         return virtual_size, nil
 }

 func (c *MyNameConnection) PRead(buf []byte, offset uint64,
        flags uint32) error {
        // copy data into buf here
        return nil
 }

You can store per-connection data in the connection struct. Global data could be stored as global variables or kept in the plugin struct as you wish.

Most plugins should finish off with this boilerplate:

 //export plugin_init
 func plugin_init() unsafe.Pointer {
        // If your plugin needs to do any initialization, you can
        // either put it here or implement a Load() method.
        // ...

        // Then you must call the following function.
        return nbdkit.PluginInitialize(pluginName, &MyNamePlugin{})
 }

 // This is never(?) called, but must exist.
 func main() {}

Compiling the plugin

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* callbacks

Important: 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), Zero (CanZero) and Extents (CanExtents).

 func (c *MyNameConnection) CanWrite() (bool, error) {
         return true, nil
 }

 func (c *MyNameConnection) PWrite(buf []byte, offset uint64,
        flags uint32) error {
         // ...
 }

CanFUA callback and writes

If you do not define CanFUA then the default mode will be FUANone (NBDKIT_FUA_NONE). This means that FUA requests will never be sent, nor emulated.

Therefore if you are implementing writes you generally need to define CanFUA:

Missing callbacks

The following callbacks are not yet implemented:

can_fast_zero
thread_model

At the moment the thread model is always NBDKIT_THREAD_MODEL_PARALLEL.

VERSION

Golang plugins first appeared in nbdkit 1.20.

SEE ALSO

nbdkit(1), nbdkit-plugin(3).

AUTHORS

Richard W.M. Jones

COPYRIGHT

Copyright Red Hat

LICENSE

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

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.