New APIs: guestfs_first_private, guestfs_next_private to walk over
authorRichard W.M. Jones <rjones@redhat.com>
Mon, 14 Mar 2011 13:19:47 +0000 (13:19 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 15 Mar 2011 12:16:50 +0000 (12:16 +0000)
the private data area.

This commit adds new APIs for walking over the keys and pointers in
the private data area associated with each handle (note this is only
applicable to the C API).

.gitignore
capitests/Makefile.am
capitests/test-private-data.c [new file with mode: 0644]
generator/generator_c.ml
src/guestfs-internal.h
src/guestfs.c
src/guestfs.pod

index 1511c4a..7df10b2 100644 (file)
@@ -17,6 +17,7 @@ capitests/test-config
 capitests/test-create-handle
 capitests/test-just-header
 capitests/test-last-errno
+capitests/test-private-data
 capitests/test*.img
 capitests/tests
 capitests/tests.c
index 542c4fb..83e62c8 100644 (file)
@@ -30,7 +30,8 @@ check_PROGRAMS = \
        test-create-handle \
        test-config \
        test-add-drive-opts \
-       test-last-errno
+       test-last-errno \
+       test-private-data
 
 TESTS = \
        tests \
@@ -38,7 +39,8 @@ TESTS = \
        test-create-handle \
        test-config \
        test-add-drive-opts \
-       test-last-errno
+       test-last-errno \
+       test-private-data
 
 # The API behind this test is not baked yet.
 #if HAVE_LIBVIRT
@@ -103,6 +105,13 @@ test_last_errno_CFLAGS = \
 test_last_errno_LDADD = \
        $(top_builddir)/src/libguestfs.la
 
+test_private_data_SOURCES = test-private-data.c
+test_private_data_CFLAGS = \
+       -I$(top_srcdir)/src -I$(top_builddir)/src \
+       $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_private_data_LDADD = \
+       $(top_builddir)/src/libguestfs.la
+
 #if HAVE_LIBVIRT
 #test_add_libvirt_dom_SOURCES = test-add-libvirt-dom.c
 #test_add_libvirt_dom_CFLAGS = \
diff --git a/capitests/test-private-data.c b/capitests/test-private-data.c
new file mode 100644 (file)
index 0000000..fad88b5
--- /dev/null
@@ -0,0 +1,85 @@
+/* libguestfs
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test aspects of the private data area API. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "guestfs.h"
+
+#define PREFIX "test_"
+
+int
+main (int argc, char *argv[])
+{
+  guestfs_h *g;
+  const char *key;
+  void *data;
+  size_t count;
+
+  g = guestfs_create ();
+  if (g == NULL) {
+    fprintf (stderr, "failed to create handle\n");
+    exit (EXIT_FAILURE);
+  }
+
+  guestfs_set_private (g, PREFIX "a", (void *) 1);
+  guestfs_set_private (g, PREFIX "b", (void *) 2);
+  guestfs_set_private (g, PREFIX "c", (void *) 3);
+  guestfs_set_private (g, PREFIX "a", (void *) 4); /* overwrites previous */
+
+  /* Check we can fetch keys. */
+  assert (guestfs_get_private (g, PREFIX "a") == (void *) 4);
+  assert (guestfs_get_private (g, PREFIX "b") == (void *) 2);
+  assert (guestfs_get_private (g, PREFIX "c") == (void *) 3);
+  assert (guestfs_get_private (g, PREFIX "d") == NULL);
+
+  /* Check we can count keys by iterating. */
+  count = 0;
+  data = guestfs_first_private (g, &key);
+  while (data != NULL) {
+    if (strncmp (key, PREFIX, strlen (PREFIX)) == 0)
+      count++;
+    data = guestfs_next_private (g, &key);
+  }
+  assert (count == 3);
+
+  /* Delete some keys. */
+  guestfs_set_private (g, PREFIX "a", NULL);
+  guestfs_set_private (g, PREFIX "b", NULL);
+
+  /* Count them again. */
+  count = 0;
+  data = guestfs_first_private (g, &key);
+  while (data != NULL) {
+    if (strncmp (key, PREFIX, strlen (PREFIX)) == 0)
+      count++;
+    data = guestfs_next_private (g, &key);
+  }
+  assert (count == 1);
+
+  guestfs_close (g);
+
+  exit (EXIT_SUCCESS);
+}
index 9b88376..656e752 100644 (file)
@@ -434,6 +434,10 @@ extern void guestfs_set_progress_callback (guestfs_h *g, guestfs_progress_cb cb,
 extern void guestfs_set_private (guestfs_h *g, const char *key, void *data);
 #define LIBGUESTFS_HAVE_GET_PRIVATE 1
 extern void *guestfs_get_private (guestfs_h *g, const char *key);
+#define LIBGUESTFS_HAVE_FIRST_PRIVATE 1
+extern void *guestfs_first_private (guestfs_h *g, const char **key_rtn);
+#define LIBGUESTFS_HAVE_NEXT_PRIVATE 1
+extern void *guestfs_next_private (guestfs_h *g, const char **key_rtn);
 
 /* Structures. */
 ";
@@ -1359,11 +1363,13 @@ and generate_linker_script () =
   let globals = [
     "guestfs_create";
     "guestfs_close";
+    "guestfs_first_private";
     "guestfs_get_error_handler";
     "guestfs_get_out_of_memory_handler";
     "guestfs_get_private";
     "guestfs_last_errno";
     "guestfs_last_error";
+    "guestfs_next_private";
     "guestfs_set_close_callback";
     "guestfs_set_error_handler";
     "guestfs_set_launch_done_callback";
index 0eb395b..297bed0 100644 (file)
@@ -154,6 +154,7 @@ struct guestfs_h
 
   /* Private data area. */
   struct hash_table *pda;
+  struct pda_entry *pda_next;
 };
 
 /* Per-filesystem data stored for inspect_os. */
index 4085506..97762ca 100644 (file)
@@ -806,6 +806,47 @@ guestfs_get_private (guestfs_h *g, const char *key)
     return NULL;
 }
 
+/* Iterator. */
+void *
+guestfs_first_private (guestfs_h *g, const char **key_rtn)
+{
+  if (g->pda == NULL)
+    return NULL;
+
+  g->pda_next = hash_get_first (g->pda);
+
+  /* Ignore any keys with NULL data pointers. */
+  while (g->pda_next && g->pda_next->data == NULL)
+    g->pda_next = hash_get_next (g->pda, g->pda_next);
+
+  if (g->pda_next == NULL)
+    return NULL;
+
+  *key_rtn = g->pda_next->key;
+  return g->pda_next->data;
+}
+
+void *
+guestfs_next_private (guestfs_h *g, const char **key_rtn)
+{
+  if (g->pda == NULL)
+    return NULL;
+
+  if (g->pda_next == NULL)
+    return NULL;
+
+  /* Walk to the next key with a non-NULL data pointer. */
+  do {
+    g->pda_next = hash_get_next (g->pda, g->pda_next);
+  } while (g->pda_next && g->pda_next->data == NULL);
+
+  if (g->pda_next == NULL)
+    return NULL;
+
+  *key_rtn = g->pda_next->key;
+  return g->pda_next->data;
+}
+
 /* When tracing, be careful how we print BufferIn parameters which
  * usually contain large amounts of binary data (RHBZ#646822).
  */
index 0b3b654..b0a408d 100644 (file)
@@ -1804,8 +1804,9 @@ print these numbers in error messages or debugging messages.
 =head1 PRIVATE DATA AREA
 
 You can attach named pieces of private data to the libguestfs handle,
-and fetch them by name for the lifetime of the handle.  This is called
-the private data area and is only available from the C API.
+fetch them by name, and walk over them, for the lifetime of the
+handle.  This is called the private data area and is only available
+from the C API.
 
 To attach a named piece of data, use the following call:
 
@@ -1836,8 +1837,100 @@ caller must either free it before calling L</guestfs_close> or must
 set up a close callback to do it (see L</guestfs_set_close_callback>,
 and note that only one callback can be registered for a handle).
 
-The private data area is implemented using a hash table, and should be
-reasonably efficient for moderate numbers of keys.
+To walk over all entries, use these two functions:
+
+ void *guestfs_first_private (guestfs_h *g, const char **key_rtn);
+
+ void *guestfs_next_private (guestfs_h *g, const char **key_rtn);
+
+C<guestfs_first_private> returns the first key, pointer pair ("first"
+does not have any particular meaning -- keys are not returned in any
+defined order).  A pointer to the key is returned in C<*key_rtn> and
+the corresponding data pointer is returned from the function.  C<NULL>
+is returned if there are no keys stored in the handle.
+
+C<guestfs_next_private> returns the next key, pointer pair.  The
+return value of this function is also C<NULL> is there are no further
+entries to return.
+
+Notes about walking over entries:
+
+=over 4
+
+=item *
+
+You must not call C<guestfs_set_private> while walking over the
+entries.
+
+=item *
+
+The handle maintains an internal iterator which is reset when you call
+C<guestfs_first_private>.  This internal iterator is invalidated when
+you call C<guestfs_set_private>.
+
+=item *
+
+If you have set the data pointer associated with a key to C<NULL>, ie:
+
+ guestfs_set_private (g, key, NULL);
+
+then that C<key> is not returned when walking.
+
+=item *
+
+C<*key_rtn> is only valid until the next call to
+C<guestfs_first_private>, C<guestfs_next_private> or
+C<guestfs_set_private>.
+
+=back
+
+The following example code shows how to print all keys and data
+pointers that are associated with the handle C<g>:
+
+ const char *key;
+ void *data = guestfs_first_private (g, &key);
+ while (data != NULL)
+   {
+     printf ("key = %s, data = %p\n", key, data);
+     data = guestfs_next_private (g, &key);
+   }
+
+More commonly you are only interested in keys that begin with an
+application-specific prefix C<foo_>.  Modify the loop like so:
+
+ const char *key;
+ void *data = guestfs_first_private (g, &key);
+ while (data != NULL)
+   {
+     if (strncmp (key, "foo_", strlen ("foo_")) == 0)
+       printf ("key = %s, data = %p\n", key, data);
+     data = guestfs_next_private (g, &key);
+   }
+
+If you need to modify keys while walking, then you have to jump back
+to the beginning of the loop.  For example, to delete all keys
+prefixed with C<foo_>:
+
+  const char *key;
+  void *data;
+ again:
+  data = guestfs_first_private (g, &key);
+  while (data != NULL)
+    {
+      if (strncmp (key, "foo_", strlen ("foo_")) == 0)
+        {
+          guestfs_set_private (g, key, NULL);
+          /* note that 'key' pointer is now invalid, and so is
+             the internal iterator */
+          goto again;
+        }
+      data = guestfs_next_private (g, &key);
+    }
+
+Note that the above loop is guaranteed to terminate because the keys
+are being deleted, but other manipulations of keys within the loop
+might not terminate unless you also maintain an indication of which
+keys have been visited.
 
 =begin html