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