summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--capitests/Makefile.am13
-rw-r--r--capitests/test-private-data.c85
-rw-r--r--generator/generator_c.ml6
-rw-r--r--src/guestfs-internal.h1
-rw-r--r--src/guestfs.c41
-rw-r--r--src/guestfs.pod101
7 files changed, 242 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index 1511c4a0..7df10b22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/capitests/Makefile.am b/capitests/Makefile.am
index 542c4fb9..83e62c8e 100644
--- a/capitests/Makefile.am
+++ b/capitests/Makefile.am
@@ -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
index 00000000..fad88b58
--- /dev/null
+++ b/capitests/test-private-data.c
@@ -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);
+}
diff --git a/generator/generator_c.ml b/generator/generator_c.ml
index 9b88376c..656e752f 100644
--- a/generator/generator_c.ml
+++ b/generator/generator_c.ml
@@ -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";
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 0eb395b9..297bed0e 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -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. */
diff --git a/src/guestfs.c b/src/guestfs.c
index 40855066..97762caa 100644
--- a/src/guestfs.c
+++ b/src/guestfs.c
@@ -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).
*/
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 0b3b6544..b0a408db 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -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