From: "Richard W.M. Jones" <rjones@redhat.com> See also: https://www.redhat.com/archives/libguestfs/2020-July/msg00090.html Message-Id: <20200722124201.1823468-2-rjones@redhat.com> For now, this implementation is only for plugins; adding filter support will come later (and probably requires further edits, for the ability for any filter to open up an independent connection to the plugin without being handed next_ops on entry). --- docs/nbdkit-plugin.pod | 57 +++++++++++++++++++++++++++++++++++++--- docs/nbdkit-protocol.pod | 7 +++-- include/nbdkit-plugin.h | 3 +++ server/plugins.c | 10 +++++-- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod index f8e9962a..7ce89e9e 100644 --- a/docs/nbdkit-plugin.pod +++ b/docs/nbdkit-plugin.pod @@ -152,6 +152,9 @@ the plugin: │ preconnect │ │ └──────┬─────┘ │ ┌──────┴─────┐ │ + │list_exports│ │ + └──────┬─────┘ │ + ┌──────┴─────┐ │ │ open │ │ └──────┬─────┘ │ ┌──────┴─────┐ NBD option │ @@ -160,10 +163,10 @@ the plugin: ┌──────┴─────┐ ┌──────┴─────┐ client #2 │ get_size │ │ preconnect │ └──────┬─────┘ └──────┬─────┘ - ┌──────┴─────┐ data ┌──────┴─────┐ - │ pread │ serving │ open │ - └──────┬─────┘↺ └──────┬─────┘ - ┌──────┴─────┐ ... + ┌──────┴─────┐ data + │ pread │ serving + └──────┬─────┘↺ ... + ┌──────┴─────┐ │ pwrite │ └──────┬─────┘↺ ┌──────┴─────┐ ┌──────┴─────┐ │ close │ @@ -236,6 +239,12 @@ L<getpid(2)>. Called when a TCP connection has been made to the server. This happens early, before NBD or TLS negotiation. +=item C<.list_exports> + +Early in option negotiation the client may try to list the exports +served by the plugin, and plugins can optionally implement this +callback to answer the client. See L</EXPORT NAME> below. + =item C<.open> A new client has connected and finished the NBD handshake. TLS @@ -652,6 +661,46 @@ Returning C<0> will allow the connection to continue. If there is an error or you want to deny the connection, call C<nbdkit_error> with an error message and return C<-1>. +=head2 C<.list_exports> + + int list_exports (int readonly, int default_only, + struct nbdkit_exports *exports); + +This optional callback is called if the client tries to list the +exports served by the plugin (using C<NBD_OPT_LIST>). If the plugin +does not supply this callback then a single export called C<""> is +returned. The NBD protocol defines C<""> as the default export, so +this is suitable for plugins which ignore the export name and always +serve the same content. See also L</EXPORT NAME> below. + +The C<exports> parameter is an opaque object for collecting the list +of exports. Call C<nbdkit_add_export> to add a single export to the +list. If the plugin has a concept of a default export (usually but +not always called C<"">) then it should return that first in the list. + + int nbdkit_add_export (struct nbdkit_export *exports, + const char *name, const char *description); + +The C<name> must be a non-NULL, UTF-8 string between 0 and 4096 bytes +in length. Export names must be unique. C<description> is an +optional description of the export which some clients can display but +which is otherwise unused (if you don't want a description, you can +pass this parameter as C<NULL>). The string(s) are copied into the +exports list so you may free them immediately after calling this +function. + +If the C<default_only> flag then the client is querying for the name +of the default export, and the plugin may add only a single export to +the returned list (the default export name, usually C<"">). The +plugin can ignore this flag and return all exports if it wants. + +The C<readonly> flag informs the plugin that the server was started +with the I<-r> flag on the command line. + +Returning C<0> will send the list of exports back to the client. If +there is an error, C<.list_exports> should call C<nbdkit_error> with +an error message and return C<-1>. + =head2 C<.open> void *open (int readonly); diff --git a/docs/nbdkit-protocol.pod b/docs/nbdkit-protocol.pod index 8fe8c67e..b1e182c1 100644 --- a/docs/nbdkit-protocol.pod +++ b/docs/nbdkit-protocol.pod @@ -99,12 +99,15 @@ Supported in nbdkit E<ge> 1.1.12, and the default in nbdkit E<ge> 1.3. =item export names Partially supported in nbdkit E<ge> 1.1.12. Support for plugins to -read the client export name added in nbdkit E<ge> 1.15.2. +read the client export name added in nbdkit E<ge> 1.15.2. Support for +C<NBD_OPT_LIST> was added in nbdkit E<ge> 1.21.20. Versions of nbdkit before 1.16 could advertise a single export name to clients, via a now deprecated side effect of the I<-e> option. In nbdkit 1.15.2, plugins could read the client requested export name using -C<nbdkit_export_name()> and serve different content. +C<nbdkit_export_name()> and serve different content. In nbdkit +1.21.20, plugins could implement C<.list_exports> to answer +C<NBD_OPT_LIST> queries. =item C<NBD_FLAG_NO_ZEROES> diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h index c6c1256c..86b8565d 100644 --- a/include/nbdkit-plugin.h +++ b/include/nbdkit-plugin.h @@ -139,6 +139,9 @@ struct nbdkit_plugin { int (*get_ready) (void); int (*after_fork) (void); + + int (*list_exports) (int readonly, int default_only, + struct nbdkit_exports *exports); }; extern void nbdkit_set_error (int err); diff --git a/server/plugins.c b/server/plugins.c index 8020046b..d4364cd2 100644 --- a/server/plugins.c +++ b/server/plugins.c @@ -161,6 +161,7 @@ plugin_dump_fields (struct backend *b) HAS (get_ready); HAS (after_fork); HAS (preconnect); + HAS (list_exports); HAS (open); HAS (close); HAS (get_size); @@ -281,8 +282,13 @@ static int plugin_list_exports (struct backend *b, int readonly, int default_only, struct nbdkit_exports *exports) { - /* XXX No plugin support yet, so for now just advertise "" */ - return nbdkit_add_export (exports, "", NULL); + GET_CONN; + struct backend_plugin *p = container_of (b, struct backend_plugin, backend); + + if (!p->plugin.list_exports) + return nbdkit_add_export (exports, "", NULL); + + return p->plugin.list_exports (readonly, default_only, exports); } static void * -- 2.28.0