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 libnbd was in use (although even
then, it does not reveal whether libnbd in turn used libxml2 to
actually have working URI support). Perhaps a future libnbd header
version will expose version information and whether uri support is
available, rather than our current best-effort guess.
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 | 33 +++++++++++++++++++------
plugins/nbd/nbd.c | 41 +++++++++++++++++++++++++------
2 files changed, 60 insertions(+), 14 deletions(-)
diff --git a/plugins/nbd/nbdkit-nbd-plugin.pod b/plugins/nbd/nbdkit-nbd-plugin.pod
index 7baff98..c78fdca 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,11 @@ 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>.
+
=over 4
=item B<socket=>SOCKNAME
@@ -42,14 +47,22 @@ 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<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>) or a Unix socket (such as
+C<nbd+unix:///?socket=/path/to/sock>). The B<uri> parameter is only
+available when the plugin was compiled against libnbd, although
+whether it works also depends on whether libnbd was compiled against
+libxml2.
=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
@@ -92,8 +105,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 +121,11 @@ client exits.
│ old client │ ────────▶│ nbdkit │ ────────▶│ new server │
└────────────┘ Unix └────────┘ TCP └────────────┘
+Look for the C<libnbd_version> line to learn if the nbd plugin was
+compiled against libnbd for C<uri> support:
+
+ nbdkit --dump-plugin nbd
+
=head1 SEE ALSO
L<nbdkit(1)>,
@@ -115,6 +133,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 7974b58..2268bd0 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,27 @@ 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) {
+ nbdkit_error ("must supply socket=, hostname= or uri= of external NBD
server");
+ return -1;
+ }
if (!export)
export = "";
@@ -188,6 +201,7 @@ nbdplug_config_complete (void)
}
#define nbdplug_config_help \
+ "[uri=]<URI> The URI of an NBD socket to connect to.\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 +210,15 @@ 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)
+{
+ /* XXX libnbd 0.1.2 doesn't expose a version in libnbd.h, nor any
+ * way to tell if nbd_connect_uri will work thanks to libxml2
+ */
+ printf ("libnbd_version=%s\n", "(unknown, at least 0.1.2)");
+}
+
#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
/* Reader loop. */
@@ -386,7 +409,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);
@@ -733,6 +758,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