processing upon lock release
[oweals/gnunet.git] / src / lockmanager / gnunet-service-lockmanager.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/gnunet-service-lockmanager.c
23  * @brief implementation of the LOCKMANAGER service
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_protocols.h"
31 #include "gnunet_service_lib.h"
32 #include "gnunet_server_lib.h"
33
34 #include "lockmanager.h"
35
36 #define VERBOSE GNUNET_YES
37
38 #define LOG(kind,...) \
39   GNUNET_log (kind, __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  * Doubly linked list of clients waiting for a lock
49  */
50 struct WaitList
51 {
52   /**
53    * The next client structure
54    */
55   struct WaitList *next;
56   
57   /**
58    * The prev client structure
59    */
60   struct WaitList *prev;
61
62   /**
63    * Pointer to the client
64    */
65   struct GNUNET_SERVER_Client *client;
66 };
67
68
69 /**
70  * A Lock element for a doubly linked list
71  */
72 struct LockList
73 {
74   /**
75    * The next element pointer
76    */
77   struct LockList *next;
78
79   /**
80    * Pointer to the previous element
81    */
82   struct LockList *prev;
83
84   /**
85    * List head of clients waiting for this lock
86    */
87   struct WaitList *wait_list_head;
88
89   /**
90    * List tail of clients waiting for this lock
91    */
92   struct WaitList *wait_list_tail;
93
94   /**
95    * The client which is currently holding this lock
96    */
97   struct GNUNET_SERVER_Client *client;
98
99   /**
100    * The name of the locking domain this lock belongs to
101    */
102   char *domain_name;
103
104   /**
105    * The number of this lock
106    */
107   uint32_t lock_num;
108 };
109
110
111 /**
112  * Doubly linked list of clients having connections to us
113  */
114 struct ClientList
115 {
116   /**
117    * The next client structure
118    */
119   struct ClientList *next;
120
121   /**
122    * The previous client structure
123    */
124   struct ClientList *prev;
125
126   /**
127    * Pointer to the client
128    */
129   struct GNUNET_SERVER_Client *client;
130 };
131
132
133 /**
134  * Head of the doubly linked list of the currently held locks
135  */
136 static struct LockList *ll_head;
137
138 /**
139  * Tail of the doubly linked list of the currently held locks
140  */
141 static struct LockList *ll_tail;
142
143 /**
144  * Head of the doubly linked list of clients currently connected
145  */
146 static struct ClientList *cl_head;
147
148 /**
149  * Tail of the doubly linked list of clients currently connected
150  */
151 static struct ClientList *cl_tail;
152
153
154 /**
155  * Function to search for a lock in lock_list matching the given domain_name and
156  * lock number
157  *
158  * @param domain_name the name of the locking domain
159  * @param lock_num the number of the lock
160  * @param ret this will be the pointer to the corresponding Lock if found; else
161  *         it will be the last element in the locks list
162  * @return GNUNET_YES if a matching lock is present in lock_list; GNUNET_NO if not
163  */
164 static int
165 ll_find_lock (const char *domain_name,
166               const uint32_t lock_num,
167               struct LockList **ret)
168 {
169   struct LockList *current_lock;
170
171   current_lock = ll_head;
172   
173   while (NULL != current_lock)
174     {
175       if ( (0 == strcmp (domain_name, current_lock->domain_name))
176            && (lock_num == current_lock->lock_num))
177         {
178           if (NULL != ret)
179             *ret = current_lock;
180           return GNUNET_YES;
181         }
182
183       current_lock = current_lock->next;
184     }
185   if (NULL != ret)
186     *ret = current_lock;
187   return GNUNET_NO;
188 }
189
190
191 /**
192  * Function to search for a lock in lock_list matching the given domain_name and
193  * lock number
194  *
195  * @param client the client owning this lock currently
196  * @param ret this will be the pointer to the corresponding Lock if found; else
197  *         it will be the last element in the locks list
198  * @return GNUNET_YES if a matching lock is present in lock_list; GNUNET_NO if not
199  */
200 static int
201 ll_find_lock_by_owner (const struct GNUNET_SERVER_Client *client,
202                        struct LockList **ret)
203 {
204   struct LockList *current_lock;
205
206   current_lock = ll_head;
207   
208   while (NULL != current_lock)
209     {
210       if (client == current_lock->client)
211         {
212           if (NULL != ret)
213             *ret = current_lock;
214           return GNUNET_YES;
215         }
216
217       current_lock = current_lock->next;
218     }
219   if (NULL != ret)
220     *ret = current_lock;
221   return GNUNET_NO;
222 }
223
224
225 /**
226  * Function to append a lock to the global lock list
227  *
228  * @param client the client which currently owns this lock
229  * @param domain_name the name of the locking domain
230  * @param lock_num the number of the lock
231  */
232 static void
233 ll_add_lock (struct GNUNET_SERVER_Client *client,
234              const char *domain_name,
235              const uint32_t lock_num)
236 {
237   struct LockList *lock;
238   size_t domain_name_len;
239
240   LOG (GNUNET_ERROR_TYPE_DEBUG,
241        "Adding a lock with num: %u and domain: %s to lock list\n",
242        lock_num, domain_name);
243
244   lock = GNUNET_malloc (sizeof (struct LockList));
245   domain_name_len = strlen (domain_name) + 1;
246   lock->domain_name = GNUNET_malloc (domain_name_len);
247   strncpy (lock->domain_name, domain_name, domain_name_len);
248   lock->lock_num = lock_num;
249   lock->client = client;
250   
251   GNUNET_CONTAINER_DLL_insert_tail (ll_head,
252                                     ll_tail,
253                                     lock);
254 }
255
256
257 /**
258  * Function to delete a lock from the lock list
259  *
260  * @param lock the lock to be deleted
261  */
262 static void
263 ll_remove_lock (struct LockList *lock)
264 {
265   LOG (GNUNET_ERROR_TYPE_DEBUG,
266        "Removing lock with num: %u, domain: %s\n",
267        lock->lock_num, lock->domain_name);
268   GNUNET_assert (NULL != ll_head);
269   GNUNET_CONTAINER_DLL_remove (ll_head,
270                                ll_tail,
271                                lock);
272   GNUNET_free (lock->domain_name);
273   GNUNET_free (lock);
274 }
275
276
277 /**
278  * Find a client in the waiting list of a lock
279  *
280  * @param lock the LockList entry of a lock
281  * @param client the client to look for
282  * @param ret where to store the matched wait list entry
283  * @return GNUNET_YES if a match is found; GNUNET_NO if not
284  */
285 static int
286 ll_wl_find_client (struct LockList *lock,
287                    const struct GNUNET_SERVER_Client *client,
288                    struct WaitList **ret)
289 {
290   struct WaitList *current_wl_entry;
291
292   current_wl_entry = lock->wait_list_head;
293
294   while (NULL != current_wl_entry)
295     {
296       if (client == current_wl_entry->client)
297         {
298           if (NULL != ret)
299             *ret = current_wl_entry;
300           return GNUNET_YES;
301         }
302       current_wl_entry = current_wl_entry->next;
303     }
304   if (NULL != ret)
305     *ret = current_wl_entry;
306   return GNUNET_NO;
307 }
308
309
310 /**
311  * Add a client to the wait list of given lock
312  *
313  * @param lock the lock list entry of a lock
314  * @param client the client to queue for the lock's wait list
315  */
316 static void
317 ll_wl_add_client (struct LockList *lock,
318                   struct GNUNET_SERVER_Client *client)
319 {
320   struct WaitList *wl_entry;
321
322   LOG (GNUNET_ERROR_TYPE_DEBUG,
323        "Adding a client to lock's wait list\n"
324        "\t lock num: %u, domain: %s\n",
325        lock->lock_num,
326        lock->domain_name);
327
328   wl_entry = GNUNET_malloc (sizeof (struct WaitList));
329   wl_entry->client = client;
330   GNUNET_CONTAINER_DLL_insert_tail (lock->wait_list_head,
331                                     lock->wait_list_tail,
332                                     wl_entry);
333 }
334
335
336 /**
337  * Remove a client from the wait list of the given lock
338  *
339  * @param lock the lock list entry of the lock
340  * @param wl_client the wait list entry to be removed
341  */
342 static void
343 ll_wl_remove_client (struct LockList *lock,
344                      struct WaitList *wl_client)
345 {
346   GNUNET_CONTAINER_DLL_remove (lock->wait_list_head,
347                                lock->wait_list_tail,
348                                wl_client);
349
350   GNUNET_free (wl_client);
351 }
352
353
354 /**
355  * Search for a client in the client list
356  *
357  * @param client the client to be searched for
358  * @param ret will be pointing to the matched list entry (if there is a match);
359  *          else to the tail of the client list
360  * @return GNUNET_YES if the client is present; GNUNET_NO if not
361  */
362 static int
363 cl_find_client (const struct GNUNET_SERVER_Client *client,
364                 struct ClientList **ret)
365 {
366   struct ClientList *current;
367
368   current = cl_head;
369
370   while (NULL != current)
371     {
372       if (client == current->client)
373         {
374           if (NULL != ret) 
375             *ret = current;
376           return GNUNET_YES;
377         }
378
379       current = current->next;
380     }
381   
382   if (NULL != ret)
383     *ret = current;
384   return GNUNET_NO;
385 }
386
387
388 /**
389  * Append a client to the client list
390  *
391  * @param client the client to be appended to the list
392  */
393 static void
394 cl_add_client (struct GNUNET_SERVER_Client *client)
395 {
396   struct ClientList *new_client;
397   
398   LOG (GNUNET_ERROR_TYPE_DEBUG,
399        "Adding a client to the client list\n");
400
401   new_client = GNUNET_malloc (sizeof (struct ClientList));
402   GNUNET_SERVER_client_keep (client);
403   new_client->client = client;
404   GNUNET_CONTAINER_DLL_insert_tail (cl_head,
405                                     cl_tail,
406                                     new_client);
407 }
408
409
410 /**
411  * Delete the given client from the client list
412  *
413  * @param client the client list entry to delete
414  */
415 static void
416 cl_remove_client (struct ClientList *client)
417 {
418   LOG (GNUNET_ERROR_TYPE_DEBUG,
419        "Removing a client from the client list\n");
420   GNUNET_SERVER_client_drop (client->client);
421   GNUNET_CONTAINER_DLL_remove (cl_head,
422                                cl_tail,
423                                client);
424   GNUNET_free (client);
425 }
426
427
428 /**
429  * Transmit notify for sending message to client
430  *
431  * @param cls the message to send
432  * @param size number of bytes available in buf
433  * @param buf where the callee should write the message
434  * @return number of bytes written to buf
435  */
436 static size_t 
437 transmit_notify (void *cls, size_t size, void *buf)
438 {
439   struct GNUNET_LOCKMANAGER_Message *msg = cls;
440   uint16_t msg_size;
441
442   if ((0 == size) || (NULL == buf))
443     {
444       /* FIXME: Timed out -- requeue? */
445       return 0;
446     }
447   msg_size = ntohs (msg->header.size);
448   GNUNET_assert (size >= msg_size);
449   memcpy (buf, msg, msg_size);
450   GNUNET_free (msg);
451   LOG (GNUNET_ERROR_TYPE_DEBUG,
452        "Message of size %u sent\n", msg_size);
453   return msg_size;
454 }
455
456
457 /**
458  * Send SUCCESS message to the client
459  *
460  * @param client the client to which the message has to be sent
461  * @param domain_name the locking domain of the successfully acquried lock
462  * @param lock_num the number of the successfully acquired lock
463  */
464 static void
465 send_success_msg (struct GNUNET_SERVER_Client *client,
466                   const char *domain_name,
467                   int lock_num)
468 {
469   struct GNUNET_LOCKMANAGER_Message *reply;
470   size_t domain_name_len;
471   uint16_t reply_size;
472
473   domain_name_len = strlen (domain_name) + 1;
474   reply_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_len;
475   reply = GNUNET_malloc (reply_size);
476   reply->header.size = htons (reply_size);
477   reply->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS);
478   reply->lock = htonl (lock_num);
479   strncpy ((char *) &reply[1], domain_name, domain_name_len);
480   GNUNET_SERVER_notify_transmit_ready (client,
481                                        reply_size,
482                                        TIMEOUT,
483                                        &transmit_notify,
484                                        reply);
485 }
486
487
488 /**
489  * Handler for GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE
490  *
491  * @param cls NULL
492  * @param client the client sending this message
493  * @param message GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE message
494  */
495 static void
496 handle_acquire (void *cls,
497                 struct GNUNET_SERVER_Client *client,
498                 const struct GNUNET_MessageHeader *message)
499 {
500   const struct GNUNET_LOCKMANAGER_Message *request;
501   const char *domain_name;
502   struct LockList *ll_entry;
503   uint32_t lock_num;
504   
505
506   LOG (GNUNET_ERROR_TYPE_DEBUG,
507        "Received an ACQUIRE message\n");
508
509
510   /* Check if the client is in client list */
511   if (GNUNET_NO == cl_find_client (client, NULL))
512     {
513       cl_add_client (client);
514     }
515   
516   request = (struct GNUNET_LOCKMANAGER_Message *) message;
517   lock_num = ntohl (request->lock);
518   domain_name = (char *) &request[1];
519   if (GNUNET_YES == ll_find_lock (domain_name,
520                                   lock_num,
521                                   &ll_entry))
522     {/* Add client to the lock's wait list */
523       ll_wl_add_client (ll_entry, client);
524     }
525   else
526     {
527       ll_add_lock (client, domain_name, lock_num);
528       send_success_msg (client, domain_name, lock_num);
529     }
530   
531   GNUNET_SERVER_receive_done (client, GNUNET_OK);
532 }
533
534
535 /**
536  * This function gives the lock to the first client in the wait list of the
537  * lock. If no clients are currently waiting for this lock, the lock is
538  * destroyed.
539  *
540  * @param ll_entry the lock list entry corresponding to a lock
541  */
542 static void
543 process_lock_release (struct LockList *ll_entry)
544 {
545   struct WaitList *wl_entry;
546
547   wl_entry = ll_entry->wait_list_head;
548   if (NULL == wl_entry)
549     {
550       ll_remove_lock (ll_entry);
551       return;
552     }
553
554   while (NULL != wl_entry)
555     {
556       ll_wl_remove_client(ll_entry, wl_entry);
557
558       if (GNUNET_NO == cl_find_client (wl_entry->client, NULL))
559         {
560           LOG (GNUNET_ERROR_TYPE_DEBUG,
561                "Removing disconnected client from wait list\n");
562           wl_entry = ll_entry->wait_list_head;
563           continue;
564         }
565       
566       LOG (GNUNET_ERROR_TYPE_DEBUG,
567            "Giving lock to a client from wait list\n");
568       ll_entry->client = wl_entry->client;
569       send_success_msg (wl_entry->client,
570                         ll_entry->domain_name,
571                         ll_entry->lock_num);
572       return;
573     }
574   /* We ran out of clients in the wait list -- destroy lock */
575   ll_remove_lock (ll_entry);
576   return;
577 }
578
579
580 /**
581  * Handle for GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE
582  *
583  * @param cls NULL
584  * @param client the client sending this message
585  * @param message the LOCKMANAGER_RELEASE message
586  */
587 static void
588 handle_release (void *cls,
589                 struct GNUNET_SERVER_Client *client,
590                 const struct GNUNET_MessageHeader *message)
591 {
592   const struct GNUNET_LOCKMANAGER_Message *request;
593   const char *domain_name;
594   struct LockList *ll_entry;
595   uint32_t lock_num;
596
597   request = (struct GNUNET_LOCKMANAGER_Message *) message;
598   lock_num = ntohl (request->lock);
599   domain_name = (char *) &request[1];
600   LOG (GNUNET_ERROR_TYPE_DEBUG,
601        "Received a RELEASE message on lock with num: %d, domain: %s\n",
602        lock_num, domain_name);
603   GNUNET_SERVER_receive_done (client, GNUNET_OK);
604   if (GNUNET_YES == ll_find_lock (domain_name, lock_num, &ll_entry))
605     {
606       if (client != ll_entry->client)
607         {
608           GNUNET_break (0);
609           return;
610         }
611       process_lock_release (ll_entry);
612     }
613   else
614     {
615       LOG (GNUNET_ERROR_TYPE_DEBUG,
616            "\t give lock doesn't exist\n");
617     }
618 }
619
620
621 /**
622  * Callback for client disconnect
623  *
624  * @param cls NULL
625  * @param client the client which has disconnected
626  */
627 static void
628 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
629 {
630   struct ClientList *cl_entry;
631
632   LOG (GNUNET_ERROR_TYPE_DEBUG,
633        "A client has been disconnected -- freeing its locks and resources\n");
634   
635   if (GNUNET_YES == cl_find_client (client, &cl_entry))
636     {
637       struct LockList *ll_entry;
638       
639       cl_remove_client (cl_entry);
640       while (GNUNET_YES == ll_find_lock_by_owner (client, &ll_entry))
641         {
642           process_lock_release (ll_entry);
643         }
644     }
645   else GNUNET_break (0);
646 }
647
648
649 /**
650  * Lock manager setup
651  *
652  * @param cls closure
653  * @param server the initialized server
654  * @param cfg configuration to use
655  */
656 static void 
657 lockmanager_run (void *cls,
658                  struct GNUNET_SERVER_Handle * server,
659                  const struct GNUNET_CONFIGURATION_Handle *cfg)
660 {
661   static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
662     {
663       {&handle_acquire, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE, 0},
664       {&handle_release, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE, 0},
665       {NULL}
666     };
667   
668   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lockmanager\n");
669   GNUNET_SERVER_add_handlers (server,
670                               message_handlers);
671   GNUNET_SERVER_disconnect_notify (server,
672                                    &client_disconnect_cb,
673                                    NULL);
674                                    
675   
676 }
677
678 /**
679  * The starting point of execution
680  */
681 int main (int argc, char *const *argv)
682 {
683   int ret;
684   
685   GNUNET_log_setup ("lockmanager",
686 #if VERBOSE
687                     "DEBUG",
688 #else
689                     "WARNING",
690 #endif
691                     NULL);
692
693   LOG (GNUNET_ERROR_TYPE_DEBUG, "main()\n");
694   ret = 
695     (GNUNET_OK ==
696      GNUNET_SERVICE_run (argc,
697                          argv,
698                          "lockmanager",
699                          GNUNET_SERVICE_OPTION_NONE,
700                          &lockmanager_run,
701                          NULL)) ? 0 : 1;
702   LOG (GNUNET_ERROR_TYPE_DEBUG, "main() END\n");
703   return ret;
704 }