summaryrefslogtreecommitdiffstats
path: root/src/events.c
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-03-10 12:32:22 +0000
committerRichard W.M. Jones <rjones@redhat.com>2011-03-15 12:16:50 +0000
commit4e0cf4dbf8a8a96288f70114fdc3939da0aa7ad1 (patch)
tree2418e32479a965eb392d08f2e648c53714118a91 /src/events.c
parent6d6b7edd1102f8383643866bf358e494e0d518ef (diff)
downloadlibguestfs-4e0cf4dbf8a8a96288f70114fdc3939da0aa7ad1.tar.gz
libguestfs-4e0cf4dbf8a8a96288f70114fdc3939da0aa7ad1.tar.xz
libguestfs-4e0cf4dbf8a8a96288f70114fdc3939da0aa7ad1.zip
New event API (RHBZ#664558).
This API allows more than one callback to be registered for each event, makes it possible to call the API from other languages, and allows [nearly all] log, debug and trace messages to be rerouted from stderr. An older version of this API was discussed on the mailing list here: https://www.redhat.com/archives/libguestfs/2010-December/msg00081.html https://www.redhat.com/archives/libguestfs/2011-January/msg00012.html This also updates guestfish to use the new API for its progress bars.
Diffstat (limited to 'src/events.c')
-rw-r--r--src/events.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/events.c b/src/events.c
new file mode 100644
index 00000000..159862a4
--- /dev/null
+++ b/src/events.c
@@ -0,0 +1,323 @@
+/* libguestfs
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#define _BSD_SOURCE /* for mkdtemp, usleep */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "ignore-value.h"
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+
+int
+guestfs_set_event_callback (guestfs_h *g,
+ guestfs_event_callback cb,
+ uint64_t event_bitmask,
+ int flags,
+ void *opaque)
+{
+ if (flags != 0) {
+ error (g, "flags parameter should be passed as 0 to this function");
+ return -1;
+ }
+
+ /* We cast size_t to int which is not always safe for large numbers,
+ * and in any case if a program is registering a huge number of
+ * callbacks then we'd want to look at using an alternate data
+ * structure in place of a linear list.
+ */
+ if (g->nr_events >= 1000) {
+ error (g, "too many event callbacks registered");
+ return -1;
+ }
+
+ int event_handle = (int) g->nr_events;
+ g->events =
+ guestfs_safe_realloc (g, g->events,
+ (g->nr_events+1) * sizeof (struct event));
+ g->nr_events++;
+
+ g->events[event_handle].event_bitmask = event_bitmask;
+ g->events[event_handle].cb = cb;
+ g->events[event_handle].opaque = opaque;
+ g->events[event_handle].opaque2 = NULL;
+
+ return event_handle;
+}
+
+void
+guestfs_delete_event_callback (guestfs_h *g, int event_handle)
+{
+ if (event_handle < 0 || event_handle >= (int) g->nr_events)
+ return;
+
+ /* Set the event_bitmask to 0, which will ensure that this callback
+ * cannot match any event and therefore cannot be called.
+ */
+ g->events[event_handle].event_bitmask = 0;
+}
+
+/* Functions to generate an event with various payloads. */
+
+void
+guestfs___call_callbacks_void (guestfs_h *g, uint64_t event)
+{
+ size_t i;
+
+ for (i = 0; i < g->nr_events; ++i)
+ if ((g->events[i].event_bitmask & event) != 0)
+ g->events[i].cb (g, g->events[i].opaque, event, i, 0, NULL, 0, NULL, 0);
+
+ /* All events with payload type void are discarded if no callback
+ * was registered.
+ */
+}
+
+void
+guestfs___call_callbacks_message (guestfs_h *g, uint64_t event,
+ const char *buf, size_t buf_len)
+{
+ size_t i, count = 0;
+
+ for (i = 0; i < g->nr_events; ++i)
+ if ((g->events[i].event_bitmask & event) != 0) {
+ g->events[i].cb (g, g->events[i].opaque, event, i, 0,
+ buf, buf_len, NULL, 0);
+ count++;
+ }
+
+ /* If nothing was registered and we're verbose or tracing, then we
+ * print the message on stderr. This essentially emulates the
+ * behaviour of the old-style handlers, while allowing callers to
+ * override print-on-stderr simply by registering a callback.
+ */
+ if (count == 0 && (g->verbose || event == GUESTFS_EVENT_TRACE)) {
+ const char *prefix = "libguestfs: ";
+ const char *trace = "trace: ";
+ const char *nl = "\n";
+
+ /* APPLIANCE => <buf>
+ * LIBRARY => libguestfs: <buf>\n
+ * TRACE => libguestfs: trace: <buf>\n (RHBZ#673479)
+ */
+
+ if (event != GUESTFS_EVENT_APPLIANCE)
+ ignore_value (write (STDERR_FILENO, prefix, strlen (prefix)));
+
+ if (event == GUESTFS_EVENT_TRACE)
+ ignore_value (write (STDERR_FILENO, trace, strlen (trace)));
+
+ ignore_value (write (STDERR_FILENO, buf, buf_len));
+
+ /* Messages from the appliance already contain \n characters, others
+ * need this to be appended.
+ */
+ if (event != GUESTFS_EVENT_APPLIANCE)
+ ignore_value (write (STDERR_FILENO, nl, strlen (nl)));
+ }
+}
+
+void
+guestfs___call_callbacks_array (guestfs_h *g, uint64_t event,
+ const uint64_t *array, size_t array_len)
+{
+ size_t i;
+
+ for (i = 0; i < g->nr_events; ++i)
+ if ((g->events[i].event_bitmask & event) != 0)
+ g->events[i].cb (g, g->events[i].opaque, event, i, 0,
+ NULL, 0, array, array_len);
+
+ /* All events with payload type array are discarded if no callback
+ * was registered.
+ */
+}
+
+/* Emulate old-style callback API.
+ *
+ * There were no event handles, so multiple callbacks per event were
+ * not supported. Calling the same 'guestfs_set_*_callback' function
+ * would replace the existing event. Calling it with cb == NULL meant
+ * that the caller wanted to remove the callback.
+ */
+
+static void
+replace_old_style_event_callback (guestfs_h *g,
+ guestfs_event_callback cb,
+ uint64_t event_bitmask,
+ void *opaque,
+ void *opaque2)
+{
+ size_t i;
+
+ /* Use 'cb' pointer as a sentinel to replace the existing callback
+ * for this event if one was registered previously. Else append a
+ * new event.
+ */
+
+ for (i = 0; i < g->nr_events; ++i)
+ if (g->events[i].cb == cb) {
+ if (opaque2 == NULL) {
+ /* opaque2 (the original callback) is NULL, which in the
+ * old-style API meant remove the callback.
+ */
+ guestfs_delete_event_callback (g, i);
+ return;
+ }
+
+ goto replace;
+ }
+
+ if (opaque2 == NULL)
+ return; /* see above */
+
+ /* i == g->nr_events */
+ g->events =
+ guestfs_safe_realloc (g, g->events,
+ (g->nr_events+1) * sizeof (struct event));
+ g->nr_events++;
+
+ replace:
+ g->events[i].event_bitmask = event_bitmask;
+ g->events[i].cb = cb;
+ g->events[i].opaque = opaque;
+ g->events[i].opaque2 = opaque2;
+}
+
+static void
+log_message_callback_wrapper (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)
+{
+ guestfs_log_message_cb cb = g->events[event_handle].opaque2;
+ /* Note that the old callback declared the message buffer as
+ * (char *, int). I sure hope message buffers aren't too large
+ * and that callers aren't writing to them. XXX
+ */
+ cb (g, opaque, (char *) buf, (int) buf_len);
+}
+
+void
+guestfs_set_log_message_callback (guestfs_h *g,
+ guestfs_log_message_cb cb, void *opaque)
+{
+ replace_old_style_event_callback (g, log_message_callback_wrapper,
+ GUESTFS_EVENT_APPLIANCE,
+ opaque, cb);
+}
+
+static void
+subprocess_quit_callback_wrapper (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)
+{
+ guestfs_subprocess_quit_cb cb = g->events[event_handle].opaque2;
+ cb (g, opaque);
+}
+
+void
+guestfs_set_subprocess_quit_callback (guestfs_h *g,
+ guestfs_subprocess_quit_cb cb, void *opaque)
+{
+ replace_old_style_event_callback (g, subprocess_quit_callback_wrapper,
+ GUESTFS_EVENT_SUBPROCESS_QUIT,
+ opaque, cb);
+}
+
+static void
+launch_done_callback_wrapper (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)
+{
+ guestfs_launch_done_cb cb = g->events[event_handle].opaque2;
+ cb (g, opaque);
+}
+
+void
+guestfs_set_launch_done_callback (guestfs_h *g,
+ guestfs_launch_done_cb cb, void *opaque)
+{
+ replace_old_style_event_callback (g, launch_done_callback_wrapper,
+ GUESTFS_EVENT_LAUNCH_DONE,
+ opaque, cb);
+}
+
+static void
+close_callback_wrapper (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)
+{
+ guestfs_close_cb cb = g->events[event_handle].opaque2;
+ cb (g, opaque);
+}
+
+void
+guestfs_set_close_callback (guestfs_h *g,
+ guestfs_close_cb cb, void *opaque)
+{
+ replace_old_style_event_callback (g, close_callback_wrapper,
+ GUESTFS_EVENT_CLOSE,
+ opaque, cb);
+}
+
+static void
+progress_callback_wrapper (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)
+{
+ guestfs_progress_cb cb = g->events[event_handle].opaque2;
+ assert (array_len >= 4);
+ cb (g, opaque, (int) array[0], (int) array[1], array[2], array[3]);
+}
+
+void
+guestfs_set_progress_callback (guestfs_h *g,
+ guestfs_progress_cb cb, void *opaque)
+{
+ replace_old_style_event_callback (g, progress_callback_wrapper,
+ GUESTFS_EVENT_PROGRESS,
+ opaque, cb);
+}