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"
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 GNUNET_SERVER_Client *client;
75 * A Lock element for a doubly linked list
80 * The next element pointer
82 struct LockList *next;
85 * Pointer to the previous element
87 struct LockList *prev;
90 * List head of clients waiting for this lock
92 struct WaitList *wait_list_head;
95 * List tail of clients waiting for this lock
97 struct WaitList *wait_list_tail;
100 * The client which is currently holding this lock
102 struct GNUNET_SERVER_Client *client;
105 * The name of the locking domain this lock belongs to
110 * The number of this lock
117 * Doubly linked list of clients having connections to us
122 * The next client structure
124 struct ClientList *next;
127 * The previous client structure
129 struct ClientList *prev;
132 * Pointer to the client
134 struct GNUNET_SERVER_Client *client;
139 * Head of the doubly linked list of the currently held locks
141 static struct LockList *ll_head;
144 * Tail of the doubly linked list of the currently held locks
146 static struct LockList *ll_tail;
149 * Head of the doubly linked list of clients currently connected
151 static struct ClientList *cl_head;
154 * Tail of the doubly linked list of clients currently connected
156 static struct ClientList *cl_tail;
160 * Function to search for a lock in lock_list matching the given domain_name and
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
169 static struct LockList *
170 ll_find_lock (const char *domain_name,
171 const uint32_t lock_num)
174 struct LockList *current_lock;
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)) )
185 * Function to search for a lock in lock_list matching the given domain_name and
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
193 static struct LockList *
194 ll_find_lock_by_owner (const struct GNUNET_SERVER_Client *client)
196 struct LockList *current_lock;
198 for (current_lock = ll_head; NULL != current_lock; current_lock = current_lock->next)
199 if (client == current_lock->client)
206 * Function to append a lock to the global lock list
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
213 ll_add_lock (struct GNUNET_SERVER_Client *client,
214 const char *domain_name,
215 const uint32_t lock_num)
217 struct LockList *lock;
218 size_t domain_name_len;
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);
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;
231 GNUNET_CONTAINER_DLL_insert_tail (ll_head,
238 * Function to delete a lock from the lock list
240 * @param lock the lock to be deleted
243 ll_remove_lock (struct LockList *lock)
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,
252 GNUNET_free (lock->domain_name);
258 * Find a client in the waiting list of a lock
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
266 ll_wl_find_client (struct LockList *lock,
267 const struct GNUNET_SERVER_Client *client,
268 struct WaitList **ret)
270 struct WaitList *current_wl_entry;
272 current_wl_entry = lock->wait_list_head;
274 while (NULL != current_wl_entry)
276 if (client == current_wl_entry->client)
279 *ret = current_wl_entry;
282 current_wl_entry = current_wl_entry->next;
285 *ret = current_wl_entry;
291 * Add a client to the wait list of given lock
293 * @param lock the lock list entry of a lock
294 * @param client the client to queue for the lock's wait list
297 ll_wl_add_client (struct LockList *lock,
298 struct GNUNET_SERVER_Client *client)
300 struct WaitList *wl_entry;
302 LOG (GNUNET_ERROR_TYPE_DEBUG,
303 "Adding a client to lock's wait list\n"
304 "\t lock num: %u, domain: %s\n",
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,
317 * Remove a client from the wait list of the given lock
319 * @param lock the lock list entry of the lock
320 * @param wl_client the wait list entry to be removed
323 ll_wl_remove_client (struct LockList *lock,
324 struct WaitList *wl_client)
326 GNUNET_CONTAINER_DLL_remove (lock->wait_list_head,
327 lock->wait_list_tail,
329 GNUNET_free (wl_client);
334 * Search for a client in the client list
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
341 static struct ClientList *
342 cl_find_client (const struct GNUNET_SERVER_Client *client)
344 struct ClientList *current;
346 for (current = cl_head; NULL != current; current = current->next)
347 if (client == current->client)
354 * Append a client to the client list
356 * @param client the client to be appended to the list
359 cl_add_client (struct GNUNET_SERVER_Client *client)
361 struct ClientList *new_client;
363 LOG (GNUNET_ERROR_TYPE_DEBUG,
364 "Adding a client to the client list\n");
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,
376 * Delete the given client from the client list
378 * @param client the client list entry to delete
381 cl_remove_client (struct ClientList *client)
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,
389 GNUNET_free (client);
394 * Transmit notify for sending message to client
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
402 transmit_notify (void *cls, size_t size, void *buf)
404 struct GNUNET_LOCKMANAGER_Message *msg = cls;
407 if ((0 == size) || (NULL == buf))
409 /* FIXME: Timed out -- requeue? */
412 msg_size = ntohs (msg->header.size);
413 GNUNET_assert (size >= msg_size);
414 memcpy (buf, msg, msg_size);
416 LOG (GNUNET_ERROR_TYPE_DEBUG,
417 "Message of size %u sent\n", msg_size);
423 * Send SUCCESS message to the client
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
430 send_success_msg (struct GNUNET_SERVER_Client *client,
431 const char *domain_name,
434 struct GNUNET_LOCKMANAGER_Message *reply;
435 size_t domain_name_len;
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,
454 * Handler for GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE
457 * @param client the client sending this message
458 * @param message GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE message
461 handle_acquire (void *cls,
462 struct GNUNET_SERVER_Client *client,
463 const struct GNUNET_MessageHeader *message)
465 const struct GNUNET_LOCKMANAGER_Message *request;
466 const char *domain_name;
467 struct LockList *ll_entry;
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);
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,
481 {/* Add client to the lock's wait list */
482 ll_wl_add_client (ll_entry, client);
486 ll_add_lock (client, domain_name, lock_num);
487 send_success_msg (client, domain_name, lock_num);
490 GNUNET_SERVER_receive_done (client, GNUNET_OK);
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
499 * @param ll_entry the lock list entry corresponding to a lock
502 process_lock_release (struct LockList *ll_entry)
504 struct WaitList *wl_entry;
506 wl_entry = ll_entry->wait_list_head;
507 if (NULL == wl_entry)
509 ll_remove_lock (ll_entry);
513 while (NULL != wl_entry)
515 ll_wl_remove_client(ll_entry, wl_entry);
517 if (NULL == cl_find_client (wl_entry->client))
519 LOG (GNUNET_ERROR_TYPE_DEBUG,
520 "Removing disconnected client from wait list\n");
521 wl_entry = ll_entry->wait_list_head;
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,
533 /* We ran out of clients in the wait list -- destroy lock */
534 ll_remove_lock (ll_entry);
540 * Handle for GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE
543 * @param client the client sending this message
544 * @param message the LOCKMANAGER_RELEASE message
547 handle_release (void *cls,
548 struct GNUNET_SERVER_Client *client,
549 const struct GNUNET_MessageHeader *message)
551 const struct GNUNET_LOCKMANAGER_Message *request;
552 const char *domain_name;
553 struct LockList *ll_entry;
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)))
565 if (client != ll_entry->client)
570 process_lock_release (ll_entry);
574 LOG (GNUNET_ERROR_TYPE_WARNING,
575 _("Client tried to release lock that it doesn't exist\n"));
581 * Callback for client disconnect
584 * @param client the client which has disconnected
587 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
589 struct ClientList *cl_entry;
590 struct LockList *ll_entry;
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)
597 cl_remove_client (cl_entry);
598 while (NULL != (ll_entry = ll_find_lock_by_owner (client)))
599 process_lock_release (ll_entry);
607 * @param server the initialized server
608 * @param cfg configuration to use
611 lockmanager_run (void *cls,
612 struct GNUNET_SERVER_Handle * server,
613 const struct GNUNET_CONFIGURATION_Handle *cfg)
615 static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
617 {&handle_acquire, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_ACQUIRE, 0},
618 {&handle_release, NULL, GNUNET_MESSAGE_TYPE_LOCKMANAGER_RELEASE, 0},
621 GNUNET_SERVER_add_handlers (server,
623 GNUNET_SERVER_disconnect_notify (server,
624 &client_disconnect_cb,
630 * The starting point of execution
632 int main (int argc, char *const *argv)
636 GNUNET_SERVICE_run (argc,
639 GNUNET_SERVICE_OPTION_NONE,