2 This file is part of GNUnet.
3 (C) 2008--2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file lockmanager/lockmanager_api.c
23 * @brief API implementation of gnunet_lockmanager_service.h
24 * @author Sree Harsha Totakura
29 #include "gnunet_common.h"
30 #include "gnunet_container_lib.h"
31 #include "gnunet_client_lib.h"
32 #include "gnunet_crypto_lib.h"
33 #include "gnunet_lockmanager_service.h"
34 #include "gnunet_protocols.h"
36 #include "lockmanager.h"
38 #define LOG(kind,...) \
39 GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
41 #define TIME_REL_MINS(min) \
42 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
44 #define TIMEOUT TIME_REL_MINS(3)
53 * The next pointer for doubly linked list
55 struct MessageQueue *next;
58 * The prev pointer for doubly linked list
60 struct MessageQueue *prev;
63 * The LOCKMANAGER Message
65 struct GNUNET_LOCKMANAGER_Message *msg;
68 * If this is a AQUIRE_LOCK message, this is the
69 * affiliated locking request.
71 struct GNUNET_LOCKMANAGER_LockingRequest *lr;
76 * Handler for the lockmanager service
78 struct GNUNET_LOCKMANAGER_Handle
81 * The client connection to the service
83 struct GNUNET_CLIENT_Connection *conn;
86 * The transmit handle for transmissions using conn
88 struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
93 struct GNUNET_CONTAINER_MultiHashMap *hashmap;
96 * Double linked list head for message queue
98 struct MessageQueue *mq_head;
101 * Double linked list tail for message queue
103 struct MessageQueue *mq_tail;
106 * Are we currently handling replies?
113 * Structure for Locking Request
115 struct GNUNET_LOCKMANAGER_LockingRequest
118 * The handle associated with this request
120 struct GNUNET_LOCKMANAGER_Handle *handle;
123 * The status callback
125 GNUNET_LOCKMANAGER_StatusCallback status_cb;
128 * Entry in the request message queue for aquiring this
129 * lock; NULL after request has been sent.
131 struct MessageQueue *mqe;
134 * Closure for the status callback
139 * The locking domain of this request
149 * The status of the lock
151 enum GNUNET_LOCKMANAGER_Status status;
154 * set to GNUNET_YES if acquire message for this lock is till in messga queue
161 * Structure for matching a lock
163 struct LockingRequestMatch
166 * The matched LockingRequest entry; Should be NULL if no entry is found
168 struct GNUNET_LOCKMANAGER_LockingRequest *matched_entry;
171 * The locking domain name of the lock
183 * Handler for server replies
185 * @param cls the LOCKMANAGER_Handle
186 * @param msg received message, NULL on timeout or fatal error
189 handle_replies (void *cls, const struct GNUNET_MessageHeader *msg);
193 * Transmit notify for sending message to server
195 * @param cls the lockmanager handle
196 * @param size number of bytes available in buf
197 * @param buf where the callee should write the message
198 * @return number of bytes written to buf
201 transmit_notify (void *cls, size_t size, void *buf)
203 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
204 struct MessageQueue *queue_entity;
207 handle->transmit_handle = NULL;
208 queue_entity = handle->mq_head;
209 GNUNET_assert (NULL != queue_entity);
210 if ((0 == size) || (NULL == buf))
212 handle->transmit_handle =
213 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
214 ntohs (queue_entity->msg->
216 GNUNET_TIME_UNIT_FOREVER_REL,
217 GNUNET_YES, &transmit_notify,
221 msg_size = ntohs (queue_entity->msg->header.size);
222 GNUNET_assert (size >= msg_size);
223 memcpy (buf, queue_entity->msg, msg_size);
224 LOG (GNUNET_ERROR_TYPE_DEBUG, "Message of size %u sent\n", msg_size);
225 if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE ==
226 ntohs (queue_entity->msg->header.type))
228 GNUNET_break (GNUNET_NO == queue_entity->lr->acquire_sent);
229 queue_entity->lr->acquire_sent = GNUNET_YES;
230 queue_entity->lr->mqe = NULL;
232 GNUNET_free (queue_entity->msg);
233 GNUNET_CONTAINER_DLL_remove (handle->mq_head, handle->mq_tail, queue_entity);
234 GNUNET_free (queue_entity);
235 queue_entity = handle->mq_head;
236 if (NULL != queue_entity)
238 handle->transmit_handle =
239 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
240 ntohs (queue_entity->msg->
241 header.size), TIMEOUT,
242 GNUNET_YES, &transmit_notify,
245 if (GNUNET_NO == handle->in_replies)
247 handle->in_replies = GNUNET_YES;
248 GNUNET_CLIENT_receive (handle->conn, &handle_replies, handle,
249 GNUNET_TIME_UNIT_FOREVER_REL);
256 * Queues a message into handle's send message queue
258 * @param handle the lockmanager handle whose queue will be used
259 * @param msg the message to be queued
260 * @param request the locking reqeust responsible for queueing this message
261 * @return the MessageQueue entity that has been queued
263 static struct MessageQueue *
264 queue_message (struct GNUNET_LOCKMANAGER_Handle *handle,
265 struct GNUNET_LOCKMANAGER_Message *msg,
266 struct GNUNET_LOCKMANAGER_LockingRequest *request)
268 struct MessageQueue *queue_entity;
270 GNUNET_assert (NULL != msg);
271 queue_entity = GNUNET_malloc (sizeof (struct MessageQueue));
272 queue_entity->msg = msg;
273 queue_entity->lr = request;
274 GNUNET_CONTAINER_DLL_insert_tail (handle->mq_head, handle->mq_tail,
276 if (NULL == handle->transmit_handle)
278 handle->transmit_handle =
279 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
280 ntohs (msg->header.size), TIMEOUT,
281 GNUNET_YES, &transmit_notify,
289 * Get the key for the given lock in the 'lock_map'.
293 * @param key set to the key
296 get_key (const char *domain_name, uint32_t lock_number,
297 struct GNUNET_HashCode *key)
301 GNUNET_CRYPTO_hash (domain_name, strlen (domain_name), key);
302 last_32 = (uint32_t *) key;
303 *last_32 ^= lock_number;
308 * Hashmap iterator for matching a LockingRequest
310 * @param cls the LockingRequestMatch structure
311 * @param key current key code
312 * @param value value in the hash map (struct GNUNET_LOCKMANAGER_LockingRequest)
313 * @return GNUNET_YES if we should continue to
318 match_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
320 struct LockingRequestMatch *match = cls;
321 struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
323 if ((match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)))
325 match->matched_entry = lr;
333 * Function to find a LockingRequest associated with the given domain and lock
334 * attributes in the map
336 * @param map the map where the LockingRequests are stored
337 * @param domain the locking domain name
338 * @param lock the lock number
339 * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
342 static struct GNUNET_LOCKMANAGER_LockingRequest *
343 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
344 const char *domain, uint32_t lock)
346 struct GNUNET_HashCode hash;
347 struct LockingRequestMatch lock_match;
349 lock_match.matched_entry = NULL;
350 lock_match.domain = domain;
351 lock_match.lock = lock;
352 get_key (domain, lock, &hash);
353 GNUNET_CONTAINER_multihashmap_get_multiple (map, &hash, &match_iterator,
355 return lock_match.matched_entry;
360 * Task for calling status change callback for a lock
362 * @param cls the LockingRequest associated with this lock
363 * @param tc the TaskScheduler context
366 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
368 const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
370 if (NULL != r->status_cb)
372 LOG (GNUNET_ERROR_TYPE_DEBUG,
373 "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
375 r->status_cb (r->status_cb_cls, r->domain, r->lock, r->status);
381 * Function to generate acquire message for a lock
383 * @param domain_name the domain name of the lock
384 * @param lock the lock number
385 * @return the generated GNUNET_LOCKMANAGER_Message
387 static struct GNUNET_LOCKMANAGER_Message *
388 generate_acquire_msg (const char *domain_name, uint32_t lock)
390 struct GNUNET_LOCKMANAGER_Message *msg;
391 size_t domain_name_len;
394 domain_name_len = strlen (domain_name) + 1;
395 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_len;
396 msg = GNUNET_malloc (msg_size);
397 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
398 msg->header.size = htons (msg_size);
399 msg->lock = htonl (lock);
400 memcpy (&msg[1], domain_name, domain_name_len);
406 * Iterator to call relase on locks; acquire messages are sent for all
407 * locks. In addition, if a lock is acquired before, it is not released and its
408 * status callback is called to signal its release
410 * @param cls the lockmanager handle
411 * @param key current key code
412 * @param value the Locking request
413 * @return GNUNET_YES if we should continue to
418 release_n_retry_iterator (void *cls, const struct GNUNET_HashCode *key,
421 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
422 struct GNUNET_LOCKMANAGER_Handle *h = cls;
423 struct GNUNET_LOCKMANAGER_Message *msg;
425 if (GNUNET_NO == r->acquire_sent) /* an acquire is still in queue */
427 r->acquire_sent = GNUNET_NO;
428 msg = generate_acquire_msg (r->domain, r->lock);
429 r->mqe = queue_message (h, msg, r);
430 if (GNUNET_LOCKMANAGER_RELEASE == r->status)
432 if (NULL != r->status_cb)
434 LOG (GNUNET_ERROR_TYPE_DEBUG,
435 "Calling status change for RELEASE on lock num: %d, domain: %s\n",
437 r->status = GNUNET_LOCKMANAGER_RELEASE;
438 r->status_cb (r->status_cb_cls, r->domain, r->lock,
439 GNUNET_LOCKMANAGER_RELEASE);
446 * Handler for server replies
448 * @param cls the LOCKMANAGER_Handle
449 * @param msg received message, NULL on timeout or fatal error
452 handle_replies (void *cls, const struct GNUNET_MessageHeader *msg)
454 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
455 const struct GNUNET_LOCKMANAGER_Message *m;
456 struct GNUNET_LOCKMANAGER_LockingRequest *lr;
458 struct GNUNET_HashCode hash;
462 handle->in_replies = GNUNET_NO;
465 LOG (GNUNET_ERROR_TYPE_DEBUG,
466 "Lockmanager service not available or went down\n");
467 /* Should release all locks and retry to acquire them */
468 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
469 &release_n_retry_iterator, handle);
472 handle->in_replies = GNUNET_YES;
473 GNUNET_CLIENT_receive (handle->conn, &handle_replies, handle,
474 GNUNET_TIME_UNIT_FOREVER_REL);
475 if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs (msg->type))
480 msize = ntohs (msg->size);
481 if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
486 m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
487 domain = (const char *) &m[1];
488 msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
489 if ('\0' != domain[msize - 1])
495 lock = ntohl (m->lock);
496 get_key (domain, lock, &hash);
497 LOG (GNUNET_ERROR_TYPE_DEBUG,
498 "Received SUCCESS message for lock: %d, domain %s\n", lock, domain);
500 (lr = hashmap_find_lockingrequest (handle->hashmap, domain, lock)))
505 if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
510 LOG (GNUNET_ERROR_TYPE_DEBUG,
511 "Changing status for lock: %d in domain: %s to SUCCESS\n", lr->lock,
513 lr->status = GNUNET_LOCKMANAGER_SUCCESS;
514 GNUNET_SCHEDULER_add_continuation (&call_status_cb_task, lr,
515 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
520 * Iterator to free hash map entries.
522 * @param cls the lockmanger handle
523 * @param key current key code
524 * @param value the Locking request
525 * @return GNUNET_YES if we should continue to
530 free_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
532 struct GNUNET_LOCKMANAGER_Handle *h = cls;
533 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
535 LOG (GNUNET_ERROR_TYPE_DEBUG, "Clearing locking request\n");
536 GNUNET_assert (GNUNET_YES ==
537 GNUNET_CONTAINER_multihashmap_remove (h->hashmap, key, value));
538 GNUNET_free (r->domain);
544 /*******************/
545 /* API Definitions */
546 /*******************/
550 * Connect to the lockmanager service
552 * @param cfg the configuration to use
554 * @return upon success the handle to the service; NULL upon error
556 struct GNUNET_LOCKMANAGER_Handle *
557 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
559 struct GNUNET_LOCKMANAGER_Handle *h;
561 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
562 h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
563 h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
567 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
570 h->hashmap = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
571 GNUNET_assert (NULL != h->hashmap);
572 h->in_replies = GNUNET_YES;
573 GNUNET_CLIENT_receive (h->conn, &handle_replies, h,
574 GNUNET_TIME_UNIT_FOREVER_REL);
575 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
581 * Disconnect from the lockmanager service
583 * @param handle the handle to the lockmanager service
586 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
588 struct MessageQueue *head;
590 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
591 if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
593 LOG (GNUNET_ERROR_TYPE_WARNING,
594 "Some locking requests are still present. Cancel them before "
595 "calling %s\n", __func__);
596 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap, &free_iterator,
599 GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
600 /* Clear the message queue */
601 if (NULL != handle->transmit_handle)
603 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->transmit_handle);
605 head = handle->mq_head;
608 GNUNET_CONTAINER_DLL_remove (handle->mq_head, handle->mq_tail, head);
609 GNUNET_free (head->msg);
611 head = handle->mq_head;
613 GNUNET_CLIENT_disconnect (handle->conn);
614 GNUNET_free (handle);
615 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
620 * Tries to acquire the given lock(even if the lock has been lost) until the
621 * request is called. If the lock is available the status_cb will be
622 * called. If the lock is busy then the request is queued and status_cb
623 * will be called when the lock has been made available and acquired by us.
625 * @param handle the handle to the lockmanager service
627 * @param domain_name name of the locking domain. Clients who want to share
628 * locks must use the same name for the locking domain. Also the
629 * domain_name should be selected with the prefix
630 * "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
633 * @param lock which lock to lock
635 * @param status_cb the callback for signalling when the lock is acquired and
638 * @param status_cb_cls the closure to the above callback
640 * @return the locking request handle for this request
642 struct GNUNET_LOCKMANAGER_LockingRequest *
643 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
644 const char *domain_name, uint32_t lock,
645 GNUNET_LOCKMANAGER_StatusCallback status_cb,
648 struct GNUNET_LOCKMANAGER_LockingRequest *r;
649 struct GNUNET_LOCKMANAGER_Message *msg;
650 struct GNUNET_HashCode hash;
651 size_t domain_name_length;
653 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
654 r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
655 domain_name_length = strlen (domain_name) + 1;
658 r->domain = GNUNET_malloc (domain_name_length);
659 r->status = GNUNET_LOCKMANAGER_RELEASE;
660 r->status_cb = status_cb;
661 r->status_cb_cls = status_cb_cls;
662 r->acquire_sent = GNUNET_NO;
663 memcpy (r->domain, domain_name, domain_name_length);
664 msg = generate_acquire_msg (r->domain, r->lock);
665 LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
666 r->mqe = queue_message (handle, msg, r);
667 get_key (r->domain, r->lock, &hash);
668 GNUNET_assert (GNUNET_OK ==
669 GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap, &hash,
671 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
672 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
678 * Function to cancel the locking request generated by
679 * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired by us then the lock
680 * is released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
681 * status changes resulting due to this call.
683 * @param request the LockingRequest to cancel
686 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
689 struct GNUNET_LOCKMANAGER_Message *msg;
690 struct GNUNET_HashCode hash;
692 size_t domain_name_length;
694 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
695 if (GNUNET_NO == request->acquire_sent)
697 GNUNET_assert (NULL != request->mqe);
698 if ((NULL != request->handle->transmit_handle) &&
699 (request->handle->mq_head == request->mqe))
701 GNUNET_CLIENT_notify_transmit_ready_cancel (request->
702 handle->transmit_handle);
703 request->handle->transmit_handle = NULL;
705 GNUNET_CONTAINER_DLL_remove (request->handle->mq_head,
706 request->handle->mq_tail, request->mqe);
707 GNUNET_free (request->mqe->msg);
708 GNUNET_free (request->mqe);
709 request->status = GNUNET_LOCKMANAGER_RELEASE;
711 if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
713 domain_name_length = strlen (request->domain) + 1;
714 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_length;
715 msg = GNUNET_malloc (msg_size);
716 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
717 msg->header.size = htons (msg_size);
718 msg->lock = htonl (request->lock);
719 memcpy (&msg[1], request->domain, domain_name_length);
720 GNUNET_assert (NULL == request->mqe);
721 (void) queue_message (request->handle, msg, request);
723 get_key (request->domain, request->lock, &hash);
724 GNUNET_assert (GNUNET_YES ==
725 GNUNET_CONTAINER_multihashmap_remove (request->handle->hashmap,
727 GNUNET_free (request->domain);
728 GNUNET_free (request);
729 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
732 /* end of lockmanager_api.c */