[Libguestfs] [libnbd PATCH v2 2/2] info: List available meta-contexts

Eric Blake eblake at redhat.com
Fri Oct 2 13:20:13 UTC 2020


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




More information about the Libguestfs mailing list