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 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs