+/*
+ * call-seq:
+ * g.set_event_callback(cb, event_bitmask) -> event_handle
+ *
+ * Call
+ * +guestfs_set_event_callback+[http://libguestfs.org/guestfs.3.html#guestfs_set_event_callback]
+ * to register an event callback. This returns an event handle.
+ */
+static VALUE
+ruby_set_event_callback (VALUE gv, VALUE cbv, VALUE event_bitmaskv)
+{
+ guestfs_h *g;
+ uint64_t event_bitmask;
+ int eh;
+ VALUE *root;
+ char key[64];
+
+ Data_Get_Struct (gv, guestfs_h, g);
+
+ event_bitmask = NUM2ULL (event_bitmaskv);
+
+ root = guestfs_safe_malloc (g, sizeof *root);
+ *root = cbv;
+
+ eh = guestfs_set_event_callback (g, ruby_event_callback_wrapper,
+ event_bitmask, 0, root);
+ if (eh == -1) {
+ free (root);
+ rb_raise (e_Error, \"%%s\", guestfs_last_error (g));
+ }
+
+ rb_gc_register_address (root);
+
+ snprintf (key, sizeof key, \"_ruby_event_%%d\", eh);
+ guestfs_set_private (g, key, root);
+
+ return INT2NUM (eh);
+}
+
+/*
+ * call-seq:
+ * g.delete_event_callback(event_handle) -> nil
+ *
+ * Call
+ * +guestfs_delete_event_callback+[http://libguestfs.org/guestfs.3.html#guestfs_delete_event_callback]
+ * to delete an event callback.
+ */
+static VALUE
+ruby_delete_event_callback (VALUE gv, VALUE event_handlev)
+{
+ guestfs_h *g;
+ char key[64];
+ int eh = NUM2INT (event_handlev);
+ VALUE *root;
+
+ Data_Get_Struct (gv, guestfs_h, g);
+
+ snprintf (key, sizeof key, \"_ruby_event_%%d\", eh);
+
+ root = guestfs_get_private (g, key);
+ if (root) {
+ rb_gc_unregister_address (root);
+ free (root);
+ guestfs_set_private (g, key, NULL);
+ guestfs_delete_event_callback (g, eh);
+ }
+
+ return Qnil;
+}
+
+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)
+{
+ size_t i;
+ VALUE eventv, event_handlev, bufv, arrayv;
+ VALUE argv[5];
+
+ eventv = ULL2NUM (event);
+ event_handlev = INT2NUM (event_handle);
+
+ bufv = rb_str_new (buf, buf_len);
+
+ arrayv = rb_ary_new2 (array_len);
+ for (i = 0; i < array_len; ++i)
+ rb_ary_push (arrayv, ULL2NUM (array[i]));
+
+ /* 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.
+ */
+ fprintf (stderr, \"libguestfs: exception in callback: %%s\\n\",
+ StringValueCStr (exn));
+
+ return Qnil;
+}
+
+static VALUE **
+get_all_event_callbacks (guestfs_h *g, size_t *len_rtn)
+{
+ VALUE **r;
+ size_t i;
+ const char *key;
+ VALUE *root;
+
+ /* Count the length of the array that will be needed. */
+ *len_rtn = 0;
+ root = guestfs_first_private (g, &key);
+ while (root != NULL) {
+ if (strncmp (key, \"_ruby_event_\", strlen (\"_ruby_event_\")) == 0)
+ (*len_rtn)++;
+ root = guestfs_next_private (g, &key);
+ }
+
+ /* Copy them into the return array. */
+ r = guestfs_safe_malloc (g, sizeof (VALUE *) * (*len_rtn));
+
+ i = 0;
+ root = guestfs_first_private (g, &key);
+ while (root != NULL) {
+ if (strncmp (key, \"_ruby_event_\", strlen (\"_ruby_event_\")) == 0) {
+ r[i] = root;
+ i++;
+ }
+ root = guestfs_next_private (g, &key);
+ }
+
+ 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;
+}
+