diff options
Diffstat (limited to 'src/testbed/gnunet-helper-testbed.c')
-rw-r--r-- | src/testbed/gnunet-helper-testbed.c | 613 |
1 files changed, 0 insertions, 613 deletions
diff --git a/src/testbed/gnunet-helper-testbed.c b/src/testbed/gnunet-helper-testbed.c deleted file mode 100644 index 938e50448..000000000 --- a/src/testbed/gnunet-helper-testbed.c +++ /dev/null | |||
@@ -1,613 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2008--2013, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your 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 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
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. | ||
28 | * | ||
29 | * This helper monitors for three termination events. They are: (1)The | ||
30 | * stdin of the helper is closed for reading; (2)the helper received | ||
31 | * SIGTERM/SIGINT; (3)the testbed crashed. In case of events 1 and 2 | ||
32 | * the helper kills the testbed service. When testbed crashed (event | ||
33 | * 3), the helper should send a SIGTERM to its own process group; this | ||
34 | * behaviour will help terminate any child processes (peers) testbed | ||
35 | * has started and prevents them from leaking and running forever. | ||
36 | * | ||
37 | * @author Sree Harsha Totakura <sreeharsha@totakura.in> | ||
38 | */ | ||
39 | |||
40 | |||
41 | #include "platform.h" | ||
42 | #include "gnunet_util_lib.h" | ||
43 | #include "gnunet_testing_lib.h" | ||
44 | #include "gnunet_testbed_service.h" | ||
45 | #include "testbed_helper.h" | ||
46 | #include "testbed_api.h" | ||
47 | #include <zlib.h> | ||
48 | |||
49 | /** | ||
50 | * Generic logging shortcut | ||
51 | */ | ||
52 | #define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) | ||
53 | |||
54 | /** | ||
55 | * Debug logging shorthand | ||
56 | */ | ||
57 | #define LOG_DEBUG(...) LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) | ||
58 | |||
59 | |||
60 | /** | ||
61 | * Context for a single write on a chunk of memory | ||
62 | */ | ||
63 | struct WriteContext | ||
64 | { | ||
65 | /** | ||
66 | * The data to write | ||
67 | */ | ||
68 | void *data; | ||
69 | |||
70 | /** | ||
71 | * The length of the data | ||
72 | */ | ||
73 | size_t length; | ||
74 | |||
75 | /** | ||
76 | * The current position from where the write operation should begin | ||
77 | */ | ||
78 | size_t pos; | ||
79 | }; | ||
80 | |||
81 | |||
82 | /** | ||
83 | * Handle to the testing system | ||
84 | */ | ||
85 | static struct GNUNET_TESTING_System *test_system; | ||
86 | |||
87 | /** | ||
88 | * Our message stream tokenizer | ||
89 | */ | ||
90 | struct GNUNET_MessageStreamTokenizer *tokenizer; | ||
91 | |||
92 | /** | ||
93 | * Disk handle from stdin | ||
94 | */ | ||
95 | static struct GNUNET_DISK_FileHandle *stdin_fd; | ||
96 | |||
97 | /** | ||
98 | * Disk handle for stdout | ||
99 | */ | ||
100 | static struct GNUNET_DISK_FileHandle *stdout_fd; | ||
101 | |||
102 | /** | ||
103 | * The process handle to the testbed service | ||
104 | */ | ||
105 | static struct GNUNET_OS_Process *testbed; | ||
106 | |||
107 | /** | ||
108 | * Pipe used to communicate shutdown via signal. | ||
109 | */ | ||
110 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
111 | |||
112 | /** | ||
113 | * Task identifier for the read task | ||
114 | */ | ||
115 | static struct GNUNET_SCHEDULER_Task *read_task_id; | ||
116 | |||
117 | /** | ||
118 | * Task identifier for the write task | ||
119 | */ | ||
120 | static struct GNUNET_SCHEDULER_Task *write_task_id; | ||
121 | |||
122 | /** | ||
123 | * Task to kill the child | ||
124 | */ | ||
125 | static struct GNUNET_SCHEDULER_Task *child_death_task_id; | ||
126 | |||
127 | /** | ||
128 | * Are we done reading messages from stdin? | ||
129 | */ | ||
130 | static int done_reading; | ||
131 | |||
132 | /** | ||
133 | * Result to return in case we fail | ||
134 | */ | ||
135 | static int status; | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Task to shut down cleanly | ||
140 | * | ||
141 | * @param cls NULL | ||
142 | */ | ||
143 | static void | ||
144 | shutdown_task (void *cls) | ||
145 | { | ||
146 | LOG_DEBUG ("Shutting down\n"); | ||
147 | if (NULL != testbed) | ||
148 | { | ||
149 | LOG_DEBUG ("Killing testbed\n"); | ||
150 | GNUNET_break (0 == GNUNET_OS_process_kill (testbed, GNUNET_TERM_SIG)); | ||
151 | } | ||
152 | if (NULL != read_task_id) | ||
153 | { | ||
154 | GNUNET_SCHEDULER_cancel (read_task_id); | ||
155 | read_task_id = NULL; | ||
156 | } | ||
157 | if (NULL != write_task_id) | ||
158 | { | ||
159 | struct WriteContext *wc; | ||
160 | |||
161 | wc = GNUNET_SCHEDULER_cancel (write_task_id); | ||
162 | write_task_id = NULL; | ||
163 | GNUNET_free (wc->data); | ||
164 | GNUNET_free (wc); | ||
165 | } | ||
166 | if (NULL != child_death_task_id) | ||
167 | { | ||
168 | GNUNET_SCHEDULER_cancel (child_death_task_id); | ||
169 | child_death_task_id = NULL; | ||
170 | } | ||
171 | if (NULL != stdin_fd) | ||
172 | (void) GNUNET_DISK_file_close (stdin_fd); | ||
173 | if (NULL != stdout_fd) | ||
174 | (void) GNUNET_DISK_file_close (stdout_fd); | ||
175 | GNUNET_MST_destroy (tokenizer); | ||
176 | tokenizer = NULL; | ||
177 | if (NULL != testbed) | ||
178 | { | ||
179 | GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (testbed)); | ||
180 | GNUNET_OS_process_destroy (testbed); | ||
181 | testbed = NULL; | ||
182 | } | ||
183 | if (NULL != test_system) | ||
184 | { | ||
185 | GNUNET_TESTING_system_destroy (test_system, GNUNET_YES); | ||
186 | test_system = NULL; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | |||
191 | /** | ||
192 | * Task to write to the standard out | ||
193 | * | ||
194 | * @param cls the WriteContext | ||
195 | */ | ||
196 | static void | ||
197 | write_task (void *cls) | ||
198 | { | ||
199 | struct WriteContext *wc = cls; | ||
200 | ssize_t bytes_wrote; | ||
201 | |||
202 | GNUNET_assert (NULL != wc); | ||
203 | write_task_id = NULL; | ||
204 | bytes_wrote = GNUNET_DISK_file_write (stdout_fd, | ||
205 | wc->data + wc->pos, | ||
206 | wc->length - wc->pos); | ||
207 | if (GNUNET_SYSERR == bytes_wrote) | ||
208 | { | ||
209 | LOG (GNUNET_ERROR_TYPE_WARNING, "Cannot reply back configuration\n"); | ||
210 | GNUNET_free (wc->data); | ||
211 | GNUNET_free (wc); | ||
212 | return; | ||
213 | } | ||
214 | wc->pos += bytes_wrote; | ||
215 | if (wc->pos == wc->length) | ||
216 | { | ||
217 | GNUNET_free (wc->data); | ||
218 | GNUNET_free (wc); | ||
219 | return; | ||
220 | } | ||
221 | write_task_id = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
222 | stdout_fd, | ||
223 | &write_task, | ||
224 | wc); | ||
225 | } | ||
226 | |||
227 | |||
228 | /** | ||
229 | * Task triggered whenever we receive a SIGCHLD (child | ||
230 | * process died). | ||
231 | * | ||
232 | * @param cls closure, NULL if we need to self-restart | ||
233 | */ | ||
234 | static void | ||
235 | child_death_task (void *cls) | ||
236 | { | ||
237 | const struct GNUNET_DISK_FileHandle *pr; | ||
238 | char c[16]; | ||
239 | enum GNUNET_OS_ProcessStatusType type; | ||
240 | unsigned long code; | ||
241 | int ret; | ||
242 | |||
243 | pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); | ||
244 | child_death_task_id = NULL; | ||
245 | /* consume the signal */ | ||
246 | GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c))); | ||
247 | LOG_DEBUG ("Got SIGCHLD\n"); | ||
248 | if (NULL == testbed) | ||
249 | { | ||
250 | GNUNET_break (0); | ||
251 | return; | ||
252 | } | ||
253 | GNUNET_break (GNUNET_SYSERR != | ||
254 | (ret = GNUNET_OS_process_status (testbed, &type, &code))); | ||
255 | if (GNUNET_NO != ret) | ||
256 | { | ||
257 | GNUNET_OS_process_destroy (testbed); | ||
258 | testbed = NULL; | ||
259 | /* Send SIGTERM to our process group */ | ||
260 | if (0 != kill (0, GNUNET_TERM_SIG)) | ||
261 | { | ||
262 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "signal"); | ||
263 | GNUNET_SCHEDULER_shutdown (); /* Couldn't send the signal, we shutdown frowning */ | ||
264 | } | ||
265 | return; | ||
266 | } | ||
267 | LOG_DEBUG ("Child hasn't died. Resuming to monitor its status\n"); | ||
268 | child_death_task_id = | ||
269 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
270 | pr, | ||
271 | &child_death_task, | ||
272 | NULL); | ||
273 | } | ||
274 | |||
275 | |||
276 | /** | ||
277 | * Functions with this signature are called whenever a | ||
278 | * complete message is received by the tokenizer. | ||
279 | * | ||
280 | * Do not call #GNUNET_mst_destroy() in this callback | ||
281 | * | ||
282 | * @param cls identification of the client | ||
283 | * @param message the actual message | ||
284 | * @return #GNUNET_OK on success, | ||
285 | * #GNUNET_NO to stop further processing (no error) | ||
286 | * #GNUNET_SYSERR to stop further processing with error | ||
287 | */ | ||
288 | static int | ||
289 | tokenizer_cb (void *cls, const struct GNUNET_MessageHeader *message) | ||
290 | { | ||
291 | const struct GNUNET_TESTBED_HelperInit *msg; | ||
292 | struct GNUNET_TESTBED_HelperReply *reply; | ||
293 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
294 | struct WriteContext *wc; | ||
295 | char *binary; | ||
296 | char *trusted_ip; | ||
297 | char *hostname; | ||
298 | char *config; | ||
299 | char *xconfig; | ||
300 | char *evstr; | ||
301 | // char *str; | ||
302 | size_t config_size; | ||
303 | uLongf ul_config_size; | ||
304 | size_t xconfig_size; | ||
305 | uint16_t trusted_ip_size; | ||
306 | uint16_t hostname_size; | ||
307 | uint16_t msize; | ||
308 | |||
309 | msize = ntohs (message->size); | ||
310 | if ((sizeof(struct GNUNET_TESTBED_HelperInit) >= msize) || | ||
311 | (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_INIT != ntohs (message->type))) | ||
312 | { | ||
313 | LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n"); | ||
314 | goto error; | ||
315 | } | ||
316 | msg = (const struct GNUNET_TESTBED_HelperInit *) message; | ||
317 | trusted_ip_size = ntohs (msg->trusted_ip_size); | ||
318 | trusted_ip = (char *) &msg[1]; | ||
319 | if ('\0' != trusted_ip[trusted_ip_size]) | ||
320 | { | ||
321 | LOG (GNUNET_ERROR_TYPE_WARNING, "Trusted IP cannot be empty -- exiting\n"); | ||
322 | goto error; | ||
323 | } | ||
324 | hostname_size = ntohs (msg->hostname_size); | ||
325 | if ((sizeof(struct GNUNET_TESTBED_HelperInit) + trusted_ip_size + 1 | ||
326 | + hostname_size) >= msize) | ||
327 | { | ||
328 | GNUNET_break (0); | ||
329 | LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n"); | ||
330 | goto error; | ||
331 | } | ||
332 | ul_config_size = (uLongf) ntohs (msg->config_size); | ||
333 | config = GNUNET_malloc (ul_config_size); | ||
334 | xconfig_size = msize - (trusted_ip_size + 1 + hostname_size | ||
335 | + sizeof(struct GNUNET_TESTBED_HelperInit)); | ||
336 | int ret = uncompress ((Bytef *) config, | ||
337 | &ul_config_size, | ||
338 | (const Bytef *) (trusted_ip + trusted_ip_size + 1 | ||
339 | + hostname_size), | ||
340 | (uLongf) xconfig_size); | ||
341 | if (Z_OK != ret) | ||
342 | { | ||
343 | switch (ret) | ||
344 | { | ||
345 | case Z_MEM_ERROR: | ||
346 | LOG (GNUNET_ERROR_TYPE_ERROR, "Not enough memory for decompression\n"); | ||
347 | break; | ||
348 | |||
349 | case Z_BUF_ERROR: | ||
350 | LOG (GNUNET_ERROR_TYPE_ERROR, "Output buffer too small\n"); | ||
351 | break; | ||
352 | |||
353 | case Z_DATA_ERROR: | ||
354 | LOG (GNUNET_ERROR_TYPE_ERROR, "Data corrupted/incomplete\n"); | ||
355 | break; | ||
356 | |||
357 | default: | ||
358 | GNUNET_break (0); | ||
359 | } | ||
360 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
361 | "Error while uncompressing config -- exiting\n"); | ||
362 | GNUNET_free (config); | ||
363 | goto error; | ||
364 | } | ||
365 | cfg = GNUNET_CONFIGURATION_create (); | ||
366 | if (GNUNET_OK != | ||
367 | GNUNET_CONFIGURATION_deserialize (cfg, config, ul_config_size, NULL)) | ||
368 | { | ||
369 | LOG (GNUNET_ERROR_TYPE_ERROR, "Unable to deserialize config -- exiting\n"); | ||
370 | GNUNET_free (config); | ||
371 | goto error; | ||
372 | } | ||
373 | GNUNET_free (config); | ||
374 | hostname = NULL; | ||
375 | if (0 != hostname_size) | ||
376 | { | ||
377 | hostname = GNUNET_malloc (hostname_size + 1); | ||
378 | GNUNET_strlcpy (hostname, | ||
379 | ((char *) &msg[1]) + trusted_ip_size + 1, | ||
380 | hostname_size + 1); | ||
381 | } | ||
382 | /* unset GNUNET_TESTING_PREFIX if present as it is more relevant for testbed */ | ||
383 | evstr = getenv (GNUNET_TESTING_PREFIX); | ||
384 | if (NULL != evstr) | ||
385 | { | ||
386 | /* unsetting the variable will invalidate the pointer! */ | ||
387 | evstr = GNUNET_strdup (evstr); | ||
388 | GNUNET_break (0 == unsetenv (GNUNET_TESTING_PREFIX)); | ||
389 | } | ||
390 | test_system = | ||
391 | GNUNET_TESTING_system_create ("testbed-helper", trusted_ip, hostname, NULL); | ||
392 | if (NULL != evstr) | ||
393 | { | ||
394 | char *evar; | ||
395 | |||
396 | GNUNET_asprintf (&evar, GNUNET_TESTING_PREFIX "=%s", evstr); | ||
397 | GNUNET_assert (0 == putenv (evar)); /* consumes 'evar', | ||
398 | see putenv(): becomes part of environment! */ | ||
399 | GNUNET_free (evstr); | ||
400 | evstr = NULL; | ||
401 | } | ||
402 | GNUNET_free (hostname); | ||
403 | hostname = NULL; | ||
404 | GNUNET_assert (NULL != test_system); | ||
405 | GNUNET_assert (GNUNET_OK == | ||
406 | GNUNET_TESTING_configuration_create (test_system, cfg)); | ||
407 | GNUNET_assert (GNUNET_OK == | ||
408 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
409 | "PATHS", | ||
410 | "DEFAULTCONFIG", | ||
411 | &config)); | ||
412 | if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config)) | ||
413 | { | ||
414 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
415 | "Unable to write config file: %s -- exiting\n", | ||
416 | config); | ||
417 | GNUNET_CONFIGURATION_destroy (cfg); | ||
418 | GNUNET_free (config); | ||
419 | goto error; | ||
420 | } | ||
421 | LOG_DEBUG ("Staring testbed with config: %s\n", config); | ||
422 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-testbed"); | ||
423 | { | ||
424 | char *evar; | ||
425 | |||
426 | /* expose testbed configuration through env variable */ | ||
427 | GNUNET_asprintf (&evar, "%s=%s", ENV_TESTBED_CONFIG, config); | ||
428 | GNUNET_assert (0 == putenv (evar)); /* consumes 'evar', | ||
429 | see putenv(): becomes part of environment! */ | ||
430 | evstr = NULL; | ||
431 | } | ||
432 | testbed = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR /*verbose? */, | ||
433 | NULL, | ||
434 | NULL, | ||
435 | NULL, | ||
436 | binary, | ||
437 | "gnunet-service-testbed", | ||
438 | "-c", | ||
439 | config, | ||
440 | NULL); | ||
441 | GNUNET_free (binary); | ||
442 | GNUNET_free (config); | ||
443 | if (NULL == testbed) | ||
444 | { | ||
445 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
446 | "Error starting gnunet-service-testbed -- exiting\n"); | ||
447 | GNUNET_CONFIGURATION_destroy (cfg); | ||
448 | goto error; | ||
449 | } | ||
450 | done_reading = GNUNET_YES; | ||
451 | config = GNUNET_CONFIGURATION_serialize (cfg, &config_size); | ||
452 | GNUNET_CONFIGURATION_destroy (cfg); | ||
453 | cfg = NULL; | ||
454 | xconfig_size = | ||
455 | GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig); | ||
456 | GNUNET_free (config); | ||
457 | wc = GNUNET_new (struct WriteContext); | ||
458 | wc->length = xconfig_size + sizeof(struct GNUNET_TESTBED_HelperReply); | ||
459 | reply = GNUNET_realloc (xconfig, wc->length); | ||
460 | memmove (&reply[1], reply, xconfig_size); | ||
461 | reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY); | ||
462 | reply->header.size = htons ((uint16_t) wc->length); | ||
463 | reply->config_size = htons ((uint16_t) config_size); | ||
464 | wc->data = reply; | ||
465 | write_task_id = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
466 | stdout_fd, | ||
467 | &write_task, | ||
468 | wc); | ||
469 | child_death_task_id = GNUNET_SCHEDULER_add_read_file ( | ||
470 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
471 | GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ), | ||
472 | &child_death_task, | ||
473 | NULL); | ||
474 | return GNUNET_OK; | ||
475 | |||
476 | error: | ||
477 | status = GNUNET_SYSERR; | ||
478 | GNUNET_SCHEDULER_shutdown (); | ||
479 | return GNUNET_SYSERR; | ||
480 | } | ||
481 | |||
482 | |||
483 | /** | ||
484 | * Task to read from stdin | ||
485 | * | ||
486 | * @param cls NULL | ||
487 | */ | ||
488 | static void | ||
489 | read_task (void *cls) | ||
490 | { | ||
491 | char buf[GNUNET_MAX_MESSAGE_SIZE]; | ||
492 | ssize_t sread; | ||
493 | |||
494 | read_task_id = NULL; | ||
495 | sread = GNUNET_DISK_file_read (stdin_fd, buf, sizeof(buf)); | ||
496 | if ((GNUNET_SYSERR == sread) || (0 == sread)) | ||
497 | { | ||
498 | LOG_DEBUG ("STDIN closed\n"); | ||
499 | GNUNET_SCHEDULER_shutdown (); | ||
500 | return; | ||
501 | } | ||
502 | if (GNUNET_YES == done_reading) | ||
503 | { | ||
504 | /* didn't expect any more data! */ | ||
505 | GNUNET_break_op (0); | ||
506 | GNUNET_SCHEDULER_shutdown (); | ||
507 | return; | ||
508 | } | ||
509 | LOG_DEBUG ("Read %u bytes\n", (unsigned int) sread); | ||
510 | /* FIXME: could introduce a GNUNET_MST_read2 to read | ||
511 | directly from 'stdin_fd' and save a memcpy() here */ | ||
512 | if (GNUNET_OK != | ||
513 | GNUNET_MST_from_buffer (tokenizer, buf, sread, GNUNET_NO, GNUNET_NO)) | ||
514 | { | ||
515 | GNUNET_break (0); | ||
516 | GNUNET_SCHEDULER_shutdown (); | ||
517 | return; | ||
518 | } | ||
519 | read_task_id /* No timeout while reading */ | ||
520 | = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
521 | stdin_fd, | ||
522 | &read_task, | ||
523 | NULL); | ||
524 | } | ||
525 | |||
526 | |||
527 | /** | ||
528 | * Main function that will be run. | ||
529 | * | ||
530 | * @param cls closure | ||
531 | * @param args remaining command-line arguments | ||
532 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
533 | * @param cfg configuration | ||
534 | */ | ||
535 | static void | ||
536 | run (void *cls, | ||
537 | char *const *args, | ||
538 | const char *cfgfile, | ||
539 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
540 | { | ||
541 | LOG_DEBUG ("Starting testbed helper...\n"); | ||
542 | tokenizer = GNUNET_MST_create (&tokenizer_cb, NULL); | ||
543 | stdin_fd = GNUNET_DISK_get_handle_from_native (stdin); | ||
544 | stdout_fd = GNUNET_DISK_get_handle_from_native (stdout); | ||
545 | read_task_id = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
546 | stdin_fd, | ||
547 | &read_task, | ||
548 | NULL); | ||
549 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
550 | } | ||
551 | |||
552 | |||
553 | /** | ||
554 | * Signal handler called for SIGCHLD. | ||
555 | */ | ||
556 | static void | ||
557 | sighandler_child_death () | ||
558 | { | ||
559 | static char c; | ||
560 | int old_errno; /* back-up errno */ | ||
561 | |||
562 | old_errno = errno; | ||
563 | GNUNET_break ( | ||
564 | 1 == | ||
565 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, | ||
566 | GNUNET_DISK_PIPE_END_WRITE), | ||
567 | &c, | ||
568 | sizeof(c))); | ||
569 | errno = old_errno; | ||
570 | } | ||
571 | |||
572 | |||
573 | /** | ||
574 | * Main function | ||
575 | * | ||
576 | * @param argc the number of command line arguments | ||
577 | * @param argv command line arg array | ||
578 | * @return return code | ||
579 | */ | ||
580 | int | ||
581 | main (int argc, char **argv) | ||
582 | { | ||
583 | struct GNUNET_SIGNAL_Context *shc_chld; | ||
584 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
585 | { GNUNET_GETOPT_OPTION_END }; | ||
586 | int ret; | ||
587 | |||
588 | status = GNUNET_OK; | ||
589 | if (NULL == | ||
590 | (sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE))) | ||
591 | { | ||
592 | GNUNET_break (0); | ||
593 | return 1; | ||
594 | } | ||
595 | shc_chld = | ||
596 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); | ||
597 | ret = GNUNET_PROGRAM_run (argc, | ||
598 | argv, | ||
599 | "gnunet-helper-testbed", | ||
600 | "Helper for starting gnunet-service-testbed", | ||
601 | options, | ||
602 | &run, | ||
603 | NULL); | ||
604 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | ||
605 | shc_chld = NULL; | ||
606 | GNUNET_DISK_pipe_close (sigpipe); | ||
607 | if (GNUNET_OK != ret) | ||
608 | return 1; | ||
609 | return (GNUNET_OK == status) ? 0 : 1; | ||
610 | } | ||
611 | |||
612 | |||
613 | /* end of gnunet-helper-testbed.c */ | ||