2 This file is part of GNUnet.
3 (C) 2012 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 * Should the handle be freed when the connection to service is lost?
30 * Should cancel_request have a call back (else simultaneous calls break)
34 #include "gnunet_common.h"
35 #include "gnunet_container_lib.h"
36 #include "gnunet_client_lib.h"
37 #include "gnunet_crypto_lib.h"
38 #include "gnunet_lockmanager_service.h"
39 #include "gnunet_protocols.h"
41 #include "lockmanager.h"
43 #define LOG(kind,...) \
44 GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
46 #define TIME_REL_MINS(min) \
47 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
49 #define TIMEOUT TIME_REL_MINS(3)
58 * The next pointer for doubly linked list
60 struct MessageQueue *next;
63 * The prev pointer for doubly linked list
65 struct MessageQueue *prev;
68 * The LOCKMANAGER Message
70 struct GNUNET_LOCKMANAGER_Message *msg;
73 * If this is a AQUIRE_LOCK message, this is the
74 * affiliated locking request.
76 struct GNUNET_LOCKMANAGER_LockingRequest *lr;
81 * Handler for the lockmanager service
83 struct GNUNET_LOCKMANAGER_Handle
86 * The client connection to the service
88 struct GNUNET_CLIENT_Connection *conn;
91 * The transmit handle for transmissions using conn
93 struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
98 struct GNUNET_CONTAINER_MultiHashMap *hashmap;
101 * Double linked list head for message queue
103 struct MessageQueue *mq_head;
106 * Double linked list tail for message queue
108 struct MessageQueue *mq_tail;
111 * Are we currently handling replies?
118 * Structure for Locking Request
120 struct GNUNET_LOCKMANAGER_LockingRequest
123 * The handle associated with this request
125 struct GNUNET_LOCKMANAGER_Handle *handle;
128 * The status callback
130 GNUNET_LOCKMANAGER_StatusCallback status_cb;
133 * Entry in the request message queue for aquiring this
134 * lock; NULL after request has been sent.
136 struct MessageQueue *mqe;
139 * Closure for the status callback
144 * The locking domain of this request
154 * The status of the lock
156 enum GNUNET_LOCKMANAGER_Status status;
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,
190 const struct GNUNET_MessageHeader *msg);
194 * Transmit notify for sending message to server
196 * @param cls the lockmanager handle
197 * @param size number of bytes available in buf
198 * @param buf where the callee should write the message
199 * @return number of bytes written to buf
202 transmit_notify (void *cls, size_t size, void *buf)
204 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
205 struct MessageQueue *queue_entity;
208 handle->transmit_handle = NULL;
209 queue_entity = handle->mq_head;
210 GNUNET_assert (NULL != queue_entity);
211 if ((0 == size) || (NULL == buf))
213 handle->transmit_handle =
214 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
216 (queue_entity->msg->header.size),
217 GNUNET_TIME_UNIT_FOREVER_REL,
223 msg_size = ntohs (queue_entity->msg->header.size);
224 GNUNET_assert (size >= msg_size);
225 memcpy (buf, queue_entity->msg, msg_size);
226 LOG (GNUNET_ERROR_TYPE_DEBUG,
227 "Message of size %u sent\n", msg_size);
228 GNUNET_free (queue_entity->msg);
229 GNUNET_CONTAINER_DLL_remove (handle->mq_head,
232 GNUNET_free (queue_entity);
233 queue_entity = handle->mq_head;
234 if (NULL != queue_entity)
236 handle->transmit_handle =
237 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
239 (queue_entity->msg->header.size),
245 if (GNUNET_NO == handle->in_replies)
247 GNUNET_CLIENT_receive (handle->conn,
250 GNUNET_TIME_UNIT_FOREVER_REL);
251 handle->in_replies = GNUNET_YES;
258 * Queues a message into handle's send message queue
260 * @param handle the lockmanager handle whose queue will be used
261 * @param msg the message to be queued
264 queue_message (struct GNUNET_LOCKMANAGER_Handle *handle,
265 struct GNUNET_LOCKMANAGER_Message *msg)
267 struct MessageQueue *queue_entity;
269 GNUNET_assert (NULL != msg);
270 queue_entity = GNUNET_malloc (sizeof (struct MessageQueue));
271 queue_entity->msg = msg;
272 GNUNET_CONTAINER_DLL_insert_tail (handle->mq_head,
275 if (NULL == handle->transmit_handle)
277 handle->transmit_handle =
278 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
279 ntohs (msg->header.size),
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,
297 uint32_t lock_number,
298 struct GNUNET_HashCode *key)
302 GNUNET_CRYPTO_hash (domain_name,
303 strlen (domain_name),
305 last_32 = (uint32_t *) key;
306 *last_32 ^= lock_number;
311 * Hashmap iterator for matching a LockingRequest
313 * @param cls the LockingRequestMatch structure
314 * @param key current key code
315 * @param value value in the hash map (struct GNUNET_LOCKMANAGER_LockingRequest)
316 * @return GNUNET_YES if we should continue to
321 match_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
323 struct LockingRequestMatch *match = cls;
324 struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
326 if ( (match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)) )
328 match->matched_entry = lr;
336 * Function to find a LockingRequest associated with the given domain and lock
337 * attributes in the map
339 * @param map the map where the LockingRequests are stored
340 * @param domain the locking domain name
341 * @param lock the lock number
342 * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
345 static struct GNUNET_LOCKMANAGER_LockingRequest *
346 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
350 struct GNUNET_HashCode hash;
351 struct LockingRequestMatch lock_match;
353 lock_match.matched_entry = NULL;
354 lock_match.domain = domain;
355 lock_match.lock = lock;
356 get_key (domain, lock, &hash);
357 GNUNET_CONTAINER_multihashmap_get_multiple (map,
361 return lock_match.matched_entry;
366 * Task for calling status change callback for a lock
368 * @param cls the LockingRequest associated with this lock
369 * @param tc the TaskScheduler context
372 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
374 const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
376 if (NULL != r->status_cb)
378 LOG (GNUNET_ERROR_TYPE_DEBUG,
379 "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
381 r->status_cb (r->status_cb_cls,
390 * Function to generate acquire message for a lock
392 * @param domain_name the domain name of the lock
393 * @param lock the lock number
394 * @return the generated GNUNET_LOCKMANAGER_Message
396 static struct GNUNET_LOCKMANAGER_Message *
397 generate_acquire_msg (const char *domain_name, uint32_t lock)
399 struct GNUNET_LOCKMANAGER_Message *msg;
400 size_t domain_name_len;
403 domain_name_len = strlen (domain_name) + 1;
404 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_len;
405 msg = GNUNET_malloc (msg_size);
406 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
407 msg->header.size = htons (msg_size);
408 msg->lock = htonl (lock);
409 memcpy (&msg[1], domain_name, domain_name_len);
415 * Iterator to call relase on locks
417 * @param cls the lockmanager handle
418 * @param key current key code
419 * @param value the Locking request
420 * @return GNUNET_YES if we should continue to
425 release_n_retry_iterator (void *cls,
426 const struct GNUNET_HashCode * key,
429 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
430 struct GNUNET_LOCKMANAGER_Handle *h = cls;
431 struct GNUNET_LOCKMANAGER_Message *msg;
433 msg = generate_acquire_msg (r->domain, r->lock);
434 queue_message (h, msg);
435 if (GNUNET_LOCKMANAGER_RELEASE == r->status)
437 if (NULL != r->status_cb)
439 LOG (GNUNET_ERROR_TYPE_DEBUG,
440 "Calling status change for RELEASE on lock num: %d, domain: %s\n",
442 r->status = GNUNET_LOCKMANAGER_RELEASE;
443 r->status_cb (r->status_cb_cls,
446 GNUNET_LOCKMANAGER_RELEASE);
453 * Handler for server replies
455 * @param cls the LOCKMANAGER_Handle
456 * @param msg received message, NULL on timeout or fatal error
459 handle_replies (void *cls,
460 const struct GNUNET_MessageHeader *msg)
462 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
463 const struct GNUNET_LOCKMANAGER_Message *m;
464 struct GNUNET_LOCKMANAGER_LockingRequest *lr;
466 struct GNUNET_HashCode hash;
470 handle->in_replies = GNUNET_NO;
473 LOG (GNUNET_ERROR_TYPE_DEBUG,
474 "Lockmanager service not available or went down\n");
475 /* Should release all locks and retry to acquire them */
476 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
477 &release_n_retry_iterator,
481 GNUNET_CLIENT_receive (handle->conn,
484 GNUNET_TIME_UNIT_FOREVER_REL);
485 handle->in_replies = GNUNET_YES;
486 if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
491 msize = ntohs (msg->size);
492 if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
497 m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
498 domain = (const char *) &m[1];
499 msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
500 if ('\0' != domain[msize-1])
506 lock = ntohl (m->lock);
507 get_key (domain, lock, &hash);
508 LOG (GNUNET_ERROR_TYPE_DEBUG,
509 "Received SUCCESS message for lock: %d, domain %s\n",
511 if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
518 if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
523 LOG (GNUNET_ERROR_TYPE_DEBUG,
524 "Changing status for lock: %d in domain: %s to SUCCESS\n",
525 lr->lock, lr->domain);
526 lr->status = GNUNET_LOCKMANAGER_SUCCESS;
527 GNUNET_SCHEDULER_add_continuation (&call_status_cb_task,
529 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
534 * Iterator to free hash map entries.
536 * @param cls the lockmanger handle
537 * @param key current key code
538 * @param value the Locking request
539 * @return GNUNET_YES if we should continue to
544 free_iterator(void *cls,
545 const struct GNUNET_HashCode * key,
548 struct GNUNET_LOCKMANAGER_Handle *h = cls;
549 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
551 LOG (GNUNET_ERROR_TYPE_DEBUG,
552 "Clearing locking request\n");
553 GNUNET_assert (GNUNET_YES ==
554 GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
557 GNUNET_free (r->domain);
563 /*******************/
564 /* API Definitions */
565 /*******************/
569 * Connect to the lockmanager service
571 * @param cfg the configuration to use
573 * @return upon success the handle to the service; NULL upon error
575 struct GNUNET_LOCKMANAGER_Handle *
576 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
578 struct GNUNET_LOCKMANAGER_Handle *h;
580 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
581 h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
582 h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
586 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
589 h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
590 GNUNET_assert (NULL != h->hashmap);
591 GNUNET_CLIENT_receive (h->conn,
594 GNUNET_TIME_UNIT_FOREVER_REL);
595 h->in_replies = GNUNET_YES;
596 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
602 * Disconnect from the lockmanager service
604 * @param handle the handle to the lockmanager service
607 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
609 struct MessageQueue *head;
611 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
612 if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
614 LOG (GNUNET_ERROR_TYPE_WARNING,
615 "Some locking requests are still present. Cancel them before "
616 "calling %s\n", __func__);
617 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
621 GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
622 /* Clear the message queue */
623 if (NULL != handle->transmit_handle)
625 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->transmit_handle);
627 head = handle->mq_head;
630 GNUNET_CONTAINER_DLL_remove (handle->mq_head,
633 GNUNET_free (head->msg);
635 head = handle->mq_head;
637 GNUNET_CLIENT_disconnect (handle->conn);
638 GNUNET_free (handle);
639 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
644 * Tries to acquire the given lock(even if the lock has been lost) until the
645 * request is called. If the lock is available the status_cb will be
646 * called. If the lock is busy then the request is queued and status_cb
647 * will be called when the lock has been made available and acquired by us.
649 * @param handle the handle to the lockmanager service
651 * @param domain_name name of the locking domain. Clients who want to share
652 * locks must use the same name for the locking domain. Also the
653 * domain_name should be selected with the prefix
654 * "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
657 * @param lock which lock to lock
659 * @param status_cb the callback for signalling when the lock is acquired and
662 * @param status_cb_cls the closure to the above callback
664 * @return the locking request handle for this request
666 struct GNUNET_LOCKMANAGER_LockingRequest *
667 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
668 const char *domain_name,
670 GNUNET_LOCKMANAGER_StatusCallback
674 struct GNUNET_LOCKMANAGER_LockingRequest *r;
675 struct GNUNET_LOCKMANAGER_Message *msg;
676 struct GNUNET_HashCode hash;
677 size_t domain_name_length;
679 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
680 r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
681 domain_name_length = strlen (domain_name) + 1;
684 r->domain = GNUNET_malloc (domain_name_length);
685 r->status = GNUNET_LOCKMANAGER_RELEASE;
686 r->status_cb = status_cb;
687 r->status_cb_cls = status_cb_cls;
688 memcpy (r->domain, domain_name, domain_name_length);
689 msg = generate_acquire_msg (r->domain, r->lock);
690 LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
691 queue_message (handle, msg);
692 get_key (r->domain, r->lock, &hash);
693 GNUNET_assert (GNUNET_OK ==
694 GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap,
697 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
698 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
704 * Function to cancel the locking request generated by
705 * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired by us then the lock
706 * is released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
707 * status changes resulting due to this call.
709 * @param request the LockingRequest to cancel
712 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
715 struct GNUNET_LOCKMANAGER_Message *msg;
716 struct GNUNET_HashCode hash;
718 size_t domain_name_length;
720 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
721 /* FIXME: Stop ACQUIRE retransmissions */
722 if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
724 domain_name_length = strlen (request->domain) + 1;
725 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message)
726 + domain_name_length;
727 msg = GNUNET_malloc (msg_size);
728 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
729 msg->header.size = htons (msg_size);
730 msg->lock = htonl (request->lock);
731 memcpy (&msg[1], request->domain, domain_name_length);
732 queue_message (request->handle, msg);
734 get_key (request->domain, request->lock, &hash);
735 GNUNET_assert (GNUNET_YES ==
736 GNUNET_CONTAINER_multihashmap_remove
737 (request->handle->hashmap, &hash, request));
738 GNUNET_free (request->domain);
739 GNUNET_free (request);
740 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);