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