lock acquire and 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  * @param tail the pointer to the tail of the global lock list
232  */
233 static void
234 ll_add_lock (struct GNUNET_SERVER_Client *client,
235              const char *domain_name,
236              const uint32_t lock_num)
237 {
238   struct LockList *lock;
239   size_t domain_name_len;
240
241   LOG (GNUNET_ERROR_TYPE_DEBUG,
242        "Adding a lock with num: %u and domain: %s to lock list\n",
243        lock_num, domain_name);
244
245   lock = GNUNET_malloc (sizeof (struct LockList));
246   domain_name_len = strlen (domain_name) + 1;
247   lock->domain_name = GNUNET_malloc (domain_name_len);
248   strncpy (lock->domain_name, domain_name, domain_name_len);
249   lock->lock_num = lock_num;
250   lock->client = client;
251   
252   GNUNET_CONTAINER_DLL_insert_tail (ll_head,
253                                     ll_tail,
254                                     lock);
255 }
256
257
258 /**
259  * Function to delete a lock from the lock list
260  *
261  * @param lock the lock to be deleted
262  */
263 static void
264 ll_remove_lock (struct LockList *lock)
265 {
266   LOG (GNUNET_ERROR_TYPE_DEBUG,
267        "Removing lock with num: %u, domain: %s\n",
268        lock->lock_num, lock->domain_name);
269   GNUNET_assert (NULL != ll_head);
270   GNUNET_CONTAINER_DLL_remove (ll_head,
271                                ll_tail,
272                                lock);
273   GNUNET_free (lock->domain_name);
274   GNUNET_free (lock);
275 }
276
277
278 /**
279  * Find a client in the waiting list of a lock
280  *
281  * @param lock the LockList entry of a lock
282  * @param client the client to look for
283  * @param ret where to store the matched wait list entry
284  * @return GNUNET_YES if a match is found; GNUNET_NO if not
285  */
286 static int
287 ll_wl_find_client (struct LockList *lock,
288                    const struct GNUNET_SERVER_Client *client,
289                    struct WaitList **ret)
290 {
291   struct WaitList *current_wl_entry;
292
293   current_wl_entry = lock->wait_list_head;
294
295   while (NULL != current_wl_entry)
296     {
297       if (client == current_wl_entry->client)
298         {
299           if (NULL != ret)
300             *ret = current_wl_entry;
301           return GNUNET_YES;
302         }
303       current_wl_entry = current_wl_entry->next;
304     }
305   if (NULL != ret)
306     *ret = current_wl_entry;
307   return GNUNET_NO;
308 }
309
310
311 /**
312  * Add a client to the wait list of given lock
313  *
314  * @param lock the lock list entry of a lock
315  * @param client the client to queue for the lock's wait list
316  */
317 static void
318 ll_wl_add_client (struct LockList *lock,
319                   struct GNUNET_SERVER_Client *client)
320 {
321   struct WaitList *wl_entry;
322
323   LOG (GNUNET_ERROR_TYPE_DEBUG,
324        "Adding a client to lock's wait list\n"
325        "\t lock num: %u, domain: %s\n",
326        lock->lock_num,
327        lock->domain_name);
328
329   wl_entry = GNUNET_malloc (sizeof (struct WaitList));
330   wl_entry->client = client;
331   GNUNET_CONTAINER_DLL_insert_tail (lock->wait_list_head,
332                                     lock->wait_list_tail,
333                                     wl_entry);
334 }
335
336
337 /**
338  * Remove a client from the wait list of the given lock
339  *
340  * @param lock the lock list entry of the lock
341  * @param wl_client the wait list entry to be removed
342  */
343 static void
344 ll_wl_remove_client (struct LockList *lock,
345                      struct WaitList *wl_client)
346 {
347   GNUNET_CONTAINER_DLL_remove (lock->wait_list_head,
348                                lock->wait_list_tail,
349                                wl_client);
350
351   GNUNET_free (wl_client);
352 }
353
354
355 /**
356  * Search for a client in the client list
357  *
358  * @param client the client to be searched for
359  * @param ret will be pointing to the matched list entry (if there is a match);
360  *          else to the tail of the client list
361  * @return GNUNET_YES if the client is present; GNUNET_NO if not
362  */
363 static int
364 cl_find_client (const struct GNUNET_SERVER_Client *client,
365                 struct ClientList **ret)
366 {
367   struct ClientList *current;
368
369   current = cl_head;
370
371   while (NULL != current)
372     {
373       if (client == current->client)
374         {
375           if (NULL != ret) 
376             *ret = current;
377           return GNUNET_YES;
378         }
379
380       current = current->next;
381     }
382   
383   if (NULL != ret)
384     *ret = current;
385   return GNUNET_NO;
386 }
387
388
389 /**
390  * Append a client to the client list
391  *
392  * @param client the client to be appended to the list
393  */
394 static void
395 cl_add_client (struct GNUNET_SERVER_Client *client)
396 {
397   struct ClientList *new_client;
398   
399   LOG (GNUNET_ERROR_TYPE_DEBUG,
400        "Adding a client to the client list\n");
401
402   new_client = GNUNET_malloc (sizeof (struct ClientList));
403   GNUNET_SERVER_client_keep (client);
404   new_client->client = client;
405   GNUNET_CONTAINER_DLL_insert_tail (cl_head,
406                                     cl_tail,
407                                     new_client);
408 }
409
410
411 /**
412  * Delete the given client from the client list
413  *
414  * @param client the client list entry to delete
415  */
416 static void
417 cl_remove_client (struct ClientList *client)
418 {
419   LOG (GNUNET_ERROR_TYPE_DEBUG,
420        "Removing a client from the client list\n");
421   GNUNET_SERVER_client_drop (client->client);
422   GNUNET_CONTAINER_DLL_remove (cl_head,
423                                cl_tail,
424                                client);
425   GNUNET_free (client);
426 }
427
428
429 /**
430  * Transmit notify for sending message to client
431  *
432  * @param cls the message to send
433  * @param size number of bytes available in buf
434  * @param buf where the callee should write the message
435  * @return number of bytes written to buf
436  */
437 static size_t 
438 transmit_notify (void *cls, size_t size, void *buf)
439 {
440   struct GNUNET_LOCKMANAGER_Message *msg = cls;
441   uint16_t msg_size;
442
443   if ((0 == size) || (NULL == buf))
444     {
445       /* FIXME: Timed out -- requeue? */
446       return 0;
447     }
448   msg_size = ntohs (msg->header.size);
449   GNUNET_assert (size >= msg_size);
450   memcpy (buf, msg, msg_size);
451   GNUNET_free (msg);
452   LOG (GNUNET_ERROR_TYPE_DEBUG,
453        "Message of size %u sent\n", msg_size);
454   return msg_size;
455 }
456
457
458 /**
459  * Send SUCCESS message to the client
460  *
461  * @param client the client to which the message has to be sent
462  * @param domain_name the locking domain of the successfully acquried lock
463  * @param lock_num the number of the successfully acquired lock
464  */
465 static void
466 send_success_msg (struct GNUNET_SERVER_Client *client,
467                   const char *domain_name,
468                   int lock_num)
469 {
470   struct GNUNET_LOCKMANAGER_Message *reply;
471   size_t domain_name_len;
472   uint16_t reply_size;
473
474   domain_name_len = strlen (domain_name) + 1;
475   reply_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_len;
476   reply = GNUNET_malloc (reply_size);
477   reply->header.size = htons (reply_size);
478   reply->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS);
479   reply->lock = htonl (lock_num);
480   strncpy ((char *) &reply[1], domain_name, domain_name_len);
481   GNUNET_SERVER_notify_transmit_ready (client,
482                                        reply_size,
483                                        TIMEOUT,
484                                        &transmit_notify,
485                                        reply);
486 }
487
488
489 /**
490  * Handler for GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE
491  *
492  * @param cls NULL
493  * @param client the client sending this message
494  * @param message GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE message
495  */
496 static void
497 handle_acquire (void *cls,
498                 struct GNUNET_SERVER_Client *client,
499                 const struct GNUNET_MessageHeader *message)
500 {
501   const struct GNUNET_LOCKMANAGER_Message *request;
502   const char *domain_name;
503   struct LockList *ll_entry;
504   uint32_t lock_num;
505   
506
507   LOG (GNUNET_ERROR_TYPE_DEBUG,
508        "Received an ACQUIRE message\n");
509
510
511   /* Check if the client is in client list */
512   if (GNUNET_NO == cl_find_client (client, NULL))
513     {
514       cl_add_client (client);
515     }
516   
517   request = (struct GNUNET_LOCKMANAGER_Message *) message;
518   lock_num = ntohl (request->lock);
519   domain_name = (char *) &request[1];
520   if (GNUNET_YES == ll_find_lock (domain_name,
521                                   lock_num,
522                                   &ll_entry))
523     {/* Add client to the lock's wait list */
524       ll_wl_add_client (ll_entry, client);
525     }
526   else
527     {
528       ll_add_lock (client, domain_name, lock_num);
529       send_success_msg (client, domain_name, lock_num);
530     }
531   
532   GNUNET_SERVER_receive_done (client, GNUNET_OK);
533 }
534
535
536 /**
537  * Handle for GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE
538  *
539  * @param cls NULL
540  * @param client the client sending this message
541  * @param message the LOCKMANAGER_RELEASE message
542  */
543 static void
544 handle_release (void *cls,
545                 struct GNUNET_SERVER_Client *client,
546                 const struct GNUNET_MessageHeader *message)
547 {
548   const struct GNUNET_LOCKMANAGER_Message *request;
549   const char *domain_name;
550   struct LockList *ll_entry;
551   uint32_t lock_num;
552
553   request = (struct GNUNET_LOCKMANAGER_Message *) message;
554   lock_num = ntohl (request->lock);
555   domain_name = (char *) &request[1];
556   LOG (GNUNET_ERROR_TYPE_DEBUG,
557        "Received a RELEASE message on lock with num: %d, domain: %s\n",
558        lock_num, domain_name);
559   if (GNUNET_YES == ll_find_lock (domain_name, lock_num, &ll_entry))
560     {
561       if (NULL == ll_entry->wait_list_head)
562         {
563           ll_remove_lock (ll_entry);
564         }
565       else
566         {
567           /* Do furthur processing on lock's wait list here */
568         }
569     }
570   else
571     {
572       LOG (GNUNET_ERROR_TYPE_DEBUG,
573            "\t give lock doesn't exist\n");
574     }
575   
576   
577   GNUNET_SERVER_receive_done (client, GNUNET_OK);
578 }
579
580
581 /**
582  * Callback for client disconnect
583  *
584  * @param cls NULL
585  * @param client the client which has disconnected
586  */
587 static void
588 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
589 {
590   struct ClientList *cl_entry;
591
592   LOG (GNUNET_ERROR_TYPE_DEBUG,
593        "A client has been disconnected -- freeing its locks and resources\n");
594   
595   if (GNUNET_YES == cl_find_client (client, &cl_entry))
596     {
597       struct LockList *lock;
598       
599       cl_remove_client (cl_entry);
600       while (GNUNET_YES == ll_find_lock_by_owner (client, &lock))
601         {
602           if (NULL == lock->wait_list_head)
603             {
604               ll_remove_lock (lock);
605             }
606           else
607             {
608               /* Do furthur processing on lock's wait list here */
609             }
610         }
611     }
612   else GNUNET_break (0);
613 }
614
615
616 /**
617  * Lock manager setup
618  *
619  * @param cls closure
620  * @param server the initialized server
621  * @param cfg configuration to use
622  */
623 static void 
624 lockmanager_run (void *cls,
625                  struct GNUNET_SERVER_Handle * server,
626                  const struct GNUNET_CONFIGURATION_Handle *cfg)
627 {
628   static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
629     {
630       {&handle_acquire, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE, 0},
631       {&handle_release, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE, 0},
632       {NULL}
633     };
634   
635   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lockmanager\n");
636   GNUNET_SERVER_add_handlers (server,
637                               message_handlers);
638   GNUNET_SERVER_disconnect_notify (server,
639                                    &client_disconnect_cb,
640                                    NULL);
641                                    
642   
643 }
644
645 /**
646  * The starting point of execution
647  */
648 int main (int argc, char *const *argv)
649 {
650   int ret;
651   
652   GNUNET_log_setup ("lockmanager",
653 #if VERBOSE
654                     "DEBUG",
655 #else
656                     "WARNING",
657 #endif
658                     NULL);
659
660   LOG (GNUNET_ERROR_TYPE_DEBUG, "main()\n");
661   ret = 
662     (GNUNET_OK ==
663      GNUNET_SERVICE_run (argc,
664                          argv,
665                          "lockmanager",
666                          GNUNET_SERVICE_OPTION_NONE,
667                          &lockmanager_run,
668                          NULL)) ? 0 : 1;
669   LOG (GNUNET_ERROR_TYPE_DEBUG, "main() END\n");
670   return ret;
671 }