gnunet-helper-testbed.c 17 KB

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