Rather than defaulting .list_exports to blindly returning "", it is
nicer to have it reflect the result of .default_export. Meanwhile,
language bindings will have a C callback for both .list_exports and
.default_export, even if the underlying script does not, which means
any logic in plugins.c for calling .default_export when .list_export
is missing would have to be duplicated in each language binding.
Better is to make it easy for languages, by wrapping things in a new
helper function; this also lets us take maximum advantage of caching
done in backend.c (since language bindings can't access our internals,
they would have to duplicate caching if we did not add this helper
function).
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
docs/nbdkit-plugin.pod | 40 +++++++++++++++++++++++++++++-----------
include/nbdkit-common.h | 2 ++
server/internal.h | 4 ++++
server/backend.c | 15 ++++++++-------
server/exports.c | 24 ++++++++++++++++++++++++
server/nbdkit.syms | 1 +
server/plugins.c | 2 +-
server/test-public.c | 9 ++++++++-
plugins/sh/methods.c | 2 +-
9 files changed, 78 insertions(+), 21 deletions(-)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 180c9daa..5962b1b8 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -675,10 +675,11 @@ error message and return C<-1>.
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.
+does not supply this callback then the result of C<.default_export> is
+advertised as the lone export. 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<readonly> flag informs the plugin that the server was started
with the I<-r> flag on the command line, which is the same value
@@ -694,9 +695,8 @@ C<"">). The plugin can ignore this flag and return
all exports if it
wants.
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.
+of exports. Call C<nbdkit_add_export> as needed to add specific
+exports to the list.
int nbdkit_add_export (struct nbdkit_export *exports,
const char *name, const char *description);
@@ -711,9 +711,25 @@ function. C<nbdkit_add_export> returns C<0> on success
or C<-1> on
failure; on failure C<nbdkit_error> has already been called, with
C<errno> set to a suitable value.
-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>.
+There are also situations where a plugin may wish to duplicate the
+nbdkit default behavior of supplying an export list containing only
+the result of C<.default_export> when C<.list_exports> is missing;
+this is most common in a language binding where it is not known at
+compile time whether the language script will be providing an
+implementation for C<.list_exports>, and is done by calling
+C<nbdkit_use_default_export>.
+
+ int nbdkit_use_default_export (struct nbdkit_export *exports);
+
+C<nbdkit_use_default_export> returns C<0> on success or C<-1> on
+failure; on failure C<nbdkit_error> has already been called, with
+C<errno> set to a suitable value.
+
+The plugin may also leave the export list empty, by not calling either
+helper. Once the plugin is happy with the list contents, 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<.default_export>
@@ -724,7 +740,9 @@ default export C<"">, where the plugin provides a
UTF-8 string between
0 and 4096 bytes. If the plugin does not supply this callback, the
connection continues with the empty name; if the plugin returns a
valid string, nbdkit behaves as if the client had passed that string
-instead of an empty name.
+instead of an empty name. Similarly, if the plugin does not supply a
+C<.list_exports> callback, the result of this callback determines what
+export name to advertise to a client requesting an export list.
The C<readonly> flag informs the plugin that the server was started
with the I<-r> flag on the command line, which is the same value
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index b3e83293..eb4416df 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -151,6 +151,8 @@ struct nbdkit_exports;
NBDKIT_EXTERN_DECL (int, nbdkit_add_export,
(struct nbdkit_exports *,
const char *name, const char *description));
+NBDKIT_EXTERN_DECL (int, nbdkit_use_default_export,
+ (struct nbdkit_exports *));
/* A static non-NULL pointer which can be used when you don't need a
* per-connection handle.
diff --git a/server/internal.h b/server/internal.h
index dfb4da31..e0422574 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -564,4 +564,8 @@ extern struct connection *threadlocal_get_conn (void);
struct connection *conn = threadlocal_get_conn (); \
assert (conn != NULL)
+/* exports.c */
+extern int exports_resolve_default (struct nbdkit_exports *exports,
+ struct backend *b, int readonly);
+
#endif /* NBDKIT_INTERNAL_H */
diff --git a/server/backend.c b/server/backend.c
index 6e82629c..74d934fc 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -164,7 +164,7 @@ backend_list_exports (struct backend *b, int readonly, int
default_only,
{
GET_CONN;
struct handle *h = get_handle (conn, b->i);
- int r;
+ size_t count;
assert (!default_only); /* XXX Switch to is_tls... */
controlpath_debug ("%s: list_exports readonly=%d default_only=%d",
@@ -173,14 +173,15 @@ backend_list_exports (struct backend *b, int readonly, int
default_only,
assert (h->handle == NULL);
assert ((h->state & HANDLE_OPEN) == 0);
- r = b->list_exports (b, readonly, default_only, exports);
- if (r == -1)
+ if (b->list_exports (b, readonly, default_only, exports) == -1 ||
+ exports_resolve_default (exports, b, readonly) == -1) {
controlpath_debug ("%s: list_exports failed", b->name);
- else {
- size_t count = nbdkit_exports_count (exports);
- controlpath_debug ("%s: list_exports returned %zu names", b->name,
count);
+ return -1;
}
- return r;
+
+ count = nbdkit_exports_count (exports);
+ controlpath_debug ("%s: list_exports returned %zu names", b->name,
count);
+ return 0;
}
const char *
diff --git a/server/exports.c b/server/exports.c
index 8d3faec4..dc804390 100644
--- a/server/exports.c
+++ b/server/exports.c
@@ -52,6 +52,7 @@ DEFINE_VECTOR_TYPE(exports, struct nbdkit_export);
struct nbdkit_exports {
exports exports;
+ bool use_default;
};
struct nbdkit_exports *
@@ -65,6 +66,7 @@ nbdkit_exports_new (void)
return NULL;
}
r->exports = (exports) empty_vector;
+ r->use_default = false;
return r;
}
@@ -141,3 +143,25 @@ nbdkit_add_export (struct nbdkit_exports *exps,
return 0;
}
+
+int
+nbdkit_use_default_export (struct nbdkit_exports *exps)
+{
+ exps->use_default = true;
+ return 0;
+}
+
+int
+exports_resolve_default (struct nbdkit_exports *exps, struct backend *b,
+ int readonly)
+{
+ const char *def = NULL;
+
+ if (exps->use_default) {
+ def = backend_default_export (b, readonly);
+ exps->use_default = false;
+ }
+ if (def)
+ return nbdkit_add_export (exps, def, NULL);
+ return 0;
+}
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 281a1459..1eb18bb0 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -76,6 +76,7 @@
nbdkit_stdio_safe;
nbdkit_strdup_intern;
nbdkit_strndup_intern;
+ nbdkit_use_default_export;
nbdkit_vdebug;
nbdkit_verror;
nbdkit_vprintf_intern;
diff --git a/server/plugins.c b/server/plugins.c
index 4cd93efc..15082acb 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -292,7 +292,7 @@ plugin_list_exports (struct backend *b, int readonly, int
default_only,
struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
if (!p->plugin.list_exports)
- return nbdkit_add_export (exports, "", NULL);
+ return nbdkit_use_default_export (exports);
return p->plugin.list_exports (readonly, default_only, exports);
}
diff --git a/server/test-public.c b/server/test-public.c
index ff0d2b28..141e9eb5 100644
--- a/server/test-public.c
+++ b/server/test-public.c
@@ -75,7 +75,14 @@ threadlocal_get_conn (void)
abort ();
}
-int connection_get_status (void)
+int
+connection_get_status (void)
+{
+ abort ();
+}
+
+const char *
+backend_default_export (struct backend *b, int readonly)
{
abort ();
}
diff --git a/plugins/sh/methods.c b/plugins/sh/methods.c
index e08baaf5..23e4f084 100644
--- a/plugins/sh/methods.c
+++ b/plugins/sh/methods.c
@@ -315,7 +315,7 @@ sh_list_exports (int readonly, int default_only,
return parse_exports (script, s, slen, exports);
case MISSING:
- return nbdkit_add_export (exports, "", NULL);
+ return nbdkit_use_default_export (exports);
case ERROR:
return -1;
--
2.28.0