-coverity
[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  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
275  *
276  * @param cls NULL
277  * @param client identification of the client
278  * @param message the actual message
279  */
280 static void 
281 handle_init (void *cls,
282              struct GNUNET_SERVER_Client *client,
283              const struct GNUNET_MessageHeader *message)
284 {
285   const struct GNUNET_TESTBED_InitMessage *msg;
286   struct GNUNET_TESTBED_Host *host;
287
288   if (NULL != master_context)
289   {
290     GNUNET_break (0);
291     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
292     return;
293   }
294   msg = (const struct GNUNET_TESTBED_InitMessage *) message;  
295   master_context = GNUNET_malloc (sizeof (struct Context));
296   master_context->client = client;
297   master_context->host_id = ntohl (msg->host_id);
298   host = GNUNET_TESTBED_host_create_with_id (master_context->host_id,
299                                              NULL, NULL, 0);
300   host_list_add (host);
301   master_context->event_mask = GNUNET_ntohll (msg->event_mask);
302   GNUNET_SERVER_client_keep (client);
303   LOG_DEBUG ("Created master context with host ID: %u\n",
304              master_context->host_id);
305   GNUNET_SERVER_receive_done (client, GNUNET_OK);
306 }
307
308
309 /**
310  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
311  *
312  * @param cls NULL
313  * @param client identification of the client
314  * @param message the actual message
315  */
316 static void 
317 handle_add_host (void *cls,
318                  struct GNUNET_SERVER_Client *client,
319                  const struct GNUNET_MessageHeader *message)
320 {
321   struct GNUNET_TESTBED_Host *host;
322   const struct GNUNET_TESTBED_AddHostMessage *msg;
323   struct GNUNET_TESTBED_HostConfirmedMessage *reply;
324   char *username;
325   char *hostname;
326   char *emsg;
327   uint32_t host_id;
328   uint16_t username_length;
329   uint16_t hostname_length;
330   uint16_t reply_size;
331   
332   msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
333   username_length = ntohs (msg->user_name_length);
334   username_length = (0 == username_length) ? 0 : username_length + 1;
335   username = (char *) &(msg[1]);
336   hostname = username + username_length;
337   if (ntohs (message->size) <=
338       (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
339   {
340     GNUNET_break (0);
341     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
342     return;
343   }
344   hostname_length = ntohs (message->size)
345     - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
346   if (strlen (hostname) != hostname_length)
347   {
348     GNUNET_break (0);
349     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
350     return;
351   }
352   host_id = ntohl (msg->host_id);
353   LOG_DEBUG ("Received ADDHOST message\n");
354   LOG_DEBUG ("-------host id: %u\n", host_id);
355   if (NULL != hostname) LOG_DEBUG ("-------hostname: %s\n", hostname);
356   if (NULL != username) LOG_DEBUG ("-------username: %s\n", username);
357   LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
358   host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
359                                              ntohs (msg->ssh_port));
360   GNUNET_SERVER_receive_done (client, GNUNET_OK);
361   reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage);
362   if (GNUNET_OK != host_list_add (host))
363   {    
364     /* We are unable to add a host */  
365     emsg = "A host exists with given host-id";
366     LOG_DEBUG ("%s: %u", emsg, host_id);
367     GNUNET_TESTBED_host_destroy (host);
368     reply_size += strlen (emsg) + 1;
369     reply = GNUNET_malloc (reply_size);
370     memcpy (&reply[1], emsg, strlen (emsg) + 1);
371   }
372   else
373     reply = GNUNET_malloc (reply_size);  
374   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM);
375   reply->header.size = htons (reply_size);
376   reply->host_id = htonl (host_id);  
377   queue_message (client, (struct GNUNET_MessageHeader *) reply);
378 }
379
380
381 /**
382  * Iterator over hash map entries.
383  *
384  * @param cls closure
385  * @param key current key code
386  * @param value value in the hash map
387  * @return GNUNET_YES if we should continue to
388  *         iterate,
389  *         GNUNET_NO if not.
390  */
391 int ss_exists_iterator (void *cls,
392                         const struct GNUNET_HashCode * key,
393                         void *value)
394 {
395   struct SharedService *queried_ss = cls;
396   struct SharedService *ss = value;
397
398   if (0 == strcmp (ss->name, queried_ss->name))
399     return GNUNET_NO;
400   else
401     return GNUNET_YES;
402 }
403
404 /**
405  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
406  *
407  * @param cls NULL
408  * @param client identification of the client
409  * @param message the actual message
410  */
411 static void 
412 handle_configure_shared_service (void *cls,
413                                  struct GNUNET_SERVER_Client *client,
414                                  const struct GNUNET_MessageHeader *message)
415 {
416   struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg;
417   struct SharedService *ss;
418   char *service_name;
419   struct GNUNET_HashCode hash;
420   uint16_t msg_size;
421   uint16_t service_name_size;
422     
423   msg = (struct GNUNET_TESTBED_ConfigureSharedServiceMessage *) message;
424   msg_size = ntohs (message->size);
425   if (msg_size <= sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage))
426   {
427     GNUNET_break (0);
428     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
429     return;
430   }
431   service_name_size = msg_size - 
432     sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage);
433   service_name = (char *) &msg[1];
434   if ('\0' != service_name[service_name_size - 1])
435   {
436     GNUNET_break (0);
437     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
438     return;
439   }
440   LOG_DEBUG ("Received service sharing request for %s, with %d peers\n",
441              service_name, ntohl (msg->num_peers));
442   if (ntohl (msg->host_id) != master_context->host_id)
443   {
444     route_message (ntohl (msg->host_id), message);
445     GNUNET_SERVER_receive_done (client, GNUNET_OK);
446     return;
447   }
448   GNUNET_SERVER_receive_done (client, GNUNET_OK);
449   ss = GNUNET_malloc (sizeof (struct SharedService));
450   ss->name = strdup (service_name);
451   ss->num_shared = ntohl (msg->num_peers);
452   GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash);
453   if (GNUNET_SYSERR == 
454       GNUNET_CONTAINER_multihashmap_get_multiple (ss_map, &hash,
455                                                   &ss_exists_iterator, ss))
456   {
457     LOG (GNUNET_ERROR_TYPE_WARNING,
458          "Service %s already configured as a shared service. "
459          "Ignoring service sharing request \n", ss->name);
460     GNUNET_free (ss->name);
461     GNUNET_free (ss);
462     return;
463   }
464   GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss,
465                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);  
466 }
467
468
469 /**
470  * Iterator over hash map entries.
471  *
472  * @param cls closure
473  * @param key current key code
474  * @param value value in the hash map
475  * @return GNUNET_YES if we should continue to
476  *         iterate,
477  *         GNUNET_NO if not.
478  */
479 static int 
480 ss_map_free_iterator (void *cls,
481                       const struct GNUNET_HashCode * key, void *value)
482 {
483   struct SharedService *ss = value;
484
485   GNUNET_assert (GNUNET_YES ==
486                  GNUNET_CONTAINER_multihashmap_remove (ss_map, key, value));
487   GNUNET_free (ss->name);
488   GNUNET_free (ss);
489   return GNUNET_YES;
490 }
491
492
493 /**
494  * Task to clean up and shutdown nicely
495  *
496  * @param cls NULL
497  * @param tc the TaskContext from scheduler
498  */
499 static void
500 shutdown_task (void *cls,
501                const struct GNUNET_SCHEDULER_TaskContext *tc)
502 {
503   uint32_t host_id;
504
505   shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
506   GNUNET_SCHEDULER_shutdown ();
507   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
508   (void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator,
509                                                 NULL);
510   GNUNET_CONTAINER_multihashmap_destroy (ss_map);
511   /* Clear host array */
512   if (NULL != fh)
513   {
514     GNUNET_DISK_file_close (fh);
515     fh = NULL;
516   }
517   for (host_id = 0; host_id < host_list_size; host_id++)
518     if (NULL != host_list[host_id])
519       GNUNET_TESTBED_host_destroy (host_list[host_id]);
520   GNUNET_free_non_null (host_list);
521   GNUNET_free_non_null (master_context);
522   master_context = NULL;
523 }
524
525
526 /**
527  * Callback for client disconnect
528  *
529  * @param cls NULL
530  * @param client the client which has disconnected
531  */
532 static void
533 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
534 {
535   if (NULL == master_context)
536     return;
537   if (client == master_context->client)
538   {
539     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
540     GNUNET_SERVER_client_drop (client);
541     /* should not be needed as we're terminated by failure to read
542        from stdin, but if stdin fails for some reason, this shouldn't 
543        hurt for now --- might need to revise this later if we ever
544        decide that master connections might be temporarily down 
545        for some reason */
546     GNUNET_SCHEDULER_shutdown ();
547   }
548 }
549
550
551 /**
552  * Testbed setup
553  *
554  * @param cls closure
555  * @param server the initialized server
556  * @param cfg configuration to use
557  */
558 static void 
559 testbed_run (void *cls,
560              struct GNUNET_SERVER_Handle *server,
561              const struct GNUNET_CONFIGURATION_Handle *cfg)
562 {
563   static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
564     {
565       {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
566        sizeof (struct GNUNET_TESTBED_InitMessage)},
567       {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
568       {&handle_configure_shared_service, NULL,
569        GNUNET_MESSAGE_TYPE_TESTBED_SERVICESHARE, 0},
570       {NULL}
571     };
572
573   GNUNET_SERVER_add_handlers (server,
574                               message_handlers);
575   GNUNET_SERVER_disconnect_notify (server,
576                                    &client_disconnect_cb,
577                                    NULL);
578   ss_map = GNUNET_CONTAINER_multihashmap_create (5);
579   fh = GNUNET_DISK_get_handle_from_native (stdin);
580   if (NULL == fh)
581     shutdown_task_id = 
582       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,                                  
583                                     &shutdown_task,
584                                     NULL);
585   else
586     shutdown_task_id = 
587       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
588                                       fh,
589                                       &shutdown_task,
590                                       NULL);
591 }
592
593
594 /**
595  * The starting point of execution
596  */
597 int main (int argc, char *const *argv)
598 {
599   return
600     (GNUNET_OK ==
601      GNUNET_SERVICE_run (argc,
602                          argv,
603                          "testbed",
604                          GNUNET_SERVICE_OPTION_NONE,
605                          &testbed_run,
606                          NULL)) ? 0 : 1;
607 }