Use the just-added nbd_opt_list_meta_context() API to give more
details about each export (matching what 'qemu-nbd --list' already
does). Note that this requires some shuffling: listing meta exports
requires being in opt mode, but is easiest to do in list_one_export(),
which requires setting opt_mode in more code paths, and deferring
ready mode until the last possible minute during get_content().
As written, the code displays the list in reverse order from how the
server presented it, thanks to my use of a simple linked list. We
could use a different data type if we don't like the data being
reversed, although the information being presented really is a set
with no inherent meaning to its presentation order.
---
info/info-json.sh | 2 +
info/info-list-json.sh | 4 ++
info/info-list.sh | 2 +
info/info-text.sh | 2 +
info/nbdinfo.c | 89 ++++++++++++++++++++++++++++++++++++++----
5 files changed, 91 insertions(+), 8 deletions(-)
diff --git a/info/info-json.sh b/info/info-json.sh
index 0f1c9fd..10bbac0 100755
--- a/info/info-json.sh
+++ b/info/info-json.sh
@@ -33,3 +33,5 @@ jq . < $out
test $( jq -r '.protocol' < $out ) != "newstyle"
test $( jq -r '.exports[0]."export-size"' < $out ) !=
"null"
test $( jq -r '.exports[0].is_read_only' < $out ) = "true"
+test $( jq -r '.exports[0].contexts[] | select(. == "base:allocation")'
\
+ < $out ) = "base:allocation"
diff --git a/info/info-list-json.sh b/info/info-list-json.sh
index 4cf5cc0..dc4d25a 100755
--- a/info/info-list-json.sh
+++ b/info/info-list-json.sh
@@ -38,6 +38,8 @@ jq . < $out
grep '"export-name": "hello"' $out
grep '"description": "world"' $out
grep '"export-size": 1048576' $out
+test $( jq -r '.exports[0].contexts[] | select(. == "base:allocation")'
\
+ < $out ) = "base:allocation"
# ...and again with the export name included
nbdkit -U - -e hello --filter=exportname memory 1M \
@@ -49,3 +51,5 @@ jq . < $out
grep '"export-name": "hello"' $out
grep '"description": "world"' $out
grep '"export-size": 1048576' $out
+test $( jq -r '.exports[0].contexts[] | select(. == "base:allocation")'
\
+ < $out ) = "base:allocation"
diff --git a/info/info-list.sh b/info/info-list.sh
index a010546..f6e1ce8 100755
--- a/info/info-list.sh
+++ b/info/info-list.sh
@@ -37,6 +37,7 @@ cat $out
grep 'export="hello":' $out
grep 'description: world' $out
grep 'export-size: 1048576' $out
+sed -n '/contexts:/ { N; p; q }; $ q1' $out
# ...and again with the export name included
nbdkit -U - -e hello --filter=exportname memory 1M \
@@ -48,3 +49,4 @@ cat $out
grep 'export="hello":' $out
grep 'description: world' $out
grep 'export-size: 1048576' $out
+sed -n '/contexts:/ { N; p; q }; $ q1' $out
diff --git a/info/info-text.sh b/info/info-text.sh
index 2bebbf1..a476ffa 100755
--- a/info/info-text.sh
+++ b/info/info-text.sh
@@ -31,3 +31,5 @@ nbdkit -U - memory size=1M \
--run '$VG nbdinfo "nbd+unix:///?socket=$unixsocket"' > $out
cat $out
grep "export-size: $((1024*1024))" $out
+sed -n '/contexts:/ { N; p; q }; $ q1' $out
+
diff --git a/info/nbdinfo.c b/info/nbdinfo.c
index 6f5d191..5358f82 100644
--- a/info/nbdinfo.c
+++ b/info/nbdinfo.c
@@ -37,12 +37,18 @@ static bool json_output = false;
static const char *map = NULL;
static bool size_only = false;
+struct context_list {
+ char *name;
+ struct context_list *next;
+};
+
static struct export_list {
size_t len;
char **names;
char **descs;
} export_list;
+static int collect_context (void *opaque, const char *name);
static int collect_export (void *opaque, const char *name,
const char *desc);
static void list_one_export (struct nbd_handle *nbd, const char *desc,
@@ -207,10 +213,10 @@ main (int argc, char *argv[])
nbd_set_uri_allow_local_file (nbd, true); /* Allow ?tls-psk-file. */
/* Set optional modes in the handle. */
- if (list_all)
+ if (!map && !size_only) {
nbd_set_opt_mode (nbd, true);
- if (!map && !size_only)
nbd_set_full_info (nbd, true);
+ }
if (map)
nbd_add_meta_context (nbd, map);
@@ -320,10 +326,32 @@ main (int argc, char *argv[])
}
free (export_list.names);
free (export_list.descs);
+ nbd_opt_abort (nbd);
+ nbd_shutdown (nbd, 0);
nbd_close (nbd);
exit (EXIT_SUCCESS);
}
+static int
+collect_context (void *opaque, const char *name)
+{
+ struct context_list **head = opaque;
+ struct context_list *next = malloc (sizeof *next);
+
+ if (!next) {
+ perror ("malloc");
+ exit (EXIT_FAILURE);
+ }
+ next->name = strdup (name);
+ if (!next->name) {
+ perror ("strdup");
+ exit (EXIT_FAILURE);
+ }
+ next->next = *head;
+ *head = next;
+ return 0;
+}
+
static int
collect_export (void *opaque, const char *name, const char *desc)
{
@@ -368,8 +396,15 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
int can_cache, can_df, can_fast_zero, can_flush, can_fua,
can_multi_conn, can_trim, can_zero;
int64_t block_minimum, block_preferred, block_maximum;
+ struct context_list *contexts = NULL;
+ bool show_context = false;
/* Collect the metadata we are going to display. */
+ if (nbd_aio_is_negotiating (nbd) &&
+ nbd_opt_info (nbd) == -1) {
+ fprintf (stderr, "%s\n", nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
size = nbd_get_size (nbd);
if (size == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
@@ -387,7 +422,6 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
/* Get description if list didn't already give us one */
if (!desc)
desc = export_desc = nbd_get_export_description (nbd);
- content = get_content (nbd, size);
is_rotational = nbd_is_rotational (nbd);
is_read_only = nbd_is_read_only (nbd);
can_cache = nbd_can_cache (nbd);
@@ -401,6 +435,12 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
block_minimum = nbd_get_block_size (nbd, LIBNBD_SIZE_MINIMUM);
block_preferred = nbd_get_block_size (nbd, LIBNBD_SIZE_PREFERRED);
block_maximum = nbd_get_block_size (nbd, LIBNBD_SIZE_MAXIMUM);
+ if (nbd_opt_list_meta_context (nbd, (nbd_context_callback) {
+ .callback = collect_context, .user_data = &contexts}) != -1)
+ show_context = true;
+
+ /* Get content last, as it moves the connection out of negotiating */
+ content = get_content (nbd, size);
if (!json_output) {
printf ("export=");
@@ -412,6 +452,11 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
printf ("\texport-size: %" PRIi64 "\n", size);
if (content)
printf ("\tcontent: %s\n", content);
+ if (show_context) {
+ printf ("\tcontexts:\n");
+ for (struct context_list *next = contexts; next; next = next->next)
+ printf ("\t\t%s\n", next->name);
+ }
if (is_rotational >= 0)
printf ("\t%s: %s\n", "is_rotational", is_rotational ?
"true" : "false");
if (is_read_only >= 0)
@@ -460,6 +505,18 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
printf (",\n");
}
+ if (show_context) {
+ printf ("\t\"contexts\": [\n");
+ for (struct context_list *next = contexts; next; next = next->next) {
+ printf ("\t\t");
+ print_json_string (next->name);
+ if (next->next)
+ putchar(',');
+ putchar('\n');
+ }
+ printf ("\t],\n");
+ }
+
if (is_rotational >= 0)
printf ("\t\"%s\": %s,\n",
"is_rotational", is_rotational ? "true" :
"false");
@@ -508,6 +565,12 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
printf ("\t},\n");
}
+ while (contexts) {
+ struct context_list *next = contexts->next;
+ free (contexts->name);
+ free (contexts);
+ contexts = next;
+ }
free (content);
free (export_name);
free (export_desc);
@@ -534,17 +597,16 @@ list_all_exports (struct nbd_handle *nbd1, const char *uri)
}
nbd_set_uri_allow_local_file (nbd2, true); /* Allow ?tls-psk-file. */
nbd_set_opt_mode (nbd2, true);
+ nbd_set_full_info (nbd2, true);
if (nbd_connect_uri (nbd2, uri) == -1 ||
- nbd_set_export_name (nbd2, name) == -1 ||
- nbd_opt_go (nbd2) == -1) {
+ nbd_set_export_name (nbd2, name) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
}
}
else { /* ! probe_content */
- if (nbd_set_export_name (nbd1, name) == -1 ||
- nbd_opt_info (nbd1) == -1) {
+ if (nbd_set_export_name (nbd1, name) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
}
@@ -555,8 +617,10 @@ list_all_exports (struct nbd_handle *nbd1, const char *uri)
list_one_export (nbd2, export_list.descs[i], i == 0,
i + 1 == export_list.len);
- if (probe_content)
+ if (probe_content) {
+ nbd_shutdown (nbd2, 0);
nbd_close (nbd2);
+ }
}
}
@@ -587,6 +651,9 @@ print_json_string (const char *s)
* If file(1) doesn't work just return NULL because this is
* best-effort. This function will exit with an error on things which
* shouldn't fail, such as out of memory or creating local files.
+ *
+ * Must be called late, and only once per connection, as this kicks
+ * the connection from negotiating to ready.
*/
static char *
get_content (struct nbd_handle *nbd, int64_t size)
@@ -603,6 +670,12 @@ get_content (struct nbd_handle *nbd, int64_t size)
if (!probe_content)
return NULL;
+ if (nbd_aio_is_negotiating (nbd) &&
+ nbd_opt_go (nbd) == -1) {
+ fprintf (stderr, "%s\n", nbd_get_error ());
+ exit (EXIT_FAILURE);
+ }
+
/* Write the first part of the NBD export to a temporary file. */
fd = mkstemp (template);
if (fd == -1) {
--
2.28.0