#define RARRAY_LEN(r) (RARRAY((r))->len)
#endif
+/* For Ruby < 1.8 */
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(r) (RSTRING((r))->len)
+#endif
+
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(r) (RSTRING((r))->ptr)
+#endif
+
static VALUE m_guestfs; /* guestfs module */
static VALUE c_guestfs; /* guestfs_h handle */
static VALUE e_Error; /* used for all errors */
static void ruby_event_callback_wrapper (guestfs_h *g, void *data, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len);
+static VALUE ruby_event_callback_wrapper_wrapper (VALUE argv);
+static VALUE ruby_event_callback_handle_exception (VALUE not_used, VALUE exn);
static VALUE **get_all_event_callbacks (guestfs_h *g, size_t *len_rtn);
static void
rb_gc_unregister_address (roots[i]);
free (roots[i]);
}
+ free (roots);
}
}
{
size_t i;
VALUE eventv, event_handlev, bufv, arrayv;
+ VALUE argv[5];
eventv = ULL2NUM (event);
event_handlev = INT2NUM (event_handle);
for (i = 0; i < array_len; ++i)
rb_ary_push (arrayv, ULL2NUM (array[i]));
- /* XXX If the Ruby callback raises any sort of exception then
- * it causes the process to segfault. I don't understand how
- * to catch exceptions here.
+ /* This is a crap limitation of rb_rescue.
+ * http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/~poffice/mail/ruby-talk/65698
+ */
+ 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.
*/
- rb_funcall (*(VALUE *) data, rb_intern (\"call\"), 4,
- eventv, event_handlev, bufv, arrayv);
+ fprintf (stderr, \"libguestfs: exception in callback: %%s\\n\",
+ StringValueCStr (exn));
+
+ return Qnil;
}
static VALUE **
return r;
}
+/*
+ * call-seq:
+ * g.user_cancel() -> nil
+ *
+ * Call
+ * +guestfs_user_cancel+[http://libguestfs.org/guestfs.3.html#guestfs_user_cancel]
+ * to cancel the current transfer. This is safe to call from Ruby
+ * signal handlers and threads.
+ */
+static VALUE
+ruby_user_cancel (VALUE gv)
+{
+ guestfs_h *g;
+
+ Data_Get_Struct (gv, guestfs_h, g);
+ if (g)
+ guestfs_user_cancel (g);
+ return Qnil;
+}
+
";
List.iter (
doc ^ "\n\n" ^ protocol_limit_warning
else doc in
let doc =
- if List.mem DangerWillRobinson flags then
- doc ^ "\n\n" ^ danger_will_robinson
- else doc in
- let doc =
match deprecation_notice flags with
| None -> doc
| Some txt -> doc ^ "\n\n" ^ txt in
pr " const char *%s = StringValueCStr (%sv);\n" n n;
| BufferIn n ->
pr " Check_Type (%sv, T_STRING);\n" n;
- pr " const char *%s = RSTRING (%sv)->ptr;\n" n n;
+ pr " const char *%s = RSTRING_PTR (%sv);\n" n n;
pr " if (!%s)\n" n;
pr " rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
pr " \"%s\", \"%s\");\n" n name;
- pr " size_t %s_size = RSTRING (%sv)->len;\n" n n
+ pr " size_t %s_size = RSTRING_LEN (%sv);\n" n n
| OptString n ->
pr " const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n
| StringList n | DeviceList n ->
ruby_set_event_callback, 2);
rb_define_method (c_guestfs, \"delete_event_callback\",
ruby_delete_event_callback, 1);
+ rb_define_method (c_guestfs, \"user_cancel\",
+ ruby_user_cancel, 0);
";