Allowing 'nbdkit nbd nbd://localhost:10810/foo' is nicer than the
existing 'nbdkit nbd hostname=localhost port=10810 export=foo'. Also,
it is useful to let the user learn if nbdkit was built against libnbd
and in turn if libnbd used libxml2 for URI support.
Thankfully, the magic parameter support in nbdkit recognizes
nbd:///?socket=/path/to/sock as a parameter needing magic treatment;
however, this form of URI includes a shell glob character, so it is
still wise to quote the ? to prevent unintended results for (very
unusual) file system contents reached from the local directory.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
plugins/nbd/nbdkit-nbd-plugin.pod | 46 +++++++++++++++++++-----
plugins/nbd/nbd.c | 59 +++++++++++++++++++++++++++----
2 files changed, 90 insertions(+), 15 deletions(-)
diff --git a/plugins/nbd/nbdkit-nbd-plugin.pod b/plugins/nbd/nbdkit-nbd-plugin.pod
index 7baff981..98f45a35 100644
--- a/plugins/nbd/nbdkit-nbd-plugin.pod
+++ b/plugins/nbd/nbdkit-nbd-plugin.pod
@@ -4,8 +4,8 @@ nbdkit-nbd-plugin - nbdkit nbd plugin
=head1 SYNOPSIS
- nbdkit nbd { socket=SOCKNAME | hostname=HOST [port=PORT] } [export=NAME]
- [retry=N] [shared=BOOL]
+ nbdkit nbd { socket=SOCKNAME | hostname=HOST [port=PORT] | [uri=]URI }
+ [export=NAME] [retry=N] [shared=BOOL]
=head1 DESCRIPTION
@@ -27,6 +27,14 @@ is feasible that future additions will support encryption.
=head1 PARAMETERS
+One of B<socket>, B<hostname> or B<uri> must be provided to designate
+the server. The server can speak either new or old style
+protocol. C<uri=> is a magic config key and may be omitted in most
+cases. See L<nbdkit(1)/Magic parameters>.
+
+The following parameters are available whether or not the plugin was
+compiled against libnbd:
+
=over 4
=item B<socket=>SOCKNAME
@@ -42,14 +50,12 @@ Connect to the NBD server at the given remote C<HOST> using a
TCP socket.
When B<hostname> is supplied, use B<PORT> instead of the default port
10809.
-Either B<socket> or B<hostname> must be provided. The server can speak
-either new or old style protocol.
-
=item B<export=>NAME
If this parameter is given, and the server speaks new style protocol,
then connect to the named export instead of the default export (the
-empty string).
+empty string). If C<uri> is supplied, the export name should be
+embedded in the URI instead.
=item B<retry=>N
@@ -69,6 +75,24 @@ nbdkit will share that single connection.
=back
+The following parameters are only available if the plugin was compiled
+against libnbd:
+
+=over 4
+
+=item B<uri=>URI
+
+When B<uri> is supplied, decode B<URI> to determine the address to
+connect to. A URI can specify a TCP connection (such as
+C<nbd://localhost:10809/export>) or a Unix socket (such as
+C<nbd+unix:///export?socket=/path/to/sock>). Remember to use proper
+shell quoting to prevent B<URI> from accidentally being handled as a
+shell glob. The B<uri> parameter is only available when the plugin was
+compiled against libnbd with URI support; C<nbdkit --dump-plugin nbd>
+will contain C<libnbd_uri=1> if this is the case.
+
+=back
+
=head1 EXAMPLES
Expose the contents of an export served by an old style server over a
@@ -92,8 +116,8 @@ C<qemu-nbd> without B<-t> normally quits after the first
client, and
utilize a 5-second retry to give qemu-nbd time to create the socket:
( sock=`mktemp -u`
- nbdkit --exit-with-parent --filter=partition nbd socket=$sock \
- shared=1 retry=5 partition=1 &
+ nbdkit --exit-with-parent --filter=partition nbd \
+ nbd+unix:///\?socket=$sock shared=1 retry=5 partition=1 &
exec qemu-nbd -k $sock -f qcow2 /path/to/image.qcow2 )
Conversely, expose the contents of export I<foo> from a new style
@@ -108,6 +132,11 @@ client exits.
│ old client │ ────────▶│ nbdkit │ ────────▶│ new server │
└────────────┘ Unix └────────┘ TCP └────────────┘
+Learn which features are provided by libnbd by inspecting the
+C<libnbd_*> lines:
+
+ nbdkit --dump-plugin nbd
+
=head1 SEE ALSO
L<nbdkit(1)>,
@@ -115,6 +144,7 @@ L<nbdkit-captive(1)>,
L<nbdkit-filter(3)>,
L<nbdkit-tls(1)>,
L<nbdkit-plugin(3)>,
+L<libnbd(3)>,
L<qemu-nbd(1)>.
=head1 AUTHORS
diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
index 5d79c981..0f54805c 100644
--- a/plugins/nbd/nbd.c
+++ b/plugins/nbd/nbd.c
@@ -87,6 +87,9 @@ static char *sockname;
static const char *hostname;
static const char *port;
+/* Connect to server via URI */
+static const char *uri;
+
/* Name of export on remote server, default '', ignored for oldstyle */
static const char *export;
@@ -109,9 +112,9 @@ nbdplug_unload (void)
}
/* Called for each key=value passed on the command line. This plugin
- * accepts socket=<sockname> or hostname=<hostname>/port=<port>
- * (exactly one connection required), and optional parameters
- * export=<name>, retry=<n> and shared=<bool>.
+ * accepts socket=<sockname>, hostname=<hostname>/port=<port>, or
+ * [uri=]<uri> (exactly one connection required), and optional
+ * parameters export=<name>, retry=<n> and shared=<bool>.
*/
static int
nbdplug_config (const char *key, const char *value)
@@ -130,6 +133,8 @@ nbdplug_config (const char *key, const char *value)
hostname = value;
else if (strcmp (key, "port") == 0)
port = value;
+ else if (strcmp (key, "uri") == 0)
+ uri = value;
else if (strcmp (key, "export") == 0)
export = value;
else if (strcmp (key, "retry") == 0) {
@@ -165,19 +170,40 @@ nbdplug_config_complete (void)
nbdkit_error ("cannot mix Unix socket and TCP hostname/port
parameters");
return -1;
}
+ else if (uri) {
+ nbdkit_error ("cannot mix Unix socket and URI parameters");
+ return -1;
+ }
if (strlen (sockname) > sizeof sock.sun_path) {
nbdkit_error ("socket file name too large");
return -1;
}
}
- else {
- if (!hostname) {
- nbdkit_error ("must supply socket= or hostname= of external NBD
server");
+ else if (hostname) {
+ if (uri) {
+ nbdkit_error ("cannot mix TCP hostname/port and URI parameters");
return -1;
}
if (!port)
port = "10809";
}
+ else if (uri) {
+ struct nbd_handle *nbd = nbd_create ();
+
+ if (!nbd) {
+ nbdkit_error ("unable to query libnbd details: %s", nbd_get_error ());
+ return -1;
+ }
+ if (!nbd_supports_uri (nbd)) {
+ nbdkit_error ("libnbd was compiled without uri support");
+ nbd_close (nbd);
+ return -1;
+ }
+ nbd_close (nbd);
+ } else {
+ nbdkit_error ("must supply socket=, hostname= or uri= of external NBD
server");
+ return -1;
+ }
if (!export)
export = "";
@@ -188,6 +214,7 @@ nbdplug_config_complete (void)
}
#define nbdplug_config_help \
+ "[uri=]<URI> URI of an NBD socket to connect to (if
supported).\n" \
"socket=<SOCKNAME> The Unix socket to connect to.\n" \
"hostname=<HOST> The hostname for the TCP socket to connect
to.\n" \
"port=<PORT> TCP port or service name to use (default
10809).\n" \
@@ -196,6 +223,20 @@ nbdplug_config_complete (void)
"shared=<BOOL> True to share one server connection among all
clients,\n" \
" rather than a connection per client (default
false).\n" \
+static void
+nbdplug_dump_plugin (void)
+{
+ struct nbd_handle *nbd = nbd_create ();
+
+ if (!nbd) {
+ nbdkit_error ("unable to query libnbd details: %s", nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+ printf ("libnbd_version=%s\n", nbd_get_version (nbd));
+ printf ("libnbd_uri=%d\n", nbd_supports_uri (nbd));
+ nbd_close (nbd);
+}
+
#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
/* Reader loop. */
@@ -386,7 +427,9 @@ nbdplug_open_handle (int readonly)
goto err;
if (nbd_add_meta_context (h->nbd, "base:allocation") == -1)
goto err;
- if (sockname)
+ if (uri)
+ r = nbd_connect_uri (h->nbd, uri);
+ else if (sockname)
r = nbd_connect_unix (h->nbd, sockname);
else
r = nbd_connect_tcp (h->nbd, hostname, port);
@@ -720,6 +763,8 @@ static struct nbdkit_plugin plugin = {
.config = nbdplug_config,
.config_complete = nbdplug_config_complete,
.config_help = nbdplug_config_help,
+ .magic_config_key = "uri",
+ .dump_plugin = nbdplug_dump_plugin,
.open = nbdplug_open,
.close = nbdplug_close,
.get_size = nbdplug_get_size,
--
2.20.1