From 4ea0abfce413767868a8f53b3d20e5d2f62dff08 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Tue, 31 Aug 2010 18:48:32 +0100 Subject: [PATCH] Implement private data area. The private data area is a hash table which is associated with libguestfs handles, that C callers may use to store arbitrary data for the lifetime of the handle. Later the OCaml bindings will use this in order to implement callbacks. --- src/generator.ml | 2 ++ src/guestfs-internal.h | 3 ++ src/guestfs.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/guestfs.h | 4 +++ src/guestfs.pod | 38 +++++++++++++++++++++++ 5 files changed, 128 insertions(+) diff --git a/src/generator.ml b/src/generator.ml index c5add6f..f298d2e 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -6890,12 +6890,14 @@ and generate_linker_script () = "guestfs_close"; "guestfs_get_error_handler"; "guestfs_get_out_of_memory_handler"; + "guestfs_get_private"; "guestfs_last_error"; "guestfs_set_close_callback"; "guestfs_set_error_handler"; "guestfs_set_launch_done_callback"; "guestfs_set_log_message_callback"; "guestfs_set_out_of_memory_handler"; + "guestfs_set_private"; "guestfs_set_progress_callback"; "guestfs_set_subprocess_quit_callback"; diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 32a6c2a..ed82fde 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -132,6 +132,9 @@ struct guestfs_h */ struct inspect_fs *fses; size_t nr_fses; + + /* Private data area. */ + struct hash_table *pda; }; /* Per-filesystem data stored for inspect_os. */ diff --git a/src/guestfs.c b/src/guestfs.c index 206347e..b958689 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,8 @@ #include "c-ctype.h" #include "glthread/lock.h" +#include "hash.h" +#include "hash-pjw.h" #include "ignore-value.h" #include "guestfs.h" @@ -244,6 +247,8 @@ guestfs_close (guestfs_h *g) } gl_lock_unlock (handles_lock); + if (g->pda) + hash_free (g->pda); free (g->last_error); free (g->path); free (g->qemu); @@ -653,3 +658,79 @@ guestfs_set_progress_callback (guestfs_h *g, g->progress_cb = cb; g->progress_cb_data = opaque; } + +/* Note the private data area is allocated lazily, since the vast + * majority of callers will never use it. This means g->pda is + * likely to be NULL. + */ +struct pda_entry { + char *key; /* key */ + void *data; /* opaque user data pointer */ +}; + +static size_t +hasher (void const *x, size_t table_size) +{ + struct pda_entry const *p = x; + return hash_pjw (p->key, table_size); +} + +static bool +comparator (void const *x, void const *y) +{ + struct pda_entry const *a = x; + struct pda_entry const *b = y; + return STREQ (a->key, b->key); +} + +static void +freer (void *x) +{ + if (x) { + struct pda_entry *p = x; + free (p->key); + free (p); + } +} + +void +guestfs_set_private (guestfs_h *g, const char *key, void *data) +{ + if (g->pda == NULL) { + g->pda = hash_initialize (16, NULL, hasher, comparator, freer); + if (g->pda == NULL) + g->abort_cb (); + } + + struct pda_entry *new_entry = safe_malloc (g, sizeof *new_entry); + new_entry->key = safe_strdup (g, key); + new_entry->data = data; + + struct pda_entry *old_entry = hash_delete (g->pda, new_entry); + freer (old_entry); + + struct pda_entry *entry = hash_insert (g->pda, new_entry); + if (entry == NULL) + g->abort_cb (); + assert (entry == new_entry); +} + +static inline char * +bad_cast (char const *s) +{ + return (char *) s; +} + +void * +guestfs_get_private (guestfs_h *g, const char *key) +{ + if (g->pda == NULL) + return NULL; /* no keys have been set */ + + const struct pda_entry k = { .key = bad_cast (key) }; + struct pda_entry *entry = hash_lookup (g->pda, &k); + if (entry) + return entry->data; + else + return NULL; +} diff --git a/src/guestfs.h b/src/guestfs.h index ec88f22..0bfad4f 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -67,6 +67,10 @@ extern void guestfs_set_launch_done_callback (guestfs_h *g, guestfs_launch_done_ extern void guestfs_set_close_callback (guestfs_h *g, guestfs_close_cb cb, void *opaque); extern void guestfs_set_progress_callback (guestfs_h *g, guestfs_progress_cb cb, void *opaque); +/*--- Private data area ---*/ +extern void guestfs_set_private (guestfs_h *g, const char *key, void *data); +extern void *guestfs_get_private (guestfs_h *g, const char *key); + /*--- Structures and actions ---*/ #include #include diff --git a/src/guestfs.pod b/src/guestfs.pod index 6a956ed..455cd89 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1236,6 +1236,44 @@ the call. These are only useful for debugging protocol issues, and the callback can normally ignore them. The callback may want to 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. + +To attach a named piece of data, use the following call: + + void guestfs_set_private (guestfs_h *g, const char *key, void *data); + +C is the name to associate with this data, and C is an +arbitrary pointer (which can be C). Any previous item with the +same name is overwritten. + +You can use any C you want, but names beginning with an +underscore character are reserved for internal libguestfs purposes +(for implementing language bindings). It is recommended to prefix the +name with some unique string to avoid collisions with other users. + +To retrieve the pointer, use: + + void *guestfs_get_private (guestfs_h *g, const char *key); + +This function returns C if either no data is found associated +with C, or if the user previously set the C's C +pointer to C. + +Libguestfs does not try to look at or interpret the C pointer in +any way. As far as libguestfs is concerned, it need not be a valid +pointer at all. In particular, libguestfs does I try to free the +data when the handle is closed. If the data must be freed, then the +caller must either free it before calling L or must +set up a close callback to do it (see L, +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. + =head1 BLOCK DEVICE NAMING In the kernel there is now quite a profusion of schemata for naming -- 1.8.3.1