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