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