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