-typo
[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
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_container_lib.h"
31 #include "gnunet_client_lib.h"
32 #include "gnunet_crypto_lib.h"
33 #include "gnunet_lockmanager_service.h"
34 #include "gnunet_protocols.h"
35
36 #include "lockmanager.h"
37
38 #define LOG(kind,...) \
39   GNUNET_log_from (kind, "lockmanager-api",__VA_ARGS__)
40
41 #define TIME_REL_MINS(min) \
42   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
43
44 #define TIMEOUT TIME_REL_MINS(3)
45
46
47 /**
48  * The message queue
49  */
50 struct MessageQueue
51 {
52   /**
53    * The next pointer for doubly linked list
54    */
55   struct MessageQueue *next;
56
57   /**
58    * The prev pointer for doubly linked list
59    */
60   struct MessageQueue *prev;
61   
62   /**
63    * The LOCKMANAGER Message
64    */
65   struct GNUNET_LOCKMANAGER_Message *msg;
66
67   /**
68    * If this is a AQUIRE_LOCK message, this is the
69    * affiliated locking request.
70    */
71   struct GNUNET_LOCKMANAGER_LockingRequest *lr;
72 };
73
74
75 /**
76  * Handler for the lockmanager service
77  */
78 struct GNUNET_LOCKMANAGER_Handle
79 {
80   /**
81    * The client connection to the service
82    */
83   struct GNUNET_CLIENT_Connection *conn;
84
85   /**
86    * The transmit handle for transmissions using conn
87    */
88   struct GNUNET_CLIENT_TransmitHandle *transmit_handle;
89
90   /**
91    * Hashmap handle
92    */
93   struct GNUNET_CONTAINER_MultiHashMap *hashmap;
94
95   /**
96    * Double linked list head for message queue
97    */
98   struct MessageQueue *mq_head;
99
100   /**
101    * Double linked list tail for message queue
102    */
103   struct MessageQueue *mq_tail;
104
105   /**
106    * Are we currently handling replies?
107    */
108   int in_replies;
109 };
110
111
112 /**
113  * Structure for Locking Request
114  */
115 struct GNUNET_LOCKMANAGER_LockingRequest
116 {
117   /**
118    * The handle associated with this request
119    */
120   struct GNUNET_LOCKMANAGER_Handle *handle;
121
122   /**
123    * The status callback
124    */
125   GNUNET_LOCKMANAGER_StatusCallback status_cb;
126
127   /**
128    * Entry in the request message queue for aquiring this
129    * lock; NULL after request has been sent.
130    */
131   struct MessageQueue *mqe;
132
133   /**
134    * Closure for the status callback
135    */
136   void *status_cb_cls;
137
138   /**
139    * The locking domain of this request
140    */
141   char *domain;
142   
143   /**
144    * The lock
145    */
146   uint32_t lock;
147
148   /**
149    * The status of the lock
150    */
151   enum GNUNET_LOCKMANAGER_Status status;
152 };
153
154
155 /**
156  * Structure for matching a lock
157  */
158 struct LockingRequestMatch
159 {
160   /**
161    * The matched LockingRequest entry; Should be NULL if no entry is found
162    */
163   struct GNUNET_LOCKMANAGER_LockingRequest *matched_entry;
164
165   /**
166    * The locking domain name of the lock
167    */
168   const char *domain;
169
170   /**
171    * The lock number
172    */
173   uint32_t lock;
174 };
175
176
177 /**
178  * Handler for server replies
179  *
180  * @param cls the LOCKMANAGER_Handle
181  * @param msg received message, NULL on timeout or fatal error
182  */
183 static void 
184 handle_replies (void *cls,
185                 const struct GNUNET_MessageHeader *msg);
186
187
188 /**
189  * Transmit notify for sending message to server
190  *
191  * @param cls the lockmanager handle
192  * @param size number of bytes available in buf
193  * @param buf where the callee should write the message
194  * @return number of bytes written to buf
195  */
196 static size_t 
197 transmit_notify (void *cls, size_t size, void *buf)
198 {
199   struct GNUNET_LOCKMANAGER_Handle *handle = cls;
200   struct MessageQueue *queue_entity;
201   uint16_t msg_size;
202
203   handle->transmit_handle = NULL;
204   queue_entity = handle->mq_head;
205   GNUNET_assert (NULL != queue_entity);
206   if ((0 == size) || (NULL == buf))
207   {
208     handle->transmit_handle =
209       GNUNET_CLIENT_notify_transmit_ready (handle->conn,
210                                            ntohs
211                                            (queue_entity->msg->header.size),
212                                            GNUNET_TIME_UNIT_FOREVER_REL,
213                                            GNUNET_YES,
214                                            &transmit_notify,
215                                            handle);
216     return 0;
217   } 
218   msg_size = ntohs (queue_entity->msg->header.size);
219   GNUNET_assert (size >= msg_size);
220   memcpy (buf, queue_entity->msg, msg_size);
221   LOG (GNUNET_ERROR_TYPE_DEBUG,
222        "Message of size %u sent\n", msg_size);
223   GNUNET_free (queue_entity->msg);
224   GNUNET_CONTAINER_DLL_remove (handle->mq_head,
225                                handle->mq_tail,
226                                queue_entity);
227   GNUNET_free (queue_entity);
228   queue_entity = handle->mq_head;
229   if (NULL != queue_entity)
230   {
231     handle->transmit_handle =
232       GNUNET_CLIENT_notify_transmit_ready (handle->conn,
233                                            ntohs
234                                            (queue_entity->msg->header.size),
235                                            TIMEOUT,
236                                            GNUNET_YES,
237                                            &transmit_notify,
238                                            handle);
239   }
240   if (GNUNET_NO == handle->in_replies)
241   {
242     handle->in_replies = GNUNET_YES;
243     GNUNET_CLIENT_receive (handle->conn,
244                            &handle_replies,
245                            handle,
246                            GNUNET_TIME_UNIT_FOREVER_REL);
247   }
248   return msg_size;
249 }
250
251
252 /**
253  * Queues a message into handle's send message queue
254  *
255  * @param handle the lockmanager handle whose queue will be used
256  * @param msg the message to be queued
257  */
258 static void
259 queue_message (struct GNUNET_LOCKMANAGER_Handle *handle,
260                struct GNUNET_LOCKMANAGER_Message *msg)
261 {
262   struct MessageQueue *queue_entity;
263
264   GNUNET_assert (NULL != msg);
265   queue_entity = GNUNET_malloc (sizeof (struct MessageQueue));
266   queue_entity->msg = msg;
267   GNUNET_CONTAINER_DLL_insert_tail (handle->mq_head,
268                                     handle->mq_tail,
269                                     queue_entity);
270   if (NULL == handle->transmit_handle)
271   {
272     handle->transmit_handle =
273       GNUNET_CLIENT_notify_transmit_ready (handle->conn,
274                                            ntohs (msg->header.size),
275                                            TIMEOUT,
276                                            GNUNET_YES,
277                                            &transmit_notify,
278                                            handle);
279   }
280 }
281
282
283 /**
284  * Get the key for the given lock in the 'lock_map'.
285  *
286  * @param domain_name
287  * @param lock_number
288  * @param key set to the key
289  */
290 static void
291 get_key (const char *domain_name,
292          uint32_t lock_number,
293          struct GNUNET_HashCode *key)
294 {
295   uint32_t *last_32;
296
297   GNUNET_CRYPTO_hash (domain_name,
298                       strlen (domain_name),
299                       key);
300   last_32 = (uint32_t *) key;
301   *last_32 ^= lock_number;
302 }
303
304
305 /**
306  * Hashmap iterator for matching a LockingRequest
307  *
308  * @param cls the LockingRequestMatch structure
309  * @param key current key code
310  * @param value value in the hash map (struct GNUNET_LOCKMANAGER_LockingRequest)
311  * @return GNUNET_YES if we should continue to
312  *         iterate,
313  *         GNUNET_NO if not. 
314  */
315 static int
316 match_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
317 {
318   struct LockingRequestMatch *match = cls;
319   struct GNUNET_LOCKMANAGER_LockingRequest *lr = value;
320
321   if ( (match->lock == lr->lock) && (0 == strcmp (match->domain, lr->domain)) )
322   {
323     match->matched_entry = lr;
324     return GNUNET_NO;
325   }
326   return GNUNET_YES;
327 }
328
329
330 /**
331  * Function to find a LockingRequest associated with the given domain and lock
332  * attributes in the map
333  *
334  * @param map the map where the LockingRequests are stored
335  * @param domain the locking domain name
336  * @param lock the lock number
337  * @return the found LockingRequest; NULL if a matching LockingRequest wasn't
338  *           found 
339  */
340 static struct GNUNET_LOCKMANAGER_LockingRequest *
341 hashmap_find_lockingrequest (const struct GNUNET_CONTAINER_MultiHashMap *map,
342                              const char *domain,
343                              uint32_t lock)
344 {
345   struct GNUNET_HashCode hash;
346   struct LockingRequestMatch lock_match;
347
348   lock_match.matched_entry = NULL;
349   lock_match.domain = domain;
350   lock_match.lock = lock;
351   get_key (domain, lock, &hash);
352   GNUNET_CONTAINER_multihashmap_get_multiple (map,
353                                               &hash,
354                                               &match_iterator,
355                                               &lock_match);
356   return lock_match.matched_entry;
357 }
358
359
360 /**
361  * Task for calling status change callback for a lock
362  *
363  * @param cls the LockingRequest associated with this lock
364  * @param tc the TaskScheduler context
365  */
366 static void
367 call_status_cb_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
368 {
369   const struct GNUNET_LOCKMANAGER_LockingRequest *r = cls;
370
371   if (NULL != r->status_cb)
372   {
373     LOG (GNUNET_ERROR_TYPE_DEBUG,
374          "Calling status change for SUCCESS on lock num: %d, domain: %s\n",
375          r->lock, r->domain);
376     r->status_cb (r->status_cb_cls,
377                   r->domain,
378                   r->lock,
379                   r->status);
380   }
381 }
382
383
384 /**
385  * Function to generate acquire message for a lock
386  *
387  * @param domain_name the domain name of the lock
388  * @param lock the lock number
389  * @return the generated GNUNET_LOCKMANAGER_Message
390  */
391 static struct GNUNET_LOCKMANAGER_Message *
392 generate_acquire_msg (const char *domain_name, uint32_t lock)
393 {
394   struct GNUNET_LOCKMANAGER_Message *msg;
395   size_t domain_name_len;
396   uint16_t  msg_size;
397   
398   domain_name_len = strlen (domain_name) + 1;
399   msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_len;
400   msg = GNUNET_malloc (msg_size);
401   msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE);
402   msg->header.size = htons (msg_size);
403   msg->lock = htonl (lock);
404   memcpy (&msg[1], domain_name, domain_name_len);
405   return msg;
406 }
407
408
409 /**
410  * Iterator to call relase on locks
411  *
412  * @param cls the lockmanager handle
413  * @param key current key code
414  * @param value the Locking request
415  * @return GNUNET_YES if we should continue to
416  *         iterate,
417  *         GNUNET_NO if not.
418  */
419 static int
420 release_n_retry_iterator (void *cls,
421                           const struct GNUNET_HashCode * key,
422                           void *value)
423 {
424   struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
425   struct GNUNET_LOCKMANAGER_Handle *h = cls;
426   struct GNUNET_LOCKMANAGER_Message *msg;
427
428   msg = generate_acquire_msg (r->domain, r->lock);
429   queue_message (h, msg);
430   if (GNUNET_LOCKMANAGER_RELEASE == r->status)
431     return GNUNET_YES;
432   if (NULL != r->status_cb)
433   {
434     LOG (GNUNET_ERROR_TYPE_DEBUG,
435          "Calling status change for RELEASE on lock num: %d, domain: %s\n",
436          r->lock, r->domain);
437     r->status = GNUNET_LOCKMANAGER_RELEASE;
438     r->status_cb (r->status_cb_cls,
439                   r->domain,
440                   r->lock,
441                   GNUNET_LOCKMANAGER_RELEASE);
442   }
443   return GNUNET_YES;
444 }
445
446
447 /**
448  * Handler for server replies
449  *
450  * @param cls the LOCKMANAGER_Handle
451  * @param msg received message, NULL on timeout or fatal error
452  */
453 static void 
454 handle_replies (void *cls,
455                 const struct GNUNET_MessageHeader *msg)
456 {
457   struct GNUNET_LOCKMANAGER_Handle *handle = cls;
458   const struct GNUNET_LOCKMANAGER_Message *m;
459   struct GNUNET_LOCKMANAGER_LockingRequest *lr;
460   const char *domain;
461   struct GNUNET_HashCode hash;
462   uint32_t lock;
463   uint16_t msize;
464
465   handle->in_replies = GNUNET_NO;
466   if (NULL == msg)
467   {
468     LOG (GNUNET_ERROR_TYPE_DEBUG,
469          "Lockmanager service not available or went down\n");    
470     /* Should release all locks and retry to acquire them */
471     GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
472                                            &release_n_retry_iterator,
473                                            handle);
474     return;
475   }
476   handle->in_replies = GNUNET_YES;
477   GNUNET_CLIENT_receive (handle->conn,
478                          &handle_replies,
479                          handle,
480                          GNUNET_TIME_UNIT_FOREVER_REL);
481   if (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS != ntohs(msg->type))
482   {
483     GNUNET_break (0);
484     return;
485   }
486   msize = ntohs (msg->size);
487   if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
488   {
489     GNUNET_break (0);
490     return;
491   }
492   m = (const struct GNUNET_LOCKMANAGER_Message *) msg;
493   domain = (const char *) &m[1];
494   msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
495   if ('\0' != domain[msize-1])
496   {
497     GNUNET_break (0);
498     return;
499   }
500
501   lock = ntohl (m->lock);
502   get_key (domain, lock, &hash);      
503   LOG (GNUNET_ERROR_TYPE_DEBUG,
504        "Received SUCCESS message for lock: %d, domain %s\n",
505        lock, domain);
506   if (NULL == (lr = hashmap_find_lockingrequest (handle->hashmap,
507                                                  domain,
508                                                  lock)))
509   {
510     GNUNET_break (0);
511     return;
512   }
513   if (GNUNET_LOCKMANAGER_SUCCESS == lr->status)
514   {
515     GNUNET_break (0);
516     return;
517   }
518   LOG (GNUNET_ERROR_TYPE_DEBUG,
519        "Changing status for lock: %d in domain: %s to SUCCESS\n",
520        lr->lock, lr->domain);
521   lr->status = GNUNET_LOCKMANAGER_SUCCESS;
522   GNUNET_SCHEDULER_add_continuation (&call_status_cb_task,
523                                      lr,
524                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
525 }
526
527
528 /**
529  * Iterator to free hash map entries.
530  *
531  * @param cls the lockmanger handle
532  * @param key current key code
533  * @param value the Locking request
534  * @return GNUNET_YES if we should continue to
535  *         iterate,
536  *         GNUNET_NO if not.
537  */
538 static int
539 free_iterator(void *cls,
540               const struct GNUNET_HashCode * key,
541               void *value)
542 {
543   struct GNUNET_LOCKMANAGER_Handle *h = cls;
544   struct GNUNET_LOCKMANAGER_LockingRequest *r = value;
545
546   LOG (GNUNET_ERROR_TYPE_DEBUG,
547        "Clearing locking request\n");
548   GNUNET_assert (GNUNET_YES == 
549                  GNUNET_CONTAINER_multihashmap_remove (h->hashmap,
550                                                        key,
551                                                        value));
552   GNUNET_free (r->domain);
553   GNUNET_free (r);
554   return GNUNET_YES;
555 }
556
557
558 /*******************/
559 /* API Definitions */
560 /*******************/
561
562
563 /**
564  * Connect to the lockmanager service
565  *
566  * @param cfg the configuration to use
567  *
568  * @return upon success the handle to the service; NULL upon error
569  */
570 struct GNUNET_LOCKMANAGER_Handle *
571 GNUNET_LOCKMANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
572 {
573   struct GNUNET_LOCKMANAGER_Handle *h;
574
575   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
576   h = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_Handle));
577   h->conn = GNUNET_CLIENT_connect ("lockmanager", cfg);
578   if (NULL == h->conn)
579   {
580     GNUNET_free (h);
581     LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
582     return NULL;
583   }  
584   h->hashmap = GNUNET_CONTAINER_multihashmap_create (15);
585   GNUNET_assert (NULL != h->hashmap);
586   h->in_replies = GNUNET_YES;
587   GNUNET_CLIENT_receive (h->conn,
588                          &handle_replies,
589                          h,
590                          GNUNET_TIME_UNIT_FOREVER_REL);  
591   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
592   return h;
593 }
594
595
596 /**
597  * Disconnect from the lockmanager service
598  *
599  * @param handle the handle to the lockmanager service
600  */
601 void
602 GNUNET_LOCKMANAGER_disconnect (struct GNUNET_LOCKMANAGER_Handle *handle)
603 {
604   struct MessageQueue *head;
605
606   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
607   if (0 != GNUNET_CONTAINER_multihashmap_size (handle->hashmap))
608   {
609     LOG (GNUNET_ERROR_TYPE_WARNING,
610          "Some locking requests are still present. Cancel them before "
611          "calling %s\n", __func__);
612     GNUNET_CONTAINER_multihashmap_iterate (handle->hashmap,
613                                            &free_iterator,
614                                            handle);
615   }
616   GNUNET_CONTAINER_multihashmap_destroy (handle->hashmap);
617   /* Clear the message queue */
618   if (NULL != handle->transmit_handle)
619   {
620     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->transmit_handle);
621   }
622   head = handle->mq_head;
623   while (NULL != head)
624   {
625     GNUNET_CONTAINER_DLL_remove (handle->mq_head,
626                                  handle->mq_tail,
627                                  head);
628     GNUNET_free (head->msg);
629     GNUNET_free (head);
630     head = handle->mq_head;
631   }
632   GNUNET_CLIENT_disconnect (handle->conn);
633   GNUNET_free (handle);
634   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
635 }
636
637
638 /**
639  * Tries to acquire the given lock(even if the lock has been lost) until the
640  * request is called. If the lock is available the status_cb will be
641  * called. If the lock is busy then the request is queued and status_cb
642  * will be called when the lock has been made available and acquired by us.
643  *
644  * @param handle the handle to the lockmanager service
645  *
646  * @param domain_name name of the locking domain. Clients who want to share
647  *          locks must use the same name for the locking domain. Also the
648  *          domain_name should be selected with the prefix
649  *          "GNUNET_<PROGRAM_NAME>_" to avoid domain name collisions.
650  *
651  *
652  * @param lock which lock to lock
653  *
654  * @param status_cb the callback for signalling when the lock is acquired and
655  *          when it is lost
656  *
657  * @param status_cb_cls the closure to the above callback
658  *
659  * @return the locking request handle for this request
660  */
661 struct GNUNET_LOCKMANAGER_LockingRequest *
662 GNUNET_LOCKMANAGER_acquire_lock (struct GNUNET_LOCKMANAGER_Handle *handle,
663                                  const char *domain_name,
664                                  uint32_t lock,
665                                  GNUNET_LOCKMANAGER_StatusCallback
666                                  status_cb,
667                                  void *status_cb_cls)
668 {
669   struct GNUNET_LOCKMANAGER_LockingRequest *r;
670   struct GNUNET_LOCKMANAGER_Message *msg;
671   struct GNUNET_HashCode hash;
672   size_t domain_name_length;
673   
674   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
675   r = GNUNET_malloc (sizeof (struct GNUNET_LOCKMANAGER_LockingRequest));
676   domain_name_length = strlen (domain_name) + 1;
677   r->handle = handle;
678   r->lock = lock;
679   r->domain = GNUNET_malloc (domain_name_length);
680   r->status = GNUNET_LOCKMANAGER_RELEASE;
681   r->status_cb = status_cb;
682   r->status_cb_cls = status_cb_cls;
683   memcpy (r->domain, domain_name, domain_name_length);
684   msg = generate_acquire_msg (r->domain, r->lock);
685   LOG (GNUNET_ERROR_TYPE_DEBUG, "Queueing ACQUIRE message\n");
686   queue_message (handle, msg);
687   get_key (r->domain, r->lock, &hash);
688   GNUNET_assert (GNUNET_OK == 
689                  GNUNET_CONTAINER_multihashmap_put (r->handle->hashmap,
690                                                     &hash,
691                                                     r,
692                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
693   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
694   return r;
695 }
696
697
698 /**
699  * Function to cancel the locking request generated by
700  * GNUNET_LOCKMANAGER_acquire_lock. If the lock is acquired by us then the lock
701  * is released. GNUNET_LOCKMANAGER_StatusCallback will not be called upon any
702  * status changes resulting due to this call.
703  *
704  * @param request the LockingRequest to cancel
705  */
706 void
707 GNUNET_LOCKMANAGER_cancel_request (struct GNUNET_LOCKMANAGER_LockingRequest
708                                    *request)
709 {
710   struct GNUNET_LOCKMANAGER_Message *msg;
711   struct GNUNET_HashCode hash;
712   uint16_t msg_size;
713   size_t domain_name_length;
714
715   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __func__);
716   /* FIXME: Stop ACQUIRE retransmissions */
717   if (GNUNET_LOCKMANAGER_SUCCESS == request->status)
718   {
719     domain_name_length = strlen (request->domain) + 1;
720     msg_size = sizeof (struct GNUNET_LOCKMANAGER_Message) 
721       + domain_name_length;
722     msg = GNUNET_malloc (msg_size);
723     msg->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE);
724     msg->header.size = htons (msg_size);
725     msg->lock = htonl (request->lock);
726     memcpy (&msg[1], request->domain, domain_name_length);
727     queue_message (request->handle, msg);
728   }
729   get_key (request->domain, request->lock, &hash);
730   GNUNET_assert (GNUNET_YES ==
731                  GNUNET_CONTAINER_multihashmap_remove
732                  (request->handle->hashmap, &hash, request));
733   GNUNET_free (request->domain);
734   GNUNET_free (request);
735   LOG (GNUNET_ERROR_TYPE_DEBUG, "%s() END\n", __func__);
736 }
737
738 /* end of lockmanager_api.c */