- rb_funcall (*(VALUE *) data, rb_intern (\"call\"), 4,
- eventv, event_handlev, bufv, arrayv);
+ argv[0] = * (VALUE *) data; /* function */
+ argv[1] = eventv;
+ argv[2] = event_handlev;
+ argv[3] = bufv;
+ argv[4] = arrayv;
+
+ rb_rescue (ruby_event_callback_wrapper_wrapper, (VALUE) argv,
+ ruby_event_callback_handle_exception, Qnil);
+}
+
+static VALUE
+ruby_event_callback_wrapper_wrapper (VALUE argvv)
+{
+ VALUE *argv = (VALUE *) argvv;
+ VALUE fn, eventv, event_handlev, bufv, arrayv;
+
+ fn = argv[0];
+
+ /* Check the Ruby callback still exists. For reasons which are not
+ * fully understood, even though we registered this as a global root,
+ * it is still possible for the callback to go away (fn value remains
+ * but its type changes from T_DATA to T_NONE). (RHBZ#733297)
+ */
+ if (rb_type (fn) != T_NONE) {
+ eventv = argv[1];
+ event_handlev = argv[2];
+ bufv = argv[3];
+ arrayv = argv[4];
+
+ rb_funcall (fn, rb_intern (\"call\"), 4,
+ eventv, event_handlev, bufv, arrayv);
+ }
+
+ return Qnil;
+}
+
+static VALUE
+ruby_event_callback_handle_exception (VALUE not_used, VALUE exn)
+{
+ /* Callbacks aren't supposed to throw exceptions. The best we
+ * can do is to print the error.
+ */
+ fprintf (stderr, \"libguestfs: exception in callback: %%s\\n\",
+ StringValueCStr (exn));
+
+ return Qnil;