aboutsummaryrefslogtreecommitdiff
path: root/src/testing/gnunet-cmds-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/gnunet-cmds-helper.c')
-rw-r--r--src/testing/gnunet-cmds-helper.c640
1 files changed, 0 insertions, 640 deletions
diff --git a/src/testing/gnunet-cmds-helper.c b/src/testing/gnunet-cmds-helper.c
deleted file mode 100644
index 5ff7c04ea..000000000
--- a/src/testing/gnunet-cmds-helper.c
+++ /dev/null
@@ -1,640 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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_testing_ng_lib.h"
43#include "testing_cmds.h"
44#include "gnunet_testing_plugin.h"
45#include <zlib.h>
46
47
48/**
49 * Generic logging shortcut
50testing_api_cmd_block_until_all_peers_started.c */
51#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
52
53/**
54 * Debug logging shorthand
55 */
56#define LOG_DEBUG(...) LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
57
58#define NODE_BASE_IP "192.168.15."
59
60#define KNOWN_BASE_IP "92.68.151."
61
62#define ROUTER_BASE_IP "92.68.150."
63
64/**
65 * Handle for a plugin.
66 */
67struct Plugin
68{
69 /**
70 * Name of the shared library.
71 */
72 char *library_name;
73
74 /**
75 * Plugin API.
76 */
77 struct GNUNET_TESTING_PluginFunctions *api;
78
79 /**
80 * IP address of the specific node the helper is running for.
81 *
82 */
83 char *node_ip;
84
85 /**
86 * Name of the test case plugin.
87 *
88 */
89 char *plugin_name;
90
91 /**
92 * The number of namespaces
93 *
94 */
95 char *global_n;
96
97 /**
98 * The number of local nodes per namespace.
99 *
100 */
101 char *local_m;
102
103 /**
104 * The number of the namespace this node is in.
105 *
106 */
107 char *n;
108
109 /**
110 * The number of the node in the namespace.
111 *
112 */
113 char *m;
114};
115
116/**
117 * Struct with information about a specific node and the whole network namespace setup.
118 *
119 */
120struct NodeIdentifier
121{
122 /**
123 * The number of the namespace this node is in.
124 *
125 */
126 char *n;
127
128 /**
129 * The number of the node in the namespace.
130 *
131 */
132 char *m;
133
134 /**
135 * The number of namespaces
136 *
137 */
138 char *global_n;
139
140 /**
141 * The number of local nodes per namespace.
142 *
143 */
144 char *local_m;
145
146 /**
147 * Shall we read the topology from file, or from a string.
148 */
149 unsigned int *read_file;
150
151 /**
152 * String with topology data or name of topology file.
153 */
154 char *topology_data;
155};
156
157/**
158 * Context for a single write on a chunk of memory
159 */
160struct WriteContext
161{
162 /**
163 * The data to write
164 */
165 void *data;
166
167 /**
168 * The length of the data
169 */
170 size_t length;
171
172 /**
173 * The current position from where the write operation should begin
174 */
175 size_t pos;
176};
177
178/**
179 * The process handle to the testbed service
180
181static struct GNUNET_OS_Process *cmd_binary_process;*/
182
183/**
184 * Plugin to dynamically load a test case.
185 */
186struct Plugin *plugin;
187
188/**
189 * Handle to the testing system
190 */
191static struct GNUNET_TESTING_System *test_system;
192
193/**
194 * Our message stream tokenizer
195 */
196struct GNUNET_MessageStreamTokenizer *tokenizer;
197
198/**
199 * Disk handle from stdin
200 */
201static struct GNUNET_DISK_FileHandle *stdin_fd;
202
203/**
204 * Disk handle for stdout
205 */
206static struct GNUNET_DISK_FileHandle *stdout_fd;
207
208/**
209 * Pipe used to communicate shutdown via signal.
210 */
211static struct GNUNET_DISK_PipeHandle *sigpipe;
212
213/**
214 * Task identifier for the read task
215 */
216static struct GNUNET_SCHEDULER_Task *read_task_id;
217
218/**
219 * Task identifier for the write task
220 */
221static struct GNUNET_SCHEDULER_Task *write_task_id;
222
223/**
224 * Are we done reading messages from stdin?
225 */
226static int done_reading;
227
228/**
229 * Result to return in case we fail
230 */
231static int status;
232
233
234/**
235 * Task to shut down cleanly
236 *
237 * @param cls NULL
238 */
239static void
240shutdown_task (void *cls)
241{
242
243 LOG_DEBUG ("Shutting down.\n");
244
245 if (NULL != read_task_id)
246 {
247 GNUNET_SCHEDULER_cancel (read_task_id);
248 read_task_id = NULL;
249 }
250 if (NULL != write_task_id)
251 {
252 struct WriteContext *wc;
253
254 wc = GNUNET_SCHEDULER_cancel (write_task_id);
255 write_task_id = NULL;
256 GNUNET_free (wc->data);
257 GNUNET_free (wc);
258 }
259 if (NULL != stdin_fd)
260 (void) GNUNET_DISK_file_close (stdin_fd);
261 if (NULL != stdout_fd)
262 (void) GNUNET_DISK_file_close (stdout_fd);
263 GNUNET_MST_destroy (tokenizer);
264 tokenizer = NULL;
265
266 if (NULL != test_system)
267 {
268 GNUNET_TESTING_system_destroy (test_system, GNUNET_YES);
269 test_system = NULL;
270 }
271}
272
273
274/**
275 * Task to write to the standard out
276 *
277 * @param cls the WriteContext
278 */
279static void
280write_task (void *cls)
281{
282 struct WriteContext *wc = cls;
283 ssize_t bytes_wrote;
284
285 GNUNET_assert (NULL != wc);
286 write_task_id = NULL;
287 bytes_wrote = GNUNET_DISK_file_write (stdout_fd,
288 wc->data + wc->pos,
289 wc->length - wc->pos);
290 if (GNUNET_SYSERR == bytes_wrote)
291 {
292 LOG (GNUNET_ERROR_TYPE_WARNING,
293 "Cannot reply back successful initialization\n");
294 GNUNET_free (wc->data);
295 GNUNET_free (wc);
296 return;
297 }
298 wc->pos += bytes_wrote;
299 if (wc->pos == wc->length)
300 {
301 GNUNET_free (wc->data);
302 GNUNET_free (wc);
303 return;
304 }
305 write_task_id = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
306 stdout_fd,
307 &write_task,
308 wc);
309}
310
311
312/**
313 * Callback to write a message to the master loop.
314 *
315 */
316static void
317write_message (struct GNUNET_MessageHeader *message, size_t msg_length)
318{
319 struct WriteContext *wc;
320
321 wc = GNUNET_new (struct WriteContext);
322 wc->length = msg_length;
323 wc->data = message;
324 write_task_id = GNUNET_SCHEDULER_add_write_file (
325 GNUNET_TIME_UNIT_FOREVER_REL,
326 stdout_fd,
327 &write_task,
328 wc);
329}
330
331
332/**
333 * Functions with this signature are called whenever a
334 * complete message is received by the tokenizer.
335 *
336 * Do not call #GNUNET_mst_destroy() in this callback
337 *
338 * @param cls identification of the client
339 * @param message the actual message
340 * @return #GNUNET_OK on success,
341 * #GNUNET_NO to stop further processing (no error)
342 * #GNUNET_SYSERR to stop further processing with error
343 */
344static int
345tokenizer_cb (void *cls, const struct GNUNET_MessageHeader *message)
346{
347
348 struct NodeIdentifier *ni = cls;
349 const struct GNUNET_CMDS_HelperInit *msg;
350 struct GNUNET_CMDS_HelperReply *reply;
351 char *binary;
352 char *plugin_name;
353 size_t plugin_name_size;
354 uint16_t msize;
355 size_t msg_length;
356 char *router_ip;
357 char *node_ip;
358 unsigned int namespace_n;
359
360 msize = ntohs (message->size);
361 if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_INIT == ntohs (message->type))
362 {
363 msg = (const struct GNUNET_CMDS_HelperInit *) message;
364 plugin_name_size = ntohs (msg->plugin_name_size);
365 if ((sizeof(struct GNUNET_CMDS_HelperInit) + plugin_name_size) > msize)
366 {
367 GNUNET_break (0);
368 LOG (GNUNET_ERROR_TYPE_WARNING,
369 "Received unexpected message -- exiting\n");
370 goto error;
371 }
372 plugin_name = GNUNET_malloc (plugin_name_size + 1);
373 GNUNET_strlcpy (plugin_name,
374 ((char *) &msg[1]),
375 plugin_name_size + 1);
376
377 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-cmd");
378
379 plugin = GNUNET_new (struct Plugin);
380 plugin->api = GNUNET_PLUGIN_load (plugin_name,
381 NULL);
382 plugin->library_name = GNUNET_strdup (basename (plugin_name));
383
384 plugin->global_n = ni->global_n;
385 plugin->local_m = ni->local_m;
386 plugin->n = ni->n;
387 plugin->m = ni->m;
388
389 router_ip = GNUNET_malloc (strlen (ROUTER_BASE_IP) + strlen (plugin->n)
390 + 1);
391 strcpy (router_ip, ROUTER_BASE_IP);
392 strcat (router_ip, plugin->n);
393
394 sscanf (plugin->n, "%u", &namespace_n);
395
396 if (0 == namespace_n)
397 {
398 LOG (GNUNET_ERROR_TYPE_ERROR,
399 "known node n: %s\n",
400 plugin->n);
401 node_ip = GNUNET_malloc (strlen (KNOWN_BASE_IP) + strlen (plugin->m) + 1);
402 strcat (node_ip, KNOWN_BASE_IP);
403 }
404 else
405 {
406 LOG (GNUNET_ERROR_TYPE_ERROR,
407 "subnet node n: %s\n",
408 plugin->n);
409 node_ip = GNUNET_malloc (strlen (NODE_BASE_IP) + strlen (plugin->m) + 1);
410 strcat (node_ip, NODE_BASE_IP);
411 }
412 strcat (node_ip, plugin->m);
413
414 plugin->api->start_testcase (&write_message, router_ip, node_ip, plugin->m,
415 plugin->n, plugin->local_m, ni->topology_data,
416 ni->read_file);
417
418 msg_length = sizeof(struct GNUNET_CMDS_HelperReply);
419 reply = GNUNET_new (struct GNUNET_CMDS_HelperReply);
420 reply->header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_REPLY);
421 reply->header.size = htons ((uint16_t) msg_length);
422
423 write_message ((struct GNUNET_MessageHeader *) reply, msg_length);
424
425 GNUNET_free (binary);
426 GNUNET_free (router_ip);
427 GNUNET_free (plugin_name);
428
429 return GNUNET_OK;
430 }
431 else if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_ALL_PEERS_STARTED == ntohs (
432 message->type))
433 {
434 LOG (GNUNET_ERROR_TYPE_DEBUG,
435 "all peers started\n");
436 plugin->api->all_peers_started ();
437 return GNUNET_OK;
438 }
439 else if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_ALL_LOCAL_TESTS_PREPARED == ntohs (
440 message->type))
441 {
442 LOG (GNUNET_ERROR_TYPE_DEBUG,
443 "all local tests prepared\n");
444 plugin->api->all_local_tests_prepared ();
445 return GNUNET_OK;
446 }
447 else
448 {
449 LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n");
450 goto error;
451 }
452
453
454 error:
455 status = GNUNET_SYSERR;
456 LOG (GNUNET_ERROR_TYPE_ERROR,
457 "tokenizer shutting down!\n");
458 GNUNET_SCHEDULER_shutdown ();
459 return GNUNET_SYSERR;
460}
461
462
463/**
464 * Task to read from stdin
465 *
466 * @param cls NULL
467 */
468static void
469read_task (void *cls)
470{
471 char buf[GNUNET_MAX_MESSAGE_SIZE];
472 ssize_t sread;
473
474 read_task_id = NULL;
475 sread = GNUNET_DISK_file_read (stdin_fd, buf, sizeof(buf));
476 if ((GNUNET_SYSERR == sread) || (0 == sread))
477 {
478 LOG_DEBUG ("STDIN closed\n");
479 GNUNET_SCHEDULER_shutdown ();
480 return;
481 }
482 if (GNUNET_YES == done_reading)
483 {
484 /* didn't expect any more data! */
485 GNUNET_break_op (0);
486 LOG (GNUNET_ERROR_TYPE_ERROR,
487 "tokenizer shutting down during reading, didn't expect any more data!\n");
488 GNUNET_SCHEDULER_shutdown ();
489 return;
490 }
491 LOG_DEBUG ("Read %u bytes\n", (unsigned int) sread);
492 /* FIXME: could introduce a GNUNET_MST_read2 to read
493 directly from 'stdin_fd' and save a memcpy() here */
494 if (GNUNET_OK !=
495 GNUNET_MST_from_buffer (tokenizer, buf, sread, GNUNET_NO, GNUNET_NO))
496 {
497 GNUNET_break (0);
498 LOG (GNUNET_ERROR_TYPE_ERROR,
499 "tokenizer shutting down during reading, writing to buffer failed!\n");
500 GNUNET_SCHEDULER_shutdown ();
501 return;
502 }
503 read_task_id /* No timeout while reading */
504 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
505 stdin_fd,
506 &read_task,
507 NULL);
508}
509
510
511/**
512 * Main function that will be run.
513 *
514 * @param cls closure
515 * @param args remaining command-line arguments
516 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
517 * @param cfg configuration
518 */
519static void
520run (void *cls,
521 char *const *args,
522 const char *cfgfile,
523 const struct GNUNET_CONFIGURATION_Handle *cfg)
524{
525 struct NodeIdentifier *ni = cls;
526
527 LOG_DEBUG ("Starting interpreter loop helper...\n");
528
529 tokenizer = GNUNET_MST_create (&tokenizer_cb, ni);
530 stdin_fd = GNUNET_DISK_get_handle_from_native (stdin);
531 stdout_fd = GNUNET_DISK_get_handle_from_native (stdout);
532 read_task_id = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
533 stdin_fd,
534 &read_task,
535 NULL);
536 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
537}
538
539
540/**
541 * Signal handler called for SIGCHLD.
542 */
543static void
544sighandler_child_death ()
545{
546 static char c;
547 int old_errno; /* back-up errno */
548
549 old_errno = errno;
550 GNUNET_break (
551 1 ==
552 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
553 GNUNET_DISK_PIPE_END_WRITE),
554 &c,
555 sizeof(c)));
556 errno = old_errno;
557}
558
559
560/**
561 * Main function
562 *
563 * @param argc the number of command line arguments
564 * @param argv command line arg array
565 * @return return code
566 */
567int
568main (int argc, char **argv)
569{
570 struct NodeIdentifier *ni;
571 struct GNUNET_SIGNAL_Context *shc_chld;
572 struct GNUNET_GETOPT_CommandLineOption options[] =
573 { GNUNET_GETOPT_OPTION_END };
574 int ret;
575 int i;
576 size_t topology_data_length = 0;
577 unsigned int read_file;
578 char cr[1] = "\n";
579
580 GNUNET_log_setup ("gnunet-cmds-helper",
581 "DEBUG",
582 NULL);
583 ni = GNUNET_new (struct NodeIdentifier);
584 ni->global_n = argv[1];
585 ni->local_m = argv[2];
586 ni->m = argv[3];
587 ni->n = argv[4];
588
589 sscanf (argv[5], "%u", &read_file);
590
591 if (1 == read_file)
592 ni->topology_data = argv[6];
593 else
594 {
595 for (i = 6; i<argc; i++)
596 topology_data_length += strlen (argv[i]) + 1;
597 LOG (GNUNET_ERROR_TYPE_DEBUG,
598 "topo data length %lu\n",
599 topology_data_length);
600 ni->topology_data = GNUNET_malloc (topology_data_length);
601 for (i = 6; i<argc; i++)
602 {
603 strcat (ni->topology_data, argv[i]);
604 strcat (ni->topology_data, cr);
605 }
606 }
607 ni->read_file = &read_file;
608 ni->topology_data[topology_data_length - 1] = '\0';
609 LOG (GNUNET_ERROR_TYPE_DEBUG,
610 "topo data %s\n",
611 ni->topology_data);
612
613 status = GNUNET_OK;
614 if (NULL ==
615 (sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
616 {
617 GNUNET_break (0);
618 return 1;
619 }
620 shc_chld =
621 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
622 ret = GNUNET_PROGRAM_run (argc,
623 argv,
624 "gnunet-cmds-helper",
625 "Helper for starting a local interpreter loop",
626 options,
627 &run,
628 ni);
629
630 GNUNET_SIGNAL_handler_uninstall (shc_chld);
631 shc_chld = NULL;
632 GNUNET_DISK_pipe_close (sigpipe);
633 GNUNET_free (ni);
634 if (GNUNET_OK != ret)
635 return 1;
636 return (GNUNET_OK == status) ? 0 : 1;
637}
638
639
640/* end of gnunet-cmds-helper.c */