Richard W.M. Jones
2011-Mar-14 13:46 UTC
[Libguestfs] [PATCH] New APIs: guestfs_first_private, guestfs_next_private to walk over the private data area.
This patch adds useful APIs for walking over the private data area. It is a prerequisite for the new event API stuff (specifically for the language bindings for that). Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v -------------- next part -------------->From 9cebd2178ffc99a233af32f2477ef4c0f84f3f1c Mon Sep 17 00:00:00 2001From: Richard W.M. Jones <rjones at redhat.com> Date: Mon, 14 Mar 2011 13:19:47 +0000 Subject: [PATCH] New APIs: guestfs_first_private, guestfs_next_private to walk over 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 | 1 + capitests/Makefile.am | 13 ++++- capitests/test-private-data.c | 120 +++++++++++++++++++++++++++++++++++++++++ generator/generator_c.ml | 6 ++ src/guestfs-internal.h | 1 + src/guestfs.c | 41 ++++++++++++++ src/guestfs.pod | 101 +++++++++++++++++++++++++++++++++-- 7 files changed, 277 insertions(+), 6 deletions(-) create mode 100644 capitests/test-private-data.c diff --git a/.gitignore b/.gitignore index 1511c4a..7df10b2 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 542c4fb..83e62c8 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 0000000..f2ff647 --- /dev/null +++ b/capitests/test-private-data.c @@ -0,0 +1,120 @@ +/* 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_" + +static size_t close_callback_called = 0; + +/* This callback deletes all test keys in the handle. */ +static void +close_callback (guestfs_h *g, + void *opaque, + uint64_t event, + int event_handle, + int flags, + const char *buf, size_t buf_len, + const uint64_t *array, size_t array_len) +{ + const char *key; + void *data; + + close_callback_called++; + + again: + data = guestfs_first_private (g, &key); + while (data != NULL) { + if (strncmp (key, PREFIX, strlen (PREFIX)) == 0) { + guestfs_set_private (g, key, NULL); + goto again; + } + data = guestfs_next_private (g, &key); + } +} + +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); + } + + if (guestfs_set_event_callback (g, close_callback, GUESTFS_EVENT_CLOSE, + 0, NULL) == -1) + 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); + + /* Closing should implicitly call the close_callback function. */ + guestfs_close (g); + + assert (close_callback_called == 1); + + exit (EXIT_SUCCESS); +} diff --git a/generator/generator_c.ml b/generator/generator_c.ml index 9b88376..656e752 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 0eb395b..297bed0 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 8b7ab4d..b859376 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 0b3b654..b0a408d 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 -- 1.7.4
Jim Meyering
2011-Mar-16 08:42 UTC
[Libguestfs] [PATCH] New APIs: guestfs_first_private, guestfs_next_private to walk over the private data area.
Richard W.M. Jones wrote:> This patch adds useful APIs for walking over the private data area. > It is a prerequisite for the new event API stuff (specifically for the > language bindings for that).Hi Rich, I reviewed your commit, 6d6b7edd1102f8383643866bf358e494e0d518ef, and wouldn't change a thing[*]. Very nice. Jim [*] Actually, I would have moved a few declarations "down" to the point of first use, but I think libguestfs has a policy of avoiding C99's declaration-after-statement feature.
Possibly Parallel Threads
- Would like to ask a * user some question over voice or a walk thru in the Hou,TX area
- extlinux VPATH build issue
- [LLVMdev] Controlling the order of a FunctionPass
- [LLVMdev] Controlling the order of a FunctionPass
- [PATCH] drm/nouveau: Remove file nouveau_fbcon.c