summaryrefslogtreecommitdiff
path: root/gck/gck-call.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2010-07-29 16:34:07 +0200
committerStef Walter <stef@memberwebs.com>2010-07-29 16:34:07 +0200
commit102ced8f53dcccd2c42c8a89abea87b91f56e297 (patch)
treed37cfd33a900f734d061b1788d046448de5e73b9 /gck/gck-call.c
parentd7da07727afd570c5b655ba08471fa417f2bba64 (diff)
downloadgcr-102ced8f53dcccd2c42c8a89abea87b91f56e297.tar.gz
[gck] In preparation for public release, rename library.
* Next steps will include cleaning up the API making it ready for gobject introspection etc..
Diffstat (limited to 'gck/gck-call.c')
-rw-r--r--gck/gck-call.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/gck/gck-call.c b/gck/gck-call.c
new file mode 100644
index 0000000..939d81b
--- /dev/null
+++ b/gck/gck-call.c
@@ -0,0 +1,541 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-call.c - the GObject PKCS#11 wrapper library
+
+ Copyright (C) 2008, Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <nielsen@memberwebs.com>
+*/
+
+#include "config.h"
+
+#include "gck-private.h"
+
+#include <string.h>
+
+typedef struct _GckCallSource GckCallSource;
+
+static gpointer _gck_call_parent_class = NULL;
+
+struct _GckCall {
+ GObject parent;
+ GckModule *module;
+
+ /* For making the call */
+ GckPerformFunc perform;
+ GckCompleteFunc complete;
+ GckArguments *args;
+ GCancellable *cancellable;
+ GDestroyNotify destroy;
+ CK_RV rv;
+
+ /* For result callback only */
+ gpointer object;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+};
+
+struct _GckCallClass {
+ GObjectClass parent;
+ GThreadPool *thread_pool;
+ GAsyncQueue *completed_queue;
+ guint completed_id;
+};
+
+struct _GckCallSource {
+ GSource source;
+ GckCallClass *klass;
+};
+
+/* ----------------------------------------------------------------------------
+ * HELPER FUNCTIONS
+ */
+
+static CK_RV
+perform_call (GckPerformFunc func, GCancellable *cancellable, GckArguments *args)
+{
+ CK_RV rv;
+
+ /* Double check a few things */
+ g_assert (func);
+ g_assert (args);
+
+ if (cancellable) {
+ if (g_cancellable_is_cancelled (cancellable)) {
+ return CKR_FUNCTION_CANCELED;
+ }
+
+ /* Push for the notify callback */
+ g_object_ref (cancellable);
+ g_cancellable_push_current (cancellable);
+ }
+
+ rv = (func) (args);
+
+ if (cancellable) {
+ g_cancellable_pop_current (cancellable);
+ g_object_unref (cancellable);
+ }
+
+ return rv;
+}
+
+static gboolean
+complete_call (GckCompleteFunc func, GckArguments *args, CK_RV result)
+{
+ /* Double check a few things */
+ g_assert (args);
+
+ /* If no complete function, then just ignore */
+ if (!func)
+ return TRUE;
+
+ return (func) (args, result);
+}
+
+
+static void
+process_async_call (gpointer data, GckCallClass *klass)
+{
+ GckCall *call = GCK_CALL (data);
+
+ g_assert (GCK_IS_CALL (call));
+
+ call->rv = perform_call (call->perform, call->cancellable, call->args);
+
+ g_async_queue_push (klass->completed_queue, call);
+
+ /* Wakeup main thread if on a separate thread */
+ g_main_context_wakeup (NULL);
+}
+
+static void
+process_result (GckCall *call, gpointer unused)
+{
+ gboolean stop = FALSE;
+
+ /* Double check a few things */
+ g_assert (GCK_IS_CALL (call));
+
+ if (call->cancellable) {
+ /* Don't call the callback when cancelled */
+ if (g_cancellable_is_cancelled (call->cancellable)) {
+ call->rv = CKR_FUNCTION_CANCELED;
+ stop = TRUE;
+ }
+ }
+
+ /*
+ * Hmmm, does the function want to actually be done?
+ * If not, then queue this call again.
+ */
+ if (!stop && !complete_call (call->complete, call->args, call->rv)) {
+ g_object_ref (call);
+ g_thread_pool_push (GCK_CALL_GET_CLASS (call)->thread_pool, call, NULL);
+
+ /* All done, finish processing */
+ } else if (call->callback) {
+ g_assert (G_IS_OBJECT (call->object));
+ (call->callback) (G_OBJECT (call->object), G_ASYNC_RESULT (call),
+ call->user_data);
+ }
+}
+
+static gboolean
+process_completed (GckCallClass *klass)
+{
+ gpointer call;
+
+ g_assert (klass->completed_queue);
+
+ call = g_async_queue_try_pop (klass->completed_queue);
+ if (call) {
+ process_result (call, NULL);
+ g_object_unref (call);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+completed_prepare(GSource* base, gint *timeout)
+{
+ GckCallSource *source = (GckCallSource*)base;
+ gboolean have;
+
+ g_assert (source->klass->completed_queue);
+ have = g_async_queue_length (source->klass->completed_queue) > 0;
+ *timeout = have ? 0 : -1;
+ return have;
+}
+
+static gboolean
+completed_check(GSource* base)
+{
+ GckCallSource *source = (GckCallSource*)base;
+ g_assert (source->klass->completed_queue);
+ return g_async_queue_length (source->klass->completed_queue) > 0;
+}
+
+static gboolean
+completed_dispatch(GSource* base, GSourceFunc callback, gpointer user_data)
+{
+ GckCallSource *source = (GckCallSource*)base;
+ process_completed (source->klass);
+ return TRUE;
+}
+
+static void
+completed_finalize(GSource* base)
+{
+
+}
+
+static GSourceFuncs completed_functions = {
+ completed_prepare,
+ completed_check,
+ completed_dispatch,
+ completed_finalize
+};
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+_gck_call_init (GckCall *call)
+{
+ call->rv = CKR_OK;
+}
+
+static void
+_gck_call_finalize (GObject *obj)
+{
+ GckCall *call = GCK_CALL (obj);
+
+ if (call->module)
+ g_object_unref (call->module);
+ call->module = NULL;
+
+ if (call->object)
+ g_object_unref (call->object);
+ call->object = NULL;
+
+ if (call->cancellable)
+ g_object_unref (call->cancellable);
+ call->cancellable = NULL;
+
+ if (call->destroy)
+ (call->destroy) (call->args);
+ call->destroy = NULL;
+ call->args = NULL;
+
+ G_OBJECT_CLASS (_gck_call_parent_class)->finalize (obj);
+}
+
+static gpointer
+_gck_call_get_user_data (GAsyncResult *async_result)
+{
+ g_return_val_if_fail (GCK_IS_CALL (async_result), NULL);
+ return GCK_CALL (async_result)->user_data;
+}
+
+static GObject*
+_gck_call_get_source_object (GAsyncResult *async_result)
+{
+ g_return_val_if_fail (GCK_IS_CALL (async_result), NULL);
+ return GCK_CALL (async_result)->object;
+}
+
+static void
+_gck_call_implement_async_result (GAsyncResultIface *iface)
+{
+ iface->get_user_data = _gck_call_get_user_data;
+ iface->get_source_object = _gck_call_get_source_object;
+}
+
+static void
+_gck_call_class_init (GckCallClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass*)klass;
+
+ _gck_call_parent_class = g_type_class_peek_parent (klass);
+ gobject_class->finalize = _gck_call_finalize;
+}
+
+static void
+_gck_call_base_init (GckCallClass *klass)
+{
+ GckCallSource *source;
+ GMainContext *context;
+ GError *err = NULL;
+
+ klass->thread_pool = g_thread_pool_new ((GFunc)process_async_call, klass, 16, FALSE, &err);
+ if (!klass->thread_pool) {
+ g_critical ("couldn't create thread pool: %s",
+ err && err->message ? err->message : "");
+ return;
+ }
+
+ klass->completed_queue = g_async_queue_new_full (g_object_unref);
+ g_assert (klass->completed_queue);
+
+ context = g_main_context_default ();
+ g_assert (context);
+
+ /* Add our idle handler which processes other tasks */
+ source = (GckCallSource*)g_source_new (&completed_functions, sizeof (GckCallSource));
+ source->klass = klass;
+ klass->completed_id = g_source_attach ((GSource*)source, context);
+ g_source_set_callback ((GSource*)source, NULL, NULL, NULL);
+ g_source_unref ((GSource*)source);
+}
+
+static void
+_gck_call_base_finalize (GckCallClass *klass)
+{
+ GMainContext *context;
+ GSource *src;
+
+ if (klass->thread_pool) {
+ g_assert (g_thread_pool_unprocessed (klass->thread_pool) == 0);
+ g_thread_pool_free (klass->thread_pool, FALSE, TRUE);
+ klass->thread_pool = NULL;
+ }
+
+ if (klass->completed_id) {
+ context = g_main_context_default ();
+ g_return_if_fail (context);
+
+ src = g_main_context_find_source_by_id (context, klass->completed_id);
+ g_assert (src);
+ g_source_destroy (src);
+ klass->completed_id = 0;
+ }
+
+ if (klass->completed_queue) {
+ g_assert (g_async_queue_length (klass->completed_queue));
+ g_async_queue_unref (klass->completed_queue);
+ klass->completed_queue = NULL;
+ }
+}
+
+GType
+_gck_call_get_type (void)
+{
+ static volatile gsize type_id__volatile = 0;
+
+ if (g_once_init_enter (&type_id__volatile)) {
+
+ static const GTypeInfo type_info = {
+ sizeof (GckCallClass),
+ (GBaseInitFunc)_gck_call_base_init,
+ (GBaseFinalizeFunc)_gck_call_base_finalize,
+ (GClassInitFunc)_gck_call_class_init,
+ (GClassFinalizeFunc)NULL,
+ NULL, // class_data
+ sizeof (GckCall),
+ 0, // n_preallocs
+ (GInstanceInitFunc)_gck_call_init,
+ };
+
+ static const GInterfaceInfo interface_info = {
+ (GInterfaceInitFunc)_gck_call_implement_async_result
+ };
+
+ GType type_id = g_type_register_static (G_TYPE_OBJECT, "_GckCall", &type_info, 0);
+ g_type_add_interface_static (type_id, G_TYPE_ASYNC_RESULT, &interface_info);
+
+ g_once_init_leave (&type_id__volatile, type_id);
+ }
+
+ return type_id__volatile;
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+void
+_gck_call_uninitialize (void)
+{
+
+}
+
+gboolean
+_gck_call_sync (gpointer object, gpointer perform, gpointer complete,
+ gpointer data, GCancellable *cancellable, GError **err)
+{
+ GckArguments *args = (GckArguments*)data;
+ GckModule *module = NULL;
+ CK_RV rv;
+
+ g_assert (G_IS_OBJECT (object));
+ g_assert (perform);
+ g_assert (args);
+
+ g_object_get (object, "module", &module, "handle", &args->handle, NULL);
+ g_assert (GCK_IS_MODULE (module));
+
+ /* We now hold a reference to module until below */
+ args->pkcs11 = gck_module_get_functions (module);
+ g_assert (args->pkcs11);
+
+ do {
+ rv = perform_call (perform, cancellable, args);
+ if (rv == CKR_FUNCTION_CANCELED)
+ break;
+
+ } while (!complete_call (complete, args, rv));
+
+ g_object_unref (module);
+
+ if (rv == CKR_OK)
+ return TRUE;
+
+ g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv));
+ return FALSE;
+}
+
+gpointer
+_gck_call_async_prep (gpointer object, gpointer cb_object, gpointer perform,
+ gpointer complete, gsize args_size, gpointer destroy)
+{
+ GckArguments *args;
+ GckCall *call;
+
+ g_assert (!object || G_IS_OBJECT (object));
+ g_assert (perform);
+
+ if (!destroy)
+ destroy = g_free;
+
+ if (args_size == 0)
+ args_size = sizeof (GckArguments);
+ g_assert (args_size >= sizeof (GckArguments));
+
+ args = g_malloc0 (args_size);
+ call = g_object_new (GCK_TYPE_CALL, NULL);
+ call->destroy = (GDestroyNotify)destroy;
+ call->perform = (GckPerformFunc)perform;
+ call->complete = (GckCompleteFunc)complete;
+ call->object = cb_object;
+ g_object_ref (cb_object);
+
+ /* Hook the two together */
+ call->args = args;
+ call->args->call = call;
+
+ /* Setup call object if available */
+ if (object != NULL)
+ _gck_call_async_object (call, object);
+
+ return args;
+}
+
+void
+_gck_call_async_object (GckCall *call, gpointer object)
+{
+ g_assert (GCK_IS_CALL (call));
+ g_assert (call->args);
+
+ if (call->module)
+ g_object_unref (call->module);
+ call->module = NULL;
+
+ g_object_get (object, "module", &call->module, "handle", &call->args->handle, NULL);
+ g_assert (GCK_IS_MODULE (call->module));
+ call->args->pkcs11 = gck_module_get_functions (call->module);
+
+ /* We now hold a reference on module until finalize */
+}
+
+GckCall*
+_gck_call_async_ready (gpointer data, GCancellable *cancellable,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ GckArguments *args = (GckArguments*)data;
+ g_assert (GCK_IS_CALL (args->call));
+
+ args->call->cancellable = cancellable;
+ if (cancellable) {
+ g_assert (G_IS_CANCELLABLE (cancellable));
+ g_object_ref (cancellable);
+ }
+
+ args->call->callback = callback;
+ args->call->user_data = user_data;
+
+ return args->call;
+}
+
+void
+_gck_call_async_go (GckCall *call)
+{
+ g_assert (GCK_IS_CALL (call));
+ g_assert (call->args->pkcs11);
+
+ /* To keep things balanced, process at one completed event */
+ process_completed(GCK_CALL_GET_CLASS (call));
+
+ g_assert (GCK_CALL_GET_CLASS (call)->thread_pool);
+ g_thread_pool_push (GCK_CALL_GET_CLASS (call)->thread_pool, call, NULL);
+}
+
+void
+_gck_call_async_ready_go (gpointer data, GCancellable *cancellable,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ GckCall *call = _gck_call_async_ready (data, cancellable, callback, user_data);
+ _gck_call_async_go (call);
+}
+
+gboolean
+_gck_call_basic_finish (GAsyncResult *result, GError **err)
+{
+ CK_RV rv;
+
+ g_return_val_if_fail (GCK_IS_CALL (result), FALSE);
+
+ rv = GCK_CALL (result)->rv;
+ if (rv == CKR_OK)
+ return TRUE;
+
+ g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv));
+ return FALSE;
+}
+
+void
+_gck_call_async_short (GckCall *call, CK_RV rv)
+{
+ g_assert (GCK_IS_CALL (call));
+
+ call->rv = rv;
+
+ /* Already complete, so just push it for processing in main loop */
+ g_assert (GCK_CALL_GET_CLASS (call)->completed_queue);
+ g_async_queue_push (GCK_CALL_GET_CLASS (call)->completed_queue, call);
+ g_main_context_wakeup (NULL);
+}
+
+gpointer
+_gck_call_get_arguments (GckCall *call)
+{
+ g_assert (GCK_IS_CALL (call));
+ return call->args;
+}