[Libguestfs] [PATCH nbdkit INCOMPLETE 2/6] Introduce filters.

Richard W.M. Jones rjones at redhat.com
Sun Jan 14 12:11:17 UTC 2018


Filters can be placed in front of plugins to modify their behaviour.

This commit introduces the <nbdkit-filter.h> header file, the manual
page, the ‘filterdir’ directory (like ‘plugindir’), and the ‘filters/’
source directory which will contain both example and real filters.
---
 Makefile.am             |   2 +-
 TODO                    |  17 +-
 configure.ac            |   3 +-
 docs/Makefile.am        |   9 +-
 docs/nbdkit-filter.pod  | 478 ++++++++++++++++++++++++++++++++++++++++++++++++
 docs/nbdkit-plugin.pod  |   3 +-
 docs/nbdkit.pod         |   3 +-
 filters/Makefile.am     |  33 ++++
 include/Makefile.am     |   4 +-
 include/nbdkit-filter.h | 132 +++++++++++++
 include/nbdkit-plugin.h |   2 +
 src/Makefile.am         |   5 +-
 src/main.c              |   1 +
 src/nbdkit.pc.in        |   1 +
 14 files changed, 671 insertions(+), 22 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index f3c88b0..9c5b4c3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,7 +49,7 @@ SUBDIRS = \
 	src
 
 if HAVE_PLUGINS
-SUBDIRS += plugins
+SUBDIRS += plugins filters
 endif
 
 SUBDIRS += tests
diff --git a/TODO b/TODO
index 0c027e2..0955db7 100644
--- a/TODO
+++ b/TODO
@@ -34,10 +34,8 @@ nbdkit there is no compelling reason unless the result is better than
 qemu-nbd.  For the majority of users it would be better if they were
 directed to qemu-nbd for these use cases.
 
-Filters
--------
-
-It should be possible to layer filters over plugins to do things like:
+Suggestions for filters
+-----------------------
 
 * adding artificial delays (see wdelay/rdelay options in the file
   plugin)
@@ -50,17 +48,6 @@ It should be possible to layer filters over plugins to do things like:
 
 * export a single partition (like qemu-nbd -P)
 
-A possible syntax would be:
-
-  nbdkit --filter=delay [--filter=...] file file=foo wdelay=10
-
-The filter(s) intercept all plugin calls and can either return, return
-an error, or pass the call down to the next layer in the stack (and
-eventually to the plugin).  By intercepting the .config call the
-filter can process its own parameters from the command line (wdelay=10
-in the example above), and by intercepting the .pread, .pwrite methods
-the filter could inject the delaying behaviour.
-
 Composing nbdkit
 ----------------
 
diff --git a/configure.ac b/configure.ac
index a2950f6..7032614 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,7 +181,7 @@ AS_IF([test "x$POD2MAN" != "xno"],[
 AM_CONDITIONAL([HAVE_POD2MAN], [test "x$POD2MAN" != "xno"])
 
 AC_ARG_ENABLE([plugins],
-    [AS_HELP_STRING([--disable-plugins], [disable all bundled plugins])])
+    [AS_HELP_STRING([--disable-plugins], [disable all bundled plugins and filters])])
 AM_CONDITIONAL([HAVE_PLUGINS], [test "x$enable_plugins" != "xno"])
 
 dnl Check for Perl, for embedding in the perl plugin.
@@ -512,6 +512,7 @@ AC_CONFIG_FILES([Makefile
                  plugins/tar/Makefile
                  plugins/vddk/Makefile
                  plugins/xz/Makefile
+                 filters/Makefile
                  src/Makefile
                  src/nbdkit.pc
                  tests/Makefile])
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 323f48d..d2330fb 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -33,6 +33,7 @@
 EXTRA_DIST = \
 	nbdkit.pod \
 	nbdkit-plugin.pod
+	nbdkit-filter.pod
 
 CLEANFILES = *~
 
@@ -40,7 +41,8 @@ if HAVE_POD2MAN
 
 man_MANS = \
 	nbdkit.1 \
-	nbdkit-plugin.3
+	nbdkit-plugin.3 \
+	nbdkit-filter.3
 CLEANFILES += $(man_MANS)
 
 nbdkit.1: nbdkit.pod
@@ -53,4 +55,9 @@ nbdkit-plugin.3: nbdkit-plugin.pod
 	if grep 'POD ERROR' $@.t; then rm $@.t; exit 1; fi && \
 	mv $@.t $@
 
+nbdkit-filter.3: nbdkit-filter.pod
+	$(POD2MAN) $(POD2MAN_ARGS) --section=3 --name=nbdkit-filter $< $@.t && \
+	if grep 'POD ERROR' $@.t; then rm $@.t; exit 1; fi && \
+	mv $@.t $@
+
 endif
diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
new file mode 100644
index 0000000..5fcd5ed
--- /dev/null
+++ b/docs/nbdkit-filter.pod
@@ -0,0 +1,478 @@
+=encoding utf8
+
+=head1 NAME
+
+nbdkit-filter - How to write nbdkit filters
+
+=head1 SYNOPSIS
+
+ #include <nbdkit-filter.h>
+ 
+ static int
+ myfilter_config (const char *key, const char *value,
+                  int (*next) (...), void *data)
+ {
+   if (strcmp (key, "myparameter") == 0) {
+     // ...
+     return 0;
+   }
+   else {
+     // pass through to next filter or plugin
+     return next (data, key, value);
+   }
+ }
+ 
+ static struct nbdkit_filter filter = {
+   .name              = "filter",
+   .config            = myfilter_config,
+   /* etc */
+ };
+ 
+ NBDKIT_REGISTER_FILTER(filter)
+
+When this has been compiled to a shared library, do:
+
+ nbdkit [--args ...] --filter=./myfilter.so plugin [key=value ...]
+
+When debugging, use the I<-fv> options:
+
+ nbdkit -fv --filter=./myfilter.so plugin [key=value ...]
+
+=head1 DESCRIPTION
+
+One or more nbdkit filters can be placed in front of an nbdkit plugin
+to modify the behaviour of the plugin.  This manual page describes how
+to create an nbdkit filter.
+
+Filters can be used for example to limit requests to an offset/limit,
+add copy-on-write support, or inject delays or errors (for testing).
+
+Different filters can be stacked:
+
+     NBD     ┌─────────┐    ┌─────────┐          ┌────────┐
+  client ───▶│ filter1 │───▶│ filter2 │── ─ ─ ──▶│ plugin │
+ request     └─────────┘    └─────────┘          └────────┘
+
+Each filter intercepts plugin functions (see L<nbdkit-plugin(3)>) and
+can modify parameters, call the following function in the chain
+before, in the middle or after, or even short-cut the chain.  As an
+example, to process its own parameters the filter can intercept the
+C<.config> method:
+
+ static int
+ myfilter_config (const char *key, const char *value,
+                  int (*next) (...), void *data)
+ {
+   if (strcmp (key, "myparameter") == 0) {
+     // ...
+     // here you would handle this key, value
+     // ...
+     return 0;
+   }
+   else {
+     // pass through to next filter or plugin
+     return next (data, key, value);
+   }
+ }
+ 
+ static struct nbdkit_filter filter = {
+   // ...
+   .config            = myfilter_config,
+   // ...
+ };
+
+The call to C<next (data, ...)> calls the next filter or plugin in the
+chain.  In the example above any instances of C<myparameter=...> on
+the command line would not be seen by the plugin.
+
+To see example filters, take a look at the source of nbdkit, in the
+C<filters> directory.
+
+Filters must be written in C and must be fully thread safe.
+
+=head1 C<nbdkit-filter.h>
+
+All filters should start by including this header file:
+
+ #include <nbdkit-filter.h>
+
+=head1 C<struct nbdkit_filter>
+
+All filters must define and register one C<struct nbdkit_filter>,
+which contains the name of the filter and pointers to plugin methods
+that the filter wants to intercept.
+
+ static struct nbdkit_filter filter = {
+   .name              = "filter",
+   .longname          = "My Filter",
+   .description       = "This is my great filter for nbdkit",
+   .config            = myfilter_config,
+   /* etc */
+ };
+ 
+ NBDKIT_REGISTER_FILTER(filter)
+
+The C<.name> field is the name of the filter.  This is the only field
+which is required.
+
+=head1 CALLBACKS
+
+C<struct nbdkit_filter> has some static fields describing the filter
+and optional callback functions which can be used to intercept plugin
+methods.
+
+The functions have extra C<next> and C<data> parameters which are used
+to call the next filter or plugin in the chain.  Note that if your
+filter registers a callback but in that callback it doesn't call the
+C<next> function then the corresponding method in the plugin will
+never be called.
+
+You can modify parameters when you call the C<next> function.  However
+be careful when modifying strings because for some methods
+(eg. C<.config>) the plugin may save the string pointer that you pass
+along.  So you may have to ensure that the string is not freed for the
+lifetime of the server.
+
+=head2 C<.name>
+
+ const char *name;
+
+This field (a string) is required, and B<must> contain only ASCII
+alphanumeric characters and be unique amongst all filters.
+
+=head2 C<.version>
+
+ const char *version;
+
+Filters may optionally set a version string which is displayed in help
+and debugging output.
+
+=head2 C<.longname>
+
+ const char *longname;
+
+An optional free text name of the filter.  This field is used in error
+messages.
+
+=head2 C<.description>
+
+ const char *description;
+
+An optional multi-line description of the filter.
+
+=head2 C<.load>
+
+ void load (void);
+
+This is called once just after the filter is loaded into memory.  You
+can use this to perform any global initialization needed by the
+filter.
+
+=head2 C<.unload>
+
+ void unload (void);
+
+This may be called once just before the filter is unloaded from
+memory.  Note that it's not guaranteed that C<.unload> will always be
+called (eg. the server might be killed or segfault), so you should try
+to make the filter as robust as possible by not requiring cleanup.
+See also L<nbdkit-plugin(3)/SHUTDOWN>.
+
+=head2 C<.config>
+
+ int config (const char *key, const char *value,
+             int (*next) (void *data, const char *key, const char *value),
+             void *data);
+
+This intercepts the plugin C<.config> method and can be used by the
+filter to parse its own command line parameters.  You should try to
+make sure that command line parameter keys that the filter uses do not
+conflict with ones that could be used by a plugin.
+
+If there is an error, C<.config_complete> should call C<nbdkit_error>
+with an error message and return C<-1>.
+
+=head2 C<.config_complete>
+
+ int config_complete (int (*next) (void *data), void *data);
+
+This intercepts the plugin C<.config_complete> method and can be used
+to ensure that all parameters needed by the filter were supplied on
+the command line.
+
+If there is an error, C<.config_complete> should call C<nbdkit_error>
+with an error message and return C<-1>.
+
+=head2 C<.config_help>
+
+ const char *config_help;
+
+This optional multi-line help message should summarize any
+C<key=value> parameters that it takes.  It does I<not> need to repeat
+what already appears in C<.description>.
+
+If the filter doesn't take any config parameters you should probably
+omit this.
+
+=head2 C<.open>
+
+ void *open (int readonly,
+             int (*next) (void *data, int readonly), void *data);
+
+This intercepts the plugin C<.open> method and can be used to allocate
+per-connection data structures needed by the filter.  The handle
+(which is not the same as the plugin handle) is passed back to other
+filter callbacks and could be freed in the C<.close> callback.
+
+Note that the handle is completely opaque to nbdkit, but it must not
+be NULL.
+
+Unlike other callbacks, you should I<not> call C<return next (...)>
+in this callback.  Instead you should check for errors by doing:
+
+ if (next (data, readonly) == -1) {
+   // error from plugin .open method
+   // next() already called nbdkit_error so just return NULL
+   return NULL;
+ }
+ // no error
+ return my_handle;
+
+If there is an error, C<.open> should call C<nbdkit_error> with an
+error message and return C<NULL>.
+
+=head2 C<.close>
+
+ void close (void *handle,
+             void (*next) (void *data), void *data);
+
+This intercepts the plugin C<.close> method
+
+This is called when the client closes the connection.  It should clean
+up any per-connection resources used by the filter.
+
+=head2 C<.get_size>
+
+ int64_t get_size (void *handle,
+                   int64_t (*next) (void *data), void *data);
+
+This intercepts the plugin C<.get_size> method and can be used to read
+or modify the apparent size of the block device that the NBD client
+will see.
+
+The returned size must be E<ge> 0.  If there is an error, C<.get_size>
+should call C<nbdkit_error> with an error message and return C<-1>.
+
+=head2 C<.can_write>
+
+=head2 C<.can_flush>
+
+=head2 C<.is_rotational>
+
+=head2 C<.can_trim>
+
+ int can_write (void *handle,
+                int (*next) (void *data), void *data);
+
+ int can_flush (void *handle,
+                int (*next) (void *data), void *data);
+
+ int is_rotational (void *handle,
+                    int (*next) (void *data), void *data);
+
+ int can_trim (void *handle,
+               int (*next) (void *data), void *data);
+
+These intercept the corresponding plugin methods.
+
+If there is an error, the callback should call C<nbdkit_error> with an
+error message and return C<-1>.
+
+=head2 C<.pread>
+
+ int pread (void *handle, void *buf, uint32_t count, uint64_t offset,
+            int (*next) (void *data,
+                         void *buf, uint32_t count, uint64_t offset),
+            void *data);
+
+This intercepts the plugin C<.pread> method and can be used to read or
+modify data read by the plugin.
+
+If there is an error (including a short read which couldn't be
+recovered from), C<.pread> should call C<nbdkit_error> with an error
+message B<and> set C<errno>, then return C<-1>.
+
+=head2 C<.pwrite>
+
+ int pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
+             int (*next) (void *data,
+                          const void *buf, uint32_t count, uint64_t offset),
+             void *data);
+
+This intercepts the plugin C<.pwrite> method and can be used to modify
+data written by the plugin.
+
+If there is an error (including a short write which couldn't be
+recovered from), C<.pwrite> should call C<nbdkit_error> with an error
+message B<and> set C<errno>, then return C<-1>.
+
+=head2 C<.flush>
+
+ int flush (void *handle,
+            int (*next) (void *data), void *data);
+
+This intercepts the plugin C<.flush> method and can be used to modify
+flush requests.
+
+If there is an error, C<.flush> should call C<nbdkit_error> with an
+error message B<and> set C<errno>, then return C<-1>.
+
+=head2 C<.trim>
+
+ int trim (void *handle, uint32_t count, uint64_t offset,
+           int (*next) (void *data, uint32_t count, uint64_t offset),
+           void *data);
+
+This intercepts the plugin C<.trim> method and can be used to modify
+trim requests.
+
+If there is an error, C<.trim> should call C<nbdkit_error> with an
+error message B<and> set C<errno>, then return C<-1>.
+
+=head2 C<.zero>
+
+ int zero (void *handle, uint32_t count, uint64_t offset, int may_trim,
+           int (*next) (void *data,
+                        uint32_t count, uint64_t offset, int may_trim),
+           void *data);
+
+This intercepts the plugin C<.zero> method and can be used to modify
+zero requests.
+
+If there is an error, C<.zero> should call C<nbdkit_error> with an
+error message B<and> set C<errno>, then return C<-1>.
+
+=head1 THREADS
+
+Because filters can be mixed and used with any plugin and thus any
+threading model supported by L<nbdkit-plugin(3)>, filters must be
+thread safe.  They must be able to handle concurrent requests even on
+the same handle.
+
+Filters may have to use pthread primitives like mutexes to achieve
+this.
+
+=head1 DEBUGGING
+
+Run the server with I<-f> and I<-v> options so it doesn't fork and you
+can see debugging information:
+
+ nbdkit -fv --filter=./myfilter.so plugin [key=value [key=value [...]]]
+
+To print debugging information from within the filter, call
+C<nbdkit_debug>, which has the following prototype and works like
+L<printf(3)>:
+
+ void nbdkit_debug (const char *fs, ...);
+ void nbdkit_vdebug (const char *fs, va_list args);
+
+For convenience, C<nbdkit_debug> preserves the value of C<errno>.
+Note that C<nbdkit_debug> only prints things when the server is in
+verbose mode (I<-v> option).
+
+=head1 INSTALLING THE FILTER
+
+The filter is a C<*.so> file and possibly a manual page.  You can of
+course install the filter C<*.so> file wherever you want, and users
+will be able to use it by running:
+
+ nbdkit --filter=/path/to/filter.so plugin [args]
+
+However B<if> the shared library has a name of the form
+C<nbdkit-I<name>-filter.so> B<and if> the library is installed in the
+C<$filterdir> directory, then users can be run it by only typing:
+
+ nbdkit --filter=name plugin [args]
+
+The location of the C<$filterdir> directory is set when nbdkit is
+compiled and can be found by doing:
+
+ nbdkit --dump-config
+
+If using the pkg-config/pkgconf system then you can also find the
+filter directory at compile time by doing:
+
+ pkgconf nbdkit --variable=filterdir
+
+=head1 PKG-CONFIG/PKGCONF
+
+nbdkit provides a pkg-config/pkgconf file called C<nbdkit.pc> which
+should be installed on the correct path when the nbdkit development
+environment is installed.  You can use this in autoconf
+F<configure.ac> scripts to test for the development environment:
+
+ PKG_CHECK_MODULES([NBDKIT], [nbdkit >= 1.2.3])
+
+The above will fail unless nbdkit E<ge> 1.2.3 and the header file is
+installed, and will set C<NBDKIT_CFLAGS> and C<NBDKIT_LIBS>
+appropriately for compiling filters.
+
+You can also run pkg-config/pkgconf directly, for example:
+
+ if ! pkgconf nbdkit --exists; then
+   echo "you must install the nbdkit development environment"
+   exit 1
+ fi
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-plugin(1)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2013-2018 Red Hat Inc.
+
+=head1 LICENSE
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+=over 4
+
+=item *
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+=item *
+
+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.
+
+=item *
+
+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.
+
+=back
+
+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.
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 9abf75f..3cafc42 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -692,6 +692,7 @@ and then users will be able to run it like this:
 =head1 SEE ALSO
 
 L<nbdkit(1)>,
+L<nbdkit-filter(3)>,
 L<nbdkit-example1-plugin(1)>,
 L<nbdkit-example2-plugin(1)>,
 L<nbdkit-example3-plugin(1)>,
@@ -711,7 +712,7 @@ Pino Toscano
 
 =head1 COPYRIGHT
 
-Copyright (C) 2013-2017 Red Hat Inc.
+Copyright (C) 2013-2018 Red Hat Inc.
 
 =head1 LICENSE
 
diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index 1687ac9..3b37db8 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -856,6 +856,7 @@ L</SOCKET ACTIVATION>.
 Other nbdkit manual pages:
 
 L<nbdkit-plugin(3)>,
+L<nbdkit-filter(3)>,
 L<nbdkit-curl-plugin(1)>,
 L<nbdkit-example1-plugin(1)>,
 L<nbdkit-example2-plugin(1)>,
@@ -895,7 +896,7 @@ Pino Toscano
 
 =head1 COPYRIGHT
 
-Copyright (C) 2013-2017 Red Hat Inc.
+Copyright (C) 2013-2018 Red Hat Inc.
 
 =head1 LICENSE
 
diff --git a/filters/Makefile.am b/filters/Makefile.am
new file mode 100644
index 0000000..ed1580b
--- /dev/null
+++ b/filters/Makefile.am
@@ -0,0 +1,33 @@
+# nbdkit
+# Copyright (C) 2013-2018 Red Hat Inc.
+# All rights reserved.
+#
+# 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.
+
+#SUBDIRS =
diff --git a/include/Makefile.am b/include/Makefile.am
index 7d54215..deccc6b 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -30,4 +30,6 @@
 # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 
-include_HEADERS = nbdkit-plugin.h
+include_HEADERS = \
+	nbdkit-plugin.h \
+	nbdkit-filter.h
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
new file mode 100644
index 0000000..789bbb0
--- /dev/null
+++ b/include/nbdkit-filter.h
@@ -0,0 +1,132 @@
+/* nbdkit
+ * Copyright (C) 2013-2017 Red Hat Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/* See nbdkit-filter(3) for documentation and how to write a filter. */
+
+#ifndef NBDKIT_FILTER_H
+#define NBDKIT_FILTER_H
+
+/* This header also defines some useful functions like nbdkit_debug
+ * and nbdkit_parse_size which are appropriate for filters to use.
+ */
+#include <nbdkit-plugin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NBDKIT_FILTER_API_VERSION 1
+
+struct nbdkit_filter {
+  /* Do not set these fields directly; use NBDKIT_REGISTER_FILTER.
+   * They exist so that we can support filters compiled against
+   * one version of the header with a runtime compiled against a
+   * different version with more (or fewer) fields.
+   */
+  uint64_t _struct_size;
+  int _api_version;
+
+  /* New fields will only be added at the end of the struct. */
+  const char *name;
+  const char *longname;
+  const char *version;
+  const char *description;
+
+  void (*load) (void);
+  void (*unload) (void);
+
+  int (*config) (const char *key, const char *value,
+                 int (*next) (void *data, const char *key, const char *value),
+                 void *data);
+  int (*config_complete) (int (*next) (void *data), void *data);
+  const char *config_help;
+
+  void * (*open) (int readonly,
+                  int (*next) (void *data, int readonly), void *data);
+  void (*close) (void *handle,
+                 void (*next) (void *data), void *data);
+
+  int64_t (*get_size) (void *handle,
+                       int64_t (*next) (void *data), void *data);
+
+  int (*can_write) (void *handle,
+                    int (*next) (void *data), void *data);
+  int (*can_flush) (void *handle,
+                    int (*next) (void *data), void *data);
+  int (*is_rotational) (void *handle,
+                        int (*next) (void *data), void *data);
+  int (*can_trim) (void *handle,
+                   int (*next) (void *data), void *data);
+
+  int (*pread) (void *handle, void *buf, uint32_t count, uint64_t offset,
+                int (*next) (void *data,
+                             void *buf, uint32_t count, uint64_t offset),
+                void *data);
+  int (*pwrite) (void *handle, const void *buf, uint32_t count, uint64_t offset,
+                 int (*next) (void *data,
+                              const void *buf, uint32_t count, uint64_t offset),
+                 void *data);
+  int (*flush) (void *handle,
+                int (*next) (void *data), void *data);
+  int (*trim) (void *handle, uint32_t count, uint64_t offset,
+               int (*next) (void *data, uint32_t count, uint64_t offset),
+               void *data);
+  int (*zero) (void *handle, uint32_t count, uint64_t offset, int may_trim,
+               int (*next) (void *data,
+                            uint32_t count, uint64_t offset, int may_trim),
+               void *data);
+};
+
+#ifndef NBDKIT_CXX_LANG_C
+#ifdef __cplusplus
+#define NBDKIT_CXX_LANG_C extern "C"
+#else
+#define NBDKIT_CXX_LANG_C /* nothing */
+#endif
+#endif
+
+#define NBDKIT_REGISTER_FILTER(filter)                                  \
+  NBDKIT_CXX_LANG_C                                                     \
+  struct nbdkit_filter *                                                \
+  filter_init (void)                                                    \
+  {                                                                     \
+    (filter)._struct_size = sizeof (filter);                            \
+    (filter)._api_version = NBDKIT_API_VERSION;                         \
+    return &(filter);                                                   \
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NBDKIT_FILTER_H */
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 2ec3b15..13541e5 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -111,11 +111,13 @@ extern char *nbdkit_absolute_path (const char *path);
 extern int64_t nbdkit_parse_size (const char *str);
 extern int nbdkit_read_password (const char *value, char **password);
 
+#ifndef NBDKIT_CXX_LANG_C
 #ifdef __cplusplus
 #define NBDKIT_CXX_LANG_C extern "C"
 #else
 #define NBDKIT_CXX_LANG_C /* nothing */
 #endif
+#endif
 
 #define NBDKIT_REGISTER_PLUGIN(plugin)                                  \
   NBDKIT_CXX_LANG_C                                                     \
diff --git a/src/Makefile.am b/src/Makefile.am
index 12b9043..47d5f38 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,7 @@
 # SUCH DAMAGE.
 
 plugindir = $(libdir)/nbdkit/plugins
+filterdir = $(libdir)/nbdkit/filters
 
 sbin_PROGRAMS = nbdkit
 
@@ -46,13 +47,15 @@ nbdkit_SOURCES = \
 	sockets.c \
 	threadlocal.c \
 	utils.c \
-	$(top_srcdir)/include/nbdkit-plugin.h
+	$(top_srcdir)/include/nbdkit-plugin.h \
+	$(top_srcdir)/include/nbdkit-filter.h
 
 nbdkit_CPPFLAGS = \
 	-Dbindir=\"$(bindir)\" \
 	-Dlibdir=\"$(libdir)\" \
 	-Dmandir=\"$(mandir)\" \
 	-Dplugindir=\"$(plugindir)\" \
+	-Dfilterdir=\"$(filterdir)\" \
 	-Dsbindir=\"$(sbindir)\" \
 	-Dsysconfdir=\"$(sysconfdir)\" \
 	-I$(top_srcdir)/include
diff --git a/src/main.c b/src/main.c
index 4eca859..9b66d55 100644
--- a/src/main.c
+++ b/src/main.c
@@ -176,6 +176,7 @@ dump_config (void)
   printf ("%s=%s\n", "mandir", mandir);
   printf ("%s=%s\n", "name", PACKAGE_NAME);
   printf ("%s=%s\n", "plugindir", plugindir);
+  printf ("%s=%s\n", "filterdir", filterdir);
   printf ("%s=%s\n", "root_tls_certificates_dir", root_tls_certificates_dir);
   printf ("%s=%s\n", "sbindir", sbindir);
 #ifdef HAVE_LIBSELINUX
diff --git a/src/nbdkit.pc.in b/src/nbdkit.pc.in
index cbb301d..fe8f511 100644
--- a/src/nbdkit.pc.in
+++ b/src/nbdkit.pc.in
@@ -3,6 +3,7 @@ exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
 plugindir=@libdir@/nbdkit/plugins
+filterdir=@libdir@/nbdkit/filters
 
 Name: @PACKAGE_NAME@
 Version: @PACKAGE_VERSION@
-- 
2.15.1




More information about the Libguestfs mailing list