diff options
author | Sree Harsha Totakura <totakura@in.tum.de> | 2012-08-31 10:57:19 +0000 |
---|---|---|
committer | Sree Harsha Totakura <totakura@in.tum.de> | 2012-08-31 10:57:19 +0000 |
commit | c0630a93cdad9646c9e96ffd23f441b242d82464 (patch) | |
tree | e77e8d89cfd7caabdfbaa769f10483254a04527b /src/testbed/gnunet-helper-testbed.c | |
parent | 7efd3e4aa1f19f8af7f919991dc271dca1fa28be (diff) | |
download | gnunet-c0630a93cdad9646c9e96ffd23f441b242d82464.tar.gz gnunet-c0630a93cdad9646c9e96ffd23f441b242d82464.zip |
renamed gnunet-testbed-helper to gnunet-helper-testbed
Diffstat (limited to 'src/testbed/gnunet-helper-testbed.c')
-rw-r--r-- | src/testbed/gnunet-helper-testbed.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/src/testbed/gnunet-helper-testbed.c b/src/testbed/gnunet-helper-testbed.c new file mode 100644 index 000000000..bc7f223af --- /dev/null +++ b/src/testbed/gnunet-helper-testbed.c | |||
@@ -0,0 +1,448 @@ | |||
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 3, 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-helper-testbed.c | ||
23 | * @brief Helper binary that is started from a remote controller to start | ||
24 | * gnunet-service-testbed. This binary also receives configuration | ||
25 | * from the remove controller which is put in a temporary location | ||
26 | * with ports and paths fixed so that gnunet-service-testbed runs | ||
27 | * without any hurdles. This binary also kills the testbed service | ||
28 | * should the connection from the remote controller is dropped | ||
29 | * @author Sree Harsha Totakura <sreeharsha@totakura.in> | ||
30 | */ | ||
31 | |||
32 | |||
33 | #include "platform.h" | ||
34 | #include "gnunet_util_lib.h" | ||
35 | #include "gnunet_testing_lib-new.h" | ||
36 | #include "gnunet_testbed_service.h" | ||
37 | #include "testbed_helper.h" | ||
38 | #include "testbed_api.h" | ||
39 | #include <zlib.h> | ||
40 | |||
41 | /** | ||
42 | * Generic logging shortcut | ||
43 | */ | ||
44 | #define LOG(kind, ...) \ | ||
45 | GNUNET_log (kind, __VA_ARGS__) | ||
46 | |||
47 | /** | ||
48 | * Debug logging shorthand | ||
49 | */ | ||
50 | #define LOG_DEBUG(...) \ | ||
51 | LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) | ||
52 | |||
53 | |||
54 | /** | ||
55 | * Context for a single write on a chunk of memory | ||
56 | */ | ||
57 | struct WriteContext | ||
58 | { | ||
59 | /** | ||
60 | * The data to write | ||
61 | */ | ||
62 | void *data; | ||
63 | |||
64 | /** | ||
65 | * The length of the data | ||
66 | */ | ||
67 | size_t length; | ||
68 | |||
69 | /** | ||
70 | * The current position from where the write operation should begin | ||
71 | */ | ||
72 | size_t pos; | ||
73 | }; | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Handle to the testing system | ||
78 | */ | ||
79 | static struct GNUNET_TESTING_System *test_system; | ||
80 | |||
81 | /** | ||
82 | * Our message stream tokenizer | ||
83 | */ | ||
84 | struct GNUNET_SERVER_MessageStreamTokenizer *tokenizer; | ||
85 | |||
86 | /** | ||
87 | * Disk handle from stdin | ||
88 | */ | ||
89 | static struct GNUNET_DISK_FileHandle *stdin_fd; | ||
90 | |||
91 | /** | ||
92 | * Disk handle for stdout | ||
93 | */ | ||
94 | static struct GNUNET_DISK_FileHandle *stdout_fd; | ||
95 | |||
96 | /** | ||
97 | * The process handle to the testbed service | ||
98 | */ | ||
99 | static struct GNUNET_OS_Process *testbed; | ||
100 | |||
101 | /** | ||
102 | * Task identifier for the read task | ||
103 | */ | ||
104 | static GNUNET_SCHEDULER_TaskIdentifier read_task_id; | ||
105 | |||
106 | /** | ||
107 | * Task identifier for the write task | ||
108 | */ | ||
109 | static GNUNET_SCHEDULER_TaskIdentifier write_task_id; | ||
110 | |||
111 | /** | ||
112 | * Are we done reading messages from stdin? | ||
113 | */ | ||
114 | static int done_reading; | ||
115 | |||
116 | /** | ||
117 | * Result to return in case we fail | ||
118 | */ | ||
119 | static int status; | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Are we shutting down | ||
124 | */ | ||
125 | static int in_shutdown; | ||
126 | |||
127 | |||
128 | /** | ||
129 | * Task to shutting down nicely | ||
130 | * | ||
131 | * @param cls NULL | ||
132 | * @param tc the task context | ||
133 | */ | ||
134 | static void | ||
135 | shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
136 | { | ||
137 | LOG_DEBUG ("Shutting down\n"); | ||
138 | in_shutdown = GNUNET_YES; | ||
139 | if (GNUNET_SCHEDULER_NO_TASK != read_task_id) | ||
140 | { | ||
141 | GNUNET_SCHEDULER_cancel (read_task_id); | ||
142 | read_task_id = GNUNET_SCHEDULER_NO_TASK; | ||
143 | } | ||
144 | if (GNUNET_SCHEDULER_NO_TASK != write_task_id) | ||
145 | { | ||
146 | GNUNET_SCHEDULER_cancel (write_task_id); | ||
147 | write_task_id = GNUNET_SCHEDULER_NO_TASK; | ||
148 | } | ||
149 | if (NULL != stdin_fd) | ||
150 | (void) GNUNET_DISK_file_close (stdin_fd); | ||
151 | if (NULL != stdout_fd) | ||
152 | (void) GNUNET_DISK_file_close (stdout_fd); | ||
153 | GNUNET_SERVER_mst_destroy (tokenizer); | ||
154 | tokenizer = NULL; | ||
155 | if (NULL != testbed) | ||
156 | { | ||
157 | LOG_DEBUG ("Killing testbed\n"); | ||
158 | GNUNET_break (0 == GNUNET_OS_process_kill (testbed, SIGTERM)); | ||
159 | GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (testbed)); | ||
160 | GNUNET_OS_process_destroy (testbed); | ||
161 | testbed = NULL; | ||
162 | } | ||
163 | if (NULL != test_system) | ||
164 | { | ||
165 | GNUNET_TESTING_system_destroy (test_system, GNUNET_YES); | ||
166 | test_system = NULL; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | |||
171 | /** | ||
172 | * Task to write to the standard out | ||
173 | * | ||
174 | * @param cls the WriteContext | ||
175 | * @param tc the TaskContext | ||
176 | */ | ||
177 | static void | ||
178 | write_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
179 | { | ||
180 | struct WriteContext *wc = cls; | ||
181 | ssize_t bytes_wrote; | ||
182 | |||
183 | GNUNET_assert (NULL != wc); | ||
184 | write_task_id = GNUNET_SCHEDULER_NO_TASK; | ||
185 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
186 | { | ||
187 | GNUNET_free (wc->data); | ||
188 | GNUNET_free (wc); | ||
189 | return; | ||
190 | } | ||
191 | bytes_wrote = | ||
192 | GNUNET_DISK_file_write (stdout_fd, wc->data + wc->pos, | ||
193 | wc->length - wc->pos); | ||
194 | GNUNET_assert (GNUNET_SYSERR != bytes_wrote); | ||
195 | wc->pos += bytes_wrote; | ||
196 | if (wc->pos == wc->length) | ||
197 | { | ||
198 | GNUNET_free (wc->data); | ||
199 | GNUNET_free (wc); | ||
200 | return; | ||
201 | } | ||
202 | write_task_id = | ||
203 | GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, stdout_fd, | ||
204 | &write_task, wc); | ||
205 | } | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Functions with this signature are called whenever a | ||
210 | * complete message is received by the tokenizer. | ||
211 | * | ||
212 | * Do not call GNUNET_SERVER_mst_destroy in callback | ||
213 | * | ||
214 | * @param cls closure | ||
215 | * @param client identification of the client | ||
216 | * @param message the actual message | ||
217 | * | ||
218 | * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing | ||
219 | */ | ||
220 | static int | ||
221 | tokenizer_cb (void *cls, void *client, | ||
222 | const struct GNUNET_MessageHeader *message) | ||
223 | { | ||
224 | const struct GNUNET_TESTBED_HelperInit *msg; | ||
225 | struct GNUNET_TESTBED_HelperReply *reply; | ||
226 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
227 | struct WriteContext *wc; | ||
228 | char *controller; | ||
229 | char *config; | ||
230 | char *xconfig; | ||
231 | size_t config_size; | ||
232 | uLongf ul_config_size; | ||
233 | size_t xconfig_size; | ||
234 | uint16_t cname_size; | ||
235 | |||
236 | if ((sizeof (struct GNUNET_TESTBED_HelperInit) >= ntohs (message->size)) || | ||
237 | (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_INIT != ntohs (message->type))) | ||
238 | { | ||
239 | LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n"); | ||
240 | goto error; | ||
241 | } | ||
242 | msg = (const struct GNUNET_TESTBED_HelperInit *) message; | ||
243 | cname_size = ntohs (msg->cname_size); | ||
244 | controller = (char *) &msg[1]; | ||
245 | if ('\0' != controller[cname_size]) | ||
246 | { | ||
247 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
248 | "Controller name cannot be empty -- exiting\n"); | ||
249 | goto error; | ||
250 | } | ||
251 | ul_config_size = (uLongf) ntohs (msg->config_size); | ||
252 | config = GNUNET_malloc (ul_config_size); | ||
253 | xconfig_size = | ||
254 | ntohs (message->size) - (cname_size + 1 + | ||
255 | sizeof (struct GNUNET_TESTBED_HelperInit)); | ||
256 | if (Z_OK != | ||
257 | uncompress ((Bytef *) config, &ul_config_size, | ||
258 | (const Bytef *) (controller + cname_size + 1), | ||
259 | (uLongf) xconfig_size)) | ||
260 | { | ||
261 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
262 | "Error while uncompressing config -- exiting\n"); | ||
263 | GNUNET_free (config); | ||
264 | goto error; | ||
265 | } | ||
266 | cfg = GNUNET_CONFIGURATION_create (); | ||
267 | if (GNUNET_OK != | ||
268 | GNUNET_CONFIGURATION_deserialize (cfg, config, ul_config_size, GNUNET_NO)) | ||
269 | { | ||
270 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
271 | "Unable to deserialize config -- exiting\n"); | ||
272 | GNUNET_free (config); | ||
273 | goto error; | ||
274 | } | ||
275 | GNUNET_free (config); | ||
276 | test_system = GNUNET_TESTING_system_create ("testbed-helper", controller); | ||
277 | GNUNET_assert (NULL != test_system); | ||
278 | GNUNET_assert (GNUNET_OK == | ||
279 | GNUNET_TESTING_configuration_create (test_system, cfg)); | ||
280 | GNUNET_assert (GNUNET_OK == | ||
281 | GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", | ||
282 | "DEFAULTCONFIG", | ||
283 | &config)); | ||
284 | if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config)) | ||
285 | { | ||
286 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
287 | "Unable to write config file: %s -- exiting\n", config); | ||
288 | GNUNET_CONFIGURATION_destroy (cfg); | ||
289 | GNUNET_free (config); | ||
290 | goto error; | ||
291 | } | ||
292 | LOG_DEBUG ("Staring testbed with config: %s\n", config); | ||
293 | testbed = | ||
294 | GNUNET_OS_start_process (GNUNET_YES, | ||
295 | GNUNET_OS_INHERIT_STD_ERR /*verbose? */ , NULL, | ||
296 | NULL, "gnunet-service-testbed", | ||
297 | "gnunet-service-testbed", "-c", config, NULL); | ||
298 | GNUNET_free (config); | ||
299 | if (NULL == testbed) | ||
300 | { | ||
301 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
302 | "Error staring gnunet-service-testbed -- exiting\n"); | ||
303 | GNUNET_CONFIGURATION_destroy (cfg); | ||
304 | goto error; | ||
305 | } | ||
306 | done_reading = GNUNET_YES; | ||
307 | config = GNUNET_CONFIGURATION_serialize (cfg, &config_size); | ||
308 | GNUNET_CONFIGURATION_destroy (cfg); | ||
309 | cfg = NULL; | ||
310 | xconfig_size = | ||
311 | GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig); | ||
312 | GNUNET_free (config); | ||
313 | wc = GNUNET_malloc (sizeof (struct WriteContext)); | ||
314 | wc->length = xconfig_size + sizeof (struct GNUNET_TESTBED_HelperReply); | ||
315 | reply = GNUNET_realloc (xconfig, wc->length); | ||
316 | memmove (&reply[1], reply, xconfig_size); | ||
317 | reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY); | ||
318 | reply->header.size = htons ((uint16_t) wc->length); | ||
319 | reply->config_size = htons ((uint16_t) config_size); | ||
320 | wc->data = reply; | ||
321 | write_task_id = | ||
322 | GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, stdout_fd, | ||
323 | &write_task, wc); | ||
324 | return GNUNET_OK; | ||
325 | |||
326 | error: | ||
327 | status = GNUNET_SYSERR; | ||
328 | GNUNET_SCHEDULER_shutdown (); | ||
329 | return GNUNET_SYSERR; | ||
330 | } | ||
331 | |||
332 | |||
333 | /** | ||
334 | * Task to read from stdin | ||
335 | * | ||
336 | * @param cls NULL | ||
337 | * @param tc the task context | ||
338 | */ | ||
339 | static void | ||
340 | read_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
341 | { | ||
342 | char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE]; | ||
343 | ssize_t sread; | ||
344 | |||
345 | read_task_id = GNUNET_SCHEDULER_NO_TASK; | ||
346 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
347 | return; | ||
348 | sread = GNUNET_DISK_file_read (stdin_fd, buf, sizeof (buf)); | ||
349 | if ((GNUNET_SYSERR == sread) || (0 == sread)) | ||
350 | { | ||
351 | GNUNET_SCHEDULER_shutdown (); | ||
352 | return; | ||
353 | } | ||
354 | if (GNUNET_YES == done_reading) | ||
355 | { | ||
356 | /* didn't expect any more data! */ | ||
357 | GNUNET_SCHEDULER_shutdown (); | ||
358 | return; | ||
359 | } | ||
360 | LOG_DEBUG ("Read %u bytes\n", sread); | ||
361 | if (GNUNET_OK != | ||
362 | GNUNET_SERVER_mst_receive (tokenizer, NULL, buf, sread, GNUNET_NO, | ||
363 | GNUNET_NO)) | ||
364 | { | ||
365 | GNUNET_break (0); | ||
366 | GNUNET_SCHEDULER_shutdown (); | ||
367 | return; | ||
368 | } | ||
369 | read_task_id = /* No timeout while reading */ | ||
370 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, stdin_fd, | ||
371 | &read_task, NULL); | ||
372 | } | ||
373 | |||
374 | |||
375 | /** | ||
376 | * Main function that will be run. | ||
377 | * | ||
378 | * @param cls closure | ||
379 | * @param args remaining command-line arguments | ||
380 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
381 | * @param cfg configuration | ||
382 | */ | ||
383 | static void | ||
384 | run (void *cls, char *const *args, const char *cfgfile, | ||
385 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
386 | { | ||
387 | LOG_DEBUG ("Starting testbed helper...\n"); | ||
388 | tokenizer = GNUNET_SERVER_mst_create (&tokenizer_cb, NULL); | ||
389 | stdin_fd = GNUNET_DISK_get_handle_from_native (stdin); | ||
390 | stdout_fd = GNUNET_DISK_get_handle_from_native (stdout); | ||
391 | read_task_id = | ||
392 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, stdin_fd, | ||
393 | &read_task, NULL); | ||
394 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, | ||
395 | NULL); | ||
396 | } | ||
397 | |||
398 | |||
399 | /** | ||
400 | * Signal handler called for SIGCHLD. | ||
401 | */ | ||
402 | static void | ||
403 | sighandler_child_death () | ||
404 | { | ||
405 | if ((NULL != testbed) && (GNUNET_NO == in_shutdown)) | ||
406 | { | ||
407 | LOG_DEBUG ("Child died\n"); | ||
408 | GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (testbed)); | ||
409 | GNUNET_OS_process_destroy (testbed); | ||
410 | testbed = NULL; | ||
411 | GNUNET_SCHEDULER_shutdown (); /* We are done too! */ | ||
412 | } | ||
413 | } | ||
414 | |||
415 | |||
416 | /** | ||
417 | * Main function | ||
418 | * | ||
419 | * @param argc the number of command line arguments | ||
420 | * @param argv command line arg array | ||
421 | * @return return code | ||
422 | */ | ||
423 | int | ||
424 | main (int argc, char **argv) | ||
425 | { | ||
426 | struct GNUNET_SIGNAL_Context *shc_chld; | ||
427 | |||
428 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
429 | GNUNET_GETOPT_OPTION_END | ||
430 | }; | ||
431 | int ret; | ||
432 | |||
433 | status = GNUNET_OK; | ||
434 | in_shutdown = GNUNET_NO; | ||
435 | shc_chld = | ||
436 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); | ||
437 | ret = | ||
438 | GNUNET_PROGRAM_run (argc, argv, "gnunet-helper-testbed", | ||
439 | "Helper for starting gnunet-service-testbed", options, | ||
440 | &run, NULL); | ||
441 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | ||
442 | shc_chld = NULL; | ||
443 | if (GNUNET_OK != ret) | ||
444 | return 1; | ||
445 | return (GNUNET_OK == status) ? 0 : 1; | ||
446 | } | ||
447 | |||
448 | /* end of gnunet-helper-testbed.c */ | ||