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;
75 * Handler for the lockmanager service
77 struct GNUNET_LOCKMANAGER_Handle
80 * The client connection to the service
82 struct GNUNET_CLIENT_Connection *conn;
85 * The transmit handle for transmissions using conn
87 struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
92 struct GNUNET_CONTAINER_MultiHashMap *hashmap;
95 * Double linked list head for message queue
97 struct MessageQueue *mq_head;
100 * Double linked list tail for message queue
102 struct MessageQueue *mq_tail;
107 * Structure for Locking Request
109 struct GNUNET_LOCKMANAGER_LockingRequest
112 * The handle associated with this request
114 struct GNUNET_LOCKMANAGER_Handle *handle;
117 * The status callback
119 GNUNET_LOCKMANAGER_StatusCallback status_cb;
122 * Closure for the status callback
127 * The locking domain of this request
137 * The status of the lock
139 enum GNUNET_LOCKMANAGER_Status status;
144 * Structure for matching a lock
146 struct LockingRequestMatch
149 * The matched LockingRequest entry; Should be NULL if no entry is found
151 struct GNUNET_LOCKMANAGER_LockingRequest *matched_entry;
154 * The locking domain name of the lock
166 * Transmit notify for sending message to server
168 * @param cls the lockmanager handle
169 * @param size number of bytes available in buf
170 * @param buf where the callee should write the message
171 * @return number of bytes written to buf
174 transmit_notify (void *cls, size_t size, void *buf)
176 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
177 struct MessageQueue *queue_entity;
180 handle->transmit_handle = NULL;
181 if ((0 == size) || (NULL == buf))
183 /* FIXME: Timed out -- requeue? */
186 queue_entity = handle->mq_head;
187 GNUNET_assert (NULL != queue_entity);
188 msg_size = ntohs (queue_entity->msg->header.size);
189 GNUNET_assert (size >= msg_size);
190 memcpy (buf, queue_entity->msg, msg_size);
191 LOG (GNUNET_ERROR_TYPE_DEBUG,
192 "Message of size %u sent\n", msg_size);
193 GNUNET_free (queue_entity->msg);
194 GNUNET_CONTAINER_DLL_remove (handle->mq_head,
197 GNUNET_free (queue_entity);
198 queue_entity = handle->mq_head;
199 if (NULL != queue_entity)
201 handle->transmit_handle =
202 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
204 (queue_entity->msg->header.size),
215 * Queues a message into handle's send message queue
217 * @param handle the lockmanager handle whose queue will be used
218 * @param msg the message to be queued
221 queue_message (struct GNUNET_LOCKMANAGER_Handle *handle,
222 struct GNUNET_LOCKMANAGER_Message *msg)
224 struct MessageQueue *queue_entity;
226 GNUNET_assert (NULL != msg);
227 queue_entity = GNUNET_malloc (sizeof (struct MessageQueue));
228 queue_entity->msg = msg;
229 GNUNET_CONTAINER_DLL_insert_tail (handle->mq_head,
232 if (NULL == handle->transmit_handle)
234 handle->transmit_handle =
235 GNUNET_CLIENT_notify_transmit_ready (handle->conn,
236 ntohs (msg->header.size),
246 * Get the key for the given lock in the 'lock_map'.
250 * @param key set to the key
253 get_key (const char *domain_name,
254 uint32_t lock_number,
255 struct GNUNET_HashCode *key)
259 GNUNET_CRYPTO_hash (domain_name,
260 strlen (domain_name),
262 last_32 = (uint32_t *) key;
263 *last_32 ^= lock_number;
268 * Hashmap iterator for matching a LockingRequest
270 * @param cls the LockingRequestMatch structure
271 * @param key current key code
272 * @param value value in the hash map (struct GNUNET_LOCKMANAGER_LockingRequest)
273 * @return GNUNET_YES if we should continue to
278 match_iterator (void *cls, const GNUNET_HashCode *key, void *value)
280 struct LockingRequestMatch *match = cls;
281 struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
283 if ( (match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)) )
285 match->matched_entry = lr;
293 * Function to find a LockingRequest associated with the given domain and lock
294 * attributes in the map
296 * @param map the map where the LockingRequests are stored
297 * @param domain the locking domain name
298 * @param lock the lock number
299 * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
302 static struct GNUNET_LOCKMANAGER_LockingRequest *
303 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
307 struct GNUNET_HashCode hash;
308 struct LockingRequestMatch lock_match;
310 lock_match.matched_entry = NULL;
311 lock_match.domain = domain;
312 lock_match.lock = lock;
313 get_key (domain, lock, &hash);
314 GNUNET_CONTAINER_multihashmap_get_multiple (map,
318 return lock_match.matched_entry;
323 * Task for calling status change callback for a lock
325 * @param cls the LockingRequest associated with this lock
326 * @param tc the TaskScheduler context
329 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
331 const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
333 if (NULL != r->status_cb)
335 LOG (GNUNET_ERROR_TYPE_DEBUG,
336 "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
338 r->status_cb (r->status_cb_cls,
347 * Iterator to call relase and free all LockingRequest entries
349 * @param cls the lockmanager handle
350 * @param key current key code
351 * @param value the Locking request
352 * @return GNUNET_YES if we should continue to
357 release_iterator(void *cls,
358 const GNUNET_HashCode * key,
361 struct GNUNET_LOCKMANAGER_Handle *h = cls;
362 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
364 if (NULL != r->status_cb)
366 LOG (GNUNET_ERROR_TYPE_DEBUG,
367 "Calling status change for RELEASE on lock num: %d, domain: %s\n",
369 r->status_cb (r->status_cb_cls,
372 GNUNET_LOCKMANAGER_RELEASE);
374 GNUNET_assert (GNUNET_YES ==
375 GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
378 GNUNET_free (r->domain);
385 * Handler for server replies
387 * @param cls the LOCKMANAGER_Handle
388 * @param msg received message, NULL on timeout or fatal error
391 handle_replies (void *cls,
392 const struct GNUNET_MessageHeader *msg)
394 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
395 const struct GNUNET_LOCKMANAGER_Message *m;
396 struct GNUNET_LOCKMANAGER_LockingRequest *lr;
398 struct GNUNET_HashCode hash;
404 LOG (GNUNET_ERROR_TYPE_DEBUG,
405 "Lockmanager service not available or went down\n");
406 /* Should release all locks and free its locking requests */
407 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
412 GNUNET_CLIENT_receive (handle->conn,
415 GNUNET_TIME_UNIT_FOREVER_REL);
416 if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
421 msize = ntohs (msg->size);
422 if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
427 m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
428 domain = (const char *) &m[1];
429 msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
430 if ('\0' != domain[msize-1])
436 lock = ntohl (m->lock);
437 get_key (domain, lock, &hash);
438 LOG (GNUNET_ERROR_TYPE_DEBUG,
439 "Received SUCCESS message for lock: %d, domain %s\n",
441 if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
448 if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
453 LOG (GNUNET_ERROR_TYPE_DEBUG,
454 "Changing status for lock: %d in domain: %s to SUCCESS\n",
455 lr->lock, lr->domain);
456 lr->status = GNUNET_LOCKMANAGER_SUCCESS;
457 GNUNET_SCHEDULER_add_continuation (&call_status_cb_task,
459 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
464 * Iterator to free hash map entries.
466 * @param cls the lockmanger handle
467 * @param key current key code
468 * @param value the Locking request
469 * @return GNUNET_YES if we should continue to
474 free_iterator(void *cls,
475 const GNUNET_HashCode * key,
478 struct GNUNET_LOCKMANAGER_Handle *h = cls;
479 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
481 LOG (GNUNET_ERROR_TYPE_DEBUG,
482 "Clearing locking request\n");
483 GNUNET_assert (GNUNET_YES ==
484 GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
487 GNUNET_free (r->domain);
493 /*******************/
494 /* API Definitions */
495 /*******************/
499 * Connect to the lockmanager service
501 * @param cfg the configuration to use
503 * @return upon success the handle to the service; NULL upon error
505 struct GNUNET_LOCKMANAGER_Handle *
506 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
508 struct GNUNET_LOCKMANAGER_Handle *h;
510 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
511 h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
512 h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
516 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
519 h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
520 GNUNET_assert (NULL != h->hashmap);
521 GNUNET_CLIENT_receive (h->conn,
524 GNUNET_TIME_UNIT_FOREVER_REL);
526 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
532 * Disconnect from the lockmanager service
534 * @param handle the handle to the lockmanager service
537 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
539 struct MessageQueue *head;
541 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
542 if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
544 LOG (GNUNET_ERROR_TYPE_WARNING,
545 "Some locking requests are still present. Cancel them before "
546 "calling %s\n", __func__);
547 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
551 GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
552 /* Clear the message queue */
553 if (NULL != handle->transmit_handle)
555 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->transmit_handle);
557 head = handle->mq_head;
560 GNUNET_CONTAINER_DLL_remove (handle->mq_head,
563 GNUNET_free (head->msg);
565 head = handle->mq_head;
567 GNUNET_CLIENT_disconnect (handle->conn);
568 GNUNET_free (handle);
569 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
574 * Tries to acquire the given lock(even if the lock has been lost) until the
575 * request is called. If the lock is available the status_cb will be
576 * called. If the lock is busy then the request is queued and status_cb
577 * will be called when the lock has been made available and acquired by us.
579 * @param handle the handle to the lockmanager service
581 * @param domain_name name of the locking domain. Clients who want to share
582 * locks must use the same name for the locking domain. Also the
583 * domain_name should be selected with the prefix
584 * "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
587 * @param lock which lock to lock
589 * @param status_cb the callback for signalling when the lock is acquired and
592 * @param status_cb_cls the closure to the above callback
594 * @return the locking request handle for this request
596 struct GNUNET_LOCKMANAGER_LockingRequest *
597 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
598 const char *domain_name,
600 GNUNET_LOCKMANAGER_StatusCallback
604 struct GNUNET_LOCKMANAGER_LockingRequest *r;
605 struct GNUNET_LOCKMANAGER_Message *msg;
606 struct GNUNET_HashCode hash;
608 size_t domain_name_length;
610 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
611 r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
612 domain_name_length = strlen (domain_name) + 1;
615 r->domain = GNUNET_malloc (domain_name_length);
616 r->status = GNUNET_LOCKMANAGER_RELEASE;
617 r->status_cb = status_cb;
618 r->status_cb_cls = status_cb_cls;
619 memcpy (r->domain, domain_name, domain_name_length);
620 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_length;
621 msg = GNUNET_malloc (msg_size);
622 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
623 msg->header.size = htons (msg_size);
624 msg->lock = htonl (lock);
625 memcpy (&msg[1], r->domain, domain_name_length);
626 LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
627 queue_message (handle, msg);
628 get_key (r->domain, r->lock, &hash);
629 GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap,
632 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
633 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
640 * Function to cancel the locking request generated by
641 * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired us then the lock is
642 * released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
643 * status changes resulting due to this call.
645 * @param request the LockingRequest to cancel
648 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
651 struct GNUNET_LOCKMANAGER_Message *msg;
652 struct GNUNET_HashCode hash;
654 size_t domain_name_length;
656 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
657 /* FIXME: Stop ACQUIRE retransmissions */
658 if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
660 domain_name_length = strlen (request->domain) + 1;
661 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message)
662 + domain_name_length;
663 msg = GNUNET_malloc (msg_size);
664 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
665 msg->header.size = htons (msg_size);
666 msg->lock = htonl (request->lock);
667 memcpy (&msg[1], request->domain, domain_name_length);
668 queue_message (request->handle, msg);
670 get_key (request->domain, request->lock, &hash);
671 GNUNET_assert (GNUNET_YES ==
672 GNUNET_CONTAINER_multihashmap_remove
673 (request->handle->hashmap, &hash, request));
674 GNUNET_free (request->domain);
675 GNUNET_free (request);
676 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);