[Libguestfs] [PATCH nbdkit v2] Add the ability to write plugins in golang.
Richard W.M. Jones
rjones at redhat.com
Thu Apr 23 09:03:45 UTC 2020
On Tue, Apr 21, 2020 at 05:07:43PM +0100, Daniel P. Berrangé wrote:
> 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.
...
This is what the patch looks like with a separate connection object.
It appears to work.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine. Supports Linux and Windows.
http://people.redhat.com/~rjones/virt-df/
-------------- next part --------------
>From a929cff8745f21df913ea3230741dc7f2a1c016e Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones at redhat.com>
Date: Thu, 9 Apr 2020 12:45:10 +0100
Subject: [PATCH] Add the ability to write plugins in golang.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Thanks: Dan Berrangé
XXX UNFINISHED:
- Default can_* methods are hard to implement. Ideally we would be
able to test if a user plugin implements a particular callback
(eg. TestPlugin.PWrite) or else defaults to the default callback,
but even with reflection I don't think this is possible in golang.
[Discussed on IRC: We decided the only option was to go for
nbdkit-sh-plugin style can_* methods]
- Write wrappers etc for all the other methods.
- Write documentation.
---
plugins/golang/nbdkit-golang-plugin.pod | 34 +++
configure.ac | 32 +++
plugins/golang/Makefile.am | 80 ++++++
.../src/libguestfs.org/nbdkit/wrappers.h | 41 +++
plugins/golang/config-test.go | 38 +++
.../src/libguestfs.org/nbdkit/nbdkit.go | 270 ++++++++++++++++++
.../golang/src/libguestfs.org/nbdkit/utils.go | 75 +++++
.../src/libguestfs.org/nbdkit/wrappers.go | 102 +++++++
plugins/golang/test/run-test.sh | 42 +++
plugins/golang/test/test.go | 119 ++++++++
.gitignore | 2 +
README | 4 +
12 files changed, 839 insertions(+)
create mode 100644 plugins/golang/nbdkit-golang-plugin.pod
create mode 100644 plugins/golang/Makefile.am
create mode 100644 plugins/golang/src/libguestfs.org/nbdkit/wrappers.h
create mode 100644 plugins/golang/config-test.go
create mode 100644 plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
create mode 100644 plugins/golang/src/libguestfs.org/nbdkit/utils.go
create mode 100644 plugins/golang/src/libguestfs.org/nbdkit/wrappers.go
create mode 100755 plugins/golang/test/run-test.sh
create mode 100644 plugins/golang/test/test.go
diff --git a/plugins/golang/nbdkit-golang-plugin.pod b/plugins/golang/nbdkit-golang-plugin.pod
new file mode 100644
index 00000000..b449a830
--- /dev/null
+++ b/plugins/golang/nbdkit-golang-plugin.pod
@@ -0,0 +1,34 @@
+=head1 NAME
+
+nbdkit-golang-plugin - writing nbdkit plugins in Go
+
+=head1 SYNOPSIS
+
+ nbdkit /path/to/plugin.so [arguments...]
+
+=head1 DESCRIPTION
+
+This manual page describes how to write nbdkit plugins in compiled
+Golang code. Go plugins are compiled to F<*.so> files (the same as
+plugins written in C) and are used in the same way.
+
+XXX MORE DOCS
+
+
+
+=head1 VERSION
+
+Golang plugins first appeared in nbdkit 1.20.
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-plugin(3)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2020 Red Hat Inc.
diff --git a/configure.ac b/configure.ac
index c1aec8fa..7fbf7abe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,7 @@ LT_INIT
dnl List of plugins and filters.
lang_plugins="\
+ golang \
lua \
ocaml \
perl \
@@ -754,6 +755,34 @@ AS_IF([test "x$enable_lua" != "xno"],[
])
AM_CONDITIONAL([HAVE_LUA],[test "x$enable_lua" = "xyes"])
+dnl Check for golang.
+AC_ARG_ENABLE([golang],
+ AS_HELP_STRING([--disable-golang], [disable Go language plugin]),
+ [],
+ [enable_golang=yes])
+AS_IF([test "x$enable_golang" != "xno"],[
+ AC_CHECK_PROG([GOLANG],[go],[go],[no])
+ AS_IF([test "x$GOLANG" != "xno"],[
+ AC_MSG_CHECKING([if $GOLANG is usable])
+ AS_IF([$GOLANG run $srcdir/plugins/golang/config-test.go 2>&AS_MESSAGE_LOG_FD],[
+ AC_MSG_RESULT([yes])
+
+ # Substitute some golang environment.
+ GOOS=`$GOLANG env GOOS`
+ GOARCH=`$GOLANG env GOARCH`
+ GOROOT=`$GOLANG env GOROOT`
+ AC_SUBST([GOOS])
+ AC_SUBST([GOARCH])
+ AC_SUBST([GOROOT])
+ ],[
+ AC_MSG_RESULT([no])
+ AC_MSG_WARN([golang ($GOLANG) is installed but not usable])
+ GOLANG=no
+ ])
+ ])
+],[GOLANG=no])
+AM_CONDITIONAL([HAVE_GOLANG],[test "x$GOLANG" != "xno"])
+
dnl Check for curl (only if you want to compile the curl plugin).
AC_ARG_WITH([curl],
[AS_HELP_STRING([--without-curl],
@@ -1002,6 +1031,7 @@ AC_CONFIG_FILES([Makefile
plugins/file/Makefile
plugins/floppy/Makefile
plugins/full/Makefile
+ plugins/golang/Makefile
plugins/guestfs/Makefile
plugins/gzip/Makefile
plugins/info/Makefile
@@ -1125,6 +1155,8 @@ feature "vddk ................................... " \
echo
echo "Languages:"
echo
+feature "go ..................................... " \
+ test "x$HAVE_GOLANG_TRUE" = "x"
feature "lua .................................... " \
test "x$HAVE_LUA_TRUE" = "x"
feature "ocaml .................................. " \
diff --git a/plugins/golang/Makefile.am b/plugins/golang/Makefile.am
new file mode 100644
index 00000000..4f69f03f
--- /dev/null
+++ b/plugins/golang/Makefile.am
@@ -0,0 +1,80 @@
+# nbdkit
+# Copyright (C) 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.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = \
+ nbdkit-golang-plugin.pod \
+ src/libguestfs.org/nbdkit/nbdkit.go \
+ src/libguestfs.org/nbdkit/utils.go \
+ src/libguestfs.org/nbdkit/wrappers.go \
+ src/libguestfs.org/nbdkit/wrappers.h \
+ test/run-test.sh \
+ test/test.go \
+ $(NULL)
+
+if HAVE_GOLANG
+
+# There is nothing to build. Everything is statically compiled and
+# linked together when we compile the test.
+
+TESTS = test/run-test.sh
+check_DATA = test/nbdkit-gotest-plugin.so
+
+test/nbdkit-gotest-plugin.so: \
+ src/libguestfs.org/nbdkit/nbdkit.go \
+ src/libguestfs.org/nbdkit/utils.go \
+ src/libguestfs.org/nbdkit/wrappers.go \
+ src/libguestfs.org/nbdkit/wrappers.h \
+ test/test.go
+ cd test && \
+ GOPATH="$(abs_builddir)" \
+ $(GOLANG) build \
+ -o nbdkit-gotest-plugin.so -buildmode=c-shared
+
+CLEANFILES += \
+ test/nbdkit-gotest-plugin.h \
+ test/nbdkit-gotest-plugin.so \
+ $(NULL)
+
+if HAVE_POD
+
+man_MANS = nbdkit-golang-plugin.3
+CLEANFILES += $(man_MANS)
+
+nbdkit-golang-plugin.3: nbdkit-golang-plugin.pod
+ $(PODWRAPPER) --section=3 --man $@ \
+ --html $(top_builddir)/html/$@.html \
+ $<
+
+endif HAVE_POD
+
+endif HAVE_GOLANG
diff --git a/plugins/golang/src/libguestfs.org/nbdkit/wrappers.h b/plugins/golang/src/libguestfs.org/nbdkit/wrappers.h
new file mode 100644
index 00000000..efa92623
--- /dev/null
+++ b/plugins/golang/src/libguestfs.org/nbdkit/wrappers.h
@@ -0,0 +1,41 @@
+/* cgo wrappers.
+ * 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.
+ */
+
+extern void wrapper_load ();
+extern void wrapper_unload ();
+extern int wrapper_config (const char *key, const char *value);
+extern int wrapper_config_complete (void);
+extern void * wrapper_open (int readonly);
+extern void wrapper_close (void *handle);
+extern int64_t wrapper_get_size (void *handle);
+extern int wrapper_pread (void *handle, void *buf,
+ uint32_t count, uint64_t offset, uint32_t flags);
diff --git a/plugins/golang/config-test.go b/plugins/golang/config-test.go
new file mode 100644
index 00000000..0f5cfe6b
--- /dev/null
+++ b/plugins/golang/config-test.go
@@ -0,0 +1,38 @@
+/* Go configuration test
+ * 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.
+ */
+
+/* Used by ./configure to check golang is functional. */
+
+package main
+
+func main() {
+}
diff --git a/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go b/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
new file mode 100644
index 00000000..cddf64a5
--- /dev/null
+++ b/plugins/golang/src/libguestfs.org/nbdkit/nbdkit.go
@@ -0,0 +1,270 @@
+/* Go helper functions.
+ * 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 nbdkit
+
+/*
+#cgo pkg-config: nbdkit
+#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NBDKIT_API_VERSION 2
+#include <nbdkit-plugin.h>
+#include "wrappers.h"
+*/
+import "C"
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+// The plugin may raise errors by returning this struct (instead of nil).
+type PluginError struct {
+ Errmsg string // string (passed to nbdkit_error)
+ Errno syscall.Errno // errno (optional, use 0 if not available)
+}
+
+func (e PluginError) String() string {
+ if e.Errno != 0 {
+ return e.Errmsg
+ } else {
+ return fmt.Sprintf("%s (errno %d)", e.Errmsg, e.Errno)
+ }
+}
+
+func (e PluginError) Error() string {
+ return e.String()
+}
+
+// The plugin interface.
+type PluginInterface interface {
+ // Open is required for all plugins.
+ // Other methods are optional.
+ Load()
+ Unload()
+ Config(key string, value string) error
+ ConfigComplete() error
+ Open(readonly bool) (ConnectionInterface, error)
+}
+
+// The client connection interface.
+type ConnectionInterface interface {
+ // GetSize and PRead are required for all plugins.
+ // Other methods are optional.
+ Close()
+ GetSize() (uint64, error)
+ PRead(buf []byte, offset uint64, flags uint32) error
+}
+
+// Default implementations for plugin interface methods.
+type Plugin struct{}
+type Connection struct{}
+
+func (p Plugin) Load() {
+}
+
+func (p Plugin) Unload() {
+}
+
+func (p Plugin) Config(key string, value string) error {
+ return nil
+}
+
+func (p Plugin) ConfigComplete() error {
+ return nil
+}
+
+func (p Plugin) Open(readonly bool) (ConnectionInterface, error) {
+ panic("plugin must implement Open()")
+}
+
+func (c Connection) Close() {
+}
+
+func (c Connection) GetSize() (uint64, error) {
+ panic("plugin must implement GetSize()")
+}
+
+func (c Connection) PRead(buf []byte, offset uint64, flags uint32) error {
+ panic("plugin must implement PRead()")
+}
+
+// The implementation of the user plugin.
+var pluginImpl PluginInterface
+var nextConnectionId uintptr
+var connectionMap map[uintptr]ConnectionInterface
+
+// Callbacks from the server. These translate C to Go and back.
+
+func set_error(err error) {
+ perr, ok := err.(PluginError)
+ if ok {
+ if perr.Errno != 0 {
+ SetError(perr.Errno)
+ }
+ Error(perr.Errmsg)
+ } else {
+ Error(err.Error())
+ }
+}
+
+//export implLoad
+func implLoad() {
+ pluginImpl.Load()
+}
+
+//export implUnload
+func implUnload() {
+ pluginImpl.Unload()
+}
+
+//export implConfig
+func implConfig(key *C.char, value *C.char) C.int {
+ err := pluginImpl.Config(C.GoString(key), C.GoString(value))
+ if err != nil {
+ set_error(err)
+ return -1
+ }
+ return 0
+}
+
+//export implConfigComplete
+func implConfigComplete() C.int {
+ err := pluginImpl.ConfigComplete()
+ if err != nil {
+ set_error(err)
+ return -1
+ }
+ return 0
+}
+
+//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
+ }
+ id := nextConnectionId
+ nextConnectionId++
+ connectionMap[id] = h
+ return unsafe.Pointer(id)
+}
+
+func getConn(handle unsafe.Pointer) ConnectionInterface {
+ id := uintptr(handle)
+ h, ok := connectionMap[id]
+ if !ok {
+ panic(fmt.Sprintf("connection %d was not open", id))
+ }
+ return h
+}
+
+//export implClose
+func implClose(handle unsafe.Pointer) {
+ h := getConn(handle)
+ h.Close()
+ id := uintptr(handle)
+ delete(connectionMap, id)
+}
+
+//export implGetSize
+func implGetSize(handle unsafe.Pointer) C.int64_t {
+ h := getConn(handle)
+ size, err := h.GetSize()
+ if err != nil {
+ set_error(err)
+ return -1
+ }
+ return C.int64_t(size)
+}
+
+//export implPRead
+func implPRead(handle unsafe.Pointer, buf unsafe.Pointer,
+ count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int {
+ h := getConn(handle)
+ err := h.PRead(C.GoBytes(buf, C.int(count)),
+ uint64(offset), uint32(flags))
+ if err != nil {
+ set_error(err)
+ return -1
+ }
+ return 0
+}
+
+// Called from C plugin_init function.
+func PluginInitialize(name string, impl PluginInterface) unsafe.Pointer {
+ // Initialize the connection map. Note that connection IDs
+ // must start counting from 1 since we must never return what
+ // looks like a NULL pointer to the C code.
+ connectionMap = make(map[uintptr]ConnectionInterface)
+ nextConnectionId = 1
+
+ pluginImpl = impl
+
+ plugin := C.struct_nbdkit_plugin{}
+
+ // Set up the hidden plugin fields as for C.
+ struct_size := C.ulong(unsafe.Sizeof(plugin))
+ plugin._struct_size = struct_size
+ plugin._api_version = C.NBDKIT_API_VERSION
+ plugin._thread_model = C.NBDKIT_THREAD_MODEL_PARALLEL
+
+ // Set up the other fields.
+ plugin.name = C.CString(name)
+ plugin.load = (*[0]byte)(C.wrapper_load)
+ plugin.unload = (*[0]byte)(C.wrapper_unload)
+ plugin.config = (*[0]byte)(C.wrapper_config)
+ plugin.config_complete = (*[0]byte)(C.wrapper_config_complete)
+ plugin.open = (*[0]byte)(C.wrapper_open)
+ plugin.close = (*[0]byte)(C.wrapper_close)
+ plugin.get_size = (*[0]byte)(C.wrapper_get_size)
+ plugin.pread = (*[0]byte)(C.wrapper_pread)
+
+ // Golang plugins don't preserve errno correctly.
+ plugin.errno_is_preserved = 0
+
+ // Return a newly malloced copy of the struct. This must be
+ // globally available to the C code in the server, so it is
+ // never freed.
+ p := (*C.struct_nbdkit_plugin)(C.malloc(struct_size))
+ *p = plugin
+ return unsafe.Pointer(p)
+}
diff --git a/plugins/golang/src/libguestfs.org/nbdkit/utils.go b/plugins/golang/src/libguestfs.org/nbdkit/utils.go
new file mode 100644
index 00000000..d9c0c188
--- /dev/null
+++ b/plugins/golang/src/libguestfs.org/nbdkit/utils.go
@@ -0,0 +1,75 @@
+/* cgo wrappers.
+ * 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 nbdkit
+
+/*
+#cgo pkg-config: nbdkit
+
+#define NBDKIT_API_VERSION 2
+#include <nbdkit-plugin.h>
+
+// cgo cannot call varargs functions.
+void
+_nbdkit_debug (const char *s)
+{
+ nbdkit_debug ("%s", s);
+}
+
+// cgo cannot call varargs functions.
+void
+_nbdkit_error (const char *s)
+{
+ nbdkit_error ("%s", s);
+}
+*/
+import "C"
+import "syscall"
+
+// Utility functions.
+
+func Debug(s string) {
+ C._nbdkit_debug(C.CString(s))
+}
+
+// This function is provided but plugins would rarely need to call
+// this explicitly since returning an error from a plugin callback
+// will call it implicitly.
+func Error(s string) {
+ C._nbdkit_error(C.CString(s))
+}
+
+// Same applies as for Error(). Callers should not usually need to
+// call this.
+func SetError(err syscall.Errno) {
+ C.nbdkit_set_error(C.int(err))
+}
diff --git a/plugins/golang/src/libguestfs.org/nbdkit/wrappers.go b/plugins/golang/src/libguestfs.org/nbdkit/wrappers.go
new file mode 100644
index 00000000..79b6d329
--- /dev/null
+++ b/plugins/golang/src/libguestfs.org/nbdkit/wrappers.go
@@ -0,0 +1,102 @@
+/* cgo wrappers.
+ * 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 nbdkit
+
+/*
+#cgo pkg-config: nbdkit
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NBDKIT_API_VERSION 2
+#include <nbdkit-plugin.h>
+#include "wrappers.h"
+
+extern void implLoad ();
+void
+wrapper_load (void)
+{
+ implLoad ();
+}
+
+extern void implUnload ();
+void
+wrapper_unload (void)
+{
+ implUnload ();
+}
+
+extern int implConfig ();
+int
+wrapper_config (const char *key, const char *value)
+{
+ return implConfig (key, value);
+}
+
+extern int implConfigComplete ();
+int
+wrapper_config_complete (void)
+{
+ return implConfigComplete ();
+}
+
+extern void *implOpen ();
+void *
+wrapper_open (int readonly)
+{
+ return implOpen (readonly);
+}
+
+extern void implClose ();
+void
+wrapper_close (void *handle)
+{
+ return implClose (handle);
+}
+
+extern int64_t implGetSize ();
+int64_t
+wrapper_get_size (void *handle)
+{
+ return implGetSize (handle);
+}
+
+extern int implPRead ();
+int
+wrapper_pread (void *handle, void *buf,
+ uint32_t count, uint64_t offset, uint32_t flags)
+{
+ return implPRead (handle, buf, count, offset, flags);
+}
+*/
+import "C"
diff --git a/plugins/golang/test/run-test.sh b/plugins/golang/test/run-test.sh
new file mode 100755
index 00000000..f4da139e
--- /dev/null
+++ b/plugins/golang/test/run-test.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 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.
+
+set -e
+set -x
+
+if ! qemu-img --version; then
+ echo "qemu-img is required to run this test"
+ exit 1
+fi
+
+../../nbdkit -f -v test/nbdkit-gotest-plugin.so size=$((1024 * 1024)) \
+ --run 'qemu-img info $nbd'
diff --git a/plugins/golang/test/test.go b/plugins/golang/test/test.go
new file mode 100644
index 00000000..7186ffa8
--- /dev/null
+++ b/plugins/golang/test/test.go
@@ -0,0 +1,119 @@
+/* Test 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"
+ "libguestfs.org/nbdkit"
+ "strconv"
+ "unsafe"
+)
+
+var pluginName = "test"
+
+type TestPlugin struct {
+ nbdkit.Plugin
+}
+
+type TestConnection struct {
+ nbdkit.Connection
+}
+
+var size uint64
+var size_set = false
+
+func (p TestPlugin) Load() {
+ nbdkit.Debug("golang code running in the .load callback")
+}
+
+func (p TestPlugin) Unload() {
+ nbdkit.Debug("golang code running in the .unload callback")
+}
+
+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.ConnectionInterface, error) {
+ nbdkit.Debug("golang code running in the .open callback")
+ return &TestConnection{}, nil
+}
+
+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 {
+ nbdkit.Debug("golang code running in the .pread callback")
+ for i := 0; i < len(buf); i++ {
+ buf[i] = 0
+ }
+ 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, &TestPlugin{})
+}
+
+// This is never(?) called, but must exist.
+func main() {}
diff --git a/.gitignore b/.gitignore
index 11cd976b..f681f526 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,8 @@ plugins/*/*.3
/plugins/example4/nbdkit-example4-plugin
/plugins/eval/call.c
/plugins/eval/methods.c
+/plugins/golang/test/nbdkit-gotest-plugin.h
+/plugins/golang/test/nbdkit-gotest-plugin.so
/plugins/ocaml/nbdkit-ocamlexample-plugin.so
/plugins/rust/Cargo.lock
/plugins/rust/Cargo.toml
diff --git a/README b/README
index a33c5693..62fc93e9 100644
--- a/README
+++ b/README
@@ -148,6 +148,10 @@ For the Rust plugin:
- cargo (other dependencies will be downloaded at build time)
+To be able to write plugins in golang:
+
+ - go >= 1.5
+
For bash tab completion:
- bash-completion >= 1.99
--
2.25.0
More information about the Libguestfs
mailing list