X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fhelper.c;h=5cdfb904a1e54019485fd12329fe1d460fc025e5;hb=6ede545d597509fefcc3d4fd2ef865bc5f57603f;hp=57e5798d8f1f43187f778ba49e30ba698556d48e;hpb=efad1d6733cd7e75a4e6c846ad7c031a0167b8be;p=oweals%2Fgnunet.git diff --git a/src/util/helper.c b/src/util/helper.c index 57e5798d8..5cdfb904a 100644 --- a/src/util/helper.c +++ b/src/util/helper.c @@ -1,21 +1,19 @@ /* This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff + Copyright (C) 2011, 2012 Christian Grothoff - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ /** @@ -27,6 +25,7 @@ */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_mst_lib.h" /** @@ -49,12 +48,12 @@ struct GNUNET_HELPER_SendHandle * Message to transmit (allocated at the end of this struct) */ const struct GNUNET_MessageHeader *msg; - + /** * The handle to a helper process. */ struct GNUNET_HELPER_Handle *h; - + /** * Function to call upon completion. */ @@ -83,22 +82,22 @@ struct GNUNET_HELPER_Handle * PipeHandle to receive data from the helper */ struct GNUNET_DISK_PipeHandle *helper_in; - + /** * PipeHandle to send data to the helper */ struct GNUNET_DISK_PipeHandle *helper_out; - + /** * FileHandle to receive data from the helper */ const struct GNUNET_DISK_FileHandle *fh_from_helper; - + /** * FileHandle to send data to the helper */ const struct GNUNET_DISK_FileHandle *fh_to_helper; - + /** * The process id of the helper */ @@ -107,7 +106,7 @@ struct GNUNET_HELPER_Handle /** * The Message-Tokenizer that tokenizes the messages comming from the helper */ - struct GNUNET_SERVER_MessageStreamTokenizer *mst; + struct GNUNET_MessageStreamTokenizer *mst; /** * The exception callback @@ -138,27 +137,31 @@ struct GNUNET_HELPER_Handle * NULL-terminated list of command-line arguments. */ char **binary_argv; - + /** * Task to read from the helper. */ - GNUNET_SCHEDULER_TaskIdentifier read_task; + struct GNUNET_SCHEDULER_Task *read_task; /** * Task to read from the helper. */ - GNUNET_SCHEDULER_TaskIdentifier write_task; + struct GNUNET_SCHEDULER_Task *write_task; /** * Restart task. */ - GNUNET_SCHEDULER_TaskIdentifier restart_task; + struct GNUNET_SCHEDULER_Task *restart_task; /** * Does the helper support the use of a control pipe for signalling? */ int with_control_pipe; + /** + * Count start attempts to increase linear back off + */ + unsigned int retry_back_off; }; @@ -169,10 +172,11 @@ struct GNUNET_HELPER_Handle * @param h the helper handle * @param soft_kill if GNUNET_YES, signals termination by closing the helper's * stdin; GNUNET_NO to signal termination by sending SIGTERM to helper - * @return GNUNET_OK on success; GNUNET_SYSERR on error + * @return #GNUNET_OK on success; #GNUNET_SYSERR on error */ int -GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, int soft_kill) +GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, + int soft_kill) { struct GNUNET_HELPER_SendHandle *sh; int ret; @@ -186,15 +190,20 @@ GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, int soft_kill) sh->cont (sh->cont_cls, GNUNET_NO); GNUNET_free (sh); } - if (GNUNET_SCHEDULER_NO_TASK != h->restart_task) + if (NULL != h->restart_task) { GNUNET_SCHEDULER_cancel (h->restart_task); - h->restart_task = GNUNET_SCHEDULER_NO_TASK; + h->restart_task = NULL; + } + if (NULL != h->read_task) + { + GNUNET_SCHEDULER_cancel (h->read_task); + h->read_task = NULL; } if (NULL == h->helper_proc) return GNUNET_SYSERR; if (GNUNET_YES == soft_kill) - { + { /* soft-kill only possible with pipes */ GNUNET_assert (NULL != h->helper_in); ret = GNUNET_DISK_pipe_close (h->helper_in); @@ -202,7 +211,7 @@ GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, int soft_kill) h->fh_to_helper = NULL; return ret; } - if (0 != GNUNET_OS_process_kill (h->helper_proc, SIGTERM)) + if (0 != GNUNET_OS_process_kill (h->helper_proc, GNUNET_TERM_SIG)) return GNUNET_SYSERR; return GNUNET_OK; } @@ -214,7 +223,7 @@ GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, int soft_kill) * calling this function * * @param h the helper handle - * @return GNUNET_OK on success; GNUNET_SYSERR on error + * @return #GNUNET_OK on success; #GNUNET_SYSERR on error */ int GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h) @@ -222,21 +231,22 @@ GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h) struct GNUNET_HELPER_SendHandle *sh; int ret; + ret = GNUNET_SYSERR; if (NULL != h->helper_proc) { ret = GNUNET_OS_process_wait (h->helper_proc); GNUNET_OS_process_destroy (h->helper_proc); h->helper_proc = NULL; } - if (GNUNET_SCHEDULER_NO_TASK != h->read_task) + if (NULL != h->read_task) { GNUNET_SCHEDULER_cancel (h->read_task); - h->read_task = GNUNET_SCHEDULER_NO_TASK; + h->read_task = NULL; } - if (GNUNET_SCHEDULER_NO_TASK != h->write_task) + if (NULL != h->write_task) { GNUNET_SCHEDULER_cancel (h->write_task); - h->write_task = GNUNET_SCHEDULER_NO_TASK; + h->write_task = NULL; } if (NULL != h->helper_in) { @@ -260,7 +270,11 @@ GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h) GNUNET_free (sh); } /* purge MST buffer */ - (void) GNUNET_SERVER_mst_receive (h->mst, NULL, NULL, 0, GNUNET_YES, GNUNET_NO); + if (NULL != h->mst) + (void) GNUNET_MST_from_buffer (h->mst, + NULL, 0, + GNUNET_YES, + GNUNET_NO); return ret; } @@ -269,14 +283,23 @@ GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h) * Stop the helper process, we're closing down or had an error. * * @param h handle to the helper process - * @param soft_kill if GNUNET_YES, signals termination by closing the helper's - * stdin; GNUNET_NO to signal termination by sending SIGTERM to helper + * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's + * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper */ static void -stop_helper (struct GNUNET_HELPER_Handle *h, int soft_kill) +stop_helper (struct GNUNET_HELPER_Handle *h, + int soft_kill) { - GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill)); - GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h)); + if (NULL != h->restart_task) + { + GNUNET_SCHEDULER_cancel (h->restart_task); + h->restart_task = NULL; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill)); + GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h)); + } } @@ -284,35 +307,24 @@ stop_helper (struct GNUNET_HELPER_Handle *h, int soft_kill) * Restart the helper process. * * @param cls handle to the helper process - * @param tc scheduler context */ static void -restart_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); +restart_task (void *cls); /** * Read from the helper-process * * @param cls handle to the helper process - * @param tc scheduler context */ static void -helper_read (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +helper_read (void *cls) { struct GNUNET_HELPER_Handle *h = cls; - char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE] GNUNET_ALIGN; + char buf[GNUNET_MAX_MESSAGE_SIZE] GNUNET_ALIGN; ssize_t t; - h->read_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { - /* try again */ - h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->fh_from_helper, &helper_read, h); - return; - } + h->read_task = NULL; t = GNUNET_DISK_file_read (h->fh_from_helper, &buf, sizeof (buf)); if (t < 0) { @@ -329,15 +341,16 @@ helper_read (void *cls, } stop_helper (h, GNUNET_NO); /* Restart the helper */ - h->restart_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &restart_task, h); + h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, + h->retry_back_off), + &restart_task, h); return; } if (0 == t) { - /* this happens if the helper is shut down via a + /* this happens if the helper is shut down via a signal, so it is not a "hard" error */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got 0 bytes from helper `%s' (EOF)\n", h->binary_name); if (NULL != h->exp_cb) @@ -348,21 +361,26 @@ helper_read (void *cls, } stop_helper (h, GNUNET_NO); /* Restart the helper */ - h->restart_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + h->restart_task + = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, + h->retry_back_off), + &restart_task, h); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes from helper `%s'\n", (unsigned int) t, h->binary_name); h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->fh_from_helper, &helper_read, h); + h->fh_from_helper, + &helper_read, h); if (GNUNET_SYSERR == - GNUNET_SERVER_mst_receive (h->mst, NULL, buf, t, GNUNET_NO, GNUNET_NO)) + GNUNET_MST_from_buffer (h->mst, + buf, t, + GNUNET_NO, + GNUNET_NO)) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to parse inbound message from helper `%s'\n"), h->binary_name); if (NULL != h->exp_cb) @@ -370,12 +388,12 @@ helper_read (void *cls, h->exp_cb (h->cb_cls); GNUNET_HELPER_stop (h, GNUNET_NO); return; - } + } stop_helper (h, GNUNET_NO); /* Restart the helper */ - h->restart_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, + h->retry_back_off), + &restart_task, h); return; } } @@ -396,8 +414,9 @@ start_helper (struct GNUNET_HELPER_Handle *h) /* out of file descriptors? try again later... */ stop_helper (h, GNUNET_NO); h->restart_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, + h->retry_back_off), + &restart_task, h); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -408,25 +427,26 @@ start_helper (struct GNUNET_HELPER_Handle *h) h->fh_to_helper = GNUNET_DISK_pipe_handle (h->helper_in, GNUNET_DISK_PIPE_END_WRITE); h->helper_proc = - GNUNET_OS_start_process_vap (h->with_control_pipe, GNUNET_OS_INHERIT_STD_ERR, - h->helper_in, h->helper_out, + GNUNET_OS_start_process_vap (h->with_control_pipe, GNUNET_OS_INHERIT_STD_ERR, + h->helper_in, h->helper_out, NULL, h->binary_name, h->binary_argv); if (NULL == h->helper_proc) { /* failed to start process? try again later... */ stop_helper (h, GNUNET_NO); - h->restart_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, + h->retry_back_off), + &restart_task, h); return; } GNUNET_DISK_pipe_close_end (h->helper_out, GNUNET_DISK_PIPE_END_WRITE); GNUNET_DISK_pipe_close_end (h->helper_in, GNUNET_DISK_PIPE_END_READ); - h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->fh_from_helper, - &helper_read, - h); + if (NULL != h->mst) + h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->fh_from_helper, + &helper_read, + h); } @@ -434,15 +454,17 @@ start_helper (struct GNUNET_HELPER_Handle *h) * Restart the helper process. * * @param cls handle to the helper process - * @param tc scheduler context */ static void -restart_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +restart_task (void *cls) { struct GNUNET_HELPER_Handle*h = cls; - h->restart_task = GNUNET_SCHEDULER_NO_TASK; + h->restart_task = NULL; + h->retry_back_off++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Restarting helper with back-off %u\n", + h->retry_back_off); start_helper (h); } @@ -467,28 +489,31 @@ struct GNUNET_HELPER_Handle * GNUNET_HELPER_start (int with_control_pipe, const char *binary_name, char *const binary_argv[], - GNUNET_SERVER_MessageTokenizerCallback cb, + GNUNET_MessageTokenizerCallback cb, GNUNET_HELPER_ExceptionCallback exp_cb, void *cb_cls) { struct GNUNET_HELPER_Handle *h; unsigned int c; - h = GNUNET_malloc (sizeof (struct GNUNET_HELPER_Handle)); + h = GNUNET_new (struct GNUNET_HELPER_Handle); h->with_control_pipe = with_control_pipe; /* Lookup in libexec path only if we are starting gnunet helpers */ if (NULL != strstr (binary_name, "gnunet")) h->binary_name = GNUNET_OS_get_libexec_binary_path (binary_name); else - h->binary_name = strdup (binary_name); + h->binary_name = GNUNET_strdup (binary_name); for (c = 0; NULL != binary_argv[c]; c++); h->binary_argv = GNUNET_malloc (sizeof (char *) * (c + 1)); for (c = 0; NULL != binary_argv[c]; c++) h->binary_argv[c] = GNUNET_strdup (binary_argv[c]); h->binary_argv[c] = NULL; h->cb_cls = cb_cls; - h->mst = GNUNET_SERVER_mst_create (cb, h->cb_cls); + if (NULL != cb) + h->mst = GNUNET_MST_create (cb, + h->cb_cls); h->exp_cb = exp_cb; + h->retry_back_off = 0; start_helper (h); return h; } @@ -503,8 +528,26 @@ void GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h) { unsigned int c; + struct GNUNET_HELPER_SendHandle *sh; - GNUNET_SERVER_mst_destroy (h->mst); + if (NULL != h->write_task) + { + GNUNET_SCHEDULER_cancel (h->write_task); + h->write_task = NULL; + } + GNUNET_assert (NULL == h->read_task); + GNUNET_assert (NULL == h->restart_task); + while (NULL != (sh = h->sh_head)) + { + GNUNET_CONTAINER_DLL_remove (h->sh_head, + h->sh_tail, + sh); + if (NULL != sh->cont) + sh->cont (sh->cont_cls, GNUNET_SYSERR); + GNUNET_free (sh); + } + if (NULL != h->mst) + GNUNET_MST_destroy (h->mst); GNUNET_free (h->binary_name); for (c = 0; h->binary_argv[c] != NULL; c++) GNUNET_free (h->binary_argv[c]); @@ -517,11 +560,12 @@ GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h) * Kills the helper, closes the pipe and frees the handle * * @param h handle to helper to stop - * @param soft_kill if GNUNET_YES, signals termination by closing the helper's - * stdin; GNUNET_NO to signal termination by sending SIGTERM to helper + * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's + * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper */ void -GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h, int soft_kill) +GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h, + int soft_kill) { h->exp_cb = NULL; stop_helper (h, soft_kill); @@ -533,34 +577,31 @@ GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h, int soft_kill) * Write to the helper-process * * @param cls handle to the helper process - * @param tc scheduler context */ static void -helper_write (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +helper_write (void *cls) { struct GNUNET_HELPER_Handle *h = cls; struct GNUNET_HELPER_SendHandle *sh; const char *buf; ssize_t t; - h->write_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { - /* try again */ - h->write_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->fh_to_helper, &helper_write, h); - return; - } + h->write_task = NULL; if (NULL == (sh = h->sh_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Helper write had no work!\n"); return; /* how did this happen? */ + } buf = (const char*) sh->msg; - t = GNUNET_DISK_file_write (h->fh_to_helper, &buf[sh->wpos], ntohs (sh->msg->size) - sh->wpos); - if (t <= 0) + t = GNUNET_DISK_file_write (h->fh_to_helper, + &buf[sh->wpos], + ntohs (sh->msg->size) - sh->wpos); + if (-1 == t) { /* On write-error, restart the helper */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Error writing to `%s': %s\n"), + _("Error writing to `%s': %s\n"), h->binary_name, STRERROR (errno)); if (NULL != h->exp_cb) @@ -569,13 +610,19 @@ helper_write (void *cls, GNUNET_HELPER_stop (h, GNUNET_NO); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping and restarting helper task!\n"); stop_helper (h, GNUNET_NO); /* Restart the helper */ - h->restart_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, + h->retry_back_off), + &restart_task, h); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitted %u bytes to %s\n", + (unsigned int) t, + h->binary_name); sh->wpos += t; if (sh->wpos == ntohs (sh->msg->size)) { @@ -588,8 +635,8 @@ helper_write (void *cls, } if (NULL != h->sh_head) h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->fh_to_helper, - &helper_write, + h->fh_to_helper, + &helper_write, h); } @@ -600,15 +647,15 @@ helper_write (void *cls, * @param h helper to send message to * @param msg message to send * @param can_drop can the message be dropped if there is already one in the queue? - * @param cont continuation to run once the message is out (PREREQ_DONE on succees, CANCEL - * if the helper process died, NULL during GNUNET_HELPER_stop). - * @param cont_cls closure for 'cont' - * @return NULL if the message was dropped, + * @param cont continuation to run once the message is out (#GNUNET_OK on succees, #GNUNET_NO + * if the helper process died, #GNUNET_SYSERR during #GNUNET_HELPER_destroy). + * @param cont_cls closure for @a cont + * @return NULL if the message was dropped, * otherwise handle to cancel *cont* (actual transmission may * not be abortable) */ struct GNUNET_HELPER_SendHandle * -GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h, +GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h, const struct GNUNET_MessageHeader *msg, int can_drop, GNUNET_HELPER_Continuation cont, @@ -625,24 +672,24 @@ GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h, mlen = ntohs (msg->size); sh = GNUNET_malloc (sizeof (struct GNUNET_HELPER_SendHandle) + mlen); sh->msg = (const struct GNUNET_MessageHeader*) &sh[1]; - memcpy (&sh[1], msg, mlen); + GNUNET_memcpy (&sh[1], msg, mlen); sh->h = h; sh->cont = cont; sh->cont_cls = cont_cls; GNUNET_CONTAINER_DLL_insert_tail (h->sh_head, h->sh_tail, sh); - if (GNUNET_SCHEDULER_NO_TASK == h->write_task) + if (NULL == h->write_task) h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->fh_to_helper, - &helper_write, + h->fh_to_helper, + &helper_write, h); - + return sh; } /** - * Cancel a 'send' operation. If possible, transmitting the + * Cancel a #GNUNET_HELPER_send operation. If possible, transmitting the * message is also aborted, but at least 'cont' won't be * called. * @@ -658,12 +705,12 @@ GNUNET_HELPER_send_cancel (struct GNUNET_HELPER_SendHandle *sh) if (0 == sh->wpos) { GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh); + GNUNET_free (sh); if (NULL == h->sh_head) { GNUNET_SCHEDULER_cancel (h->write_task); - h->write_task = GNUNET_SCHEDULER_NO_TASK; + h->write_task = NULL; } - GNUNET_free (sh); } }