Inspired by but not completely related to:
https://bugzilla.redhat.com/show_bug.cgi?id=664558
"RFE: Allow to set log callback in Ruby bindings"
How can we make callbacks useful?
First off what are we talking about? There are several callbacks that
can be registered through the C API:
(a) error callback
http://libguestfs.org/guestfs.3.html#guestfs_set_error_handler
(b) out of memory callback
http://libguestfs.org/guestfs.3.html#guestfs_set_out_of_memory_handler
(c) log messages from the daemon
http://libguestfs.org/guestfs.3.html#guestfs_set_log_message_callback
(d) appliance quits
http://libguestfs.org/guestfs.3.html#guestfs_set_subprocess_quit_callback
(e) appliance launched
http://libguestfs.org/guestfs.3.html#guestfs_set_launch_done_callback
(f) handle closed
http://libguestfs.org/guestfs.3.html#guestfs_set_close_callback
(g) progress notification
http://libguestfs.org/guestfs.3.html#guestfs_set_progress_callback
(a) is not exposed to language bindings because it is required in
order to implement error handling and exceptions. (b) is very
specialized and is not likely to be used outside specific C programs.
The ones I'm interested in here are (c), (d), (e), (f), (g) -- generic
events.
There are three problems that I'm finding:
(1) You can't register more than one callback for an event. The
handle holds one callback pointer for each event type so when you
register a second callback it overwrites the first.
(2) We don't generate callback bindings, so many language bindings
lack coverage. This is the basis of Marek's RFE BZ above.
(3) There's no way to capture and redirect debug messages and trace
messages (see https://bugzilla.redhat.com/show_bug.cgi?id=664558#c2).
It would be useful to do this from GUI programs which usually have
their own preferred method to log things.
I think we need a more generic mechanism for (c)-(f) which solves
problems (1)-(3).
Here's my proposed more usable solution:
(i) Each event has a single class, one of:
{ LOG, VERBOSE, TRACE, SUBPROCESS_QUIT, LAUNCH_DONE, CLOSE, PROGRESS }
(ii) Give each event a level (an integer).
(iii) Each event can optionally have some content. For log messages this
is a string with length, for progress messages it is a struct
of numbers.
Callers can register to receive events in a bitmask class and at a set
of levels by doing:
int /* event_handle */
guestfs_set_event_callback (guestfs_h *g,
guestfs_event_callback cb,
uint64_t event_class_bitmask,
int min_level,
void *opaque);
void guestfs_delete_event_callback (guestfs_h *g, int event_handle);
'guestfs_set_event_callback' would register a callback for all event
classes in 'event_class_bitmask' at level >= 'min_level'.
This would
return an integer which can be used to delete this callback, by
calling 'guestfs_delete_event_callback'.
Callbacks remain until the handle is closed or the callback is
deleted, and multiple overlapping callbacks can exist.
The callback function is the same for every class of event.
LOG|VERBOSE|TRACE events would contain a string. PROGRESS events
would contain an array of numbers.
typedef void (*guestfs_event_callback) (guestfs_h *g,
void *opaque,
uint64_t event_class,
int level,
int event_handle,
char *buf, size_t buf_len,
uint64_t *array, size_t array_len);
[The string/array hack is very ugly, but I cannot think of a better
way at the moment ...]
We should export the same API through all the language bindings to
satisfy condition (2).
On the library side we should change verbose message so that instead of:
if (g->verbose)
fprintf (stderr, "...");
we should have:
debug ("...");
where the debug message is converted to a verbose level event if
g->verbose is set, otherwise it is discarded. If no handler has been
registered for debug messages, they should go to stderr as they do
now, otherwise they should go to handlers only.
A similar thing should happen for trace messages.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
libguestfs lets you edit virtual machines. Supports shell scripting,
bindings from many languages. http://et.redhat.com/~rjones/libguestfs/
See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html