2 This file is part of GNUnet.
3 (C) 2012 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"
36 #define VERBOSE GNUNET_YES
38 #define LOG(kind,...) \
39 GNUNET_log (kind, __VA_ARGS__)
41 #define TIME_REL_MINS(min) \
42 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, min)
44 #define TIMEOUT TIME_REL_MINS(3)
48 * Doubly linked list of clients waiting for a lock
53 * The next client structure
55 struct WaitList *next;
58 * The prev client structure
60 struct WaitList *prev;
63 * Pointer to the client
65 struct GNUNET_SERVER_Client *client;
70 * A Lock element for a doubly linked list
75 * The next element pointer
77 struct LockList *next;
80 * Pointer to the previous element
82 struct LockList *prev;
85 * List head of clients waiting for this lock
87 struct WaitList *wait_list_head;
90 * List tail of clients waiting for this lock
92 struct WaitList *wait_list_tail;
95 * The client which is currently holding this lock
97 struct GNUNET_SERVER_Client *client;
100 * The name of the locking domain this lock belongs to
105 * The number of this lock
112 * Doubly linked list of clients having connections to us
117 * The next client structure
119 struct ClientList *next;
122 * The previous client structure
124 struct ClientList *prev;
127 * Pointer to the client
129 struct GNUNET_SERVER_Client *client;
134 * Head of the doubly linked list of the currently held locks
136 static struct LockList *ll_head;
139 * Tail of the doubly linked list of the currently held locks
141 static struct LockList *ll_tail;
144 * Head of the doubly linked list of clients currently connected
146 static struct ClientList *cl_head;
149 * Tail of the doubly linked list of clients currently connected
151 static struct ClientList *cl_tail;
155 * Function to search for a lock in lock_list matching the given domain_name and
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
165 ll_find_lock (const char *domain_name,
166 const uint32_t lock_num,
167 struct LockList **ret)
169 struct LockList *current_lock;
171 current_lock = ll_head;
173 while (NULL != current_lock)
175 if ( (0 == strcmp (domain_name, current_lock->domain_name))
176 && (lock_num == current_lock->lock_num))
183 current_lock = current_lock->next;
192 * Function to search for a lock in lock_list matching the given domain_name and
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
201 ll_find_lock_by_owner (const struct GNUNET_SERVER_Client *client,
202 struct LockList **ret)
204 struct LockList *current_lock;
206 current_lock = ll_head;
208 while (NULL != current_lock)
210 if (client == current_lock->client)
217 current_lock = current_lock->next;
226 * Function to append a lock to the global lock list
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
233 ll_add_lock (struct GNUNET_SERVER_Client *client,
234 const char *domain_name,
235 const uint32_t lock_num)
237 struct LockList *lock;
238 size_t domain_name_len;
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);
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;
251 GNUNET_CONTAINER_DLL_insert_tail (ll_head,
258 * Function to delete a lock from the lock list
260 * @param lock the lock to be deleted
263 ll_remove_lock (struct LockList *lock)
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,
272 GNUNET_free (lock->domain_name);
278 * Find a client in the waiting list of a lock
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
286 ll_wl_find_client (struct LockList *lock,
287 const struct GNUNET_SERVER_Client *client,
288 struct WaitList **ret)
290 struct WaitList *current_wl_entry;
292 current_wl_entry = lock->wait_list_head;
294 while (NULL != current_wl_entry)
296 if (client == current_wl_entry->client)
299 *ret = current_wl_entry;
302 current_wl_entry = current_wl_entry->next;
305 *ret = current_wl_entry;
311 * Add a client to the wait list of given lock
313 * @param lock the lock list entry of a lock
314 * @param client the client to queue for the lock's wait list
317 ll_wl_add_client (struct LockList *lock,
318 struct GNUNET_SERVER_Client *client)
320 struct WaitList *wl_entry;
322 LOG (GNUNET_ERROR_TYPE_DEBUG,
323 "Adding a client to lock's wait list\n"
324 "\t lock num: %u, domain: %s\n",
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,
337 * Remove a client from the wait list of the given lock
339 * @param lock the lock list entry of the lock
340 * @param wl_client the wait list entry to be removed
343 ll_wl_remove_client (struct LockList *lock,
344 struct WaitList *wl_client)
346 GNUNET_CONTAINER_DLL_remove (lock->wait_list_head,
347 lock->wait_list_tail,
350 GNUNET_free (wl_client);
355 * Search for a client in the client list
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
363 cl_find_client (const struct GNUNET_SERVER_Client *client,
364 struct ClientList **ret)
366 struct ClientList *current;
370 while (NULL != current)
372 if (client == current->client)
379 current = current->next;
389 * Append a client to the client list
391 * @param client the client to be appended to the list
394 cl_add_client (struct GNUNET_SERVER_Client *client)
396 struct ClientList *new_client;
398 LOG (GNUNET_ERROR_TYPE_DEBUG,
399 "Adding a client to the client list\n");
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,
411 * Delete the given client from the client list
413 * @param client the client list entry to delete
416 cl_remove_client (struct ClientList *client)
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,
424 GNUNET_free (client);
429 * Transmit notify for sending message to client
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
437 transmit_notify (void *cls, size_t size, void *buf)
439 struct GNUNET_LOCKMANAGER_Message *msg = cls;
442 if ((0 == size) || (NULL == buf))
444 /* FIXME: Timed out -- requeue? */
447 msg_size = ntohs (msg->header.size);
448 GNUNET_assert (size >= msg_size);
449 memcpy (buf, msg, msg_size);
451 LOG (GNUNET_ERROR_TYPE_DEBUG,
452 "Message of size %u sent\n", msg_size);
458 * Send SUCCESS message to the client
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
465 send_success_msg (struct GNUNET_SERVER_Client *client,
466 const char *domain_name,
469 struct GNUNET_LOCKMANAGER_Message *reply;
470 size_t domain_name_len;
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,
489 * Handler for GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE
492 * @param client the client sending this message
493 * @param message GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE message
496 handle_acquire (void *cls,
497 struct GNUNET_SERVER_Client *client,
498 const struct GNUNET_MessageHeader *message)
500 const struct GNUNET_LOCKMANAGER_Message *request;
501 const char *domain_name;
502 struct LockList *ll_entry;
506 LOG (GNUNET_ERROR_TYPE_DEBUG,
507 "Received an ACQUIRE message\n");
510 /* Check if the client is in client list */
511 if (GNUNET_NO == cl_find_client (client, NULL))
513 cl_add_client (client);
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,
522 {/* Add client to the lock's wait list */
523 ll_wl_add_client (ll_entry, client);
527 ll_add_lock (client, domain_name, lock_num);
528 send_success_msg (client, domain_name, lock_num);
531 GNUNET_SERVER_receive_done (client, GNUNET_OK);
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
540 * @param ll_entry the lock list entry corresponding to a lock
543 process_lock_release (struct LockList *ll_entry)
545 struct WaitList *wl_entry;
547 wl_entry = ll_entry->wait_list_head;
548 if (NULL == wl_entry)
550 ll_remove_lock (ll_entry);
554 while (NULL != wl_entry)
556 ll_wl_remove_client(ll_entry, wl_entry);
558 if (GNUNET_NO == cl_find_client (wl_entry->client, NULL))
560 LOG (GNUNET_ERROR_TYPE_DEBUG,
561 "Removing disconnected client from wait list\n");
562 wl_entry = ll_entry->wait_list_head;
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,
574 /* We ran out of clients in the wait list -- destroy lock */
575 ll_remove_lock (ll_entry);
581 * Handle for GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE
584 * @param client the client sending this message
585 * @param message the LOCKMANAGER_RELEASE message
588 handle_release (void *cls,
589 struct GNUNET_SERVER_Client *client,
590 const struct GNUNET_MessageHeader *message)
592 const struct GNUNET_LOCKMANAGER_Message *request;
593 const char *domain_name;
594 struct LockList *ll_entry;
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))
606 if (client != ll_entry->client)
611 process_lock_release (ll_entry);
615 LOG (GNUNET_ERROR_TYPE_DEBUG,
616 "\t give lock doesn't exist\n");
622 * Callback for client disconnect
625 * @param client the client which has disconnected
628 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
630 struct ClientList *cl_entry;
632 LOG (GNUNET_ERROR_TYPE_DEBUG,
633 "A client has been disconnected -- freeing its locks and resources\n");
635 if (GNUNET_YES == cl_find_client (client, &cl_entry))
637 struct LockList *ll_entry;
639 cl_remove_client (cl_entry);
640 while (GNUNET_YES == ll_find_lock_by_owner (client, &ll_entry))
642 process_lock_release (ll_entry);
645 else GNUNET_break (0);
653 * @param server the initialized server
654 * @param cfg configuration to use
657 lockmanager_run (void *cls,
658 struct GNUNET_SERVER_Handle * server,
659 const struct GNUNET_CONFIGURATION_Handle *cfg)
661 static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
663 {&handle_acquire, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE, 0},
664 {&handle_release, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE, 0},
668 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lockmanager\n");
669 GNUNET_SERVER_add_handlers (server,
671 GNUNET_SERVER_disconnect_notify (server,
672 &client_disconnect_cb,
679 * The starting point of execution
681 int main (int argc, char *const *argv)
685 GNUNET_log_setup ("lockmanager",
693 LOG (GNUNET_ERROR_TYPE_DEBUG, "main()\n");
696 GNUNET_SERVICE_run (argc,
699 GNUNET_SERVICE_OPTION_NONE,
702 LOG (GNUNET_ERROR_TYPE_DEBUG, "main() END\n");