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
28 #include "gnunet_common.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_client_lib.h"
31 #include "gnunet_crypto_lib.h"
32 #include "gnunet_lockmanager_service.h"
33 #include "gnunet_protocols.h"
35 #include "lockmanager.h"
37 #define LOG(kind,...) \
38 GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
40 #define TIME_REL_MINS(min) \
41 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
43 #define TIMEOUT TIME_REL_MINS(3)
46 * Handler for the lockmanager service
48 struct GNUNET_LOCKMANAGER_Handle
51 * The client connection to the service
53 struct GNUNET_CLIENT_Connection *conn;
58 struct GNUNET_CONTAINER_MultiHashMap *hashmap;
63 * Structure for Locking Request
65 struct GNUNET_LOCKMANAGER_LockingRequest
68 * The handle associated with this request
70 struct GNUNET_LOCKMANAGER_Handle *handle;
75 GNUNET_LOCKMANAGER_StatusCallback status_cb;
78 * Closure for the status callback
83 * The pending transmit handle for the ACQUIRE message
85 struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
88 * The locking domain of this request
98 * The status of the lock
100 enum GNUNET_LOCKMANAGER_Status status;
105 * Structure for matching a lock
107 struct LockingRequestMatch
110 * The matched LockingRequest entry; Should be NULL if no entry is found
112 struct GNUNET_LOCKMANAGER_LockingRequest *matched_entry;
115 * The locking domain name of the lock
127 * Get the key for the given lock in the 'lock_map'.
131 * @param key set to the key
134 get_key (const char *domain_name,
135 uint32_t lock_number,
136 struct GNUNET_HashCode *key)
140 GNUNET_CRYPTO_hash (domain_name,
141 strlen (domain_name),
143 last_32 = (uint32_t *) key;
144 *last_32 ^= lock_number;
149 * Hashmap iterator for matching a LockingRequest
151 * @param cls the LockingRequestMatch structure
152 * @param key current key code
153 * @param value value in the hash map (struct GNUNET_LOCKMANAGER_LockingRequest)
154 * @return GNUNET_YES if we should continue to
159 match_iterator (void *cls, const GNUNET_HashCode *key, void *value)
161 struct LockingRequestMatch *match = cls;
162 struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
164 if ( (match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)) )
166 match->matched_entry = lr;
174 * Function to find a LockingRequest associated with the given domain and lock
175 * attributes in the map
177 * @param map the map where the LockingRequests are stored
178 * @param domain the locking domain name
179 * @param lock the lock number
180 * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
183 static struct GNUNET_LOCKMANAGER_LockingRequest *
184 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
188 struct GNUNET_HashCode hash;
189 struct LockingRequestMatch lock_match;
191 lock_match.matched_entry = NULL;
192 lock_match.domain = domain;
193 lock_match.lock = lock;
194 get_key (domain, lock, &hash);
195 GNUNET_CONTAINER_multihashmap_get_multiple (map,
199 return lock_match.matched_entry;
204 * Task for calling status change callback for a lock
206 * @param cls the LockingRequest associated with this lock
207 * @param tc the TaskScheduler context
210 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
212 const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
214 if (NULL != r->status_cb)
216 LOG (GNUNET_ERROR_TYPE_DEBUG,
217 "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
219 r->status_cb (r->status_cb_cls,
228 * Iterator to call relase and free all LockingRequest entries
230 * @param cls the lockmanager handle
231 * @param key current key code
232 * @param value the Locking request
233 * @return GNUNET_YES if we should continue to
238 release_iterator(void *cls,
239 const GNUNET_HashCode * key,
242 struct GNUNET_LOCKMANAGER_Handle *h = cls;
243 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
245 if (NULL != r->status_cb)
247 LOG (GNUNET_ERROR_TYPE_DEBUG,
248 "Calling status change for RELEASE on lock num: %d, domain: %s\n",
250 r->status_cb (r->status_cb_cls,
253 GNUNET_LOCKMANAGER_RELEASE);
255 GNUNET_assert (GNUNET_YES ==
256 GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
259 GNUNET_free (r->domain);
266 * Handler for server replies
268 * @param cls the LOCKMANAGER_Handle
269 * @param msg received message, NULL on timeout or fatal error
272 handle_replies (void *cls,
273 const struct GNUNET_MessageHeader *msg)
275 struct GNUNET_LOCKMANAGER_Handle *handle = cls;
276 const struct GNUNET_LOCKMANAGER_Message *m;
277 struct GNUNET_LOCKMANAGER_LockingRequest *lr;
279 struct GNUNET_HashCode hash;
285 LOG (GNUNET_ERROR_TYPE_DEBUG,
286 "Lockmanager service not available or went down\n");
287 /* Should release all locks and free its locking requests */
288 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
293 if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
298 msize = ntohs (msg->size);
299 if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
304 m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
305 domain = (const char *) &m[1];
306 msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
307 if ('\0' != domain[msize-1])
313 lock = ntohl (m->lock);
314 get_key (domain, lock, &hash);
315 LOG (GNUNET_ERROR_TYPE_DEBUG,
316 "Received SUCCESS message for lock: %d, domain %s\n",
318 if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
325 if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
330 LOG (GNUNET_ERROR_TYPE_DEBUG,
331 "Changing status for lock: %d in domain: %s to SUCCESS\n",
332 lr->lock, lr->domain);
333 lr->status = GNUNET_LOCKMANAGER_SUCCESS;
334 GNUNET_SCHEDULER_add_continuation (&call_status_cb_task,
336 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
341 * Transmit notify for sending message to server
343 * @param cls the message to send
344 * @param size number of bytes available in buf
345 * @param buf where the callee should write the message
346 * @return number of bytes written to buf
349 transmit_notify (void *cls, size_t size, void *buf)
351 struct GNUNET_LOCKMANAGER_Message *msg = cls;
354 if ((0 == size) || (NULL == buf))
356 /* FIXME: Timed out -- requeue? */
359 msg_size = ntohs (msg->header.size);
360 GNUNET_assert (size >= msg_size);
361 memcpy (buf, msg, msg_size);
363 LOG (GNUNET_ERROR_TYPE_DEBUG,
364 "Message of size %u sent\n", msg_size);
370 * Iterator to free hash map entries.
372 * @param cls the lockmanger handle
373 * @param key current key code
374 * @param value the Locking request
375 * @return GNUNET_YES if we should continue to
380 free_iterator(void *cls,
381 const GNUNET_HashCode * key,
384 struct GNUNET_LOCKMANAGER_Handle *h = cls;
385 struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
387 LOG (GNUNET_ERROR_TYPE_DEBUG,
388 "Clearing locking request\n");
389 GNUNET_assert (GNUNET_YES ==
390 GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
393 GNUNET_free (r->domain);
399 /*******************/
400 /* API Definitions */
401 /*******************/
405 * Connect to the lockmanager service
407 * @param cfg the configuration to use
409 * @return upon success the handle to the service; NULL upon error
411 struct GNUNET_LOCKMANAGER_Handle *
412 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
414 struct GNUNET_LOCKMANAGER_Handle *h;
416 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
417 h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
418 h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
422 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
425 h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
426 GNUNET_assert (NULL != h->hashmap);
427 GNUNET_CLIENT_receive (h->conn,
430 GNUNET_TIME_UNIT_FOREVER_REL);
432 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
438 * Disconnect from the lockmanager service
440 * @param handle the handle to the lockmanager service
443 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
445 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
446 GNUNET_CLIENT_disconnect (handle->conn);
447 if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
449 LOG (GNUNET_ERROR_TYPE_WARNING,
450 "Some locking requests are still present. Cancel them before "
451 "calling %s\n", __func__);
452 GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
456 GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
457 GNUNET_free (handle);
458 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
463 * Tries to acquire the given lock(even if the lock has been lost) until the
464 * request is called. If the lock is available the status_cb will be
465 * called. If the lock is busy then the request is queued and status_cb
466 * will be called when the lock has been made available and acquired by us.
468 * @param handle the handle to the lockmanager service
470 * @param domain_name name of the locking domain. Clients who want to share
471 * locks must use the same name for the locking domain. Also the
472 * domain_name should be selected with the prefix
473 * "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
476 * @param lock which lock to lock
478 * @param status_cb the callback for signalling when the lock is acquired and
481 * @param status_cb_cls the closure to the above callback
483 * @return the locking request handle for this request
485 struct GNUNET_LOCKMANAGER_LockingRequest *
486 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
487 const char *domain_name,
489 GNUNET_LOCKMANAGER_StatusCallback
493 struct GNUNET_LOCKMANAGER_LockingRequest *r;
494 struct GNUNET_LOCKMANAGER_Message *msg;
495 struct GNUNET_HashCode hash;
497 size_t domain_name_length;
499 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
500 r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
501 domain_name_length = strlen (domain_name) + 1;
504 r->domain = GNUNET_malloc (domain_name_length);
505 r->status = GNUNET_LOCKMANAGER_RELEASE;
506 r->status_cb = status_cb;
507 r->status_cb_cls = status_cb_cls;
508 memcpy (r->domain, domain_name, domain_name_length);
509 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_length;
510 msg = GNUNET_malloc (msg_size);
511 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
512 msg->header.size = htons (msg_size);
513 msg->lock = htonl (lock);
514 memcpy (&msg[1], r->domain, domain_name_length);
515 LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
517 GNUNET_CLIENT_notify_transmit_ready (r->handle->conn,
523 get_key (r->domain, r->lock, &hash);
524 GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap,
527 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
528 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
535 * Function to cancel the locking request generated by
536 * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired us then the lock is
537 * released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
538 * status changes resulting due to this call.
540 * @param request the LockingRequest to cancel
543 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
546 struct GNUNET_LOCKMANAGER_Message *msg;
547 struct GNUNET_HashCode hash;
549 size_t domain_name_length;
551 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
552 /* FIXME: Stop ACQUIRE retransmissions */
553 if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
555 domain_name_length = strlen (request->domain) + 1;
556 msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message)
557 + domain_name_length;
558 msg = GNUNET_malloc (msg_size);
559 msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
560 msg->header.size = htons (msg_size);
561 msg->lock = htonl (request->lock);
562 memcpy (&msg[1], request->domain, domain_name_length);
563 GNUNET_CLIENT_notify_transmit_ready (request->handle->conn,
565 TIMEOUT, /* What if this fails */
570 get_key (request->domain, request->lock, &hash);
571 GNUNET_assert (GNUNET_YES ==
572 GNUNET_CONTAINER_multihashmap_remove
573 (request->handle->hashmap, &hash, request));
574 GNUNET_free (request->domain);
575 GNUNET_free (request);
576 LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);