helper.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2011, 2012 Christian Grothoff
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. 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. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file util/helper.c
  19. * @brief API for dealing with (SUID) helper processes that communicate via
  20. * GNUNET_MessageHeaders on stdin/stdout
  21. * @author Philipp Toelke
  22. * @author Christian Grothoff
  23. */
  24. #include "platform.h"
  25. #include "gnunet_util_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 comming from the helper
  87. */
  88. struct GNUNET_SERVER_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. GNUNET_SCHEDULER_TaskIdentifier read_task;
  117. /**
  118. * Task to read from the helper.
  119. */
  120. GNUNET_SCHEDULER_TaskIdentifier write_task;
  121. /**
  122. * Restart task.
  123. */
  124. GNUNET_SCHEDULER_TaskIdentifier restart_task;
  125. /**
  126. * Does the helper support the use of a control pipe for signalling?
  127. */
  128. int with_control_pipe;
  129. };
  130. /**
  131. * Sends termination signal to the helper process. The helper process is not
  132. * reaped; call GNUNET_HELPER_wait() for reaping the dead helper process.
  133. *
  134. * @param h the helper handle
  135. * @param soft_kill if GNUNET_YES, signals termination by closing the helper's
  136. * stdin; GNUNET_NO to signal termination by sending SIGTERM to helper
  137. * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
  138. */
  139. int
  140. GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h,
  141. int soft_kill)
  142. {
  143. struct GNUNET_HELPER_SendHandle *sh;
  144. int ret;
  145. while (NULL != (sh = h->sh_head))
  146. {
  147. GNUNET_CONTAINER_DLL_remove (h->sh_head,
  148. h->sh_tail,
  149. sh);
  150. if (NULL != sh->cont)
  151. sh->cont (sh->cont_cls, GNUNET_NO);
  152. GNUNET_free (sh);
  153. }
  154. if (GNUNET_SCHEDULER_NO_TASK != h->restart_task)
  155. {
  156. GNUNET_SCHEDULER_cancel (h->restart_task);
  157. h->restart_task = GNUNET_SCHEDULER_NO_TASK;
  158. }
  159. if (GNUNET_SCHEDULER_NO_TASK != h->read_task)
  160. {
  161. GNUNET_SCHEDULER_cancel (h->read_task);
  162. h->read_task = GNUNET_SCHEDULER_NO_TASK;
  163. }
  164. if (NULL == h->helper_proc)
  165. return GNUNET_SYSERR;
  166. if (GNUNET_YES == soft_kill)
  167. {
  168. /* soft-kill only possible with pipes */
  169. GNUNET_assert (NULL != h->helper_in);
  170. ret = GNUNET_DISK_pipe_close (h->helper_in);
  171. h->helper_in = NULL;
  172. h->fh_to_helper = NULL;
  173. return ret;
  174. }
  175. if (0 != GNUNET_OS_process_kill (h->helper_proc, GNUNET_TERM_SIG))
  176. return GNUNET_SYSERR;
  177. return GNUNET_OK;
  178. }
  179. /**
  180. * Reap the helper process. This call is blocking(!). The helper process
  181. * should either be sent a termination signal before or should be dead before
  182. * calling this function
  183. *
  184. * @param h the helper handle
  185. * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
  186. */
  187. int
  188. GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h)
  189. {
  190. struct GNUNET_HELPER_SendHandle *sh;
  191. int ret;
  192. ret = GNUNET_SYSERR;
  193. if (NULL != h->helper_proc)
  194. {
  195. ret = GNUNET_OS_process_wait (h->helper_proc);
  196. GNUNET_OS_process_destroy (h->helper_proc);
  197. h->helper_proc = NULL;
  198. }
  199. if (GNUNET_SCHEDULER_NO_TASK != h->read_task)
  200. {
  201. GNUNET_SCHEDULER_cancel (h->read_task);
  202. h->read_task = GNUNET_SCHEDULER_NO_TASK;
  203. }
  204. if (GNUNET_SCHEDULER_NO_TASK != h->write_task)
  205. {
  206. GNUNET_SCHEDULER_cancel (h->write_task);
  207. h->write_task = GNUNET_SCHEDULER_NO_TASK;
  208. }
  209. if (NULL != h->helper_in)
  210. {
  211. GNUNET_DISK_pipe_close (h->helper_in);
  212. h->helper_in = NULL;
  213. h->fh_to_helper = NULL;
  214. }
  215. if (NULL != h->helper_out)
  216. {
  217. GNUNET_DISK_pipe_close (h->helper_out);
  218. h->helper_out = NULL;
  219. h->fh_from_helper = NULL;
  220. }
  221. while (NULL != (sh = h->sh_head))
  222. {
  223. GNUNET_CONTAINER_DLL_remove (h->sh_head,
  224. h->sh_tail,
  225. sh);
  226. if (NULL != sh->cont)
  227. sh->cont (sh->cont_cls, GNUNET_NO);
  228. GNUNET_free (sh);
  229. }
  230. /* purge MST buffer */
  231. if (NULL != h->mst)
  232. (void) GNUNET_SERVER_mst_receive (h->mst, NULL, NULL, 0, GNUNET_YES, GNUNET_NO);
  233. return ret;
  234. }
  235. /**
  236. * Stop the helper process, we're closing down or had an error.
  237. *
  238. * @param h handle to the helper process
  239. * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
  240. * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
  241. */
  242. static void
  243. stop_helper (struct GNUNET_HELPER_Handle *h,
  244. int soft_kill)
  245. {
  246. if (GNUNET_SCHEDULER_NO_TASK != h->restart_task)
  247. {
  248. GNUNET_SCHEDULER_cancel (h->restart_task);
  249. h->restart_task = GNUNET_SCHEDULER_NO_TASK;
  250. }
  251. else
  252. {
  253. GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill));
  254. GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h));
  255. }
  256. }
  257. /**
  258. * Restart the helper process.
  259. *
  260. * @param cls handle to the helper process
  261. * @param tc scheduler context
  262. */
  263. static void
  264. restart_task (void *cls,
  265. const struct GNUNET_SCHEDULER_TaskContext *tc);
  266. /**
  267. * Read from the helper-process
  268. *
  269. * @param cls handle to the helper process
  270. * @param tc scheduler context
  271. */
  272. static void
  273. helper_read (void *cls,
  274. const struct GNUNET_SCHEDULER_TaskContext *tc)
  275. {
  276. struct GNUNET_HELPER_Handle *h = cls;
  277. char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE] GNUNET_ALIGN;
  278. ssize_t t;
  279. h->read_task = GNUNET_SCHEDULER_NO_TASK;
  280. if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  281. {
  282. /* try again */
  283. h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  284. h->fh_from_helper, &helper_read, h);
  285. return;
  286. }
  287. t = GNUNET_DISK_file_read (h->fh_from_helper, &buf, sizeof (buf));
  288. if (t < 0)
  289. {
  290. /* On read-error, restart the helper */
  291. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  292. _("Error reading from `%s': %s\n"),
  293. h->binary_name,
  294. STRERROR (errno));
  295. if (NULL != h->exp_cb)
  296. {
  297. h->exp_cb (h->cb_cls);
  298. GNUNET_HELPER_stop (h, GNUNET_NO);
  299. return;
  300. }
  301. stop_helper (h, GNUNET_NO);
  302. /* Restart the helper */
  303. h->restart_task =
  304. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &restart_task, h);
  305. return;
  306. }
  307. if (0 == t)
  308. {
  309. /* this happens if the helper is shut down via a
  310. signal, so it is not a "hard" error */
  311. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  312. "Got 0 bytes from helper `%s' (EOF)\n",
  313. h->binary_name);
  314. if (NULL != h->exp_cb)
  315. {
  316. h->exp_cb (h->cb_cls);
  317. GNUNET_HELPER_stop (h, GNUNET_NO);
  318. return;
  319. }
  320. stop_helper (h, GNUNET_NO);
  321. /* Restart the helper */
  322. h->restart_task =
  323. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  324. &restart_task, h);
  325. return;
  326. }
  327. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  328. "Got %u bytes from helper `%s'\n",
  329. (unsigned int) t,
  330. h->binary_name);
  331. h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  332. h->fh_from_helper, &helper_read, h);
  333. if (GNUNET_SYSERR ==
  334. GNUNET_SERVER_mst_receive (h->mst, NULL, buf, t, GNUNET_NO, GNUNET_NO))
  335. {
  336. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  337. _("Failed to parse inbound message from helper `%s'\n"),
  338. h->binary_name);
  339. if (NULL != h->exp_cb)
  340. {
  341. h->exp_cb (h->cb_cls);
  342. GNUNET_HELPER_stop (h, GNUNET_NO);
  343. return;
  344. }
  345. stop_helper (h, GNUNET_NO);
  346. /* Restart the helper */
  347. h->restart_task =
  348. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  349. &restart_task, h);
  350. return;
  351. }
  352. }
  353. /**
  354. * Start the helper process.
  355. *
  356. * @param h handle to the helper process
  357. */
  358. static void
  359. start_helper (struct GNUNET_HELPER_Handle *h)
  360. {
  361. h->helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
  362. h->helper_out = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
  363. if ( (h->helper_in == NULL) || (h->helper_out == NULL))
  364. {
  365. /* out of file descriptors? try again later... */
  366. stop_helper (h, GNUNET_NO);
  367. h->restart_task =
  368. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  369. &restart_task, 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 =
  380. GNUNET_OS_start_process_vap (h->with_control_pipe, GNUNET_OS_INHERIT_STD_ERR,
  381. h->helper_in, h->helper_out, NULL,
  382. h->binary_name,
  383. h->binary_argv);
  384. if (NULL == h->helper_proc)
  385. {
  386. /* failed to start process? try again later... */
  387. stop_helper (h, GNUNET_NO);
  388. h->restart_task =
  389. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  390. &restart_task, h);
  391. return;
  392. }
  393. GNUNET_DISK_pipe_close_end (h->helper_out, GNUNET_DISK_PIPE_END_WRITE);
  394. GNUNET_DISK_pipe_close_end (h->helper_in, GNUNET_DISK_PIPE_END_READ);
  395. if (NULL != h->mst)
  396. h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  397. h->fh_from_helper,
  398. &helper_read,
  399. h);
  400. }
  401. /**
  402. * Restart the helper process.
  403. *
  404. * @param cls handle to the helper process
  405. * @param tc scheduler context
  406. */
  407. static void
  408. restart_task (void *cls,
  409. const struct GNUNET_SCHEDULER_TaskContext *tc)
  410. {
  411. struct GNUNET_HELPER_Handle*h = cls;
  412. h->restart_task = GNUNET_SCHEDULER_NO_TASK;
  413. start_helper (h);
  414. }
  415. /**
  416. * Starts a helper and begins reading from it. The helper process is
  417. * restarted when it dies except when it is stopped using GNUNET_HELPER_stop()
  418. * or when the exp_cb callback is not NULL.
  419. *
  420. * @param with_control_pipe does the helper support the use of a control pipe for signalling?
  421. * @param binary_name name of the binary to run
  422. * @param binary_argv NULL-terminated list of arguments to give when starting the binary (this
  423. * argument must not be modified by the client for
  424. * the lifetime of the helper handle)
  425. * @param cb function to call if we get messages from the helper
  426. * @param exp_cb the exception callback to call. Set this to NULL if the helper
  427. * process has to be restarted automatically when it dies/crashes
  428. * @param cb_cls closure for the above callback
  429. * @return the new Handle, NULL on error
  430. */
  431. struct GNUNET_HELPER_Handle *
  432. GNUNET_HELPER_start (int with_control_pipe,
  433. const char *binary_name,
  434. char *const binary_argv[],
  435. GNUNET_SERVER_MessageTokenizerCallback cb,
  436. GNUNET_HELPER_ExceptionCallback exp_cb,
  437. void *cb_cls)
  438. {
  439. struct GNUNET_HELPER_Handle *h;
  440. unsigned int c;
  441. h = GNUNET_new (struct GNUNET_HELPER_Handle);
  442. h->with_control_pipe = with_control_pipe;
  443. /* Lookup in libexec path only if we are starting gnunet helpers */
  444. if (NULL != strstr (binary_name, "gnunet"))
  445. h->binary_name = GNUNET_OS_get_libexec_binary_path (binary_name);
  446. else
  447. h->binary_name = strdup (binary_name);
  448. for (c = 0; NULL != binary_argv[c]; c++);
  449. h->binary_argv = GNUNET_malloc (sizeof (char *) * (c + 1));
  450. for (c = 0; NULL != binary_argv[c]; c++)
  451. h->binary_argv[c] = GNUNET_strdup (binary_argv[c]);
  452. h->binary_argv[c] = NULL;
  453. h->cb_cls = cb_cls;
  454. if (NULL != cb)
  455. h->mst = GNUNET_SERVER_mst_create (cb, h->cb_cls);
  456. h->exp_cb = exp_cb;
  457. start_helper (h);
  458. return h;
  459. }
  460. /**
  461. * Free's the resources occupied by the helper handle
  462. *
  463. * @param h the helper handle to free
  464. */
  465. void
  466. GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h)
  467. {
  468. unsigned int c;
  469. struct GNUNET_HELPER_SendHandle *sh;
  470. if (GNUNET_SCHEDULER_NO_TASK != h->write_task)
  471. {
  472. GNUNET_SCHEDULER_cancel (h->write_task);
  473. h->write_task = GNUNET_SCHEDULER_NO_TASK;
  474. }
  475. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->read_task);
  476. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->restart_task);
  477. while (NULL != (sh = h->sh_head))
  478. {
  479. GNUNET_CONTAINER_DLL_remove (h->sh_head,
  480. h->sh_tail,
  481. sh);
  482. if (NULL != sh->cont)
  483. sh->cont (sh->cont_cls, GNUNET_SYSERR);
  484. GNUNET_free (sh);
  485. }
  486. if (NULL != h->mst)
  487. GNUNET_SERVER_mst_destroy (h->mst);
  488. GNUNET_free (h->binary_name);
  489. for (c = 0; h->binary_argv[c] != NULL; c++)
  490. GNUNET_free (h->binary_argv[c]);
  491. GNUNET_free (h->binary_argv);
  492. GNUNET_free (h);
  493. }
  494. /**
  495. * Kills the helper, closes the pipe and frees the handle
  496. *
  497. * @param h handle to helper to stop
  498. * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
  499. * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
  500. */
  501. void
  502. GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h,
  503. int soft_kill)
  504. {
  505. h->exp_cb = NULL;
  506. stop_helper (h, soft_kill);
  507. GNUNET_HELPER_destroy (h);
  508. }
  509. /**
  510. * Write to the helper-process
  511. *
  512. * @param cls handle to the helper process
  513. * @param tc scheduler context
  514. */
  515. static void
  516. helper_write (void *cls,
  517. const struct GNUNET_SCHEDULER_TaskContext *tc)
  518. {
  519. struct GNUNET_HELPER_Handle *h = cls;
  520. struct GNUNET_HELPER_SendHandle *sh;
  521. const char *buf;
  522. ssize_t t;
  523. h->write_task = GNUNET_SCHEDULER_NO_TASK;
  524. if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  525. {
  526. /* try again */
  527. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  528. "Helper write triggered during shutdown, retrying\n");
  529. h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
  530. h->fh_to_helper, &helper_write, h);
  531. return;
  532. }
  533. if (NULL == (sh = h->sh_head))
  534. {
  535. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  536. "Helper write had no work!\n");
  537. return; /* how did this happen? */
  538. }
  539. buf = (const char*) sh->msg;
  540. t = GNUNET_DISK_file_write (h->fh_to_helper,
  541. &buf[sh->wpos],
  542. ntohs (sh->msg->size) - sh->wpos);
  543. if (-1 == t)
  544. {
  545. /* On write-error, restart the helper */
  546. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  547. _("Error writing to `%s': %s\n"),
  548. h->binary_name,
  549. STRERROR (errno));
  550. if (NULL != h->exp_cb)
  551. {
  552. h->exp_cb (h->cb_cls);
  553. GNUNET_HELPER_stop (h, GNUNET_NO);
  554. return;
  555. }
  556. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  557. "Stopping and restarting helper task!\n");
  558. stop_helper (h, GNUNET_NO);
  559. /* Restart the helper */
  560. h->restart_task =
  561. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  562. &restart_task, h);
  563. return;
  564. }
  565. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  566. "Transmitted %u bytes to %s\n",
  567. (unsigned int) t,
  568. h->binary_name);
  569. sh->wpos += t;
  570. if (sh->wpos == ntohs (sh->msg->size))
  571. {
  572. GNUNET_CONTAINER_DLL_remove (h->sh_head,
  573. h->sh_tail,
  574. sh);
  575. if (NULL != sh->cont)
  576. sh->cont (sh->cont_cls, GNUNET_YES);
  577. GNUNET_free (sh);
  578. }
  579. if (NULL != h->sh_head)
  580. h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
  581. h->fh_to_helper,
  582. &helper_write,
  583. h);
  584. }
  585. /**
  586. * Send an message to the helper.
  587. *
  588. * @param h helper to send message to
  589. * @param msg message to send
  590. * @param can_drop can the message be dropped if there is already one in the queue?
  591. * @param cont continuation to run once the message is out (#GNUNET_OK on succees, #GNUNET_NO
  592. * if the helper process died, #GNUNET_SYSERR during #GNUNET_HELPER_destroy).
  593. * @param cont_cls closure for @a cont
  594. * @return NULL if the message was dropped,
  595. * otherwise handle to cancel *cont* (actual transmission may
  596. * not be abortable)
  597. */
  598. struct GNUNET_HELPER_SendHandle *
  599. GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h,
  600. const struct GNUNET_MessageHeader *msg,
  601. int can_drop,
  602. GNUNET_HELPER_Continuation cont,
  603. void *cont_cls)
  604. {
  605. struct GNUNET_HELPER_SendHandle *sh;
  606. uint16_t mlen;
  607. if (NULL == h->fh_to_helper)
  608. return NULL;
  609. if ( (GNUNET_YES == can_drop) &&
  610. (NULL != h->sh_head) )
  611. return NULL;
  612. mlen = ntohs (msg->size);
  613. sh = GNUNET_malloc (sizeof (struct GNUNET_HELPER_SendHandle) + mlen);
  614. sh->msg = (const struct GNUNET_MessageHeader*) &sh[1];
  615. memcpy (&sh[1], msg, mlen);
  616. sh->h = h;
  617. sh->cont = cont;
  618. sh->cont_cls = cont_cls;
  619. GNUNET_CONTAINER_DLL_insert_tail (h->sh_head,
  620. h->sh_tail,
  621. sh);
  622. if (GNUNET_SCHEDULER_NO_TASK == h->write_task)
  623. h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
  624. h->fh_to_helper,
  625. &helper_write,
  626. h);
  627. return sh;
  628. }
  629. /**
  630. * Cancel a #GNUNET_HELPER_send operation. If possible, transmitting the
  631. * message is also aborted, but at least 'cont' won't be
  632. * called.
  633. *
  634. * @param sh operation to cancel
  635. */
  636. void
  637. GNUNET_HELPER_send_cancel (struct GNUNET_HELPER_SendHandle *sh)
  638. {
  639. struct GNUNET_HELPER_Handle *h = sh->h;
  640. sh->cont = NULL;
  641. sh->cont_cls = NULL;
  642. if (0 == sh->wpos)
  643. {
  644. GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
  645. GNUNET_free (sh);
  646. if (NULL == h->sh_head)
  647. {
  648. GNUNET_SCHEDULER_cancel (h->write_task);
  649. h->write_task = GNUNET_SCHEDULER_NO_TASK;
  650. }
  651. }
  652. }
  653. /* end of helper.c */