change in API documentation and function for finding lockingRequest in hashmap
[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 #include "platform.h"
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"
34
35 #include "lockmanager.h"
36
37 #define LOG(kind,...) \
38   GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
39
40 #define TIME_REL_MINS(min) \
41   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
42
43 #define TIMEOUT TIME_REL_MINS(3)
44
45 /**
46  * Handler for the lockmanager service
47  */
48 struct GNUNET_LOCKMANAGER_Handle
49 {
50   /**
51    * The client connection to the service
52    */
53   struct GNUNET_CLIENT_Connection *conn;
54
55   /**
56    * Hashmap handle
57    */
58   struct GNUNET_CONTAINER_MultiHashMap *hashmap;
59 };
60
61
62 /**
63  * Structure for Locking Request
64  */
65 struct GNUNET_LOCKMANAGER_LockingRequest
66 {
67   /**
68    * The handle associated with this request
69    */
70   struct GNUNET_LOCKMANAGER_Handle *handle;
71
72   /**
73    * The status callback
74    */
75   GNUNET_LOCKMANAGER_StatusCallback status_cb;
76
77   /**
78    * Closure for the status callback
79    */
80   void *status_cb_cls;
81
82   /**
83    * The pending transmit handle for the ACQUIRE message
84    */
85   struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
86
87   /**
88    * The locking domain of this request
89    */
90   char *domain;
91   
92   /**
93    * The lock
94    */
95   uint32_t lock;
96
97   /**
98    * The status of the lock
99    */
100   enum GNUNET_LOCKMANAGER_Status status;
101 };
102
103
104 /**
105  * Get the key for the given lock in the 'lock_map'.
106  *
107  * @param domain_name
108  * @param lock_number
109  * @param key set to the key
110  */
111 static void
112 get_key (const char *domain_name,
113          uint32_t lock_number,
114          struct GNUNET_HashCode *key)
115 {
116   uint32_t *last_32;
117
118   GNUNET_CRYPTO_hash (domain_name,
119                       strlen (domain_name),
120                       key);
121   last_32 = (uint32_t *) key;
122   *last_32 ^= lock_number;
123 }
124
125
126 /**
127  * Function to find a LockingRequest associated with the given domain and lock
128  * attributes in the map
129  *
130  * @param map the map where the LockingRequests are stored
131  * @param domain the locking domain name
132  * @param lock the lock number
133  * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
134  *           found 
135  */
136 static struct GNUNET_LOCKMANAGER_LockingRequest *
137 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
138                              const char *domain,
139                              uint32_t lock)
140 {
141   struct GNUNET_LOCKMANAGER_LockingRequest *lr;
142   struct GNUNET_HashCode hash;
143   int match_found;
144
145   int match_iterator (void *cls, const GNUNET_HashCode *key, void *value)
146   {
147     lr = value;
148     if ( (lock == lr->lock) && (0 == strcmp (domain, lr->domain)) )
149     {
150       match_found = GNUNET_YES;
151       return GNUNET_NO;
152     }
153     return GNUNET_YES;
154   }
155   get_key (domain, lock, &hash);
156   match_found = GNUNET_NO;
157   GNUNET_CONTAINER_multihashmap_get_multiple (map,
158                                               &hash,
159                                               &match_iterator,
160                                               NULL);
161   return (GNUNET_YES == match_found) ? lr : NULL;
162 }
163
164
165 /**
166  * Task for calling status change callback for a lock
167  *
168  * @param cls the LockingRequest associated with this lock
169  * @param tc the TaskScheduler context
170  */
171 static void
172 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
173 {
174   const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
175
176   if (NULL != r->status_cb)
177   {
178     LOG (GNUNET_ERROR_TYPE_DEBUG,
179          "Calling status change callback for lock: %d in domain: %s\n",
180          r->lock, r->domain);
181     r->status_cb (r->status_cb_cls,
182                   r->domain,
183                   r->lock,
184                   r->status);
185   }
186 }
187
188
189 /**
190  * Handler for server replies
191  *
192  * @param cls the LOCKMANAGER_Handle
193  * @param msg received message, NULL on timeout or fatal error
194  */
195 static void 
196 handle_replies (void *cls,
197                 const struct GNUNET_MessageHeader *msg)
198 {
199   struct GNUNET_LOCKMANAGER_Handle *handle = cls;
200   const struct GNUNET_LOCKMANAGER_Message *m;
201   struct GNUNET_LOCKMANAGER_LockingRequest *lr;
202   const char *domain;
203   struct GNUNET_HashCode hash;
204   uint32_t lock;
205   uint16_t msize;
206   
207   if (NULL == msg)
208   {
209     LOG (GNUNET_ERROR_TYPE_DEBUG,
210          "Lockmanager service not available or went down\n");
211     return;
212   }
213   if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
214   {
215     GNUNET_break (0);
216     return;
217   }
218   msize = ntohs (msg->size);
219   if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
220   {
221     GNUNET_break (0);
222     return;
223   }
224   m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
225   domain = (const char *) &m[1];
226   msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
227   if ('\0' != domain[msize-1])
228   {
229     GNUNET_break (0);
230     return;
231   }
232
233   lock = ntohl (m->lock);
234   get_key (domain, lock, &hash);      
235   LOG (GNUNET_ERROR_TYPE_DEBUG,
236        "Received SUCCESS message for lock: %d, domain %s\n",
237        lock, domain);
238   if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
239                                                  domain,
240                                                  lock)))
241   {
242     GNUNET_break (0);
243     return;
244   }
245   if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
246   {
247     GNUNET_break (0);
248     return;
249   }
250   LOG (GNUNET_ERROR_TYPE_DEBUG,
251        "Changing status for lock: %d in domain: %s to SUCCESS\n",
252        lr->lock, lr->domain);
253   lr->status = GNUNET_LOCKMANAGER_SUCCESS;
254   GNUNET_SCHEDULER_add_continuation (&call_status_cb_task,
255                                      lr,
256                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
257 }
258
259
260 /**
261  * Transmit notify for sending message to server
262  *
263  * @param cls the message to send
264  * @param size number of bytes available in buf
265  * @param buf where the callee should write the message
266  * @return number of bytes written to buf
267  */
268 static size_t 
269 transmit_notify (void *cls, size_t size, void *buf)
270 {
271   struct GNUNET_LOCKMANAGER_Message *msg = cls;
272   uint16_t msg_size;
273
274   if ((0 == size) || (NULL == buf))
275   {
276     /* FIXME: Timed out -- requeue? */
277     return 0;
278   }
279   msg_size = ntohs (msg->header.size);
280   GNUNET_assert (size >= msg_size);
281   memcpy (buf, msg, msg_size);
282   GNUNET_free (msg);
283   LOG (GNUNET_ERROR_TYPE_DEBUG,
284        "Message of size %u sent\n", msg_size);
285   return msg_size;
286 }
287
288
289 /**
290  * Iterator to free hash map entries.
291  *
292  * @param cls NULL
293  * @param key current key code
294  * @param value the Locking request
295  * @return GNUNET_YES if we should continue to
296  *         iterate,
297  *         GNUNET_NO if not.
298  */
299 static int
300 free_iterator(void *cls,
301               const GNUNET_HashCode * key,
302               void *value)
303 {
304   struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
305
306   LOG (GNUNET_ERROR_TYPE_DEBUG,
307        "Clearing locking request\n");
308   GNUNET_free (r->domain);
309   GNUNET_free (r);
310   return GNUNET_YES;
311 }
312
313
314 /*******************/
315 /* API Definitions */
316 /*******************/
317
318
319 /**
320  * Connect to the lockmanager service
321  *
322  * @param cfg the configuration to use
323  *
324  * @return upon success the handle to the service; NULL upon error
325  */
326 struct GNUNET_LOCKMANAGER_Handle *
327 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
328 {
329   struct GNUNET_LOCKMANAGER_Handle *h;
330
331   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
332   h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
333   h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
334   if (NULL == h->conn)
335   {
336     GNUNET_free (h);
337     LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
338     return NULL;
339   }  
340   h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
341   GNUNET_assert (NULL != h->hashmap);
342   GNUNET_CLIENT_receive (h->conn,
343                          &handle_replies,
344                          h,
345                          GNUNET_TIME_UNIT_FOREVER_REL);
346   
347   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
348   return h;
349 }
350
351
352 /**
353  * Disconnect from the lockmanager service
354  *
355  * @param handle the handle to the lockmanager service
356  */
357 void
358 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
359 {
360   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
361   GNUNET_CLIENT_disconnect (handle->conn);
362   if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
363   {
364     LOG (GNUNET_ERROR_TYPE_WARNING,
365          "Some locking requests are still present. Cancel them before "
366          "calling %s\n", __func__);
367     GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
368                                            &free_iterator,
369                                            NULL);
370   }
371   GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
372   GNUNET_free (handle);
373   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
374 }
375
376
377 /**
378  * Tries to acquire the given lock(even if the lock has been lost) until the
379  * request is called. If the lock is available the status_cb will be
380  * called. If the lock is busy then the request is queued and status_cb
381  * will be called when the lock has been made available and acquired by us.
382  *
383  * @param handle the handle to the lockmanager service
384  *
385  * @param domain_name name of the locking domain. Clients who want to share
386  *          locks must use the same name for the locking domain. Also the
387  *          domain_name should be selected with the prefix
388  *          "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
389  *
390  *
391  * @param lock which lock to lock
392  *
393  * @param status_cb the callback for signalling when the lock is acquired and
394  *          when it is lost
395  *
396  * @param status_cb_cls the closure to the above callback
397  *
398  * @return the locking request handle for this request
399  */
400 struct GNUNET_LOCKMANAGER_LockingRequest *
401 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
402                                  const char *domain_name,
403                                  uint32_t lock,
404                                  GNUNET_LOCKMANAGER_StatusCallback
405                                  status_cb,
406                                  void *status_cb_cls)
407 {
408   struct GNUNET_LOCKMANAGER_LockingRequest *r;
409   struct GNUNET_LOCKMANAGER_Message *msg;
410   struct GNUNET_HashCode hash;
411   uint16_t msg_size;
412   size_t domain_name_length;
413   
414   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
415   r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
416   domain_name_length = strlen (domain_name) + 1;
417   r->handle = handle;
418   r->lock = lock;
419   r->domain = GNUNET_malloc (domain_name_length);
420   r->status = GNUNET_LOCKMANAGER_RELEASE;
421   r->status_cb = status_cb;
422   r->status_cb_cls = status_cb_cls;
423   memcpy (r->domain, domain_name, domain_name_length);
424   msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_length;
425   msg = GNUNET_malloc (msg_size);
426   msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
427   msg->header.size = htons (msg_size);
428   msg->lock = htonl (lock);
429   memcpy (&msg[1], r->domain, domain_name_length);
430   LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
431   r->transmit_handle =
432     GNUNET_CLIENT_notify_transmit_ready (r->handle->conn,
433                                          msg_size,
434                                          TIMEOUT,
435                                          GNUNET_YES,
436                                          &transmit_notify,
437                                          msg);
438   get_key (r->domain, r->lock, &hash);
439   GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap,
440                                      &hash,
441                                      r,
442                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
443   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
444   return r;
445 }
446
447
448
449 /**
450  * Function to cancel the locking request generated by
451  * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired us then the lock is
452  * released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
453  * status changes resulting due to this call.
454  *
455  * @param request the LockingRequest to cancel
456  */
457 void
458 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
459                                    *request)
460 {
461   struct GNUNET_LOCKMANAGER_Message *msg;
462   struct GNUNET_HashCode hash;
463   uint16_t msg_size;
464   size_t domain_name_length;
465
466   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
467   /* FIXME: Stop ACQUIRE retransmissions */
468   if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
469   {
470     domain_name_length = strlen (request->domain) + 1;
471     msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) 
472       + domain_name_length;
473     msg = GNUNET_malloc (msg_size);
474     msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
475     msg->header.size = htons (msg_size);
476     msg->lock = htonl (request->lock);
477     memcpy (&msg[1], request->domain, domain_name_length);
478     GNUNET_CLIENT_notify_transmit_ready (request->handle->conn,
479                                          msg_size,
480                                          TIMEOUT, /* What if this fails */
481                                          GNUNET_NO,
482                                          &transmit_notify,
483                                          msg);
484   }
485   get_key (request->domain, request->lock, &hash);
486   GNUNET_assert (GNUNET_YES ==
487                  GNUNET_CONTAINER_multihashmap_remove
488                  (request->handle->hashmap, &hash, request));
489   GNUNET_free (request->domain);
490   GNUNET_free (request);
491   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
492 }