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