diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | capitests/Makefile.am | 13 | ||||
-rw-r--r-- | capitests/test-private-data.c | 85 | ||||
-rw-r--r-- | generator/generator_c.ml | 6 | ||||
-rw-r--r-- | src/guestfs-internal.h | 1 | ||||
-rw-r--r-- | src/guestfs.c | 41 | ||||
-rw-r--r-- | src/guestfs.pod | 101 |
7 files changed, 242 insertions, 6 deletions
@@ -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 |