This will make a best-effort attempt to construct an NBD URI for
connecting back to the current server. In many cases this is not
really possible (eg. if we were connected with nbd_connect_socket),
and it's not guaranteed to be correct.
---
generator/API.ml | 31 ++++++++++-
generator/C.ml | 8 +--
lib/uri.c | 129 +++++++++++++++++++++++++++++++++++++++++++
tests/connect-tcp.c | 20 +++++++
tests/connect-unix.c | 20 +++++++
tests/connect-uri.c | 17 ++++++
6 files changed, 218 insertions(+), 7 deletions(-)
diff --git a/generator/API.ml b/generator/API.ml
index dd66fdb..2f1baa8 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -1383,7 +1383,7 @@ compiled with gnutls; you can test whether this is the case
with L<nbd_supports_tls(3)>.";
see_also = [URLLink
"https://github.com/NetworkBlockDevice/nbd/blob/master/doc/uri.md";
Link "set_export_name"; Link "set_tls";
- Link "set_opt_mode"];
+ Link "set_opt_mode"; Link "get_uri"];
};
"connect_unix", {
@@ -2875,7 +2875,30 @@ to support TLS encryption, or false if not.";
longdesc = "\
Returns true if libnbd was compiled with libxml2 which is required
to support NBD URIs, or false if not.";
- see_also = [Link "connect_uri"; Link "aio_connect_uri"];
+ see_also = [Link "connect_uri"; Link "aio_connect_uri";
+ Link "get_uri"];
+ };
+
+ "get_uri", {
+ default_call with
+ args = []; ret = RString;
+ permitted_states = [ Connecting; Negotiating; Connected; Closed; Dead ];
+ shortdesc = "construct an NBD URI for a connection";
+ longdesc = "\
+This makes a best effort attempt to construct an NBD URI which
+could be used to connect to this NBD server (eg. using
+L<nbd_connect_uri(3)>).
+
+The URI returned is not guaranteed to work, and in some cases
+(eg. if connected with L<nbd_connect_socket(3)>) it is not possible
+at all. Even if a URI is returned it may not be optimal.
+
+On error, L<nbd_get_errno(3)> will be set to C<ENOTSUP> if the
+library was compiled without support for URIs. In other error
+cases, L<nbd_get_errno(3)> and L<nbd_get_error(3)> should contain
+information about why constructing a URI was not possible.";
+ see_also = [Link "connect_uri"; Link "aio_connect_uri";
+ Link "supports_uri"];
};
]
@@ -3009,6 +3032,7 @@ let first_version = [
(* Added in 1.7.x development cycle, will be stable and supported in 1.8. *)
"set_private_data", (1, 8);
"get_private_data", (1, 8);
+ "get_uri", (1, 8);
(* These calls are proposed for a future version of libnbd, but
* have not been added to any released version so far.
@@ -3045,7 +3069,8 @@ let pod_of_link = function
let verify_link =
let pages = List.map fst handle_calls in
function
- | Link "create" | Link "close" -> ()
+ | Link "create" | Link "close"
+ | Link "get_error" | Link "get_errno" -> ()
| Link page ->
if not (List.mem page pages) then
failwithf "verify_link: page nbd_%s does not exist" page
diff --git a/generator/C.ml b/generator/C.ml
index fe8eafc..3e9975f 100644
--- a/generator/C.ml
+++ b/generator/C.ml
@@ -416,10 +416,10 @@ let generate_lib_unlocked_h () =
pr "\n";
pr "#endif /* LIBNBD_UNLOCKED_H */\n"
-let permitted_state_text permitted_states =
+let permitted_state_text ?(fold=false) permitted_states =
assert (permitted_states <> []);
- String.concat
- ", or "
+ let sep = if fold then ", or\n" else ", or " in
+ String.concat sep
(List.map (
function
| Created -> "newly created"
@@ -913,7 +913,7 @@ let generate_docs_nbd_pod name { args; optargs; ret;
pr "=head1 HANDLE STATE\n";
pr "\n";
pr "The handle must be\n";
- pr "%s,\n" (permitted_state_text permitted_states);
+ pr "%s,\n" (permitted_state_text ~fold:true permitted_states);
pr "otherwise this call will return an error.\n";
pr "\n"
);
diff --git a/lib/uri.c b/lib/uri.c
index 9f5a290..e75b04f 100644
--- a/lib/uri.c
+++ b/lib/uri.c
@@ -25,6 +25,13 @@
#include <string.h>
#include <errno.h>
#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#endif
#include "internal.h"
#include "vector.h"
@@ -335,6 +342,121 @@ cleanup:
return ret;
}
+static int
+append_query_params (char **query_params, const char *key, const char *value)
+{
+ char *old_query_params = *query_params;
+
+ if (asprintf (query_params, "%s%s%s=%s",
+ old_query_params ? : "",
+ old_query_params ? "&" : "",
+ key, value) == -1)
+ return -1;
+ free (old_query_params);
+ return 0;
+}
+
+/* This is best effort. If we didn't save enough information when
+ * connecting then return NULL but try to set errno and the error
+ * string to something useful.
+ */
+char *
+nbd_unlocked_get_uri (struct nbd_handle *h)
+{
+ xmlURI uri = { 0 };
+ bool using_tls;
+ char *server = NULL;
+ char *query_params = NULL;
+ char *path = NULL;
+ char *ret = NULL;
+
+ if (h->tls == 2) /* TLS == require */
+ using_tls = true;
+ else if (h->tls_negotiated)
+ using_tls = true;
+ else
+ using_tls = false;
+
+ /* Set scheme, server or socket. */
+ if (h->hostname && h->port) {
+ uri.scheme = using_tls ? "nbds" : "nbd";
+ if (asprintf (&server, "%s:%s", h->hostname, h->port) == -1) {
+ set_error (errno, "asprintf");
+ goto out;
+ }
+ uri.server = server;
+ }
+ else if (h->connaddrlen > 0) {
+ switch (h->connaddr.ss_family) {
+ case AF_UNIX: {
+ struct sockaddr_un *sun = (struct sockaddr_un *) &h->connaddr;
+
+ uri.scheme = using_tls ? "nbds+unix" : "nbd+unix";
+ if (append_query_params (&query_params, "socket", sun->sun_path)
== -1) {
+ set_error (errno, "asprintf");
+ goto out;
+ }
+ /* You have to set this otherwise xmlSaveUri generates bogus
+ * URIs "nbd+unix:/?socket=..."
+ */
+ uri.server = "";
+ break;
+ }
+#ifdef AF_VSOCK
+ case AF_VSOCK: {
+ struct sockaddr_vm *svm = (struct sockaddr_vm *) &h->connaddr;
+
+ uri.scheme = using_tls ? "nbds+vsock" : "nbd+vsock";
+ if (asprintf (&server, "%u:%u", svm->svm_cid, svm->svm_port) ==
-1) {
+ set_error (errno, "asprintf");
+ goto out;
+ }
+ uri.server = server;
+ break;
+ }
+#endif
+ default:
+ set_error (EAFNOSUPPORT,
+ "address family %d not supported", h->connaddr.ss_family);
+ goto out;
+ }
+ }
+ else {
+ set_error (EINVAL, "cannot construct a URI for this connection type");
+ goto out;
+ }
+
+ /* Set other fields. */
+ if (h->tls_username)
+ uri.user = h->tls_username;
+ if (h->export_name) {
+ if (asprintf (&path, "/%s", h->export_name) == -1) {
+ set_error (errno, "asprintf");
+ goto out;
+ }
+ uri.path = path;
+ }
+ if (h->tls_psk_file) {
+ if (append_query_params (&query_params,
+ "tls-psk-file", h->tls_psk_file) == -1) {
+ set_error (errno, "asprintf");
+ goto out;
+ }
+ }
+
+ uri.query_raw = query_params;
+
+ /* Construct the final URI and return it. */
+ ret = (char *) xmlSaveUri (&uri);
+ if (ret == NULL)
+ set_error (errno, "xmlSaveUri failed");
+ out:
+ free (server);
+ free (query_params);
+ free (path);
+ return ret;
+}
+
#else /* !HAVE_LIBXML2 */
#define NOT_SUPPORTED_ERROR \
@@ -354,4 +476,11 @@ nbd_unlocked_aio_connect_uri (struct nbd_handle *h, const char
*raw_uri)
return -1;
}
+char *
+nbd_unlocked_get_uri (struct nbd_handle *h)
+{
+ set_error (ENOTSUP, NOT_SUPPORTED_ERROR);
+ return NULL;
+}
+
#endif /* !HAVE_LIBXML2 */
diff --git a/tests/connect-tcp.c b/tests/connect-tcp.c
index d7a36e4..e96b1ec 100644
--- a/tests/connect-tcp.c
+++ b/tests/connect-tcp.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
@@ -38,6 +39,7 @@ main (int argc, char *argv[])
char port_str[16];
pid_t pid;
size_t i;
+ char *actual_uri, *expected_uri;
unlink (PIDFILE);
@@ -79,6 +81,24 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
+ /* libnbd should be able to construct a URI for this connection. */
+ if (asprintf (&expected_uri, "nbd://localhost:%s/", port_str) == -1) {
+ perror ("asprintf");
+ exit (EXIT_FAILURE);
+ }
+ actual_uri = nbd_get_uri (nbd);
+ if (actual_uri == NULL) {
+ fprintf (stderr, "%s\n", nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+ if (strcmp (actual_uri, expected_uri) != 0) {
+ fprintf (stderr, "%s: actual URI %s != expected URI %s\n",
+ argv[0], actual_uri, expected_uri);
+ exit (EXIT_FAILURE);
+ }
+ free (actual_uri);
+ free (expected_uri);
+
if (nbd_shutdown (nbd, 0) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
diff --git a/tests/connect-unix.c b/tests/connect-unix.c
index 8c18166..0000b98 100644
--- a/tests/connect-unix.c
+++ b/tests/connect-unix.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <fcntl.h>
#include <unistd.h>
@@ -36,6 +37,7 @@ main (int argc, char *argv[])
struct nbd_handle *nbd;
pid_t pid;
size_t i;
+ char *actual_uri, *expected_uri;
if (mkstemp (socket) == -1) {
perror (socket);
@@ -77,6 +79,24 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
+ /* libnbd should be able to construct a URI for this connection. */
+ if (asprintf (&expected_uri, "nbd+unix:///?socket=%s", socket) == -1) {
+ perror ("asprintf");
+ exit (EXIT_FAILURE);
+ }
+ actual_uri = nbd_get_uri (nbd);
+ if (actual_uri == NULL) {
+ fprintf (stderr, "%s\n", nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+ if (strcmp (actual_uri, expected_uri) != 0) {
+ fprintf (stderr, "%s: actual URI %s != expected URI %s\n",
+ argv[0], actual_uri, expected_uri);
+ exit (EXIT_FAILURE);
+ }
+ free (actual_uri);
+ free (expected_uri);
+
if (nbd_shutdown (nbd, 0) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
diff --git a/tests/connect-uri.c b/tests/connect-uri.c
index 6e7d168..9093247 100644
--- a/tests/connect-uri.c
+++ b/tests/connect-uri.c
@@ -35,6 +35,7 @@ main (int argc, char *argv[])
struct nbd_handle *nbd;
pid_t pid;
size_t i;
+ char *get_uri;
#ifdef SOCKET
unlink (SOCKET);
@@ -89,6 +90,22 @@ main (int argc, char *argv[])
}
}
+ /* Usually the URI returned by nbd_get_uri should be the same as the
+ * one passed to nbd_connect_uri, or at least it will be in our test
+ * cases.
+ */
+ get_uri = nbd_get_uri (nbd);
+ if (get_uri == NULL) {
+ fprintf (stderr, "%s\n", nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+ if (strcmp (URI, get_uri) != 0) {
+ fprintf (stderr, "%s: connect URI %s != get URI %s\n",
+ argv[0], URI, get_uri);
+ exit (EXIT_FAILURE);
+ }
+ free (get_uri);
+
if (nbd_shutdown (nbd, 0) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
--
2.29.0.rc2