-testbed hackery
[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
34 #define LOG(kind,...)                           \
35   GNUNET_log (kind, __VA_ARGS__)
36
37
38 struct Context
39 {
40   /**
41    * The client handle associated with this context
42    */
43   struct GNUNET_SERVER_Client *client;
44   
45   /**
46    * Event mask of event to be responded in this context
47    */
48   uint64_t event_mask;
49
50   /**
51    * Our host id according to this context
52    */
53   uint32_t host_id;
54 };
55
56
57 /**
58  * Wrapped stdin.
59  */
60 static struct GNUNET_DISK_FileHandle *fh;
61
62 /**
63  * The master context; generated with the first INIT message
64  */
65 static struct Context *master_context;
66
67 /**
68  * The shutdown task handle
69  */
70 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
71
72
73 /**
74  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
75  *
76  * @param cls NULL
77  * @param client identification of the client
78  * @param message the actual message
79  */
80 static void 
81 handle_init (void *cls,
82              struct GNUNET_SERVER_Client *client,
83              const struct GNUNET_MessageHeader *message)
84 {
85   const struct GNUNET_TESTBED_InitMessage *msg;
86
87   if (NULL != master_context)
88   {
89     GNUNET_break (0);
90     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
91     return;
92   }
93   msg = (const struct GNUNET_TESTBED_InitMessage *) message;  
94   master_context = GNUNET_malloc (sizeof (struct Context));
95   master_context->client = client;
96   master_context->host_id = ntohl (msg->host_id);
97   master_context->event_mask = GNUNET_ntohll (msg->event_mask);
98   GNUNET_SERVER_client_keep (client);
99   LOG (GNUNET_ERROR_TYPE_DEBUG,
100        "Created master context with host ID: %u\n", master_context->host_id);
101   GNUNET_SERVER_receive_done (client, GNUNET_OK);
102 }
103
104
105 /**
106  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
107  *
108  * @param cls NULL
109  * @param client identification of the client
110  * @param message the actual message
111  */
112 static void 
113 handle_addhost (void *cls,
114                 struct GNUNET_SERVER_Client *client,
115                 const struct GNUNET_MessageHeader *message)
116 {
117   struct GNUNET_TESTBED_Host *host;
118   const struct GNUNET_TESTBED_AddHostMessage *msg;
119   char *username;
120   char *hostname;
121   uint16_t username_length;
122   uint16_t hostname_length;
123   
124   msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
125   username_length = ntohs (msg->user_name_length);
126   username_length = (0 == username_length) ? 0 : username_length + 1;
127   username = (char *) &(msg[1]);
128   hostname = username + username_length;
129   if (ntohs (message->size) <=
130       (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
131   {
132     GNUNET_break (0);
133     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
134     return;
135   }
136   hostname_length = ntohs (message->size)
137     - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
138   if (strlen (hostname) != hostname_length)
139   {
140     GNUNET_break (0);
141     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
142     return;
143   }
144   host = GNUNET_TESTBED_host_create (hostname, username, ntohs
145                                      (msg->ssh_port));
146   /* Store host in a hashmap? But the host_id will be different */
147   /* hashmap? maybe array? 4-8 bytes/host and O(1) lookup vs.
148      > 80 bytes for hash map with slightly worse lookup; only
149      if we really get a tiny fraction of the hosts, the hash
150      map would result in any savings... (GNUNET_array_grow) */
151 }
152
153
154 /**
155  * Task to clean up and shutdown nicely
156  *
157  * @param cls NULL
158  * @param tc the TaskContext from scheduler
159  */
160 static void
161 shutdown_task (void *cls,
162                const struct GNUNET_SCHEDULER_TaskContext *tc)
163 {
164   shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
165   GNUNET_SCHEDULER_shutdown ();
166   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
167   if (NULL != fh)
168   {
169     GNUNET_DISK_file_close (fh);
170     fh = NULL;
171   }
172   GNUNET_free_non_null (master_context);
173   master_context = NULL;
174 }
175
176
177 /**
178  * Callback for client disconnect
179  *
180  * @param cls NULL
181  * @param client the client which has disconnected
182  */
183 static void
184 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
185 {
186   if (NULL == master_context)
187     return;
188   if (client == master_context->client)
189   {
190     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
191     GNUNET_SERVER_client_drop (client);
192     /* should not be needed as we're terminated by failure to read
193        from stdin, but if stdin fails for some reason, this shouldn't 
194        hurt for now --- might need to revise this later if we ever
195        decide that master connections might be temporarily down 
196        for some reason */
197     GNUNET_SCHEDULER_shutdown ();
198   }
199 }
200
201
202 /**
203  * Testbed setup
204  *
205  * @param cls closure
206  * @param server the initialized server
207  * @param cfg configuration to use
208  */
209 static void 
210 testbed_run (void *cls,
211              struct GNUNET_SERVER_Handle *server,
212              const struct GNUNET_CONFIGURATION_Handle *cfg)
213 {
214   static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
215     {
216       {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
217        sizeof (struct GNUNET_TESTBED_InitMessage)},
218       {&handle_addhost, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
219       {NULL}
220     };
221
222   GNUNET_SERVER_add_handlers (server,
223                               message_handlers);
224   GNUNET_SERVER_disconnect_notify (server,
225                                    &client_disconnect_cb,
226                                    NULL);  
227   fh = GNUNET_DISK_get_handle_from_native (stdin);
228   if (NULL == fh)
229     shutdown_task_id = 
230       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,                                  
231                                     &shutdown_task,
232                                     NULL);
233   else
234     shutdown_task_id = 
235       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
236                                       fh,
237                                       &shutdown_task,
238                                       NULL);
239 }
240
241
242 /**
243  * The starting point of execution
244  */
245 int main (int argc, char *const *argv)
246 {
247   return
248     (GNUNET_OK ==
249      GNUNET_SERVICE_run (argc,
250                          argv,
251                          "testbed",
252                          GNUNET_SERVICE_OPTION_NONE,
253                          &testbed_run,
254                          NULL)) ? 0 : 1;
255 }