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.c629
1 files changed, 0 insertions, 629 deletions
diff --git a/src/testing/gnunet-cmds-helper.c b/src/testing/gnunet-cmds-helper.c
deleted file mode 100644
index f6f079802..000000000
--- a/src/testing/gnunet-cmds-helper.c
+++ /dev/null
@@ -1,629 +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 * Our message stream tokenizer
190 */
191struct GNUNET_MessageStreamTokenizer *tokenizer;
192
193/**
194 * Disk handle from stdin
195 */
196static struct GNUNET_DISK_FileHandle *stdin_fd;
197
198/**
199 * Disk handle for stdout
200 */
201static struct GNUNET_DISK_FileHandle *stdout_fd;
202
203/**
204 * Pipe used to communicate shutdown via signal.
205 */
206static struct GNUNET_DISK_PipeHandle *sigpipe;
207
208/**
209 * Task identifier for the read task
210 */
211static struct GNUNET_SCHEDULER_Task *read_task_id;
212
213/**
214 * Task identifier for the write task
215 */
216static struct GNUNET_SCHEDULER_Task *write_task_id;
217
218/**
219 * Are we done reading messages from stdin?
220 */
221static int done_reading;
222
223/**
224 * Result to return in case we fail
225 */
226static int status;
227
228
229/**
230 * Task to shut down cleanly
231 *
232 * @param cls NULL
233 */
234static void
235shutdown_task (void *cls)
236{
237
238 LOG_DEBUG ("Shutting down.\n");
239
240 if (NULL != read_task_id)
241 {
242 GNUNET_SCHEDULER_cancel (read_task_id);
243 read_task_id = NULL;
244 }
245 if (NULL != write_task_id)
246 {
247 struct WriteContext *wc;
248
249 wc = GNUNET_SCHEDULER_cancel (write_task_id);
250 write_task_id = NULL;
251 GNUNET_free (wc->data);
252 GNUNET_free (wc);
253 }
254 if (NULL != stdin_fd)
255 (void) GNUNET_DISK_file_close (stdin_fd);
256 if (NULL != stdout_fd)
257 (void) GNUNET_DISK_file_close (stdout_fd);
258 GNUNET_MST_destroy (tokenizer);
259 tokenizer = NULL;
260}
261
262
263/**
264 * Task to write to the standard out
265 *
266 * @param cls the WriteContext
267 */
268static void
269write_task (void *cls)
270{
271 struct WriteContext *wc = cls;
272 ssize_t bytes_wrote;
273
274 GNUNET_assert (NULL != wc);
275 write_task_id = NULL;
276 bytes_wrote = GNUNET_DISK_file_write (stdout_fd,
277 wc->data + wc->pos,
278 wc->length - wc->pos);
279 if (GNUNET_SYSERR == bytes_wrote)
280 {
281 LOG (GNUNET_ERROR_TYPE_WARNING,
282 "Cannot reply back successful initialization\n");
283 GNUNET_free (wc->data);
284 GNUNET_free (wc);
285 return;
286 }
287 wc->pos += bytes_wrote;
288 if (wc->pos == wc->length)
289 {
290 GNUNET_free (wc->data);
291 GNUNET_free (wc);
292 return;
293 }
294 write_task_id = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
295 stdout_fd,
296 &write_task,
297 wc);
298}
299
300
301/**
302 * Callback to write a message to the master loop.
303 *
304 */
305static void
306write_message (struct GNUNET_MessageHeader *message, size_t msg_length)
307{
308 struct WriteContext *wc;
309
310 wc = GNUNET_new (struct WriteContext);
311 wc->length = msg_length;
312 wc->data = message;
313 write_task_id = GNUNET_SCHEDULER_add_write_file (
314 GNUNET_TIME_UNIT_FOREVER_REL,
315 stdout_fd,
316 &write_task,
317 wc);
318}
319
320
321/**
322 * Functions with this signature are called whenever a
323 * complete message is received by the tokenizer.
324 *
325 * Do not call #GNUNET_mst_destroy() in this callback
326 *
327 * @param cls identification of the client
328 * @param message the actual message
329 * @return #GNUNET_OK on success,
330 * #GNUNET_NO to stop further processing (no error)
331 * #GNUNET_SYSERR to stop further processing with error
332 */
333static int
334tokenizer_cb (void *cls, const struct GNUNET_MessageHeader *message)
335{
336
337 struct NodeIdentifier *ni = cls;
338 const struct GNUNET_CMDS_HelperInit *msg;
339 struct GNUNET_CMDS_HelperReply *reply;
340 char *binary;
341 char *plugin_name;
342 size_t plugin_name_size;
343 uint16_t msize;
344 size_t msg_length;
345 char *router_ip;
346 char *node_ip;
347 unsigned int namespace_n;
348
349 msize = ntohs (message->size);
350 if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_INIT == ntohs (message->type))
351 {
352 msg = (const struct GNUNET_CMDS_HelperInit *) message;
353 plugin_name_size = ntohs (msg->plugin_name_size);
354 if ((sizeof(struct GNUNET_CMDS_HelperInit) + plugin_name_size) > msize)
355 {
356 GNUNET_break (0);
357 LOG (GNUNET_ERROR_TYPE_WARNING,
358 "Received unexpected message -- exiting\n");
359 goto error;
360 }
361 plugin_name = GNUNET_malloc (plugin_name_size + 1);
362 GNUNET_strlcpy (plugin_name,
363 ((char *) &msg[1]),
364 plugin_name_size + 1);
365
366 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-cmd");
367
368 plugin = GNUNET_new (struct Plugin);
369 plugin->api = GNUNET_PLUGIN_load (plugin_name,
370 NULL);
371 plugin->library_name = GNUNET_strdup (basename (plugin_name));
372
373 plugin->global_n = ni->global_n;
374 plugin->local_m = ni->local_m;
375 plugin->n = ni->n;
376 plugin->m = ni->m;
377
378 router_ip = GNUNET_malloc (strlen (ROUTER_BASE_IP) + strlen (plugin->n)
379 + 1);
380 strcpy (router_ip, ROUTER_BASE_IP);
381 strcat (router_ip, plugin->n);
382
383 sscanf (plugin->n, "%u", &namespace_n);
384
385 if (0 == namespace_n)
386 {
387 LOG (GNUNET_ERROR_TYPE_ERROR,
388 "known node n: %s\n",
389 plugin->n);
390 node_ip = GNUNET_malloc (strlen (KNOWN_BASE_IP) + strlen (plugin->m) + 1);
391 strcat (node_ip, KNOWN_BASE_IP);
392 }
393 else
394 {
395 LOG (GNUNET_ERROR_TYPE_ERROR,
396 "subnet node n: %s\n",
397 plugin->n);
398 node_ip = GNUNET_malloc (strlen (NODE_BASE_IP) + strlen (plugin->m) + 1);
399 strcat (node_ip, NODE_BASE_IP);
400 }
401 strcat (node_ip, plugin->m);
402
403 plugin->api->start_testcase (&write_message, router_ip, node_ip, plugin->m,
404 plugin->n, plugin->local_m, ni->topology_data,
405 ni->read_file);
406
407 msg_length = sizeof(struct GNUNET_CMDS_HelperReply);
408 reply = GNUNET_new (struct GNUNET_CMDS_HelperReply);
409 reply->header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_REPLY);
410 reply->header.size = htons ((uint16_t) msg_length);
411
412 write_message ((struct GNUNET_MessageHeader *) reply, msg_length);
413
414 GNUNET_free (binary);
415 GNUNET_free (router_ip);
416 GNUNET_free (plugin_name);
417
418 return GNUNET_OK;
419 }
420 else if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_ALL_PEERS_STARTED == ntohs (
421 message->type))
422 {
423 LOG (GNUNET_ERROR_TYPE_DEBUG,
424 "all peers started\n");
425 plugin->api->all_peers_started ();
426 return GNUNET_OK;
427 }
428 else if (GNUNET_MESSAGE_TYPE_CMDS_HELPER_ALL_LOCAL_TESTS_PREPARED == ntohs (
429 message->type))
430 {
431 LOG (GNUNET_ERROR_TYPE_DEBUG,
432 "all local tests prepared\n");
433 plugin->api->all_local_tests_prepared ();
434 return GNUNET_OK;
435 }
436 else
437 {
438 LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n");
439 goto error;
440 }
441
442
443 error:
444 status = GNUNET_SYSERR;
445 LOG (GNUNET_ERROR_TYPE_ERROR,
446 "tokenizer shutting down!\n");
447 GNUNET_SCHEDULER_shutdown ();
448 return GNUNET_SYSERR;
449}
450
451
452/**
453 * Task to read from stdin
454 *
455 * @param cls NULL
456 */
457static void
458read_task (void *cls)
459{
460 char buf[GNUNET_MAX_MESSAGE_SIZE];
461 ssize_t sread;
462
463 read_task_id = NULL;
464 sread = GNUNET_DISK_file_read (stdin_fd, buf, sizeof(buf));
465 if ((GNUNET_SYSERR == sread) || (0 == sread))
466 {
467 LOG_DEBUG ("STDIN closed\n");
468 GNUNET_SCHEDULER_shutdown ();
469 return;
470 }
471 if (GNUNET_YES == done_reading)
472 {
473 /* didn't expect any more data! */
474 GNUNET_break_op (0);
475 LOG (GNUNET_ERROR_TYPE_ERROR,
476 "tokenizer shutting down during reading, didn't expect any more data!\n");
477 GNUNET_SCHEDULER_shutdown ();
478 return;
479 }
480 LOG_DEBUG ("Read %u bytes\n", (unsigned int) sread);
481 /* FIXME: could introduce a GNUNET_MST_read2 to read
482 directly from 'stdin_fd' and save a memcpy() here */
483 if (GNUNET_OK !=
484 GNUNET_MST_from_buffer (tokenizer, buf, sread, GNUNET_NO, GNUNET_NO))
485 {
486 GNUNET_break (0);
487 LOG (GNUNET_ERROR_TYPE_ERROR,
488 "tokenizer shutting down during reading, writing to buffer failed!\n");
489 GNUNET_SCHEDULER_shutdown ();
490 return;
491 }
492 read_task_id /* No timeout while reading */
493 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
494 stdin_fd,
495 &read_task,
496 NULL);
497}
498
499
500/**
501 * Main function that will be run.
502 *
503 * @param cls closure
504 * @param args remaining command-line arguments
505 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
506 * @param cfg configuration
507 */
508static void
509run (void *cls,
510 char *const *args,
511 const char *cfgfile,
512 const struct GNUNET_CONFIGURATION_Handle *cfg)
513{
514 struct NodeIdentifier *ni = cls;
515
516 LOG_DEBUG ("Starting interpreter loop helper...\n");
517
518 tokenizer = GNUNET_MST_create (&tokenizer_cb, ni);
519 stdin_fd = GNUNET_DISK_get_handle_from_native (stdin);
520 stdout_fd = GNUNET_DISK_get_handle_from_native (stdout);
521 read_task_id = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
522 stdin_fd,
523 &read_task,
524 NULL);
525 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
526}
527
528
529/**
530 * Signal handler called for SIGCHLD.
531 */
532static void
533sighandler_child_death ()
534{
535 static char c;
536 int old_errno; /* back-up errno */
537
538 old_errno = errno;
539 GNUNET_break (
540 1 ==
541 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
542 GNUNET_DISK_PIPE_END_WRITE),
543 &c,
544 sizeof(c)));
545 errno = old_errno;
546}
547
548
549/**
550 * Main function
551 *
552 * @param argc the number of command line arguments
553 * @param argv command line arg array
554 * @return return code
555 */
556int
557main (int argc, char **argv)
558{
559 struct NodeIdentifier *ni;
560 struct GNUNET_SIGNAL_Context *shc_chld;
561 struct GNUNET_GETOPT_CommandLineOption options[] =
562 { GNUNET_GETOPT_OPTION_END };
563 int ret;
564 int i;
565 size_t topology_data_length = 0;
566 unsigned int read_file;
567 char cr[1] = "\n";
568
569 GNUNET_log_setup ("gnunet-cmds-helper",
570 "DEBUG",
571 NULL);
572 ni = GNUNET_new (struct NodeIdentifier);
573 ni->global_n = argv[1];
574 ni->local_m = argv[2];
575 ni->m = argv[3];
576 ni->n = argv[4];
577
578 sscanf (argv[5], "%u", &read_file);
579
580 if (1 == read_file)
581 ni->topology_data = argv[6];
582 else
583 {
584 for (i = 6; i<argc; i++)
585 topology_data_length += strlen (argv[i]) + 1;
586 LOG (GNUNET_ERROR_TYPE_DEBUG,
587 "topo data length %lu\n",
588 topology_data_length);
589 ni->topology_data = GNUNET_malloc (topology_data_length);
590 for (i = 6; i<argc; i++)
591 {
592 strcat (ni->topology_data, argv[i]);
593 strcat (ni->topology_data, cr);
594 }
595 }
596 ni->read_file = &read_file;
597 ni->topology_data[topology_data_length - 1] = '\0';
598 LOG (GNUNET_ERROR_TYPE_DEBUG,
599 "topo data %s\n",
600 ni->topology_data);
601
602 status = GNUNET_OK;
603 if (NULL ==
604 (sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
605 {
606 GNUNET_break (0);
607 return 1;
608 }
609 shc_chld =
610 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
611 ret = GNUNET_PROGRAM_run (argc,
612 argv,
613 "gnunet-cmds-helper",
614 "Helper for starting a local interpreter loop",
615 options,
616 &run,
617 ni);
618
619 GNUNET_SIGNAL_handler_uninstall (shc_chld);
620 shc_chld = NULL;
621 GNUNET_DISK_pipe_close (sigpipe);
622 GNUNET_free (ni);
623 if (GNUNET_OK != ret)
624 return 1;
625 return (GNUNET_OK == status) ? 0 : 1;
626}
627
628
629/* end of gnunet-cmds-helper.c */