-service sharing message handling
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed.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 testbed/gnunet-service-testbed.c
23  * @brief implementation of the TESTBED service
24  * @author Sree Harsha Totakura
25  */
26
27 #include "platform.h"
28 #include "gnunet_service_lib.h"
29 #include "gnunet_server_lib.h"
30
31 #include "testbed.h"
32 #include "gnunet_testbed_service.h"
33 #include "testbed_api_hosts.h"
34
35 /**
36  * Generic logging
37  */
38 #define LOG(kind,...)                           \
39   GNUNET_log (kind, __VA_ARGS__)
40
41 /**
42  * Debug logging
43  */
44 #define LOG_DEBUG(...)                          \
45   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
46
47 struct Context
48 {
49   /**
50    * The client handle associated with this context
51    */
52   struct GNUNET_SERVER_Client *client;
53   
54   /**
55    * Event mask of event to be responded in this context
56    */
57   uint64_t event_mask;
58
59   /**
60    * Our host id according to this context
61    */
62   uint32_t host_id;
63 };
64
65
66 /**
67  * The message queue for sending messages to clients
68  */
69 struct MessageQueue
70 {
71   /**
72    * The message to be sent
73    */
74   struct GNUNET_MessageHeader *msg;
75
76   /**
77    * The client to send the message to
78    */
79   struct GNUNET_SERVER_Client *client;
80   
81   /**
82    * next pointer for DLL
83    */
84   struct MessageQueue *next;
85   
86   /**
87    * prev pointer for DLL
88    */
89   struct MessageQueue *prev;
90 };
91
92
93 /**
94  * The structure for identifying a shared service
95  */
96 struct SharedService
97 {
98   /**
99    * The name of the shared service
100    */
101   char *name;
102
103   /**
104    * Number of shared peers per instance of the shared service
105    */
106   uint32_t num_shared;
107
108   /**
109    * Number of peers currently sharing the service
110    */
111   uint32_t num_sharing;
112 };
113
114
115 /**
116  * Wrapped stdin.
117  */
118 static struct GNUNET_DISK_FileHandle *fh;
119
120 /**
121  * The master context; generated with the first INIT message
122  */
123 static struct Context *master_context;
124
125 /**
126  * The shutdown task handle
127  */
128 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
129
130 /**
131  * Array of host list
132  */
133 static struct GNUNET_TESTBED_Host **host_list;
134
135 /**
136  * The size of the host list
137  */
138 static uint32_t host_list_size;
139
140 /**
141  * The message queue head
142  */
143 static struct MessageQueue *mq_head;
144
145 /**
146  * The message queue tail
147  */
148 static struct MessageQueue *mq_tail;
149
150 /**
151  * Current Transmit Handle; NULL if no notify transmit exists currently
152  */
153 struct GNUNET_SERVER_TransmitHandle *transmit_handle;
154
155 /**
156  * The hashmap of shared services
157  */
158 struct GNUNET_CONTAINER_MultiHashMap *ss_map;
159
160
161 /**
162  * Function called to notify a client about the connection begin ready to queue
163  * more data.  "buf" will be NULL and "size" zero if the connection was closed
164  * for writing in the meantime.
165  *
166  * @param cls NULL
167  * @param size number of bytes available in buf
168  * @param buf where the callee should write the message
169  * @return number of bytes written to buf
170  */
171 static size_t
172 transmit_ready_notify (void *cls, size_t size, void *buf)
173 {
174   struct MessageQueue *mq_entry;
175
176   transmit_handle = NULL;
177   mq_entry = mq_head;
178   GNUNET_assert (NULL != mq_entry);
179   if (0 == size)
180     return 0;
181   GNUNET_assert (ntohs (mq_entry->msg->size) <= size);
182   size = ntohs (mq_entry->msg->size);
183   memcpy (buf, mq_entry->msg, size);
184   GNUNET_free (mq_entry->msg);
185   GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
186   GNUNET_free (mq_entry);
187   mq_entry = mq_head;
188   if (NULL != mq_entry)
189     transmit_handle = 
190       GNUNET_SERVER_notify_transmit_ready (mq_entry->client,
191                                            ntohs (mq_entry->msg->size),
192                                            GNUNET_TIME_UNIT_FOREVER_REL,
193                                            &transmit_ready_notify, NULL);
194   return size;
195 }
196
197
198 /**
199  * Queues a message in send queue for sending to the service
200  *
201  * @param client the client to whom the queued message has to be sent
202  * @param msg the message to queue
203  */
204 static void
205 queue_message (struct GNUNET_SERVER_Client *client,
206                struct GNUNET_MessageHeader *msg)
207 {
208   struct MessageQueue *mq_entry;
209   uint16_t type;
210   uint16_t size;
211
212   type = ntohs (msg->type);
213   size = ntohs (msg->size);
214   GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
215                  (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));                 
216   mq_entry = GNUNET_malloc (sizeof (struct MessageQueue));
217   mq_entry->msg = msg;
218   mq_entry->client = client;
219   LOG_DEBUG ( "Queueing message of type %u, size %u for sending\n", type,
220               ntohs (msg->size));
221   GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry);
222   if (NULL == transmit_handle)
223     transmit_handle = 
224       GNUNET_SERVER_notify_transmit_ready (client, size,
225                                            GNUNET_TIME_UNIT_FOREVER_REL,
226                                            &transmit_ready_notify, NULL);
227 }
228
229
230 /**
231  * Function to add a host to the current list of known hosts
232  *
233  * @param host the host to add 
234  * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id
235  *           already in use
236  */
237 static int
238 host_list_add (struct GNUNET_TESTBED_Host *host)
239 {
240   uint32_t host_id;
241   
242   host_id = GNUNET_TESTBED_host_get_id_ (host);
243   if (host_list_size <= host_id)
244   {
245     host_list = GNUNET_realloc (host_list, 
246                                 sizeof (struct GNUNET_TESTBED_Host *)
247                                 * (host_id + 10));
248     host_list_size += (host_id + 10);
249   }
250   if (NULL != host_list[host_id])
251   {
252     LOG_DEBUG ("A host with id: %u already exists\n", host_id);
253     return GNUNET_SYSERR;
254   }
255   host_list[host_id] = host;
256   return GNUNET_OK;
257 }
258
259
260 /**
261  * Routes message to a host given its host_id
262  *
263  * @param host_id the id of the destination host
264  * @param msg the message to be routed
265  */
266 static void
267 route_message (uint32_t host_id, const struct GNUNET_MessageHeader *msg)
268 {
269   GNUNET_break (0);
270 }
271
272
273 /**
274  * 
275  */
276
277
278 /**
279  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
280  *
281  * @param cls NULL
282  * @param client identification of the client
283  * @param message the actual message
284  */
285 static void 
286 handle_init (void *cls,
287              struct GNUNET_SERVER_Client *client,
288              const struct GNUNET_MessageHeader *message)
289 {
290   const struct GNUNET_TESTBED_InitMessage *msg;
291   struct GNUNET_TESTBED_Host *host;
292
293   if (NULL != master_context)
294   {
295     GNUNET_break (0);
296     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
297     return;
298   }
299   msg = (const struct GNUNET_TESTBED_InitMessage *) message;  
300   master_context = GNUNET_malloc (sizeof (struct Context));
301   master_context->client = client;
302   master_context->host_id = ntohl (msg->host_id);
303   host = GNUNET_TESTBED_host_create_with_id (master_context->host_id,
304                                              NULL, NULL, 0);
305   host_list_add (host);
306   master_context->event_mask = GNUNET_ntohll (msg->event_mask);
307   GNUNET_SERVER_client_keep (client);
308   LOG_DEBUG ("Created master context with host ID: %u\n",
309              master_context->host_id);
310   GNUNET_SERVER_receive_done (client, GNUNET_OK);
311 }
312
313
314 /**
315  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
316  *
317  * @param cls NULL
318  * @param client identification of the client
319  * @param message the actual message
320  */
321 static void 
322 handle_add_host (void *cls,
323                  struct GNUNET_SERVER_Client *client,
324                  const struct GNUNET_MessageHeader *message)
325 {
326   struct GNUNET_TESTBED_Host *host;
327   const struct GNUNET_TESTBED_AddHostMessage *msg;
328   struct GNUNET_TESTBED_HostConfirmedMessage *reply;
329   char *username;
330   char *hostname;
331   char *emsg;
332   uint32_t host_id;
333   uint16_t username_length;
334   uint16_t hostname_length;
335   uint16_t reply_size;
336   
337   msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
338   username_length = ntohs (msg->user_name_length);
339   username_length = (0 == username_length) ? 0 : username_length + 1;
340   username = (char *) &(msg[1]);
341   hostname = username + username_length;
342   if (ntohs (message->size) <=
343       (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
344   {
345     GNUNET_break (0);
346     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
347     return;
348   }
349   hostname_length = ntohs (message->size)
350     - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
351   if (strlen (hostname) != hostname_length)
352   {
353     GNUNET_break (0);
354     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
355     return;
356   }
357   host_id = ntohl (msg->host_id);
358   LOG_DEBUG ("Received ADDHOST message\n");
359   LOG_DEBUG ("-------host id: %u\n", host_id);
360   if (NULL != hostname) LOG_DEBUG ("-------hostname: %s\n", hostname);
361   if (NULL != username) LOG_DEBUG ("-------username: %s\n", username);
362   LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
363   host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
364                                              ntohs (msg->ssh_port));
365   GNUNET_SERVER_receive_done (client, GNUNET_OK);
366   if (GNUNET_OK == host_list_add (host))
367     return;
368   /* We are unable to add a host */
369   emsg = "A host exists with given host-id";
370   GNUNET_TESTBED_host_destroy (host);
371   reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)
372     + strlen (emsg) + 1;
373   reply = GNUNET_malloc (reply_size);
374   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTSUCCESS);
375   reply->header.size = htons (reply_size);
376   reply->host_id = htonl (host_id);
377   memcpy (&reply[1], emsg, strlen (emsg) + 1);
378   queue_message (client, (struct GNUNET_MessageHeader *) reply);
379 }
380
381
382 /**
383  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
384  *
385  * @param cls NULL
386  * @param client identification of the client
387  * @param message the actual message
388  */
389 static void 
390 handle_configure_shared_service (void *cls,
391                                  struct GNUNET_SERVER_Client *client,
392                                  const struct GNUNET_MessageHeader *message)
393 {
394   struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg;
395   struct SharedService *ss;
396   char *service_name;
397   struct GNUNET_HashCode hash;
398   uint16_t msg_size;
399   uint16_t service_name_size;
400     
401   msg = (struct GNUNET_TESTBED_ConfigureSharedServiceMessage *) message;
402   msg_size = ntohs (message->size);
403   if (msg_size <= sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage))
404   {
405     GNUNET_break (0);
406     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
407     return;
408   }
409   service_name_size = msg_size - 
410     sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage);
411   service_name = (char *) &msg[1];
412   if ('\0' != service_name[service_name_size - 1])
413   {
414     GNUNET_break (0);
415     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
416     return;
417   }
418   LOG_DEBUG ("Received service sharing request for %s, with %d peers\n",
419              service_name, ntohl (msg->num_peers));
420   if (ntohl (msg->host_id) != master_context->host_id)
421   {
422     route_message (ntohl (msg->host_id), message);
423     GNUNET_SERVER_receive_done (client, GNUNET_OK);
424     return;
425   }
426   ss = GNUNET_malloc (sizeof (struct SharedService));
427   ss->name = strdup (service_name);
428   ss->num_shared = ntohl (msg->num_peers);
429   GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash);
430   GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss,
431                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
432   GNUNET_SERVER_receive_done (client, GNUNET_OK);
433 }
434
435
436 /**
437  * Iterator over hash map entries.
438  *
439  * @param cls closure
440  * @param key current key code
441  * @param value value in the hash map
442  * @return GNUNET_YES if we should continue to
443  *         iterate,
444  *         GNUNET_NO if not.
445  */
446 static int 
447 ss_map_free_iterator (void *cls,
448                       const struct GNUNET_HashCode * key, void *value)
449 {
450   struct SharedService *ss = value;
451
452   GNUNET_CONTAINER_multihashmap_remove (ss_map, key, value);
453   GNUNET_free (ss->name);
454   GNUNET_free (ss);
455   return GNUNET_YES;
456 }
457
458
459 /**
460  * Task to clean up and shutdown nicely
461  *
462  * @param cls NULL
463  * @param tc the TaskContext from scheduler
464  */
465 static void
466 shutdown_task (void *cls,
467                const struct GNUNET_SCHEDULER_TaskContext *tc)
468 {
469   uint32_t host_id;
470
471   shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
472   GNUNET_SCHEDULER_shutdown ();
473   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
474   (void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator,
475                                                 NULL);
476   GNUNET_CONTAINER_multihashmap_destroy (ss_map);
477   if (NULL != fh)
478   {
479     GNUNET_DISK_file_close (fh);
480     fh = NULL;
481   }
482   for (host_id = 0; host_id < host_list_size; host_id++)
483     if (NULL != host_list[host_id])
484       GNUNET_TESTBED_host_destroy (host_list[host_id]);
485   GNUNET_free_non_null (master_context);
486   master_context = NULL;
487 }
488
489
490 /**
491  * Callback for client disconnect
492  *
493  * @param cls NULL
494  * @param client the client which has disconnected
495  */
496 static void
497 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
498 {
499   if (NULL == master_context)
500     return;
501   if (client == master_context->client)
502   {
503     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
504     GNUNET_SERVER_client_drop (client);
505     /* should not be needed as we're terminated by failure to read
506        from stdin, but if stdin fails for some reason, this shouldn't 
507        hurt for now --- might need to revise this later if we ever
508        decide that master connections might be temporarily down 
509        for some reason */
510     GNUNET_SCHEDULER_shutdown ();
511   }
512 }
513
514
515 /**
516  * Testbed setup
517  *
518  * @param cls closure
519  * @param server the initialized server
520  * @param cfg configuration to use
521  */
522 static void 
523 testbed_run (void *cls,
524              struct GNUNET_SERVER_Handle *server,
525              const struct GNUNET_CONFIGURATION_Handle *cfg)
526 {
527   static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
528     {
529       {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
530        sizeof (struct GNUNET_TESTBED_InitMessage)},
531       {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
532       {&handle_configure_shared_service, NULL,
533        GNUNET_MESSAGE_TYPE_TESTBED_SERVICESHARE, 0},
534       {NULL}
535     };
536
537   GNUNET_SERVER_add_handlers (server,
538                               message_handlers);
539   GNUNET_SERVER_disconnect_notify (server,
540                                    &client_disconnect_cb,
541                                    NULL);
542   ss_map = GNUNET_CONTAINER_multihashmap_create (5);
543   fh = GNUNET_DISK_get_handle_from_native (stdin);
544   if (NULL == fh)
545     shutdown_task_id = 
546       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,                                  
547                                     &shutdown_task,
548                                     NULL);
549   else
550     shutdown_task_id = 
551       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
552                                       fh,
553                                       &shutdown_task,
554                                       NULL);
555 }
556
557
558 /**
559  * The starting point of execution
560  */
561 int main (int argc, char *const *argv)
562 {
563   return
564     (GNUNET_OK ==
565      GNUNET_SERVICE_run (argc,
566                          argv,
567                          "testbed",
568                          GNUNET_SERVICE_OPTION_NONE,
569                          &testbed_run,
570                          NULL)) ? 0 : 1;
571 }