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