helper.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2011, 2012 Christian Grothoff
  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 util/helper.c
  18. * @brief API for dealing with (SUID) helper processes that communicate via
  19. * GNUNET_MessageHeaders on stdin/stdout
  20. * @author Philipp Toelke
  21. * @author Christian Grothoff
  22. */
  23. #include "platform.h"
  24. #include "gnunet_util_lib.h"
  25. #include "gnunet_mst_lib.h"
  26. /**
  27. * Entry in the queue of messages we need to transmit to the helper.
  28. */
  29. struct GNUNET_HELPER_SendHandle
  30. {
  31. /**
  32. * This is an entry in a DLL.
  33. */
  34. struct GNUNET_HELPER_SendHandle *next;
  35. /**
  36. * This is an entry in a DLL.
  37. */
  38. struct GNUNET_HELPER_SendHandle *prev;
  39. /**
  40. * Message to transmit (allocated at the end of this struct)
  41. */
  42. const struct GNUNET_MessageHeader *msg;
  43. /**
  44. * The handle to a helper process.
  45. */
  46. struct GNUNET_HELPER_Handle *h;
  47. /**
  48. * Function to call upon completion.
  49. */
  50. GNUNET_HELPER_Continuation cont;
  51. /**
  52. * Closure to 'cont'.
  53. */
  54. void *cont_cls;
  55. /**
  56. * Current write position.
  57. */
  58. unsigned int wpos;
  59. };
  60. /**
  61. * The handle to a helper process.
  62. */
  63. struct GNUNET_HELPER_Handle
  64. {
  65. /**
  66. * PipeHandle to receive data from the helper
  67. */
  68. struct GNUNET_DISK_PipeHandle *helper_in;
  69. /**
  70. * PipeHandle to send data to the helper
  71. */
  72. struct GNUNET_DISK_PipeHandle *helper_out;
  73. /**
  74. * FileHandle to receive data from the helper
  75. */
  76. const struct GNUNET_DISK_FileHandle *fh_from_helper;
  77. /**
  78. * FileHandle to send data to the helper
  79. */
  80. const struct GNUNET_DISK_FileHandle *fh_to_helper;
  81. /**
  82. * The process id of the helper
  83. */
  84. struct GNUNET_OS_Process *helper_proc;
  85. /**
  86. * The Message-Tokenizer that tokenizes the messages coming from the helper
  87. */
  88. struct GNUNET_MessageStreamTokenizer *mst;
  89. /**
  90. * The exception callback
  91. */
  92. GNUNET_HELPER_ExceptionCallback exp_cb;
  93. /**
  94. * The closure for callbacks
  95. */
  96. void *cb_cls;
  97. /**
  98. * First message queued for transmission to helper.
  99. */
  100. struct GNUNET_HELPER_SendHandle *sh_head;
  101. /**
  102. * Last message queued for transmission to helper.
  103. */
  104. struct GNUNET_HELPER_SendHandle *sh_tail;
  105. /**
  106. * Binary to run.
  107. */
  108. char *binary_name;
  109. /**
  110. * NULL-terminated list of command-line arguments.
  111. */
  112. char **binary_argv;
  113. /**
  114. * Task to read from the helper.
  115. */
  116. struct GNUNET_SCHEDULER_Task *read_task;
  117. /**
  118. * Task to read from the helper.
  119. */
  120. struct GNUNET_SCHEDULER_Task *write_task;
  121. /**
  122. * Restart task.
  123. */
  124. struct GNUNET_SCHEDULER_Task *restart_task;
  125. /**
  126. * Does the helper support the use of a control pipe for signalling?
  127. */
  128. int with_control_pipe;
  129. /**
  130. * Count start attempts to increase linear back off
  131. */
  132. unsigned int retry_back_off;
  133. };
  134. /**
  135. * Sends termination signal to the helper process. The helper process is not
  136. * reaped; call GNUNET_HELPER_wait() for reaping the dead helper process.
  137. *
  138. * @param h the helper handle
  139. * @param soft_kill if GNUNET_YES, signals termination by closing the helper's
  140. * stdin; GNUNET_NO to signal termination by sending SIGTERM to helper
  141. * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
  142. */
  143. int
  144. GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, int soft_kill)
  145. {
  146. struct GNUNET_HELPER_SendHandle *sh;
  147. int ret;
  148. while (NULL != (sh = h->sh_head))
  149. {
  150. GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
  151. if (NULL != sh->cont)
  152. sh->cont (sh->cont_cls, GNUNET_NO);
  153. GNUNET_free (sh);
  154. }
  155. if (NULL != h->restart_task)
  156. {
  157. GNUNET_SCHEDULER_cancel (h->restart_task);
  158. h->restart_task = NULL;
  159. }
  160. if (NULL != h->read_task)
  161. {
  162. GNUNET_SCHEDULER_cancel (h->read_task);
  163. h->read_task = NULL;
  164. }
  165. if (NULL == h->helper_proc)
  166. return GNUNET_SYSERR;
  167. if (GNUNET_YES == soft_kill)
  168. {
  169. /* soft-kill only possible with pipes */
  170. GNUNET_assert (NULL != h->helper_in);
  171. ret = GNUNET_DISK_pipe_close (h->helper_in);
  172. h->helper_in = NULL;
  173. h->fh_to_helper = NULL;
  174. return ret;
  175. }
  176. if (0 != GNUNET_OS_process_kill (h->helper_proc, GNUNET_TERM_SIG))
  177. return GNUNET_SYSERR;
  178. return GNUNET_OK;
  179. }
  180. /**
  181. * Reap the helper process. This call is blocking(!). The helper process
  182. * should either be sent a termination signal before or should be dead before
  183. * calling this function
  184. *
  185. * @param h the helper handle
  186. * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
  187. */
  188. int
  189. GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h)
  190. {
  191. struct GNUNET_HELPER_SendHandle *sh;
  192. int ret;
  193. ret = GNUNET_SYSERR;
  194. if (NULL != h->helper_proc)
  195. {
  196. ret = GNUNET_OS_process_wait (h->helper_proc);
  197. GNUNET_OS_process_destroy (h->helper_proc);
  198. h->helper_proc = NULL;
  199. }
  200. if (NULL != h->read_task)
  201. {
  202. GNUNET_SCHEDULER_cancel (h->read_task);
  203. h->read_task = NULL;
  204. }
  205. if (NULL != h->write_task)
  206. {
  207. GNUNET_SCHEDULER_cancel (h->write_task);
  208. h->write_task = NULL;
  209. }
  210. if (NULL != h->helper_in)
  211. {
  212. GNUNET_DISK_pipe_close (h->helper_in);
  213. h->helper_in = NULL;
  214. h->fh_to_helper = NULL;
  215. }
  216. if (NULL != h->helper_out)
  217. {
  218. GNUNET_DISK_pipe_close (h->helper_out);
  219. h->helper_out = NULL;
  220. h->fh_from_helper = NULL;
  221. }
  222. while (NULL != (sh = h->sh_head))
  223. {
  224. GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
  225. if (NULL != sh->cont)
  226. sh->cont (sh->cont_cls, GNUNET_NO);
  227. GNUNET_free (sh);
  228. }
  229. /* purge MST buffer */
  230. if (NULL != h->mst)
  231. (void) GNUNET_MST_from_buffer (h->mst, NULL, 0, GNUNET_YES, GNUNET_NO);
  232. return ret;
  233. }
  234. /**
  235. * Stop the helper process, we're closing down or had an error.
  236. *
  237. * @param h handle to the helper process
  238. * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
  239. * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
  240. */
  241. static void
  242. stop_helper (struct GNUNET_HELPER_Handle *h, int soft_kill)
  243. {
  244. if (NULL != h->restart_task)
  245. {
  246. GNUNET_SCHEDULER_cancel (h->restart_task);
  247. h->restart_task = NULL;
  248. }
  249. else
  250. {
  251. GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill));
  252. GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h));
  253. }
  254. }
  255. /**
  256. * Restart the helper process.
  257. *
  258. * @param cls handle to the helper process
  259. */
  260. static void
  261. restart_task (void *cls);
  262. /**
  263. * Read from the helper-process
  264. *
  265. * @param cls handle to the helper process
  266. */
  267. static void
  268. helper_read (void *cls)
  269. {
  270. struct GNUNET_HELPER_Handle *h = cls;
  271. char buf[GNUNET_MAX_MESSAGE_SIZE] GNUNET_ALIGN;
  272. ssize_t t;
  273. h->read_task = NULL;
  274. t = GNUNET_DISK_file_read (h->fh_from_helper, &buf, sizeof(buf));
  275. if (t < 0)
  276. {
  277. /* On read-error, restart the helper */
  278. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  279. _ ("Error reading from `%s': %s\n"),
  280. h->binary_name,
  281. strerror (errno));
  282. if (NULL != h->exp_cb)
  283. {
  284. h->exp_cb (h->cb_cls);
  285. GNUNET_HELPER_stop (h, GNUNET_NO);
  286. return;
  287. }
  288. stop_helper (h, GNUNET_NO);
  289. /* Restart the helper */
  290. h->restart_task = GNUNET_SCHEDULER_add_delayed (
  291. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
  292. h->retry_back_off),
  293. &restart_task,
  294. h);
  295. return;
  296. }
  297. if (0 == t)
  298. {
  299. /* this happens if the helper is shut down via a
  300. signal, so it is not a "hard" error */
  301. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  302. "Got 0 bytes from helper `%s' (EOF)\n",
  303. h->binary_name);
  304. if (NULL != h->exp_cb)
  305. {
  306. h->exp_cb (h->cb_cls);
  307. GNUNET_HELPER_stop (h, GNUNET_NO);
  308. return;
  309. }
  310. stop_helper (h, GNUNET_NO);
  311. /* Restart the helper */
  312. h->restart_task = GNUNET_SCHEDULER_add_delayed (
  313. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
  314. h->retry_back_off),
  315. &restart_task,
  316. h);
  317. return;
  318. }
  319. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  320. "Got %u bytes from helper `%s'\n",
  321. (unsigned int) t,
  322. h->binary_name);
  323. h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  324. h->fh_from_helper,
  325. &helper_read,
  326. h);
  327. if (GNUNET_SYSERR ==
  328. GNUNET_MST_from_buffer (h->mst, buf, t, GNUNET_NO, GNUNET_NO))
  329. {
  330. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  331. _ ("Failed to parse inbound message from helper `%s'\n"),
  332. h->binary_name);
  333. if (NULL != h->exp_cb)
  334. {
  335. h->exp_cb (h->cb_cls);
  336. GNUNET_HELPER_stop (h, GNUNET_NO);
  337. return;
  338. }
  339. stop_helper (h, GNUNET_NO);
  340. /* Restart the helper */
  341. h->restart_task = GNUNET_SCHEDULER_add_delayed (
  342. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
  343. h->retry_back_off),
  344. &restart_task,
  345. h);
  346. return;
  347. }
  348. }
  349. /**
  350. * Start the helper process.
  351. *
  352. * @param h handle to the helper process
  353. */
  354. static void
  355. start_helper (struct GNUNET_HELPER_Handle *h)
  356. {
  357. h->helper_in =
  358. GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
  359. h->helper_out =
  360. GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
  361. if ((h->helper_in == NULL) || (h->helper_out == NULL))
  362. {
  363. /* out of file descriptors? try again later... */
  364. stop_helper (h, GNUNET_NO);
  365. h->restart_task = GNUNET_SCHEDULER_add_delayed (
  366. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
  367. h->retry_back_off),
  368. &restart_task,
  369. h);
  370. return;
  371. }
  372. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  373. "Starting HELPER process `%s'\n",
  374. h->binary_name);
  375. h->fh_from_helper =
  376. GNUNET_DISK_pipe_handle (h->helper_out, GNUNET_DISK_PIPE_END_READ);
  377. h->fh_to_helper =
  378. GNUNET_DISK_pipe_handle (h->helper_in, GNUNET_DISK_PIPE_END_WRITE);
  379. h->helper_proc = GNUNET_OS_start_process_vap (h->with_control_pipe
  380. ? GNUNET_OS_INHERIT_STD_ERR
  381. | GNUNET_OS_USE_PIPE_CONTROL
  382. : GNUNET_OS_INHERIT_STD_ERR,
  383. h->helper_in,
  384. h->helper_out,
  385. NULL,
  386. h->binary_name,
  387. h->binary_argv);
  388. if (NULL == h->helper_proc)
  389. {
  390. /* failed to start process? try again later... */
  391. stop_helper (h, GNUNET_NO);
  392. h->restart_task = GNUNET_SCHEDULER_add_delayed (
  393. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
  394. h->retry_back_off),
  395. &restart_task,
  396. h);
  397. return;
  398. }
  399. GNUNET_DISK_pipe_close_end (h->helper_out, GNUNET_DISK_PIPE_END_WRITE);
  400. GNUNET_DISK_pipe_close_end (h->helper_in, GNUNET_DISK_PIPE_END_READ);
  401. if (NULL != h->mst)
  402. h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  403. h->fh_from_helper,
  404. &helper_read,
  405. h);
  406. }
  407. /**
  408. * Restart the helper process.
  409. *
  410. * @param cls handle to the helper process
  411. */
  412. static void
  413. restart_task (void *cls)
  414. {
  415. struct GNUNET_HELPER_Handle *h = cls;
  416. h->restart_task = NULL;
  417. h->retry_back_off++;
  418. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  419. "Restarting helper with back-off %u\n",
  420. h->retry_back_off);
  421. start_helper (h);
  422. }
  423. /**
  424. * Starts a helper and begins reading from it. The helper process is
  425. * restarted when it dies except when it is stopped using GNUNET_HELPER_stop()
  426. * or when the exp_cb callback is not NULL.
  427. *
  428. * @param with_control_pipe does the helper support the use of a control pipe for signalling?
  429. * @param binary_name name of the binary to run
  430. * @param binary_argv NULL-terminated list of arguments to give when starting the binary (this
  431. * argument must not be modified by the client for
  432. * the lifetime of the helper handle)
  433. * @param cb function to call if we get messages from the helper
  434. * @param exp_cb the exception callback to call. Set this to NULL if the helper
  435. * process has to be restarted automatically when it dies/crashes
  436. * @param cb_cls closure for the above callback
  437. * @return the new Handle, NULL on error
  438. */
  439. struct GNUNET_HELPER_Handle *
  440. GNUNET_HELPER_start (int with_control_pipe,
  441. const char *binary_name,
  442. char *const binary_argv[],
  443. GNUNET_MessageTokenizerCallback cb,
  444. GNUNET_HELPER_ExceptionCallback exp_cb,
  445. void *cb_cls)
  446. {
  447. struct GNUNET_HELPER_Handle *h;
  448. unsigned int c;
  449. h = GNUNET_new (struct GNUNET_HELPER_Handle);
  450. h->with_control_pipe = with_control_pipe;
  451. /* Lookup in libexec path only if we are starting gnunet helpers */
  452. if (NULL != strstr (binary_name, "gnunet"))
  453. h->binary_name = GNUNET_OS_get_libexec_binary_path (binary_name);
  454. else
  455. h->binary_name = GNUNET_strdup (binary_name);
  456. for (c = 0; NULL != binary_argv[c]; c++)
  457. ;
  458. h->binary_argv = GNUNET_malloc (sizeof(char *) * (c + 1));
  459. for (c = 0; NULL != binary_argv[c]; c++)
  460. h->binary_argv[c] = GNUNET_strdup (binary_argv[c]);
  461. h->binary_argv[c] = NULL;
  462. h->cb_cls = cb_cls;
  463. if (NULL != cb)
  464. h->mst = GNUNET_MST_create (cb, h->cb_cls);
  465. h->exp_cb = exp_cb;
  466. h->retry_back_off = 0;
  467. start_helper (h);
  468. return h;
  469. }
  470. /**
  471. * Free's the resources occupied by the helper handle
  472. *
  473. * @param h the helper handle to free
  474. */
  475. void
  476. GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h)
  477. {
  478. unsigned int c;
  479. struct GNUNET_HELPER_SendHandle *sh;
  480. if (NULL != h->write_task)
  481. {
  482. GNUNET_SCHEDULER_cancel (h->write_task);
  483. h->write_task = NULL;
  484. }
  485. GNUNET_assert (NULL == h->read_task);
  486. GNUNET_assert (NULL == h->restart_task);
  487. while (NULL != (sh = h->sh_head))
  488. {
  489. GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
  490. if (NULL != sh->cont)
  491. sh->cont (sh->cont_cls, GNUNET_SYSERR);
  492. GNUNET_free (sh);
  493. }
  494. if (NULL != h->mst)
  495. GNUNET_MST_destroy (h->mst);
  496. GNUNET_free (h->binary_name);
  497. for (c = 0; h->binary_argv[c] != NULL; c++)
  498. GNUNET_free (h->binary_argv[c]);
  499. GNUNET_free (h->binary_argv);
  500. GNUNET_free (h);
  501. }
  502. /**
  503. * Kills the helper, closes the pipe and frees the handle
  504. *
  505. * @param h handle to helper to stop
  506. * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
  507. * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
  508. */
  509. void
  510. GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h, int soft_kill)
  511. {
  512. h->exp_cb = NULL;
  513. stop_helper (h, soft_kill);
  514. GNUNET_HELPER_destroy (h);
  515. }
  516. /**
  517. * Write to the helper-process
  518. *
  519. * @param cls handle to the helper process
  520. */
  521. static void
  522. helper_write (void *cls)
  523. {
  524. struct GNUNET_HELPER_Handle *h = cls;
  525. struct GNUNET_HELPER_SendHandle *sh;
  526. const char *buf;
  527. ssize_t t;
  528. h->write_task = NULL;
  529. if (NULL == (sh = h->sh_head))
  530. {
  531. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Helper write had no work!\n");
  532. return; /* how did this happen? */
  533. }
  534. buf = (const char *) sh->msg;
  535. t = GNUNET_DISK_file_write (h->fh_to_helper,
  536. &buf[sh->wpos],
  537. ntohs (sh->msg->size) - sh->wpos);
  538. if (-1 == t)
  539. {
  540. /* On write-error, restart the helper */
  541. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  542. _ ("Error writing to `%s': %s\n"),
  543. h->binary_name,
  544. strerror (errno));
  545. if (NULL != h->exp_cb)
  546. {
  547. h->exp_cb (h->cb_cls);
  548. GNUNET_HELPER_stop (h, GNUNET_NO);
  549. return;
  550. }
  551. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  552. "Stopping and restarting helper task!\n");
  553. stop_helper (h, GNUNET_NO);
  554. /* Restart the helper */
  555. h->restart_task = GNUNET_SCHEDULER_add_delayed (
  556. GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
  557. h->retry_back_off),
  558. &restart_task,
  559. h);
  560. return;
  561. }
  562. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  563. "Transmitted %u bytes to %s\n",
  564. (unsigned int) t,
  565. h->binary_name);
  566. sh->wpos += t;
  567. if (sh->wpos == ntohs (sh->msg->size))
  568. {
  569. GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
  570. if (NULL != sh->cont)
  571. sh->cont (sh->cont_cls, GNUNET_YES);
  572. GNUNET_free (sh);
  573. }
  574. if (NULL != h->sh_head)
  575. h->write_task =
  576. GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
  577. h->fh_to_helper,
  578. &helper_write,
  579. h);
  580. }
  581. /**
  582. * Send an message to the helper.
  583. *
  584. * @param h helper to send message to
  585. * @param msg message to send
  586. * @param can_drop can the message be dropped if there is already one in the queue?
  587. * @param cont continuation to run once the message is out (#GNUNET_OK on success, #GNUNET_NO
  588. * if the helper process died, #GNUNET_SYSERR during #GNUNET_HELPER_destroy).
  589. * @param cont_cls closure for @a cont
  590. * @return NULL if the message was dropped,
  591. * otherwise handle to cancel *cont* (actual transmission may
  592. * not be abortable)
  593. */
  594. struct GNUNET_HELPER_SendHandle *
  595. GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h,
  596. const struct GNUNET_MessageHeader *msg,
  597. int can_drop,
  598. GNUNET_HELPER_Continuation cont,
  599. void *cont_cls)
  600. {
  601. struct GNUNET_HELPER_SendHandle *sh;
  602. uint16_t mlen;
  603. if (NULL == h->fh_to_helper)
  604. return NULL;
  605. if ((GNUNET_YES == can_drop) && (NULL != h->sh_head))
  606. return NULL;
  607. mlen = ntohs (msg->size);
  608. sh = GNUNET_malloc (sizeof(struct GNUNET_HELPER_SendHandle) + mlen);
  609. sh->msg = (const struct GNUNET_MessageHeader *) &sh[1];
  610. GNUNET_memcpy (&sh[1], msg, mlen);
  611. sh->h = h;
  612. sh->cont = cont;
  613. sh->cont_cls = cont_cls;
  614. GNUNET_CONTAINER_DLL_insert_tail (h->sh_head, h->sh_tail, sh);
  615. if (NULL == h->write_task)
  616. h->write_task =
  617. GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
  618. h->fh_to_helper,
  619. &helper_write,
  620. h);
  621. return sh;
  622. }
  623. /**
  624. * Cancel a #GNUNET_HELPER_send operation. If possible, transmitting the
  625. * message is also aborted, but at least 'cont' won't be
  626. * called.
  627. *
  628. * @param sh operation to cancel
  629. */
  630. void
  631. GNUNET_HELPER_send_cancel (struct GNUNET_HELPER_SendHandle *sh)
  632. {
  633. struct GNUNET_HELPER_Handle *h = sh->h;
  634. sh->cont = NULL;
  635. sh->cont_cls = NULL;
  636. if (0 == sh->wpos)
  637. {
  638. GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
  639. GNUNET_free (sh);
  640. if (NULL == h->sh_head)
  641. {
  642. GNUNET_SCHEDULER_cancel (h->write_task);
  643. h->write_task = NULL;
  644. }
  645. }
  646. }
  647. /* end of helper.c */