-consistently use struct GNUNET_HashCode
[oweals/gnunet.git] / src / lockmanager / lockmanager_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file lockmanager/lockmanager_api.c
23  * @brief API implementation of gnunet_lockmanager_service.h
24  * @author Sree Harsha Totakura
25  */
26
27 /**
28  * To be fixed:
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)
31  */
32
33 #include "platform.h"
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"
40
41 #include "lockmanager.h"
42
43 #define LOG(kind,...) \
44   GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
45
46 #define TIME_REL_MINS(min) \
47   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
48
49 #define TIMEOUT TIME_REL_MINS(3)
50
51
52 /**
53  * The message queue
54  */
55 struct MessageQueue
56 {
57   /**
58    * The next pointer for doubly linked list
59    */
60   struct MessageQueue *next;
61
62   /**
63    * The prev pointer for doubly linked list
64    */
65   struct MessageQueue *prev;
66   
67   /**
68    * The LOCKMANAGER Message
69    */
70   struct GNUNET_LOCKMANAGER_Message *msg;
71 };
72
73
74 /**
75  * Handler for the lockmanager service
76  */
77 struct GNUNET_LOCKMANAGER_Handle
78 {
79   /**
80    * The client connection to the service
81    */
82   struct GNUNET_CLIENT_Connection *conn;
83
84   /**
85    * The transmit handle for transmissions using conn
86    */
87   struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
88
89   /**
90    * Hashmap handle
91    */
92   struct GNUNET_CONTAINER_MultiHashMap *hashmap;
93
94   /**
95    * Double linked list head for message queue
96    */
97   struct MessageQueue *mq_head;
98
99   /**
100    * Double linked list tail for message queue
101    */
102   struct MessageQueue *mq_tail;
103 };
104
105
106 /**
107  * Structure for Locking Request
108  */
109 struct GNUNET_LOCKMANAGER_LockingRequest
110 {
111   /**
112    * The handle associated with this request
113    */
114   struct GNUNET_LOCKMANAGER_Handle *handle;
115
116   /**
117    * The status callback
118    */
119   GNUNET_LOCKMANAGER_StatusCallback status_cb;
120
121   /**
122    * Closure for the status callback
123    */
124   void *status_cb_cls;
125
126   /**
127    * The locking domain of this request
128    */
129   char *domain;
130   
131   /**
132    * The lock
133    */
134   uint32_t lock;
135
136   /**
137    * The status of the lock
138    */
139   enum GNUNET_LOCKMANAGER_Status status;
140 };
141
142
143 /**
144  * Structure for matching a lock
145  */
146 struct LockingRequestMatch
147 {
148   /**
149    * The matched LockingRequest entry; Should be NULL if no entry is found
150    */
151   struct GNUNET_LOCKMANAGER_LockingRequest *matched_entry;
152
153   /**
154    * The locking domain name of the lock
155    */
156   const char *domain;
157
158   /**
159    * The lock number
160    */
161   uint32_t lock;
162 };
163
164
165 /**
166  * Transmit notify for sending message to server
167  *
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
172  */
173 static size_t 
174 transmit_notify (void *cls, size_t size, void *buf)
175 {
176   struct GNUNET_LOCKMANAGER_Handle *handle = cls;
177   struct MessageQueue *queue_entity;
178   uint16_t msg_size;
179
180   handle->transmit_handle = NULL;
181   if ((0 == size) || (NULL == buf))
182   {
183     /* FIXME: Timed out -- requeue? */
184     return 0;
185   }
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,
195                                handle->mq_tail,
196                                queue_entity);
197   GNUNET_free (queue_entity);
198   queue_entity = handle->mq_head;
199   if (NULL != queue_entity)
200   {
201     handle->transmit_handle =
202       GNUNET_CLIENT_notify_transmit_ready (handle->conn,
203                                            ntohs
204                                            (queue_entity->msg->header.size),
205                                            TIMEOUT,
206                                            GNUNET_YES,
207                                            &transmit_notify,
208                                            handle);
209   }
210   return msg_size;
211 }
212
213
214 /**
215  * Queues a message into handle's send message queue
216  *
217  * @param handle the lockmanager handle whose queue will be used
218  * @param msg the message to be queued
219  */
220 static void
221 queue_message (struct GNUNET_LOCKMANAGER_Handle *handle,
222                struct GNUNET_LOCKMANAGER_Message *msg)
223 {
224   struct MessageQueue *queue_entity;
225
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,
230                                     handle->mq_tail,
231                                     queue_entity);
232   if (NULL == handle->transmit_handle)
233   {
234     handle->transmit_handle =
235       GNUNET_CLIENT_notify_transmit_ready (handle->conn,
236                                            ntohs (msg->header.size),
237                                            TIMEOUT,
238                                            GNUNET_YES,
239                                            &transmit_notify,
240                                            handle);
241   }
242 }
243
244
245 /**
246  * Get the key for the given lock in the 'lock_map'.
247  *
248  * @param domain_name
249  * @param lock_number
250  * @param key set to the key
251  */
252 static void
253 get_key (const char *domain_name,
254          uint32_t lock_number,
255          struct GNUNET_HashCode *key)
256 {
257   uint32_t *last_32;
258
259   GNUNET_CRYPTO_hash (domain_name,
260                       strlen (domain_name),
261                       key);
262   last_32 = (uint32_t *) key;
263   *last_32 ^= lock_number;
264 }
265
266
267 /**
268  * Hashmap iterator for matching a LockingRequest
269  *
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
274  *         iterate,
275  *         GNUNET_NO if not. 
276  */
277 static int
278 match_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
279 {
280   struct LockingRequestMatch *match = cls;
281   struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
282
283   if ( (match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)) )
284   {
285     match->matched_entry = lr;    
286     return GNUNET_NO;
287   }
288   return GNUNET_YES;
289 }
290
291
292 /**
293  * Function to find a LockingRequest associated with the given domain and lock
294  * attributes in the map
295  *
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
300  *           found 
301  */
302 static struct GNUNET_LOCKMANAGER_LockingRequest *
303 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
304                              const char *domain,
305                              uint32_t lock)
306 {
307   struct GNUNET_HashCode hash;
308   struct LockingRequestMatch lock_match;
309
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,
315                                               &hash,
316                                               &match_iterator,
317                                               &lock_match);
318   return lock_match.matched_entry;
319 }
320
321
322 /**
323  * Task for calling status change callback for a lock
324  *
325  * @param cls the LockingRequest associated with this lock
326  * @param tc the TaskScheduler context
327  */
328 static void
329 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
330 {
331   const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
332
333   if (NULL != r->status_cb)
334   {
335     LOG (GNUNET_ERROR_TYPE_DEBUG,
336          "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
337          r->lock, r->domain);
338     r->status_cb (r->status_cb_cls,
339                   r->domain,
340                   r->lock,
341                   r->status);
342   }
343 }
344
345
346 /**
347  * Iterator to call relase and free all LockingRequest entries
348  *
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
353  *         iterate,
354  *         GNUNET_NO if not.
355  */
356 static int
357 release_iterator(void *cls,
358                  const struct GNUNET_HashCode * key,
359                  void *value)
360 {
361   struct GNUNET_LOCKMANAGER_Handle *h = cls;
362   struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
363
364   if (NULL != r->status_cb)
365   {
366     LOG (GNUNET_ERROR_TYPE_DEBUG,
367          "Calling status change for RELEASE on lock num: %d, domain: %s\n",
368          r->lock, r->domain);
369     r->status_cb (r->status_cb_cls,
370                   r->domain,
371                   r->lock,
372                   GNUNET_LOCKMANAGER_RELEASE);
373   }
374   GNUNET_assert (GNUNET_YES == 
375                  GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
376                                                        key,
377                                                        value));
378   GNUNET_free (r->domain);
379   GNUNET_free (r);
380   return GNUNET_YES;
381 }
382
383
384 /**
385  * Handler for server replies
386  *
387  * @param cls the LOCKMANAGER_Handle
388  * @param msg received message, NULL on timeout or fatal error
389  */
390 static void 
391 handle_replies (void *cls,
392                 const struct GNUNET_MessageHeader *msg)
393 {
394   struct GNUNET_LOCKMANAGER_Handle *handle = cls;
395   const struct GNUNET_LOCKMANAGER_Message *m;
396   struct GNUNET_LOCKMANAGER_LockingRequest *lr;
397   const char *domain;
398   struct GNUNET_HashCode hash;
399   uint32_t lock;
400   uint16_t msize;
401   
402   if (NULL == msg)
403   {
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,
408                                            &release_iterator,
409                                            handle);
410     return;
411   }
412   GNUNET_CLIENT_receive (handle->conn,
413                          &handle_replies,
414                          handle,
415                          GNUNET_TIME_UNIT_FOREVER_REL);
416   if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
417   {
418     GNUNET_break (0);
419     return;
420   }
421   msize = ntohs (msg->size);
422   if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
423   {
424     GNUNET_break (0);
425     return;
426   }
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])
431   {
432     GNUNET_break (0);
433     return;
434   }
435
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",
440        lock, domain);
441   if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
442                                                  domain,
443                                                  lock)))
444   {
445     GNUNET_break (0);
446     return;
447   }
448   if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
449   {
450     GNUNET_break (0);
451     return;
452   }
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,
458                                      lr,
459                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
460 }
461
462
463 /**
464  * Iterator to free hash map entries.
465  *
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
470  *         iterate,
471  *         GNUNET_NO if not.
472  */
473 static int
474 free_iterator(void *cls,
475               const struct GNUNET_HashCode * key,
476               void *value)
477 {
478   struct GNUNET_LOCKMANAGER_Handle *h = cls;
479   struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
480
481   LOG (GNUNET_ERROR_TYPE_DEBUG,
482        "Clearing locking request\n");
483   GNUNET_assert (GNUNET_YES == 
484                  GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
485                                                        key,
486                                                        value));
487   GNUNET_free (r->domain);
488   GNUNET_free (r);
489   return GNUNET_YES;
490 }
491
492
493 /*******************/
494 /* API Definitions */
495 /*******************/
496
497
498 /**
499  * Connect to the lockmanager service
500  *
501  * @param cfg the configuration to use
502  *
503  * @return upon success the handle to the service; NULL upon error
504  */
505 struct GNUNET_LOCKMANAGER_Handle *
506 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
507 {
508   struct GNUNET_LOCKMANAGER_Handle *h;
509
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);
513   if (NULL == h->conn)
514   {
515     GNUNET_free (h);
516     LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
517     return NULL;
518   }  
519   h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
520   GNUNET_assert (NULL != h->hashmap);
521   GNUNET_CLIENT_receive (h->conn,
522                          &handle_replies,
523                          h,
524                          GNUNET_TIME_UNIT_FOREVER_REL);
525   
526   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
527   return h;
528 }
529
530
531 /**
532  * Disconnect from the lockmanager service
533  *
534  * @param handle the handle to the lockmanager service
535  */
536 void
537 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
538 {
539   struct MessageQueue *head;
540
541   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
542   if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
543   {
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,
548                                            &free_iterator,
549                                            handle);
550   }
551   GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
552   /* Clear the message queue */
553   if (NULL != handle->transmit_handle)
554   {
555     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->transmit_handle);
556   }
557   head = handle->mq_head;
558   while (NULL != head)
559   {
560     GNUNET_CONTAINER_DLL_remove (handle->mq_head,
561                                  handle->mq_tail,
562                                  head);
563     GNUNET_free (head->msg);
564     GNUNET_free (head);
565     head = handle->mq_head;
566   }
567   GNUNET_CLIENT_disconnect (handle->conn);
568   GNUNET_free (handle);
569   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
570 }
571
572
573 /**
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.
578  *
579  * @param handle the handle to the lockmanager service
580  *
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.
585  *
586  *
587  * @param lock which lock to lock
588  *
589  * @param status_cb the callback for signalling when the lock is acquired and
590  *          when it is lost
591  *
592  * @param status_cb_cls the closure to the above callback
593  *
594  * @return the locking request handle for this request
595  */
596 struct GNUNET_LOCKMANAGER_LockingRequest *
597 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
598                                  const char *domain_name,
599                                  uint32_t lock,
600                                  GNUNET_LOCKMANAGER_StatusCallback
601                                  status_cb,
602                                  void *status_cb_cls)
603 {
604   struct GNUNET_LOCKMANAGER_LockingRequest *r;
605   struct GNUNET_LOCKMANAGER_Message *msg;
606   struct GNUNET_HashCode hash;
607   uint16_t msg_size;
608   size_t domain_name_length;
609   
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;
613   r->handle = handle;
614   r->lock = lock;
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,
630                                      &hash,
631                                      r,
632                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
633   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
634   return r;
635 }
636
637
638
639 /**
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.
644  *
645  * @param request the LockingRequest to cancel
646  */
647 void
648 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
649                                    *request)
650 {
651   struct GNUNET_LOCKMANAGER_Message *msg;
652   struct GNUNET_HashCode hash;
653   uint16_t msg_size;
654   size_t domain_name_length;
655
656   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
657   /* FIXME: Stop ACQUIRE retransmissions */
658   if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
659   {
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);
669   }
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__);
677 }