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