Don't shadow the system() function
[oweals/gnunet.git] / src / util / helper.c
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
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
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 /**
33  * Entry in the queue of messages we need to transmit to the helper.
34  */
35 struct GNUNET_HELPER_SendHandle
36 {
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 /**
77  * The handle to a helper process.
78  */
79 struct GNUNET_HELPER_Handle
80 {
81
82   /**
83    * PipeHandle to receive data from the helper
84    */
85   struct GNUNET_DISK_PipeHandle *helper_in;
86
87   /**
88    * PipeHandle to send data to the helper
89    */
90   struct GNUNET_DISK_PipeHandle *helper_out;
91
92   /**
93    * FileHandle to receive data from the helper
94    */
95   const struct GNUNET_DISK_FileHandle *fh_from_helper;
96
97   /**
98    * FileHandle to send data to the helper
99    */
100   const struct GNUNET_DISK_FileHandle *fh_to_helper;
101
102   /**
103    * The process id of the helper
104    */
105   struct GNUNET_OS_Process *helper_proc;
106
107   /**
108    * The Message-Tokenizer that tokenizes the messages comming from the helper
109    */
110   struct GNUNET_SERVER_MessageStreamTokenizer *mst;
111
112   /**
113    * The exception callback
114    */
115   GNUNET_HELPER_ExceptionCallback exp_cb;
116
117   /**
118    * The closure for callbacks
119    */
120   void *cb_cls;
121
122   /**
123    * First message queued for transmission to helper.
124    */
125   struct GNUNET_HELPER_SendHandle *sh_head;
126
127   /**
128    * Last message queued for transmission to helper.
129    */
130   struct GNUNET_HELPER_SendHandle *sh_tail;
131
132   /**
133    * Binary to run.
134    */
135   char *binary_name;
136
137   /**
138    * NULL-terminated list of command-line arguments.
139    */
140   char **binary_argv;
141                 
142   /**
143    * Task to read from the helper.
144    */
145   struct GNUNET_SCHEDULER_Task * read_task;
146
147   /**
148    * Task to read from the helper.
149    */
150   struct GNUNET_SCHEDULER_Task * write_task;
151
152   /**
153    * Restart task.
154    */
155   struct GNUNET_SCHEDULER_Task * restart_task;
156
157   /**
158    * Does the helper support the use of a control pipe for signalling?
159    */
160   int with_control_pipe;
161
162   /**
163    * Count start attempts to increase linear back off
164    */
165   unsigned int retry_back_off;
166 };
167
168
169 /**
170  * Sends termination signal to the helper process.  The helper process is not
171  * reaped; call GNUNET_HELPER_wait() for reaping the dead helper process.
172  *
173  * @param h the helper handle
174  * @param soft_kill if GNUNET_YES, signals termination by closing the helper's
175  *          stdin; GNUNET_NO to signal termination by sending SIGTERM to helper
176  * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
177  */
178 int
179 GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h,
180                     int soft_kill)
181 {
182   struct GNUNET_HELPER_SendHandle *sh;
183   int ret;
184
185   while (NULL != (sh = h->sh_head))
186   {
187     GNUNET_CONTAINER_DLL_remove (h->sh_head,
188                                  h->sh_tail,
189                                  sh);
190     if (NULL != sh->cont)
191       sh->cont (sh->cont_cls, GNUNET_NO);
192     GNUNET_free (sh);
193   }
194   if (NULL != h->restart_task)
195   {
196     GNUNET_SCHEDULER_cancel (h->restart_task);
197     h->restart_task = NULL;
198   }
199   if (NULL != h->read_task)
200   {
201     GNUNET_SCHEDULER_cancel (h->read_task);
202     h->read_task = NULL;
203   }
204   if (NULL == h->helper_proc)
205     return GNUNET_SYSERR;
206   if (GNUNET_YES == soft_kill)
207   {
208     /* soft-kill only possible with pipes */
209     GNUNET_assert (NULL != h->helper_in);
210     ret = GNUNET_DISK_pipe_close (h->helper_in);
211     h->helper_in = NULL;
212     h->fh_to_helper = NULL;
213     return ret;
214   }
215   if (0 != GNUNET_OS_process_kill (h->helper_proc, GNUNET_TERM_SIG))
216     return GNUNET_SYSERR;
217   return GNUNET_OK;
218 }
219
220
221 /**
222  * Reap the helper process.  This call is blocking(!).  The helper process
223  * should either be sent a termination signal before or should be dead before
224  * calling this function
225  *
226  * @param h the helper handle
227  * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
228  */
229 int
230 GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h)
231 {
232   struct GNUNET_HELPER_SendHandle *sh;
233   int ret;
234
235   ret = GNUNET_SYSERR;
236   if (NULL != h->helper_proc)
237   {
238     ret = GNUNET_OS_process_wait (h->helper_proc);
239     GNUNET_OS_process_destroy (h->helper_proc);
240     h->helper_proc = NULL;
241   }
242   if (NULL != h->read_task)
243   {
244     GNUNET_SCHEDULER_cancel (h->read_task);
245     h->read_task = NULL;
246   }
247   if (NULL != h->write_task)
248   {
249     GNUNET_SCHEDULER_cancel (h->write_task);
250     h->write_task = NULL;
251   }
252   if (NULL != h->helper_in)
253   {
254     GNUNET_DISK_pipe_close (h->helper_in);
255     h->helper_in = NULL;
256     h->fh_to_helper = NULL;
257   }
258   if (NULL != h->helper_out)
259   {
260     GNUNET_DISK_pipe_close (h->helper_out);
261     h->helper_out = NULL;
262     h->fh_from_helper = NULL;
263   }
264   while (NULL != (sh = h->sh_head))
265   {
266     GNUNET_CONTAINER_DLL_remove (h->sh_head,
267                                  h->sh_tail,
268                                  sh);
269     if (NULL != sh->cont)
270       sh->cont (sh->cont_cls, GNUNET_NO);
271     GNUNET_free (sh);
272   }
273   /* purge MST buffer */
274   if (NULL != h->mst)
275     (void) GNUNET_SERVER_mst_receive (h->mst, NULL, NULL, 0, GNUNET_YES, GNUNET_NO);
276   return ret;
277 }
278
279
280 /**
281  * Stop the helper process, we're closing down or had an error.
282  *
283  * @param h handle to the helper process
284  * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
285  *          stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
286  */
287 static void
288 stop_helper (struct GNUNET_HELPER_Handle *h,
289              int soft_kill)
290 {
291   if (NULL != h->restart_task)
292   {
293     GNUNET_SCHEDULER_cancel (h->restart_task);
294     h->restart_task = NULL;
295   }
296   else
297   {
298     GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill));
299     GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h));
300   }
301 }
302
303
304 /**
305  * Restart the helper process.
306  *
307  * @param cls handle to the helper process
308  * @param tc scheduler context
309  */
310 static void
311 restart_task (void *cls,
312               const struct GNUNET_SCHEDULER_TaskContext *tc);
313
314
315 /**
316  * Read from the helper-process
317  *
318  * @param cls handle to the helper process
319  * @param tc scheduler context
320  */
321 static void
322 helper_read (void *cls,
323              const struct GNUNET_SCHEDULER_TaskContext *tc)
324 {
325   struct GNUNET_HELPER_Handle *h = cls;
326   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE] GNUNET_ALIGN;
327   ssize_t t;
328
329   h->read_task = NULL;
330   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
331   {
332     /* try again */
333     h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
334                                                    h->fh_from_helper, &helper_read, h);
335     return;
336   }
337   t = GNUNET_DISK_file_read (h->fh_from_helper, &buf, sizeof (buf));
338   if (t < 0)
339   {
340     /* On read-error, restart the helper */
341     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
342                 _("Error reading from `%s': %s\n"),
343                 h->binary_name,
344                 STRERROR (errno));
345     if (NULL != h->exp_cb)
346     {
347       h->exp_cb (h->cb_cls);
348       GNUNET_HELPER_stop (h, GNUNET_NO);
349       return;
350     }
351     stop_helper (h, GNUNET_NO);
352     /* Restart the helper */
353     h->restart_task = GNUNET_SCHEDULER_add_delayed(
354         GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
355             h->retry_back_off), &restart_task, h);
356     return;
357   }
358   if (0 == t)
359   {
360     /* this happens if the helper is shut down via a
361        signal, so it is not a "hard" error */
362     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363                 "Got 0 bytes from helper `%s' (EOF)\n",
364                 h->binary_name);
365     if (NULL != h->exp_cb)
366     {
367       h->exp_cb (h->cb_cls);
368       GNUNET_HELPER_stop (h, GNUNET_NO);
369       return;
370     }
371     stop_helper (h, GNUNET_NO);
372     /* Restart the helper */
373     h->restart_task = GNUNET_SCHEDULER_add_delayed(
374         GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
375             h->retry_back_off), &restart_task, h);
376     return;
377   }
378   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379               "Got %u bytes from helper `%s'\n",
380               (unsigned int) t,
381               h->binary_name);
382   h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
383                                                  h->fh_from_helper, &helper_read, h);
384   if (GNUNET_SYSERR ==
385       GNUNET_SERVER_mst_receive (h->mst, NULL, buf, t, GNUNET_NO, GNUNET_NO))
386   {
387     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
388                 _("Failed to parse inbound message from helper `%s'\n"),
389                 h->binary_name);
390     if (NULL != h->exp_cb)
391     {
392       h->exp_cb (h->cb_cls);
393       GNUNET_HELPER_stop (h, GNUNET_NO);
394       return;
395     }
396     stop_helper (h, GNUNET_NO);
397     /* Restart the helper */
398     h->restart_task = GNUNET_SCHEDULER_add_delayed(
399         GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
400             h->retry_back_off), &restart_task, h);
401     return;
402   }
403 }
404
405
406 /**
407  * Start the helper process.
408  *
409  * @param h handle to the helper process
410  */
411 static void
412 start_helper (struct GNUNET_HELPER_Handle *h)
413 {
414   h->helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
415   h->helper_out = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
416   if ( (h->helper_in == NULL) || (h->helper_out == NULL))
417   {
418     /* out of file descriptors? try again later... */
419     stop_helper (h, GNUNET_NO);
420     h->restart_task =
421       GNUNET_SCHEDULER_add_delayed(
422           GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
423               h->retry_back_off), &restart_task, h);
424     return;
425   }
426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427               "Starting HELPER process `%s'\n",
428               h->binary_name);
429   h->fh_from_helper =
430       GNUNET_DISK_pipe_handle (h->helper_out, GNUNET_DISK_PIPE_END_READ);
431   h->fh_to_helper =
432       GNUNET_DISK_pipe_handle (h->helper_in, GNUNET_DISK_PIPE_END_WRITE);
433   h->helper_proc =
434     GNUNET_OS_start_process_vap (h->with_control_pipe, GNUNET_OS_INHERIT_STD_ERR,
435                                  h->helper_in, h->helper_out, NULL,
436                                  h->binary_name,
437                                  h->binary_argv);
438   if (NULL == h->helper_proc)
439   {
440     /* failed to start process? try again later... */
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), &restart_task, h);
445     return;
446   }
447   GNUNET_DISK_pipe_close_end (h->helper_out, GNUNET_DISK_PIPE_END_WRITE);
448   GNUNET_DISK_pipe_close_end (h->helper_in, GNUNET_DISK_PIPE_END_READ);
449   if (NULL != h->mst)
450     h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
451                                                    h->fh_from_helper,
452                                                    &helper_read,
453                                                    h);
454 }
455
456
457 /**
458  * Restart the helper process.
459  *
460  * @param cls handle to the helper process
461  * @param tc scheduler context
462  */
463 static void
464 restart_task (void *cls,
465               const struct GNUNET_SCHEDULER_TaskContext *tc)
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_SERVER_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   h->binary_argv = GNUNET_malloc (sizeof (char *) * (c + 1));
514   for (c = 0; NULL != binary_argv[c]; c++)
515     h->binary_argv[c] = GNUNET_strdup (binary_argv[c]);
516   h->binary_argv[c] = NULL;
517   h->cb_cls = cb_cls;
518   if (NULL != cb)
519     h->mst = GNUNET_SERVER_mst_create (cb, h->cb_cls);
520   h->exp_cb = exp_cb;
521   h->retry_back_off = 0;
522   start_helper (h);
523   return h;
524 }
525
526
527 /**
528  * Free's the resources occupied by the helper handle
529  *
530  * @param h the helper handle to free
531  */
532 void
533 GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h)
534 {
535   unsigned int c;
536   struct GNUNET_HELPER_SendHandle *sh;
537
538   if (NULL != h->write_task)
539   {
540     GNUNET_SCHEDULER_cancel (h->write_task);
541     h->write_task = NULL;
542   }
543   GNUNET_assert (NULL == h->read_task);
544   GNUNET_assert (NULL == h->restart_task);
545   while (NULL != (sh = h->sh_head))
546   {
547     GNUNET_CONTAINER_DLL_remove (h->sh_head,
548                                  h->sh_tail,
549                                  sh);
550     if (NULL != sh->cont)
551       sh->cont (sh->cont_cls, GNUNET_SYSERR);
552     GNUNET_free (sh);
553   }
554   if (NULL != h->mst)
555     GNUNET_SERVER_mst_destroy (h->mst);
556   GNUNET_free (h->binary_name);
557   for (c = 0; h->binary_argv[c] != NULL; c++)
558     GNUNET_free (h->binary_argv[c]);
559   GNUNET_free (h->binary_argv);
560   GNUNET_free (h);
561 }
562
563
564 /**
565  * Kills the helper, closes the pipe and frees the handle
566  *
567  * @param h handle to helper to stop
568  * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
569  *          stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
570  */
571 void
572 GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h,
573                     int soft_kill)
574 {
575   h->exp_cb = NULL;
576   stop_helper (h, soft_kill);
577   GNUNET_HELPER_destroy (h);
578 }
579
580
581 /**
582  * Write to the helper-process
583  *
584  * @param cls handle to the helper process
585  * @param tc scheduler context
586  */
587 static void
588 helper_write (void *cls,
589              const struct GNUNET_SCHEDULER_TaskContext *tc)
590 {
591   struct GNUNET_HELPER_Handle *h = cls;
592   struct GNUNET_HELPER_SendHandle *sh;
593   const char *buf;
594   ssize_t t;
595
596   h->write_task = NULL;
597   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
598   {
599     /* try again */
600     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601                 "Helper write triggered during shutdown, retrying\n");
602     h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
603                                                      h->fh_to_helper, &helper_write, h);
604     return;
605   }
606   if (NULL == (sh = h->sh_head))
607   {
608     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609                 "Helper write had no work!\n");
610     return; /* how did this happen? */
611   }
612   buf = (const char*) sh->msg;
613   t = GNUNET_DISK_file_write (h->fh_to_helper,
614                               &buf[sh->wpos],
615                               ntohs (sh->msg->size) - sh->wpos);
616   if (-1 == t)
617   {
618     /* On write-error, restart the helper */
619     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
620                 _("Error writing to `%s': %s\n"),
621                 h->binary_name,
622                 STRERROR (errno));
623     if (NULL != h->exp_cb)
624     {
625       h->exp_cb (h->cb_cls);
626       GNUNET_HELPER_stop (h, GNUNET_NO);
627       return;
628     }
629     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
630                 "Stopping and restarting helper task!\n");
631     stop_helper (h, GNUNET_NO);
632     /* Restart the helper */
633       h->restart_task = GNUNET_SCHEDULER_add_delayed(
634           GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
635               h->retry_back_off), &restart_task, h);
636     return;
637   }
638   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639               "Transmitted %u bytes to %s\n",
640               (unsigned int) t,
641               h->binary_name);
642   sh->wpos += t;
643   if (sh->wpos == ntohs (sh->msg->size))
644   {
645     GNUNET_CONTAINER_DLL_remove (h->sh_head,
646                                  h->sh_tail,
647                                  sh);
648     if (NULL != sh->cont)
649       sh->cont (sh->cont_cls, GNUNET_YES);
650     GNUNET_free (sh);
651   }
652   if (NULL != h->sh_head)
653     h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
654                                                      h->fh_to_helper,
655                                                      &helper_write,
656                                                      h);
657 }
658
659
660 /**
661  * Send an message to the helper.
662  *
663  * @param h helper to send message to
664  * @param msg message to send
665  * @param can_drop can the message be dropped if there is already one in the queue?
666  * @param cont continuation to run once the message is out (#GNUNET_OK on succees, #GNUNET_NO
667  *             if the helper process died, #GNUNET_SYSERR during #GNUNET_HELPER_destroy).
668  * @param cont_cls closure for @a cont
669  * @return NULL if the message was dropped,
670  *         otherwise handle to cancel *cont* (actual transmission may
671  *         not be abortable)
672  */
673 struct GNUNET_HELPER_SendHandle *
674 GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h,
675                     const struct GNUNET_MessageHeader *msg,
676                     int can_drop,
677                     GNUNET_HELPER_Continuation cont,
678                     void *cont_cls)
679 {
680   struct GNUNET_HELPER_SendHandle *sh;
681   uint16_t mlen;
682
683   if (NULL == h->fh_to_helper)
684     return NULL;
685   if ( (GNUNET_YES == can_drop) &&
686        (NULL != h->sh_head) )
687     return NULL;
688   mlen = ntohs (msg->size);
689   sh = GNUNET_malloc (sizeof (struct GNUNET_HELPER_SendHandle) + mlen);
690   sh->msg = (const struct GNUNET_MessageHeader*) &sh[1];
691   memcpy (&sh[1], msg, mlen);
692   sh->h = h;
693   sh->cont = cont;
694   sh->cont_cls = cont_cls;
695   GNUNET_CONTAINER_DLL_insert_tail (h->sh_head,
696                                     h->sh_tail,
697                                     sh);
698   if (NULL == h->write_task)
699     h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
700                                                      h->fh_to_helper,
701                                                      &helper_write,
702                                                      h);
703
704   return sh;
705 }
706
707 /**
708  * Cancel a #GNUNET_HELPER_send operation.  If possible, transmitting the
709  * message is also aborted, but at least 'cont' won't be
710  * called.
711  *
712  * @param sh operation to cancel
713  */
714 void
715 GNUNET_HELPER_send_cancel (struct GNUNET_HELPER_SendHandle *sh)
716 {
717   struct GNUNET_HELPER_Handle *h = sh->h;
718
719   sh->cont = NULL;
720   sh->cont_cls = NULL;
721   if (0 == sh->wpos)
722   {
723     GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
724     GNUNET_free (sh);
725     if (NULL == h->sh_head)
726     {
727       GNUNET_SCHEDULER_cancel (h->write_task);
728       h->write_task = NULL;
729     }
730   }
731 }
732
733
734 /* end of helper.c */