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).
capitests/test-create-handle
capitests/test-just-header
capitests/test-last-errno
+capitests/test-private-data
capitests/test*.img
capitests/tests
capitests/tests.c
test-create-handle \
test-config \
test-add-drive-opts \
- test-last-errno
+ test-last-errno \
+ test-private-data
TESTS = \
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
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 = \
--- /dev/null
+/* 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);
+}
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. */
";
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";
/* Private data area. */
struct hash_table *pda;
+ struct pda_entry *pda_next;
};
/* Per-filesystem data stored for inspect_os. */
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).
*/
=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:
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