For the current set of examples this doesn't matter. This also adds
another example where we use the Connection to store data, just to
check that actually works.
Thanks: James @ purpleidea, Dan Berrangé
---
plugins/golang/nbdkit-golang-plugin.pod | 10 +-
plugins/golang/Makefile.am | 9 +
plugins/golang/examples/disk/disk.go | 168 ++++++++++++++++++
.../golang/examples/dump-plugin/dumpplugin.go | 8 +-
plugins/golang/examples/minimal/minimal.go | 6 +-
plugins/golang/examples/ramdisk/ramdisk.go | 18 +-
.../src/libguestfs.org/nbdkit/nbdkit.go | 42 ++---
plugins/golang/test/test.go | 14 +-
8 files changed, 226 insertions(+), 49 deletions(-)
diff --git a/plugins/golang/nbdkit-golang-plugin.pod
b/plugins/golang/nbdkit-golang-plugin.pod
index b15bb481..90732289 100644
--- a/plugins/golang/nbdkit-golang-plugin.pod
+++ b/plugins/golang/nbdkit-golang-plugin.pod
@@ -62,16 +62,16 @@ C<GetSize> and C<PRead>. What connects the two is the
C<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() {
+ func (p *MyPlugin) Load() {
// global callback used for initializing the plugin
}
- func (p MyPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
+ func (p *MyPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
// new client has connected
return &MyConnection{}, nil
}
- func (c MyConnection) GetSize() (uint64, error) {
+ func (c *MyConnection) GetSize() (uint64, error) {
// called per-client
return virtual_size, nil
}
@@ -106,11 +106,11 @@ unless you also implement a C<CanWrite> callback that returns
true.
The same applies to C<Flush> (C<CanFlush>), C<Trim> (C<CanTrim>)
and
C<Zero> (C<CanZero>).
- func (c MyConnection) CanWrite() (bool, error) {
+ func (c *MyConnection) CanWrite() (bool, error) {
return true, nil
}
- func (c MyConnection) PWrite(buf []byte, offset uint64,
+ func (c *MyConnection) PWrite(buf []byte, offset uint64,
flags uint32) error {
// ...
}
diff --git a/plugins/golang/Makefile.am b/plugins/golang/Makefile.am
index 74ad4a72..91711724 100644
--- a/plugins/golang/Makefile.am
+++ b/plugins/golang/Makefile.am
@@ -42,6 +42,7 @@ EXTRA_DIST = \
$(plugin_sources) \
config-test.go \
dump-plugin-examples.sh \
+ examples/disk/disk.go \
examples/dump-plugin/dumpplugin.go \
examples/minimal/minimal.go \
examples/ramdisk/ramdisk.go \
@@ -58,11 +59,19 @@ if HAVE_GOLANG
# Examples.
noinst_DATA = \
+ examples/disk/nbdkit-godisk-plugin.so \
examples/dump-plugin/nbdkit-godump-plugin.so \
examples/minimal/nbdkit-gominimal-plugin.so \
examples/ramdisk/nbdkit-goramdisk-plugin.so \
$(NULL)
+examples/disk/nbdkit-godisk-plugin.so: \
+ $(plugin_sources) examples/disk/disk.go
+ cd examples/disk && \
+ PKG_CONFIG_PATH="$(abs_top_builddir)/server/local$${PKG_CONFIG_PATH:+:$$PKG_CONFIG_PATH}"
\
+ GOPATH="$(abs_builddir)" \
+ $(GOLANG) build -o nbdkit-godisk-plugin.so -buildmode=c-shared
+
examples/dump-plugin/nbdkit-godump-plugin.so: \
$(plugin_sources) examples/dump-plugin/dumpplugin.go
cd examples/dump-plugin && \
diff --git a/plugins/golang/examples/disk/disk.go b/plugins/golang/examples/disk/disk.go
new file mode 100644
index 00000000..37f449e0
--- /dev/null
+++ b/plugins/golang/examples/disk/disk.go
@@ -0,0 +1,168 @@
+/* Example plugin.
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * 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.
+ */
+
+package main
+
+import (
+ "C"
+ "io/ioutil"
+ "libguestfs.org/nbdkit"
+ "os"
+ "strconv"
+ "unsafe"
+)
+
+var pluginName = "disk"
+
+type DiskPlugin struct {
+ nbdkit.Plugin
+}
+
+type DiskConnection struct {
+ nbdkit.Connection
+ fd *os.File
+}
+
+var size uint64
+var size_set = false
+
+func (p *DiskPlugin) 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 *DiskPlugin) ConfigComplete() error {
+ if !size_set {
+ return nbdkit.PluginError{Errmsg: "size parameter is required"}
+ }
+ return nil
+}
+
+func (p *DiskPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
+ // Open a temporary file.
+ fd, err := ioutil.TempFile("/var/tmp", "nbdkitdisk")
+ if err != nil {
+ return nil, err
+ }
+ os.Remove(fd.Name())
+
+ // Truncate it to the right size.
+ err = fd.Truncate(int64(size))
+ if err != nil {
+ return nil, err
+ }
+
+ // Store the file descriptor of the temporary file in the
+ // Connection struct.
+ return &DiskConnection{fd: fd}, nil
+}
+
+func (c *DiskConnection) Close() {
+ c.fd.Close()
+}
+
+func (c *DiskConnection) GetSize() (uint64, error) {
+ // Return the size of the disk. We could just return the
+ // global "size" here, but make the example more interesting.
+ info, err := c.fd.Stat()
+ if err != nil {
+ return 0, err
+ }
+ return uint64(info.Size()), nil
+}
+
+// Multi-conn is NOT safe because each client sees a different disk.
+func (c *DiskConnection) CanMultiConn() (bool, error) {
+ return false, nil
+}
+
+func (c *DiskConnection) PRead(buf []byte, offset uint64,
+ flags uint32) error {
+ n, err := c.fd.ReadAt(buf, int64(offset))
+ if err != nil {
+ return err
+ }
+ // NBD requests must always read/write the whole requested
+ // amount, or else fail. Actually we should loop here (XXX).
+ if n != len(buf) {
+ return nbdkit.PluginError{Errmsg: "short read"}
+ }
+ return nil
+}
+
+// Note that CanWrite is required in golang plugins, otherwise PWrite
+// will never be called.
+func (c *DiskConnection) CanWrite() (bool, error) {
+ return true, nil
+}
+
+func (c *DiskConnection) PWrite(buf []byte, offset uint64,
+ flags uint32) error {
+ n, err := c.fd.WriteAt(buf, int64(offset))
+ if err != nil {
+ return err
+ }
+ // NBD requests must always read/write the whole requested
+ // amount, or else fail. Actually we should loop here (XXX).
+ if n != len(buf) {
+ return nbdkit.PluginError{Errmsg: "short write"}
+ }
+ return nil
+}
+
+//----------------------------------------------------------------------
+//
+// The boilerplate below this line is required by all golang plugins,
+// as well as importing "C" and "unsafe" modules at the top of the
+// file.
+
+//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, &DiskPlugin{})
+}
+
+// This is never(?) called, but must exist.
+func main() {}
diff --git a/plugins/golang/examples/dump-plugin/dumpplugin.go
b/plugins/golang/examples/dump-plugin/dumpplugin.go
index 518be078..d30ec3f1 100644
--- a/plugins/golang/examples/dump-plugin/dumpplugin.go
+++ b/plugins/golang/examples/dump-plugin/dumpplugin.go
@@ -51,19 +51,19 @@ type DumpConnection struct {
var size uint64 = 1024 * 1024
-func (p DumpPlugin) DumpPlugin() {
+func (p *DumpPlugin) DumpPlugin() {
fmt.Println("golang_dump_plugin=1")
}
-func (p DumpPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
+func (p *DumpPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
return &DumpConnection{}, nil
}
-func (c DumpConnection) GetSize() (uint64, error) {
+func (c *DumpConnection) GetSize() (uint64, error) {
return size, nil
}
-func (c DumpConnection) PRead(buf []byte, offset uint64,
+func (c *DumpConnection) PRead(buf []byte, offset uint64,
flags uint32) error {
for i := 0; i < len(buf); i++ {
buf[i] = 0
diff --git a/plugins/golang/examples/minimal/minimal.go
b/plugins/golang/examples/minimal/minimal.go
index e010bfcb..0f862889 100644
--- a/plugins/golang/examples/minimal/minimal.go
+++ b/plugins/golang/examples/minimal/minimal.go
@@ -50,15 +50,15 @@ type MinimalConnection struct {
var size uint64 = 1024 * 1024
-func (p MinimalPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
+func (p *MinimalPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
return &MinimalConnection{}, nil
}
-func (c MinimalConnection) GetSize() (uint64, error) {
+func (c *MinimalConnection) GetSize() (uint64, error) {
return size, nil
}
-func (c MinimalConnection) PRead(buf []byte, offset uint64,
+func (c *MinimalConnection) PRead(buf []byte, offset uint64,
flags uint32) error {
for i := 0; i < len(buf); i++ {
buf[i] = 0
diff --git a/plugins/golang/examples/ramdisk/ramdisk.go
b/plugins/golang/examples/ramdisk/ramdisk.go
index c848f575..78cb1f4c 100644
--- a/plugins/golang/examples/ramdisk/ramdisk.go
+++ b/plugins/golang/examples/ramdisk/ramdisk.go
@@ -53,7 +53,7 @@ var size uint64
var size_set = false
var disk []byte
-func (p RAMDiskPlugin) Config(key string, value string) error {
+func (p *RAMDiskPlugin) Config(key string, value string) error {
if key == "size" {
var err error
size, err = strconv.ParseUint(value, 0, 64)
@@ -67,33 +67,33 @@ func (p RAMDiskPlugin) Config(key string, value string) error {
}
}
-func (p RAMDiskPlugin) ConfigComplete() error {
+func (p *RAMDiskPlugin) ConfigComplete() error {
if !size_set {
return nbdkit.PluginError{Errmsg: "size parameter is required"}
}
return nil
}
-func (p RAMDiskPlugin) GetReady() error {
+func (p *RAMDiskPlugin) GetReady() error {
// Allocate the RAM disk.
disk = make([]byte, size)
return nil
}
-func (p RAMDiskPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
+func (p *RAMDiskPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
return &RAMDiskConnection{}, nil
}
-func (c RAMDiskConnection) GetSize() (uint64, error) {
+func (c *RAMDiskConnection) GetSize() (uint64, error) {
return size, nil
}
// Clients are allowed to make multiple connections safely.
-func (c RAMDiskConnection) CanMultiConn() (bool, error) {
+func (c *RAMDiskConnection) CanMultiConn() (bool, error) {
return true, nil
}
-func (c RAMDiskConnection) PRead(buf []byte, offset uint64,
+func (c *RAMDiskConnection) PRead(buf []byte, offset uint64,
flags uint32) error {
copy(buf, disk[offset:int(offset)+len(buf)])
return nil
@@ -101,11 +101,11 @@ func (c RAMDiskConnection) PRead(buf []byte, offset uint64,
// Note that CanWrite is required in golang plugins, otherwise PWrite
// will never be called.
-func (c RAMDiskConnection) CanWrite() (bool, error) {
+func (c *RAMDiskConnection) CanWrite() (bool, error) {
return true, nil
}
-func (c RAMDiskConnection) PWrite(buf []byte, offset uint64,
+func (c *RAMDiskConnection) PWrite(buf []byte, offset uint64,
flags uint32) error {
copy(disk[offset:int(offset)+len(buf)], buf)
return nil
diff --git
a/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
b/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
index 155034d9..8e9e9bbd 100644
---
a/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
+++
b/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
@@ -145,83 +145,83 @@ type ConnectionInterface interface {
type Plugin struct{}
type Connection struct{}
-func (p Plugin) Load() {
+func (p *Plugin) Load() {
}
-func (p Plugin) Unload() {
+func (p *Plugin) Unload() {
}
-func (p Plugin) DumpPlugin() {
+func (p *Plugin) DumpPlugin() {
}
-func (p Plugin) Config(key string, value string) error {
+func (p *Plugin) Config(key string, value string) error {
return nil
}
-func (p Plugin) ConfigComplete() error {
+func (p *Plugin) ConfigComplete() error {
return nil
}
-func (p Plugin) GetReady() error {
+func (p *Plugin) GetReady() error {
return nil
}
-func (p Plugin) PreConnect(readonly bool) error {
+func (p *Plugin) PreConnect(readonly bool) error {
return nil
}
-func (p Plugin) Open(readonly bool) (ConnectionInterface, error) {
+func (p *Plugin) Open(readonly bool) (ConnectionInterface, error) {
panic("plugin must implement Open()")
}
-func (c Connection) Close() {
+func (c *Connection) Close() {
}
-func (c Connection) GetSize() (uint64, error) {
+func (c *Connection) GetSize() (uint64, error) {
panic("plugin must implement GetSize()")
}
-func (c Connection) CanWrite() (bool, error) {
+func (c *Connection) CanWrite() (bool, error) {
return false, nil
}
-func (c Connection) CanFlush() (bool, error) {
+func (c *Connection) CanFlush() (bool, error) {
return false, nil
}
-func (c Connection) IsRotational() (bool, error) {
+func (c *Connection) IsRotational() (bool, error) {
return false, nil
}
-func (c Connection) CanTrim() (bool, error) {
+func (c *Connection) CanTrim() (bool, error) {
return false, nil
}
-func (c Connection) CanZero() (bool, error) {
+func (c *Connection) CanZero() (bool, error) {
return false, nil
}
-func (c Connection) CanMultiConn() (bool, error) {
+func (c *Connection) CanMultiConn() (bool, error) {
return false, nil
}
-func (c Connection) PRead(buf []byte, offset uint64, flags uint32) error {
+func (c *Connection) PRead(buf []byte, offset uint64, flags uint32) error {
panic("plugin must implement PRead()")
}
-func (c Connection) PWrite(buf []byte, offset uint64, flags uint32) error {
+func (c *Connection) PWrite(buf []byte, offset uint64, flags uint32) error {
panic("plugin CanWrite returns true, but no PWrite() function")
}
-func (c Connection) Flush(flags uint32) error {
+func (c *Connection) Flush(flags uint32) error {
panic("plugin CanFlush returns true, but no Flush() function")
}
-func (c Connection) Trim(count uint32, offset uint64, flags uint32) error {
+func (c *Connection) Trim(count uint32, offset uint64, flags uint32) error {
panic("plugin CanTrim returns true, but no Trim() function")
}
-func (c Connection) Zero(count uint32, offset uint64, flags uint32) error {
+func (c *Connection) Zero(count uint32, offset uint64, flags uint32) error {
panic("plugin CanZero returns true, but no Zero() function")
}
diff --git a/plugins/golang/test/test.go b/plugins/golang/test/test.go
index 7186ffa8..e585a971 100644
--- a/plugins/golang/test/test.go
+++ b/plugins/golang/test/test.go
@@ -52,15 +52,15 @@ type TestConnection struct {
var size uint64
var size_set = false
-func (p TestPlugin) Load() {
+func (p *TestPlugin) Load() {
nbdkit.Debug("golang code running in the .load callback")
}
-func (p TestPlugin) Unload() {
+func (p *TestPlugin) Unload() {
nbdkit.Debug("golang code running in the .unload callback")
}
-func (p TestPlugin) Config(key string, value string) error {
+func (p *TestPlugin) Config(key string, value string) error {
if key == "size" {
var err error
size, err = strconv.ParseUint(value, 0, 64)
@@ -74,24 +74,24 @@ func (p TestPlugin) Config(key string, value string) error {
}
}
-func (p TestPlugin) ConfigComplete() error {
+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.ConnectionInterface, error) {
+func (p *TestPlugin) Open(readonly bool) (nbdkit.ConnectionInterface, error) {
nbdkit.Debug("golang code running in the .open callback")
return &TestConnection{}, nil
}
-func (c TestConnection) GetSize() (uint64, error) {
+func (c *TestConnection) GetSize() (uint64, error) {
nbdkit.Debug("golang code running in the .get_size callback")
return size, nil
}
-func (c TestConnection) PRead(buf []byte, offset uint64, flags uint32) error {
+func (c *TestConnection) 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
--
2.25.0