2 This file is part of GNUnet.
3 (C) 2008--2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file lockmanager/gnunet-service-lockmanager.c
23 * @brief implementation of the LOCKMANAGER service
24 * @author Sree Harsha Totakura
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"
34 #include "lockmanager.h"
37 #define LOG(kind,...) \
38 GNUNET_log (kind, __VA_ARGS__)
40 #define TIME_REL_MINS(min) \
41 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
43 #define TIMEOUT TIME_REL_MINS(3)
47 * Doubly linked list of clients having connections to us
53 * Doubly linked list of clients waiting for a lock
58 * The next client structure
60 struct WaitList *next;
63 * The prev client structure
65 struct WaitList *prev;
68 * Pointer to the client
70 struct ClientList *cl_entry;
75 * Structure representing a Lock
80 * List head of clients waiting for this lock
82 struct WaitList *wl_head;
85 * List tail of clients waiting for this lock
87 struct WaitList *wl_tail;
90 * The client which is currently holding this lock
92 struct ClientList *cl_entry;
95 * The name of the locking domain this lock belongs to
100 * The number of this lock
107 * A Lock element for a doubly linked list
112 * The next element pointer
114 struct LockList *next;
117 * Pointer to the previous element
119 struct LockList *prev;
122 * Pointer to the Lock
129 * Doubly linked list of clients having connections to us
135 * The next client structure
137 struct ClientList *next;
140 * The previous client structure
142 struct ClientList *prev;
145 * Head of the doubly linked list of the currently held locks by this client
147 struct LockList *ll_head;
150 * Tail of the doubly linked list of the currently held locks by this client
152 struct LockList *ll_tail;
155 * Pointer to the client
157 struct GNUNET_SERVER_Client *client;
162 * Structure for matching a lock
167 * The matched LockingRequest entry; Should be NULL if no entry is found
169 struct Lock *matched_entry;
172 * The locking domain name of the lock
174 const char *domain_name;
184 * Map of lock-keys to the 'struct LockList' entry for the key.
186 static struct GNUNET_CONTAINER_MultiHashMap *lock_map;
189 * Head of the doubly linked list of clients currently connected
191 static struct ClientList *cl_head;
194 * Tail of the doubly linked list of clients currently connected
196 static struct ClientList *cl_tail;
200 * Get the key for the given lock in the 'lock_map'.
204 * @param key set to the key
207 get_key (const char *domain_name, uint32_t lock_number,
208 struct GNUNET_HashCode *key)
212 GNUNET_CRYPTO_hash (domain_name, strlen (domain_name), key);
213 last_32 = (uint32_t *) key;
214 *last_32 ^= lock_number;
219 * Hashmap iterator for matching a lock
221 * @param cls the LockMatch structure
222 * @param key current key code
223 * @param value value in the hash map (struct Lock)
224 * @return GNUNET_YES if we should continue to
229 match_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
231 struct LockMatch *match = cls;
232 struct Lock *lock = value;
234 if ((match->lock_num == lock->lock_num) &&
235 (0 == strcmp (match->domain_name, lock->domain_name)))
237 match->matched_entry = lock;
245 * Function to search for a lock in the global lock hashmap
247 * @param domain_name the name of the locking domain
248 * @param lock_num the number of the lock
249 * @return the lock if found; NULL if not
252 find_lock (const char *domain_name, const uint32_t lock_num)
254 struct LockMatch match;
255 struct GNUNET_HashCode key;
257 match.lock_num = lock_num;
258 match.domain_name = domain_name;
259 match.matched_entry = NULL;
260 get_key (domain_name, lock_num, &key);
261 GNUNET_CONTAINER_multihashmap_get_multiple (lock_map, &key, &match_iterator,
263 return match.matched_entry;
268 * Adds a lock to the global lock hashmap
270 * @param domain_name the name of the lock's locking domain
271 * @param lock_num the lock number
272 * @return pointer to the lock structure which is added to lock map
275 add_lock (const char *domain_name, uint32_t lock_num)
278 struct GNUNET_HashCode key;
279 size_t domain_name_len;
281 lock = GNUNET_malloc (sizeof (struct Lock));
282 domain_name_len = strlen (domain_name) + 1;
283 lock->domain_name = GNUNET_malloc (domain_name_len);
284 strncpy (lock->domain_name, domain_name, domain_name_len);
285 lock->lock_num = lock_num;
286 get_key (domain_name, lock_num, &key);
287 LOG (GNUNET_ERROR_TYPE_DEBUG,
288 "Adding a lock with num: %d and domain: %s to the lock map\n",
289 lock->lock_num, lock->domain_name);
290 GNUNET_CONTAINER_multihashmap_put (lock_map, &key, lock,
291 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
297 * Removes a lock from the lock map. The WaitList of the lock should be empty
299 * @param lock the lock to remove
302 remove_lock (struct Lock *lock)
304 struct GNUNET_HashCode key;
306 GNUNET_assert (NULL == lock->wl_head);
307 get_key (lock->domain_name, lock->lock_num, &key);
308 LOG (GNUNET_ERROR_TYPE_DEBUG,
309 "Removing lock with num: %u, domain: %s from lock map\n", lock->lock_num,
311 GNUNET_assert (GNUNET_YES ==
312 GNUNET_CONTAINER_multihashmap_remove (lock_map, &key, lock));
313 GNUNET_free (lock->domain_name);
319 * Find the LockList entry corresponding to the given Lock in a ClientList
322 * @param cl_entry the ClientList entry whose lock list has to be searched
323 * @param lock the lock which has to be matched
324 * @return the matching LockList entry; NULL if no match is found
326 static struct LockList *
327 cl_ll_find_lock (struct ClientList *cl_entry, const struct Lock *lock)
329 struct LockList *ll_entry;
331 for (ll_entry = cl_entry->ll_head; NULL != ll_entry;
332 ll_entry = ll_entry->next)
334 if (lock == ll_entry->lock)
342 * Function to append a lock to the lock list of a ClientList entry
344 * @param cl_entry the client which currently owns this lock
345 * @param lock the lock to be added to the cl_entry's lock list
348 cl_ll_add_lock (struct ClientList *cl_entry, struct Lock *lock)
350 struct LockList *ll_entry;
352 ll_entry = GNUNET_malloc (sizeof (struct LockList));
353 ll_entry->lock = lock;
354 LOG (GNUNET_ERROR_TYPE_DEBUG,
355 "Adding a lock with num: %u and domain: %s to lock list\n",
356 lock->lock_num, lock->domain_name);
357 GNUNET_CONTAINER_DLL_insert_tail (cl_entry->ll_head, cl_entry->ll_tail,
363 * Function to delete a lock from the lock list of the given ClientList entry
365 * @param cl_entry the ClientList entry
366 * @param ll_entry the LockList entry to be deleted
369 cl_ll_remove_lock (struct ClientList *cl_entry, struct LockList *ll_entry)
371 LOG (GNUNET_ERROR_TYPE_DEBUG,
372 "Removing lock with num: %u, domain: %s from lock list of a client\n",
373 ll_entry->lock->lock_num, ll_entry->lock->domain_name);
374 GNUNET_assert (NULL != cl_entry->ll_head);
375 GNUNET_CONTAINER_DLL_remove (cl_entry->ll_head, cl_entry->ll_tail, ll_entry);
376 GNUNET_free (ll_entry);
381 * Find a WaitList entry in the waiting list of a lock
383 * @param lock the lock whose wait list has to be searched
384 * @param cl_entry the ClientList entry to be searched
385 * @return the WaitList entry matching the given cl_entry; NULL if not match
388 static struct WaitList *
389 lock_wl_find (const struct Lock *lock, const struct ClientList *cl_entry)
391 struct WaitList *wl_entry;
393 for (wl_entry = lock->wl_head; NULL != wl_entry; wl_entry = wl_entry->next)
395 if (cl_entry == wl_entry->cl_entry)
403 * Add a client to the wait list of given lock
405 * @param lock the lock list entry of a lock
406 * @param cl_entry the client to queue for the lock's wait list
409 lock_wl_add_client (struct Lock *lock, struct ClientList *cl_entry)
411 struct WaitList *wl_entry;
413 LOG (GNUNET_ERROR_TYPE_DEBUG,
414 "Adding a client to lock's wait list (lock num: %u, domain: %s)\n",
415 lock->lock_num, lock->domain_name);
416 wl_entry = GNUNET_malloc (sizeof (struct WaitList));
417 wl_entry->cl_entry = cl_entry;
418 GNUNET_CONTAINER_DLL_insert_tail (lock->wl_head, lock->wl_tail, wl_entry);
423 * Remove an entry from the wait list of the given lock
425 * @param lock the lock
426 * @param wl_entry the wait list entry to be removed
429 lock_wl_remove (struct Lock *lock, struct WaitList *wl_entry)
431 LOG (GNUNET_ERROR_TYPE_DEBUG,
432 "Removing client from wait list of lock with num: %u, domain: %s\n",
433 lock->lock_num, lock->domain_name);
434 GNUNET_CONTAINER_DLL_remove (lock->wl_head, lock->wl_tail, wl_entry);
435 GNUNET_free (wl_entry);
440 * Search for a client in the client list
442 * @param client the client to be searched for
443 * @return the ClientList entry; NULL if the client is not found
445 static struct ClientList *
446 cl_find_client (const struct GNUNET_SERVER_Client *client)
448 struct ClientList *current;
450 for (current = cl_head; NULL != current; current = current->next)
451 if (client == current->client)
458 * Append a client to the client list
460 * @param client the client to be appended to the list
461 * @return the client list entry which is added to the client list
463 static struct ClientList *
464 cl_add_client (struct GNUNET_SERVER_Client *client)
466 struct ClientList *new_client;
468 LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding a client to the client list\n");
469 new_client = GNUNET_malloc (sizeof (struct ClientList));
470 GNUNET_SERVER_client_keep (client);
471 new_client->client = client;
472 GNUNET_CONTAINER_DLL_insert_tail (cl_head, cl_tail, new_client);
478 * Delete the given client from the client list. The LockList should be empty
480 * @param cl_entry the client list entry to delete
483 cl_remove_client (struct ClientList *cl_entry)
485 GNUNET_assert (NULL == cl_entry->ll_head);
486 LOG (GNUNET_ERROR_TYPE_DEBUG, "Removing a client from the client list\n");
487 GNUNET_SERVER_client_drop (cl_entry->client);
488 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl_entry);
489 GNUNET_free (cl_entry);
494 * Transmit notify for sending message to client
496 * @param cls the message to send
497 * @param size number of bytes available in buf
498 * @param buf where the callee should write the message
499 * @return number of bytes written to buf
502 transmit_notify (void *cls, size_t size, void *buf)
504 struct GNUNET_LOCKMANAGER_Message *msg = cls;
507 if ((0 == size) || (NULL == buf))
509 /* FIXME: Timed out -- requeue? */
512 msg_size = ntohs (msg->header.size);
513 GNUNET_assert (size >= msg_size);
514 memcpy (buf, msg, msg_size);
516 LOG (GNUNET_ERROR_TYPE_DEBUG, "Message of size %u sent\n", msg_size);
522 * Send SUCCESS message to the client
524 * @param client the client to which the message has to be sent
525 * @param domain_name the locking domain of the successfully acquried lock
526 * @param lock_num the number of the successfully acquired lock
529 send_success_msg (struct GNUNET_SERVER_Client *client, const char *domain_name,
532 struct GNUNET_LOCKMANAGER_Message *reply;
533 size_t domain_name_len;
536 domain_name_len = strlen (domain_name) + 1;
537 reply_size = sizeof (struct GNUNET_LOCKMANAGER_Message) + domain_name_len;
538 reply = GNUNET_malloc (reply_size);
539 reply->header.size = htons (reply_size);
540 reply->header.type = htons (GNUNET_MESSAGE_TYPE_LOCKMANAGER_SUCCESS);
541 reply->lock = htonl (lock_num);
542 strncpy ((char *) &reply[1], domain_name, domain_name_len);
543 LOG (GNUNET_ERROR_TYPE_DEBUG,
544 "Sending SUCCESS message for lock with num: %u, domain: %s\n", lock_num,
546 GNUNET_SERVER_notify_transmit_ready (client, reply_size, TIMEOUT,
547 &transmit_notify, reply);
552 * Handler for GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE
555 * @param client the client sending this message
556 * @param message GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE message
559 handle_acquire (void *cls, struct GNUNET_SERVER_Client *client,
560 const struct GNUNET_MessageHeader *message)
562 const struct GNUNET_LOCKMANAGER_Message *request;
563 const char *domain_name;
565 struct ClientList *cl_entry;
569 msize = htons (message->size);
570 if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
573 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
576 request = (struct GNUNET_LOCKMANAGER_Message *) message;
577 domain_name = (const char *) &request[1];
578 msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
579 if ('\0' != domain_name[msize - 1])
581 LOG (GNUNET_ERROR_TYPE_DEBUG,
582 "Bad domain `%.*s' - byte with index %u is %X, not 0.\n", msize,
583 domain_name, msize - 1, (unsigned int) domain_name[msize - 1]);
585 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
588 lock_num = ntohl (request->lock);
589 LOG (GNUNET_ERROR_TYPE_DEBUG,
590 "Received an ACQUIRE message for lock num: %u domain: %s\n", lock_num,
592 if (NULL == (cl_entry = cl_find_client (client)))
593 cl_entry = cl_add_client (client); /* Add client if not in client list */
594 if (NULL != (lock = find_lock (domain_name, lock_num)))
596 if (lock->cl_entry == cl_entry)
597 { /* Client is requesting a lock it already owns */
599 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
602 lock_wl_add_client (lock, cl_entry);
603 cl_ll_add_lock (cl_entry, lock);
605 else /* Lock not present */
607 lock = add_lock (domain_name, lock_num);
608 lock->cl_entry = cl_entry;
609 cl_ll_add_lock (cl_entry, lock);
610 send_success_msg (cl_entry->client, domain_name, lock_num);
612 GNUNET_SERVER_receive_done (client, GNUNET_OK);
617 * This function gives the lock to the first client in the wait list of the
618 * lock. If no clients are currently waiting for this lock, the lock is then
621 * @param lock the lock which has to be processed for release
624 process_lock_release (struct Lock *lock)
626 struct WaitList *wl_entry;
628 LOG (GNUNET_ERROR_TYPE_DEBUG,
629 "Processing lock release for lock with num: %u, domain: %s\n",
630 lock->lock_num, lock->domain_name);
631 wl_entry = lock->wl_head;
632 if (NULL == wl_entry)
634 remove_lock (lock); /* No clients waiting for this lock - delete */
637 LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving lock to a client from wait list\n");
638 lock->cl_entry = wl_entry->cl_entry;
639 lock_wl_remove (lock, wl_entry);
640 send_success_msg (lock->cl_entry->client, lock->domain_name, lock->lock_num);
645 * Handle for GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE
648 * @param client the client sending this message
649 * @param message the LOCKMANAGER_RELEASE message
652 handle_release (void *cls, struct GNUNET_SERVER_Client *client,
653 const struct GNUNET_MessageHeader *message)
655 const struct GNUNET_LOCKMANAGER_Message *request;
656 struct ClientList *cl_entry;
657 struct WaitList *wl_entry;
658 struct LockList *ll_entry;
659 const char *domain_name;
664 msize = ntohs (message->size);
665 if (msize <= sizeof (struct GNUNET_LOCKMANAGER_Message))
668 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
671 request = (const struct GNUNET_LOCKMANAGER_Message *) message;
672 domain_name = (const char *) &request[1];
673 msize -= sizeof (struct GNUNET_LOCKMANAGER_Message);
674 if ('\0' != domain_name[msize - 1])
677 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
682 lock_num = ntohl (request->lock);
683 LOG (GNUNET_ERROR_TYPE_DEBUG,
684 "Received RELEASE message for lock with num: %d, domain: %s\n", lock_num,
686 if (NULL == (cl_entry = cl_find_client (client)))
689 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
692 lock = find_lock (domain_name, lock_num);
696 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
699 if (NULL == (ll_entry = cl_ll_find_lock (cl_entry, lock)))
702 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
705 cl_ll_remove_lock (cl_entry, ll_entry);
706 if (cl_entry == lock->cl_entry)
708 process_lock_release (lock);
709 GNUNET_SERVER_receive_done (client, GNUNET_OK);
712 /* remove 'client' from wait list (check that it is not there...) */
713 if (NULL != (wl_entry = lock_wl_find (lock, cl_entry)))
715 lock_wl_remove (lock, wl_entry);
717 GNUNET_SERVER_receive_done (client, GNUNET_OK);
722 * Callback for client disconnect
725 * @param client the client which has disconnected
728 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
730 struct ClientList *cl_entry;
731 struct LockList *ll_entry;
733 struct WaitList *wl_entry;
737 LOG (GNUNET_ERROR_TYPE_DEBUG,
738 "A client has been disconnected -- freeing its locks and resources\n");
739 cl_entry = cl_find_client (client);
740 if (NULL == cl_entry)
742 while (NULL != (ll_entry = cl_entry->ll_head))
744 lock = ll_entry->lock;
745 cl_ll_remove_lock (cl_entry, ll_entry);
746 if (lock->cl_entry == cl_entry)
747 process_lock_release (lock);
750 wl_entry = lock_wl_find (lock, cl_entry);
751 GNUNET_assert (NULL != wl_entry);
752 lock_wl_remove (lock, wl_entry);
755 cl_remove_client (cl_entry);
760 * Hashmap Iterator to delete lock entries in hash map
763 * @param key current key code
764 * @param value value in the hash map
765 * @return GNUNET_YES if we should continue to
770 lock_delete_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
772 struct Lock *lock = value;
774 GNUNET_assert (NULL != lock);
775 while (NULL != lock->wl_head)
777 lock_wl_remove (lock, lock->wl_head);
779 GNUNET_assert (GNUNET_YES ==
780 GNUNET_CONTAINER_multihashmap_remove (lock_map, key, lock));
781 GNUNET_free (lock->domain_name);
788 * Task to clean up and shutdown nicely
791 * @param tc the TaskContext from scheduler
794 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
796 LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down lock manager\n");
797 /* Clean the global ClientList */
798 while (NULL != cl_head)
800 while (NULL != cl_head->ll_head) /* Clear the LockList */
802 cl_ll_remove_lock (cl_head, cl_head->ll_head);
804 cl_remove_client (cl_head);
806 /* Clean the global hash table */
807 GNUNET_CONTAINER_multihashmap_iterate (lock_map, &lock_delete_iterator, NULL);
808 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (lock_map));
809 GNUNET_CONTAINER_multihashmap_destroy (lock_map);
818 * @param server the initialized server
819 * @param cfg configuration to use
822 lockmanager_run (void *cls, struct GNUNET_SERVER_Handle *server,
823 const struct GNUNET_CONFIGURATION_Handle *cfg)
825 static const struct GNUNET_SERVER_MessageHandler message_handlers[] = {
826 {&handle_acquire, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE, 0},
827 {&handle_release, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE, 0},
830 GNUNET_SERVER_add_handlers (server, message_handlers);
831 GNUNET_SERVER_disconnect_notify (server, &client_disconnect_cb, NULL);
832 lock_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
833 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
839 * The starting point of execution
842 main (int argc, char *const *argv)
845 GNUNET_SERVICE_run (argc, argv, "lockmanager",
846 GNUNET_SERVICE_OPTION_NONE, &lockmanager_run,
850 /* end of gnunet-service-lockmanager.c */