aboutsummaryrefslogtreecommitdiff
path: root/src/lockmanager/lockmanager_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lockmanager/lockmanager_api.c')
-rw-r--r--src/lockmanager/lockmanager_api.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/src/lockmanager/lockmanager_api.c b/src/lockmanager/lockmanager_api.c
new file mode 100644
index 0000000..99f5ab5
--- /dev/null
+++ b/src/lockmanager/lockmanager_api.c
@@ -0,0 +1,677 @@
+/*
+ This file is part of GNUnet.
+ (C) 2012 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file lockmanager/lockmanager_api.c
+ * @brief API implementation of gnunet_lockmanager_service.h
+ * @author Sree Harsha Totakura
+ */
+
+/**
+ * To be fixed:
+ * Should the handle be freed when the connection to service is lost?
+ * Should cancel_request have a call back (else simultaneous calls break)
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_client_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_lockmanager_service.h"
+#include "gnunet_protocols.h"
+
+#include "lockmanager.h"
+
+#define LOG(kind,...) \
+ GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
+
+#define TIME_REL_MINS(min) \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
+
+#define TIMEOUT TIME_REL_MINS(3)
+
+
+/**
+ * The message queue
+ */
+struct MessageQueue
+{
+ /**
+ * The next pointer for doubly linked list
+ */
+ struct MessageQueue *next;
+
+ /**
+ * The prev pointer for doubly linked list
+ */
+ struct MessageQueue *prev;
+
+ /**
+ * The LOCKMANAGER Message
+ */
+ struct GNUNET_LOCKMANAGER_Message *msg;
+};
+
+
+/**
+ * Handler for the lockmanager service
+ */
+struct GNUNET_LOCKMANAGER_Handle
+{
+ /**
+ * The client connection to the service
+ */
+ struct GNUNET_CLIENT_Connection *conn;
+
+ /**
+ * The transmit handle for transmissions using conn
+ */
+ struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
+
+ /**
+ * Hashmap handle
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *hashmap;
+
+ /**
+ * Double linked list head for message queue
+ */
+ struct MessageQueue *mq_head;
+
+ /**
+ * Double linked list tail for message queue
+ */
+ struct MessageQueue *mq_tail;
+};
+
+
+/**
+ * Structure for Locking Request
+ */
+struct GNUNET_LOCKMANAGER_LockingRequest
+{
+ /**
+ * The handle associated with this request
+ */
+ struct GNUNET_LOCKMANAGER_Handle *handle;
+
+ /**
+ * The status callback
+ */
+ GNUNET_LOCKMANAGER_StatusCallback status_cb;
+
+ /**
+ * Closure for the status callback
+ */
+ void *status_cb_cls;
+
+ /**
+ * The locking domain of this request
+ */
+ char *domain;
+
+ /**
+ * The lock
+ */
+ uint32_t lock;
+
+ /**
+ * The status of the lock
+ */
+ enum GNUNET_LOCKMANAGER_Status status;
+};
+
+
+/**
+ * Structure for matching a lock
+ */
+struct LockingRequestMatch
+{
+ /**
+ * The matched LockingRequest entry; Should be NULL if no entry is found
+ */
+ struct GNUNET_LOCKMANAGER_LockingRequest *matched_entry;
+
+ /**
+ * The locking domain name of the lock
+ */
+ const char *domain;
+
+ /**
+ * The lock number
+ */
+ uint32_t lock;
+};
+
+
+/**
+ * Transmit notify for sending message to server
+ *
+ * @param cls the lockmanager handle
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+transmit_notify (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_LOCKMANAGER_Handle *handle = cls;
+ struct MessageQueue *queue_entity;
+ uint16_t msg_size;
+
+ handle->transmit_handle = NULL;
+ if ((0 == size) || (NULL == buf))
+ {
+ /* FIXME: Timed out -- requeue? */
+ return 0;
+ }
+ queue_entity = handle->mq_head;
+ GNUNET_assert (NULL != queue_entity);
+ msg_size = ntohs (queue_entity->msg->header.size);
+ GNUNET_assert (size >= msg_size);
+ memcpy (buf, queue_entity->msg, msg_size);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Message of size %u sent\n", msg_size);
+ GNUNET_free (queue_entity->msg);
+ GNUNET_CONTAINER_DLL_remove (handle->mq_head,
+ handle->mq_tail,
+ queue_entity);
+ GNUNET_free (queue_entity);
+ queue_entity = handle->mq_head;
+ if (NULL != queue_entity)
+ {
+ handle->transmit_handle =
+ GNUNET_CLIENT_notify_transmit_ready (handle->conn,
+ ntohs
+ (queue_entity->msg->header.size),
+ TIMEOUT,
+ GNUNET_YES,
+ &transmit_notify,
+ handle);
+ }
+ return msg_size;
+}
+
+
+/**
+ * Queues a message into handle's send message queue
+ *
+ * @param handle the lockmanager handle whose queue will be used
+ * @param msg the message to be queued
+ */
+static void
+queue_message (struct GNUNET_LOCKMANAGER_Handle *handle,
+ struct GNUNET_LOCKMANAGER_Message *msg)
+{
+ struct MessageQueue *queue_entity;
+
+ GNUNET_assert (NULL != msg);
+ queue_entity = GNUNET_malloc (sizeof (struct MessageQueue));
+ queue_entity->msg = msg;
+ GNUNET_CONTAINER_DLL_insert_tail (handle->mq_head,
+ handle->mq_tail,
+ queue_entity);
+ if (NULL == handle->transmit_handle)
+ {
+ handle->transmit_handle =
+ GNUNET_CLIENT_notify_transmit_ready (handle->conn,
+ ntohs (msg->header.size),
+ TIMEOUT,
+ GNUNET_YES,
+ &transmit_notify,
+ handle);
+ }
+}
+
+
+/**
+ * Get the key for the given lock in the 'lock_map'.
+ *
+ * @param domain_name
+ * @param lock_number
+ * @param key set to the key
+ */
+static void
+get_key (const char *domain_name,
+ uint32_t lock_number,
+ struct GNUNET_HashCode *key)
+{
+ uint32_t *last_32;
+
+ GNUNET_CRYPTO_hash (domain_name,
+ strlen (domain_name),
+ key);
+ last_32 = (uint32_t *) key;
+ *last_32 ^= lock_number;
+}
+
+
+/**
+ * Hashmap iterator for matching a LockingRequest
+ *
+ * @param cls the LockingRequestMatch structure
+ * @param key current key code
+ * @param value value in the hash map (struct GNUNET_LOCKMANAGER_LockingRequest)
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int
+match_iterator (void *cls, const GNUNET_HashCode *key, void *value)
+{
+ struct LockingRequestMatch *match = cls;
+ struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
+
+ if ( (match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)) )
+ {
+ match->matched_entry = lr;
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function to find a LockingRequest associated with the given domain and lock
+ * attributes in the map
+ *
+ * @param map the map where the LockingRequests are stored
+ * @param domain the locking domain name
+ * @param lock the lock number
+ * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
+ * found
+ */
+static struct GNUNET_LOCKMANAGER_LockingRequest *
+hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
+ const char *domain,
+ uint32_t lock)
+{
+ struct GNUNET_HashCode hash;
+ struct LockingRequestMatch lock_match;
+
+ lock_match.matched_entry = NULL;
+ lock_match.domain = domain;
+ lock_match.lock = lock;
+ get_key (domain, lock, &hash);
+ GNUNET_CONTAINER_multihashmap_get_multiple (map,
+ &hash,
+ &match_iterator,
+ &lock_match);
+ return lock_match.matched_entry;
+}
+
+
+/**
+ * Task for calling status change callback for a lock
+ *
+ * @param cls the LockingRequest associated with this lock
+ * @param tc the TaskScheduler context
+ */
+static void
+call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
+
+ if (NULL != r->status_cb)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
+ r->lock, r->domain);
+ r->status_cb (r->status_cb_cls,
+ r->domain,
+ r->lock,
+ r->status);
+ }
+}
+
+
+/**
+ * Iterator to call relase and free all LockingRequest entries
+ *
+ * @param cls the lockmanager handle
+ * @param key current key code
+ * @param value the Locking request
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int
+release_iterator(void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct GNUNET_LOCKMANAGER_Handle *h = cls;
+ struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
+
+ if (NULL != r->status_cb)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Calling status change for RELEASE on lock num: %d, domain: %s\n",
+ r->lock, r->domain);
+ r->status_cb (r->status_cb_cls,
+ r->domain,
+ r->lock,
+ GNUNET_LOCKMANAGER_RELEASE);
+ }
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
+ key,
+ value));
+ GNUNET_free (r->domain);
+ GNUNET_free (r);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Handler for server replies
+ *
+ * @param cls the LOCKMANAGER_Handle
+ * @param msg received message, NULL on timeout or fatal error
+ */
+static void
+handle_replies (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct GNUNET_LOCKMANAGER_Handle *handle = cls;
+ const struct GNUNET_LOCKMANAGER_Message *m;
+ struct GNUNET_LOCKMANAGER_LockingRequest *lr;
+ const char *domain;
+ struct GNUNET_HashCode hash;
+ uint32_t lock;
+ uint16_t msize;
+
+ if (NULL == msg)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Lockmanager service not available or went down\n");
+ /* Should release all locks and free its locking requests */
+ GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
+ &release_iterator,
+ handle);
+ return;
+ }
+ GNUNET_CLIENT_receive (handle->conn,
+ &handle_replies,
+ handle,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+ if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ msize = ntohs (msg->size);
+ if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
+ domain = (const char *) &m[1];
+ msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
+ if ('\0' != domain[msize-1])
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ lock = ntohl (m->lock);
+ get_key (domain, lock, &hash);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received SUCCESS message for lock: %d, domain %s\n",
+ lock, domain);
+ if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
+ domain,
+ lock)))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Changing status for lock: %d in domain: %s to SUCCESS\n",
+ lr->lock, lr->domain);
+ lr->status = GNUNET_LOCKMANAGER_SUCCESS;
+ GNUNET_SCHEDULER_add_continuation (&call_status_cb_task,
+ lr,
+ GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+}
+
+
+/**
+ * Iterator to free hash map entries.
+ *
+ * @param cls the lockmanger handle
+ * @param key current key code
+ * @param value the Locking request
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int
+free_iterator(void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct GNUNET_LOCKMANAGER_Handle *h = cls;
+ struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Clearing locking request\n");
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
+ key,
+ value));
+ GNUNET_free (r->domain);
+ GNUNET_free (r);
+ return GNUNET_YES;
+}
+
+
+/*******************/
+/* API Definitions */
+/*******************/
+
+
+/**
+ * Connect to the lockmanager service
+ *
+ * @param cfg the configuration to use
+ *
+ * @return upon success the handle to the service; NULL upon error
+ */
+struct GNUNET_LOCKMANAGER_Handle *
+GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct GNUNET_LOCKMANAGER_Handle *h;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
+ h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
+ h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
+ if (NULL == h->conn)
+ {
+ GNUNET_free (h);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
+ return NULL;
+ }
+ h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
+ GNUNET_assert (NULL != h->hashmap);
+ GNUNET_CLIENT_receive (h->conn,
+ &handle_replies,
+ h,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
+ return h;
+}
+
+
+/**
+ * Disconnect from the lockmanager service
+ *
+ * @param handle the handle to the lockmanager service
+ */
+void
+GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
+{
+ struct MessageQueue *head;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
+ if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Some locking requests are still present. Cancel them before "
+ "calling %s\n", __func__);
+ GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
+ &free_iterator,
+ handle);
+ }
+ GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
+ /* Clear the message queue */
+ if (NULL != handle->transmit_handle)
+ {
+ GNUNET_CLIENT_notify_transmit_ready_cancel (handle->transmit_handle);
+ }
+ head = handle->mq_head;
+ while (NULL != head)
+ {
+ GNUNET_CONTAINER_DLL_remove (handle->mq_head,
+ handle->mq_tail,
+ head);
+ GNUNET_free (head->msg);
+ GNUNET_free (head);
+ head = handle->mq_head;
+ }
+ GNUNET_CLIENT_disconnect (handle->conn);
+ GNUNET_free (handle);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
+}
+
+
+/**
+ * Tries to acquire the given lock(even if the lock has been lost) until the
+ * request is called. If the lock is available the status_cb will be
+ * called. If the lock is busy then the request is queued and status_cb
+ * will be called when the lock has been made available and acquired by us.
+ *
+ * @param handle the handle to the lockmanager service
+ *
+ * @param domain_name name of the locking domain. Clients who want to share
+ * locks must use the same name for the locking domain. Also the
+ * domain_name should be selected with the prefix
+ * "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
+ *
+ *
+ * @param lock which lock to lock
+ *
+ * @param status_cb the callback for signalling when the lock is acquired and
+ * when it is lost
+ *
+ * @param status_cb_cls the closure to the above callback
+ *
+ * @return the locking request handle for this request
+ */
+struct GNUNET_LOCKMANAGER_LockingRequest *
+GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
+ const char *domain_name,
+ uint32_t lock,
+ GNUNET_LOCKMANAGER_StatusCallback
+ status_cb,
+ void *status_cb_cls)
+{
+ struct GNUNET_LOCKMANAGER_LockingRequest *r;
+ struct GNUNET_LOCKMANAGER_Message *msg;
+ struct GNUNET_HashCode hash;
+ uint16_t msg_size;
+ size_t domain_name_length;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
+ r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
+ domain_name_length = strlen (domain_name) + 1;
+ r->handle = handle;
+ r->lock = lock;
+ r->domain = GNUNET_malloc (domain_name_length);
+ r->status = GNUNET_LOCKMANAGER_RELEASE;
+ r->status_cb = status_cb;
+ r->status_cb_cls = status_cb_cls;
+ memcpy (r->domain, domain_name, domain_name_length);
+ msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_length;
+ msg = GNUNET_malloc (msg_size);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
+ msg->header.size = htons (msg_size);
+ msg->lock = htonl (lock);
+ memcpy (&msg[1], r->domain, domain_name_length);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
+ queue_message (handle, msg);
+ get_key (r->domain, r->lock, &hash);
+ GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap,
+ &hash,
+ r,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
+ return r;
+}
+
+
+
+/**
+ * Function to cancel the locking request generated by
+ * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired us then the lock is
+ * released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
+ * status changes resulting due to this call.
+ *
+ * @param request the LockingRequest to cancel
+ */
+void
+GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
+ *request)
+{
+ struct GNUNET_LOCKMANAGER_Message *msg;
+ struct GNUNET_HashCode hash;
+ uint16_t msg_size;
+ size_t domain_name_length;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
+ /* FIXME: Stop ACQUIRE retransmissions */
+ if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
+ {
+ domain_name_length = strlen (request->domain) + 1;
+ msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message)
+ + domain_name_length;
+ msg = GNUNET_malloc (msg_size);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
+ msg->header.size = htons (msg_size);
+ msg->lock = htonl (request->lock);
+ memcpy (&msg[1], request->domain, domain_name_length);
+ queue_message (request->handle, msg);
+ }
+ get_key (request->domain, request->lock, &hash);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove
+ (request->handle->hashmap, &hash, request));
+ GNUNET_free (request->domain);
+ GNUNET_free (request);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
+}