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