aboutsummaryrefslogtreecommitdiff
path: root/src/testbed/gnunet-cmds-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testbed/gnunet-cmds-helper.c')
-rw-r--r--src/testbed/gnunet-cmds-helper.c719
1 files changed, 719 insertions, 0 deletions
diff --git a/src/testbed/gnunet-cmds-helper.c b/src/testbed/gnunet-cmds-helper.c
new file mode 100644
index 000000000..693892a9c
--- /dev/null
+++ b/src/testbed/gnunet-cmds-helper.c
@@ -0,0 +1,719 @@
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-cmds-helper.c
23 * @brief Helper binary that is started from a remote interpreter loop to start
24 * a local interpreter loop.
25 *
26 * This helper monitors for three termination events. They are: (1)The
27 * stdin of the helper is closed for reading; (2)the helper received
28 * SIGTERM/SIGINT; (3)the local loop crashed. In case of events 1 and 2
29 * the helper kills the interpreter loop. When the interpreter loop
30 * crashed (event 3), the helper should send a SIGTERM to its own process
31 * group; this behaviour will help terminate any child processes the loop
32 * has started and prevents them from leaking and running forever.
33 *
34 * @author t3sserakt
35 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
36 */
37
38
39#include "platform.h"
40#include "gnunet_util_lib.h"
41#include "gnunet_testing_lib.h"
42#include "gnunet_testbed_service.h"
43#include "testbed_helper.h"
44#include "testbed_api.h"
45#include "gnunet_testing_plugin.h"
46#include <zlib.h>
47#include "execinfo.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#define NODE_BASE_IP "192.168.15."
60
61#define ROUTER_BASE_IP "92.68.150."
62
63#define MAX_TRACE_DEPTH 50
64
65/**
66 * Handle for a plugin.
67 */
68struct Plugin
69{
70 /**
71 * Name of the shared library.
72 */
73 char *library_name;
74
75 /**
76 * Plugin API.
77 */
78 struct GNUNET_TESTING_PluginFunctions *api;
79
80 char *node_ip;
81
82 char *plugin_name;
83
84 char *global_n;
85
86 char *local_m;
87
88 char *n;
89
90 char *m;
91};
92
93struct NodeIdentifier
94{
95 char *n;
96
97 char *m;
98
99 char *global_n;
100
101 char *local_m;
102};
103
104/**
105 * Context for a single write on a chunk of memory
106 */
107struct WriteContext
108{
109 /**
110 * The data to write
111 */
112 void *data;
113
114 /**
115 * The length of the data
116 */
117 size_t length;
118
119 /**
120 * The current position from where the write operation should begin
121 */
122 size_t pos;
123};
124
125struct Plugin *plugin;
126
127/**
128 * The process handle to the testbed service
129 */
130static struct GNUNET_OS_Process *cmd_binary_process;
131
132/**
133 * Handle to the testing system
134 */
135static struct GNUNET_TESTING_System *test_system;
136
137/**
138 * Our message stream tokenizer
139 */
140struct GNUNET_MessageStreamTokenizer *tokenizer;
141
142/**
143 * Disk handle from stdin
144 */
145static struct GNUNET_DISK_FileHandle *stdin_fd;
146
147/**
148 * Disk handle for stdout
149 */
150static struct GNUNET_DISK_FileHandle *stdout_fd;
151
152/**
153 * Pipe used to communicate shutdown via signal.
154 */
155static struct GNUNET_DISK_PipeHandle *sigpipe;
156
157/**
158 * Task identifier for the read task
159 */
160static struct GNUNET_SCHEDULER_Task *read_task_id;
161
162/**
163 * Task identifier for the write task
164 */
165static struct GNUNET_SCHEDULER_Task *write_task_id;
166
167/**
168 * Task to kill the child
169 */
170static struct GNUNET_SCHEDULER_Task *child_death_task_id;
171
172/**
173 * Are we done reading messages from stdin?
174 */
175static int done_reading;
176
177/**
178 * Result to return in case we fail
179 */
180static int status;
181
182
183struct BacktraceInfo
184{
185 /**
186 * Array of strings which make up a backtrace from the point when this
187 * task was scheduled (essentially, who scheduled the task?)
188 */
189 char **backtrace_strings;
190
191 /**
192 * Size of the backtrace_strings array
193 */
194 int num_backtrace_strings;
195};
196
197/**
198 * Output stack trace of task @a t.
199 *
200 * @param t task to dump stack trace of
201 */
202static void
203dump_backtrace (struct BacktraceInfo *t)
204{
205
206 for (unsigned int i = 0; i < t->num_backtrace_strings; i++)
207 LOG (GNUNET_ERROR_TYPE_ERROR,
208 "Task %p trace %u: %s\n",
209 t,
210 i,
211 t->backtrace_strings[i]);
212
213}
214
215
216/**
217 * Initialize backtrace data for task @a t
218 *
219 * @param t task to initialize
220 */
221static void
222init_backtrace ()
223{
224 struct BacktraceInfo *t;
225 void *backtrace_array[MAX_TRACE_DEPTH];
226
227 t = GNUNET_new (struct BacktraceInfo);
228 t->num_backtrace_strings
229 = backtrace (backtrace_array, MAX_TRACE_DEPTH);
230 t->backtrace_strings =
231 backtrace_symbols (backtrace_array,
232 t->num_backtrace_strings);
233 dump_backtrace (t);
234
235}
236
237
238/**
239 * Task to shut down cleanly
240 *
241 * @param cls NULL
242 */
243static void
244shutdown_task (void *cls)
245{
246
247 init_backtrace ();
248 LOG_DEBUG ("Shutting down.\n");
249 LOG (GNUNET_ERROR_TYPE_ERROR,
250 "Shutting down tokenizer!\n");
251
252 if (NULL != read_task_id)
253 {
254 GNUNET_SCHEDULER_cancel (read_task_id);
255 read_task_id = NULL;
256 }
257 if (NULL != write_task_id)
258 {
259 struct WriteContext *wc;
260
261 wc = GNUNET_SCHEDULER_cancel (write_task_id);
262 write_task_id = NULL;
263 GNUNET_free (wc->data);
264 GNUNET_free (wc);
265 }
266 if (NULL != child_death_task_id)
267 {
268 GNUNET_SCHEDULER_cancel (child_death_task_id);
269 child_death_task_id = NULL;
270 }
271 if (NULL != stdin_fd)
272 (void) GNUNET_DISK_file_close (stdin_fd);
273 if (NULL != stdout_fd)
274 (void) GNUNET_DISK_file_close (stdout_fd);
275 GNUNET_MST_destroy (tokenizer);
276 tokenizer = NULL;
277
278 if (NULL != test_system)
279 {
280 GNUNET_TESTING_system_destroy (test_system, GNUNET_YES);
281 test_system = NULL;
282 }
283}
284
285
286
287
288/**
289 * Task to write to the standard out
290 *
291 * @param cls the WriteContext
292 */
293static void
294write_task (void *cls)
295{
296 struct WriteContext *wc = cls;
297 ssize_t bytes_wrote;
298
299 LOG (GNUNET_ERROR_TYPE_ERROR,
300 "Writing data!\n");
301
302 GNUNET_assert (NULL != wc);
303 write_task_id = NULL;
304 bytes_wrote = GNUNET_DISK_file_write (stdout_fd,
305 wc->data + wc->pos,
306 wc->length - wc->pos);
307 if (GNUNET_SYSERR == bytes_wrote)
308 {
309 LOG (GNUNET_ERROR_TYPE_WARNING,
310 "Cannot reply back successful initialization\n");
311 GNUNET_free (wc->data);
312 GNUNET_free (wc);
313 return;
314 }
315 wc->pos += bytes_wrote;
316 if (wc->pos == wc->length)
317 {
318 GNUNET_free (wc->data);
319 GNUNET_free (wc);
320 LOG (GNUNET_ERROR_TYPE_ERROR,
321 "Written successfully!\n");
322 return;
323 }
324 LOG (GNUNET_ERROR_TYPE_ERROR,
325 "Written data!\n");
326 write_task_id = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
327 stdout_fd,
328 &write_task,
329 wc);
330}
331
332
333/**
334 * Task triggered whenever we receive a SIGCHLD (child
335 * process died).
336 *
337 * @param cls closure, NULL if we need to self-restart
338 */
339static void
340child_death_task (void *cls)
341{
342 const struct GNUNET_DISK_FileHandle *pr;
343 char c[16];
344
345 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
346 child_death_task_id = NULL;
347 /* consume the signal */
348 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c)));
349 LOG_DEBUG ("Got SIGCHLD\n");
350
351 LOG_DEBUG ("Child hasn't died. Resuming to monitor its status\n");
352 child_death_task_id =
353 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
354 pr,
355 &child_death_task,
356 NULL);
357}
358
359
360static void
361write_message (struct GNUNET_MessageHeader *message, size_t msg_length)
362{
363 struct WriteContext *wc;
364
365 LOG (GNUNET_ERROR_TYPE_ERROR,
366 "enter write_message!\n");
367 wc = GNUNET_new (struct WriteContext);
368 wc->length = msg_length;
369 wc->data = message;
370 write_task_id = GNUNET_SCHEDULER_add_write_file (
371 GNUNET_TIME_UNIT_FOREVER_REL,
372 stdout_fd,
373 &write_task,
374 wc);
375 LOG (GNUNET_ERROR_TYPE_ERROR,
376 "leave write_message!\n");
377}
378
379
380/**
381 * Function to run the test cases.
382 *
383 * @param cls plugin to use.
384 *
385 */
386static void
387run_plugin (void *cls)
388{
389 struct Plugin *plugin = cls;
390 char *router_ip;
391 char *node_ip;
392
393 router_ip = GNUNET_malloc (strlen (ROUTER_BASE_IP) + strlen (plugin->m) + 1);
394 strcpy (router_ip, ROUTER_BASE_IP);
395 strcat (router_ip, plugin->m);
396
397 node_ip = GNUNET_malloc (strlen (NODE_BASE_IP) + strlen (plugin->n) + 1);
398 strcat (node_ip, NODE_BASE_IP);
399 strcat (node_ip, plugin->n);
400
401 plugin->api->start_testcase (&write_message, router_ip, node_ip);
402
403}
404
405
406/**
407 * Functions with this signature are called whenever a
408 * complete message is received by the tokenizer.
409 *
410 * Do not call #GNUNET_mst_destroy() in this callback
411 *
412 * @param cls identification of the client
413 * @param message the actual message
414 * @return #GNUNET_OK on success,
415 * #GNUNET_NO to stop further processing (no error)
416 * #GNUNET_SYSERR to stop further processing with error
417 */
418static int
419tokenizer_cb (void *cls, const struct GNUNET_MessageHeader *message)
420{
421 struct NodeIdentifier *ni = cls;
422 const struct GNUNET_CMDS_HelperInit *msg;
423 struct GNUNET_CMDS_HelperReply *reply;
424 char *binary;
425 char *plugin_name;
426 size_t plugin_name_size;
427 uint16_t msize;
428 size_t msg_length;
429 char *router_ip;
430 char *node_ip;
431
432 LOG (GNUNET_ERROR_TYPE_ERROR,
433 "tokenizer \n");
434
435 msize = ntohs (message->size);
436 if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_ALL_PEERS_STARTED == ntohs (
437 message->type))
438 {
439 plugin->api->all_peers_started ();
440 }
441 else if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_INIT == ntohs (message->type))
442 {
443 msg = (const struct GNUNET_CMDS_HelperInit *) message;
444 plugin_name_size = ntohs (msg->plugin_name_size);
445 if ((sizeof(struct GNUNET_CMDS_HelperInit) + plugin_name_size) > msize)
446 {
447 GNUNET_break (0);
448 LOG (GNUNET_ERROR_TYPE_WARNING,
449 "Received unexpected message -- exiting\n");
450 goto error;
451 }
452 plugin_name = GNUNET_malloc (plugin_name_size + 1);
453 GNUNET_strlcpy (plugin_name,
454 ((char *) &msg[1]),
455 plugin_name_size + 1);
456
457 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-cmd");
458
459 LOG (GNUNET_ERROR_TYPE_ERROR,
460 "plugin_name: %s \n",
461 plugin_name);
462
463 // cmd_binary_process = GNUNET_OS_start_process (
464 /*GNUNET_OS_INHERIT_STD_ERR verbose? ,
465 NULL,
466 NULL,
467 NULL,
468 binary,
469 plugin_name,
470 ni->global_n,
471 ni->local_m,
472 ni->n,
473 ni->m,
474 NULL);*/
475
476 plugin = GNUNET_new (struct Plugin);
477 plugin->api = GNUNET_PLUGIN_load (plugin_name,
478 NULL);
479 plugin->library_name = GNUNET_strdup (plugin_name);
480
481 plugin->global_n = ni->global_n;
482 plugin->local_m = ni->local_m;
483 plugin->n = ni->n;
484 plugin->m = ni->m;
485
486 router_ip = GNUNET_malloc (strlen (ROUTER_BASE_IP) + strlen (plugin->m)
487 + 1);
488 strcpy (router_ip, ROUTER_BASE_IP);
489 strcat (router_ip, plugin->m);
490
491 node_ip = GNUNET_malloc (strlen (NODE_BASE_IP) + strlen (plugin->n) + 1);
492 strcat (node_ip, NODE_BASE_IP);
493 strcat (node_ip, plugin->n);
494
495 plugin->api->start_testcase (&write_message, router_ip, node_ip);
496
497 LOG (GNUNET_ERROR_TYPE_ERROR,
498 "We got here!\n");
499
500 /*if (NULL == cmd_binary_process)
501 {
502 LOG (GNUNET_ERROR_TYPE_ERROR,
503 "Starting plugin failed!\n");
504 return GNUNET_SYSERR;
505 }*/
506
507 LOG (GNUNET_ERROR_TYPE_ERROR,
508 "We got here 2!\n");
509
510 LOG (GNUNET_ERROR_TYPE_ERROR,
511 "global_n: %s local_n: %s n: %s m: %s.\n",
512 ni->global_n,
513 ni->local_m,
514 ni->n,
515 ni->m);
516
517 LOG (GNUNET_ERROR_TYPE_ERROR,
518 "We got here 3!\n");
519
520 GNUNET_free (binary);
521
522 done_reading = GNUNET_YES;
523
524 msg_length = sizeof(struct GNUNET_CMDS_HelperReply);
525 reply = GNUNET_new (struct GNUNET_CMDS_HelperReply);
526 reply->header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_REPLY);
527 reply->header.size = htons ((uint16_t) msg_length);
528
529 LOG (GNUNET_ERROR_TYPE_ERROR,
530 "We got here 4!\n");
531
532 write_message ((struct GNUNET_MessageHeader *) reply, msg_length);
533
534 LOG (GNUNET_ERROR_TYPE_ERROR,
535 "We got here 5!\n");
536
537 /*child_death_task_id = GNUNET_SCHEDULER_add_read_file (
538 GNUNET_TIME_UNIT_FOREVER_REL,
539 GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ),
540 &child_death_task,
541 NULL);*/
542 return GNUNET_OK;
543 }
544 else
545 {
546 LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n");
547 goto error;
548 }
549
550
551 error:
552 status = GNUNET_SYSERR;
553 LOG (GNUNET_ERROR_TYPE_ERROR,
554 "tokenizer shuting down!\n");
555 GNUNET_SCHEDULER_shutdown ();
556 return GNUNET_SYSERR;
557}
558
559
560/**
561 * Task to read from stdin
562 *
563 * @param cls NULL
564 */
565static void
566read_task (void *cls)
567{
568 char buf[GNUNET_MAX_MESSAGE_SIZE];
569 ssize_t sread;
570
571 read_task_id = NULL;
572 sread = GNUNET_DISK_file_read (stdin_fd, buf, sizeof(buf));
573 if ((GNUNET_SYSERR == sread) || (0 == sread))
574 {
575 LOG_DEBUG ("STDIN closed\n");
576 LOG (GNUNET_ERROR_TYPE_ERROR,
577 "tokenizer shuting down during reading!\n");
578 GNUNET_SCHEDULER_shutdown ();
579 return;
580 }
581 if (GNUNET_YES == done_reading)
582 {
583 /* didn't expect any more data! */
584 GNUNET_break_op (0);
585 LOG (GNUNET_ERROR_TYPE_ERROR,
586 "tokenizer shuting down during reading, didn't expect any more data!\n");
587 GNUNET_SCHEDULER_shutdown ();
588 return;
589 }
590 LOG_DEBUG ("Read %u bytes\n", (unsigned int) sread);
591 /* FIXME: could introduce a GNUNET_MST_read2 to read
592 directly from 'stdin_fd' and save a memcpy() here */
593 if (GNUNET_OK !=
594 GNUNET_MST_from_buffer (tokenizer, buf, sread, GNUNET_NO, GNUNET_NO))
595 {
596 GNUNET_break (0);
597 LOG (GNUNET_ERROR_TYPE_ERROR,
598 "tokenizer shuting down during reading, writing to buffer failed!\n");
599 GNUNET_SCHEDULER_shutdown ();
600 return;
601 }
602 read_task_id /* No timeout while reading */
603 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
604 stdin_fd,
605 &read_task,
606 NULL);
607}
608
609
610/**
611 * Main function that will be run.
612 *
613 * @param cls closure
614 * @param args remaining command-line arguments
615 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
616 * @param cfg configuration
617 */
618static void
619run (void *cls,
620 char *const *args,
621 const char *cfgfile,
622 const struct GNUNET_CONFIGURATION_Handle *cfg)
623{
624 struct NodeIdentifier *ni = cls;
625
626 LOG_DEBUG ("Starting interpreter loop helper...\n");
627
628 tokenizer = GNUNET_MST_create (&tokenizer_cb, ni);
629 stdin_fd = GNUNET_DISK_get_handle_from_native (stdin);
630 stdout_fd = GNUNET_DISK_get_handle_from_native (stdout);
631 read_task_id = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
632 stdin_fd,
633 &read_task,
634 NULL);
635 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
636}
637
638
639/**
640 * Signal handler called for SIGCHLD.
641 */
642static void
643sighandler_child_death ()
644{
645 static char c;
646 int old_errno; /* back-up errno */
647
648 old_errno = errno;
649 GNUNET_break (
650 1 ==
651 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
652 GNUNET_DISK_PIPE_END_WRITE),
653 &c,
654 sizeof(c)));
655 errno = old_errno;
656}
657
658
659/**
660 * Main function
661 *
662 * @param argc the number of command line arguments
663 * @param argv command line arg array
664 * @return return code
665 */
666int
667main (int argc, char **argv)
668{
669 struct NodeIdentifier *ni;
670 struct GNUNET_SIGNAL_Context *shc_chld;
671 struct GNUNET_GETOPT_CommandLineOption options[] =
672 { GNUNET_GETOPT_OPTION_END };
673 int ret;
674
675 GNUNET_log_setup ("gnunet-cmds-helper",
676 "DEBUG",
677 NULL);
678 ni = GNUNET_new (struct NodeIdentifier);
679 ni->global_n = argv[1];
680 ni->local_m = argv[2];
681 ni->n = argv[3];
682 ni->m = argv[4];
683
684 LOG (GNUNET_ERROR_TYPE_ERROR,
685 "global_n: %s local_n: %s n: %s m: %s.\n",
686 ni->global_n,
687 ni->local_m,
688 ni->n,
689 ni->m);
690
691 status = GNUNET_OK;
692 if (NULL ==
693 (sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
694 {
695 GNUNET_break (0);
696 return 1;
697 }
698 shc_chld =
699 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
700 ret = GNUNET_PROGRAM_run (argc,
701 argv,
702 "gnunet-cmds-helper",
703 "Helper for starting a local interpreter loop",
704 options,
705 &run,
706 ni);
707 LOG (GNUNET_ERROR_TYPE_ERROR,
708 "run finished\n");
709 GNUNET_SIGNAL_handler_uninstall (shc_chld);
710 shc_chld = NULL;
711 GNUNET_DISK_pipe_close (sigpipe);
712 GNUNET_free (ni);
713 if (GNUNET_OK != ret)
714 return 1;
715 return (GNUNET_OK == status) ? 0 : 1;
716}
717
718
719/* end of gnunet-cmds-helper.c */