X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fhelper.c;h=404dad4fa61d7ccaffcac24417331ae409baf893;hb=fdbe690beeec04066f18302401096eb5212c3f6a;hp=b88789b9d86e6d775cda556e54e3f304c01fb8fe;hpb=1af6c9bcef1500cdf1c59be391c70c4dc6617401;p=oweals%2Fgnunet.git diff --git a/src/util/helper.c b/src/util/helper.c index b88789b9d..404dad4fa 100644 --- a/src/util/helper.c +++ b/src/util/helper.c @@ -20,7 +20,8 @@ /** * @file util/helper.c - * @brief API for dealing with (SUID) helper processes that communicate via GNUNET_MessageHeaders on stdin/stdout + * @brief API for dealing with (SUID) helper processes that communicate via + * GNUNET_MessageHeaders on stdin/stdout * @author Philipp Toelke * @author Christian Grothoff */ @@ -48,12 +49,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. */ @@ -82,22 +83,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 */ @@ -131,13 +132,13 @@ struct GNUNET_HELPER_Handle /** * Binary to run. */ - const char *binary_name; + char *binary_name; /** * NULL-terminated list of command-line arguments. */ char **binary_argv; - + /** * Task to read from the helper. */ @@ -162,21 +163,29 @@ struct GNUNET_HELPER_Handle /** - * Stop the helper process, we're closing down or had an error. + * Sends termination signal to the helper process. The helper process is not + * reaped; call GNUNET_HELPER_wait() for reaping the dead helper process. * - * @param h handle to the helper process + * @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 */ -static void -stop_helper (struct GNUNET_HELPER_Handle *h) +int +GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, + int soft_kill) { struct GNUNET_HELPER_SendHandle *sh; + int ret; - if (NULL != h->helper_proc) + while (NULL != (sh = h->sh_head)) { - GNUNET_break (0 == GNUNET_OS_process_kill (h->helper_proc, SIGTERM)); - GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (h->helper_proc)); - GNUNET_OS_process_destroy (h->helper_proc); - h->helper_proc = NULL; + GNUNET_CONTAINER_DLL_remove (h->sh_head, + h->sh_tail, + sh); + if (NULL != sh->cont) + sh->cont (sh->cont_cls, GNUNET_NO); + GNUNET_free (sh); } if (GNUNET_SCHEDULER_NO_TASK != h->restart_task) { @@ -188,6 +197,49 @@ stop_helper (struct GNUNET_HELPER_Handle *h) GNUNET_SCHEDULER_cancel (h->read_task); h->read_task = GNUNET_SCHEDULER_NO_TASK; } + 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); + h->helper_in = NULL; + h->fh_to_helper = NULL; + return ret; + } + if (0 != GNUNET_OS_process_kill (h->helper_proc, GNUNET_TERM_SIG)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Reap the helper process. This call is blocking(!). The helper process + * should either be sent a termination signal before or should be dead before + * calling this function + * + * @param h the helper handle + * @return #GNUNET_OK on success; #GNUNET_SYSERR on error + */ +int +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) + { + GNUNET_SCHEDULER_cancel (h->read_task); + h->read_task = GNUNET_SCHEDULER_NO_TASK; + } if (GNUNET_SCHEDULER_NO_TASK != h->write_task) { GNUNET_SCHEDULER_cancel (h->write_task); @@ -215,7 +267,33 @@ stop_helper (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_SERVER_mst_receive (h->mst, NULL, NULL, 0, GNUNET_YES, GNUNET_NO); + return ret; +} + + +/** + * 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 + */ +static void +stop_helper (struct GNUNET_HELPER_Handle *h, + int soft_kill) +{ + if (GNUNET_SCHEDULER_NO_TASK != h->restart_task) + { + GNUNET_SCHEDULER_cancel (h->restart_task); + h->restart_task = GNUNET_SCHEDULER_NO_TASK; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill)); + GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h)); + } } @@ -263,10 +341,10 @@ helper_read (void *cls, if (NULL != h->exp_cb) { h->exp_cb (h->cb_cls); - GNUNET_HELPER_stop (h); + GNUNET_HELPER_stop (h, GNUNET_NO); return; } - stop_helper (h); + stop_helper (h, GNUNET_NO); /* Restart the helper */ h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &restart_task, h); @@ -274,26 +352,26 @@ helper_read (void *cls, } 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_INFO, - _("Got 0 bytes from helper `%s' (EOF)\n"), + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got 0 bytes from helper `%s' (EOF)\n", h->binary_name); if (NULL != h->exp_cb) { h->exp_cb (h->cb_cls); - GNUNET_HELPER_stop (h); + GNUNET_HELPER_stop (h, GNUNET_NO); return; } - stop_helper (h); + stop_helper (h, GNUNET_NO); /* Restart the helper */ h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &restart_task, h); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Got %u bytes from helper `%s'\n"), + 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, @@ -301,16 +379,16 @@ helper_read (void *cls, if (GNUNET_SYSERR == GNUNET_SERVER_mst_receive (h->mst, NULL, 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) { h->exp_cb (h->cb_cls); - GNUNET_HELPER_stop (h); + GNUNET_HELPER_stop (h, GNUNET_NO); return; - } - stop_helper (h); + } + stop_helper (h, GNUNET_NO); /* Restart the helper */ h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, @@ -333,39 +411,40 @@ start_helper (struct GNUNET_HELPER_Handle *h) if ( (h->helper_in == NULL) || (h->helper_out == NULL)) { /* out of file descriptors? try again later... */ - stop_helper (h); + stop_helper (h, GNUNET_NO); h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + &restart_task, h); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Starting HELPER process `%s'\n"), + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting HELPER process `%s'\n", h->binary_name); h->fh_from_helper = GNUNET_DISK_pipe_handle (h->helper_out, GNUNET_DISK_PIPE_END_READ); 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, + GNUNET_OS_start_process_vap (h->with_control_pipe, GNUNET_OS_INHERIT_STD_ERR, h->helper_in, h->helper_out, h->binary_name, h->binary_argv); if (NULL == h->helper_proc) { /* failed to start process? try again later... */ - stop_helper (h); + stop_helper (h, GNUNET_NO); h->restart_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &restart_task, h); + &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); } @@ -410,21 +489,24 @@ GNUNET_HELPER_start (int with_control_pipe, GNUNET_HELPER_ExceptionCallback exp_cb, void *cb_cls) { - struct GNUNET_HELPER_Handle*h; - int c = 0; - h = GNUNET_malloc (sizeof (struct GNUNET_HELPER_Handle)); - h->with_control_pipe = with_control_pipe; - h->binary_name = GNUNET_strdup (binary_name); + struct GNUNET_HELPER_Handle *h; + unsigned int c; - for (c = 0; binary_argv[c] != NULL; c++) - c ++; - h->binary_argv = GNUNET_malloc (sizeof (char *[c + 1])); - for (c = 0; binary_argv[c] != NULL; c++) + 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); + 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_SERVER_mst_create (cb, h->cb_cls); h->exp_cb = exp_cb; start_helper (h); return h; @@ -432,17 +514,23 @@ GNUNET_HELPER_start (int with_control_pipe, /** - * @brief Kills the helper, closes the pipe and frees the h + * Free's the resources occupied by the helper handle * - * @param h h to helper to stop + * @param h the helper handle to free */ void -GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h) +GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h) { + unsigned int c; struct GNUNET_HELPER_SendHandle *sh; - int c; - h->exp_cb = NULL; - /* signal pending writes that we were stopped */ + + if (GNUNET_SCHEDULER_NO_TASK != h->write_task) + { + GNUNET_SCHEDULER_cancel (h->write_task); + h->write_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->read_task); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->restart_task); while (NULL != (sh = h->sh_head)) { GNUNET_CONTAINER_DLL_remove (h->sh_head, @@ -452,9 +540,9 @@ GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h) sh->cont (sh->cont_cls, GNUNET_SYSERR); GNUNET_free (sh); } - stop_helper (h); - GNUNET_SERVER_mst_destroy (h->mst); - GNUNET_free ((char *) h->binary_name); + if (NULL != h->mst) + GNUNET_SERVER_mst_destroy (h->mst); + GNUNET_free (h->binary_name); for (c = 0; h->binary_argv[c] != NULL; c++) GNUNET_free (h->binary_argv[c]); GNUNET_free (h->binary_argv); @@ -462,6 +550,23 @@ GNUNET_HELPER_stop (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 + */ +void +GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h, + int soft_kill) +{ + h->exp_cb = NULL; + stop_helper (h, soft_kill); + GNUNET_HELPER_destroy (h); +} + + /** * Write to the helper-process * @@ -481,34 +586,48 @@ helper_write (void *cls, 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); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Helper write triggered during shutdown, retrying\n"); + h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->fh_to_helper, &helper_write, h); return; - } + } 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) { h->exp_cb (h->cb_cls); - GNUNET_HELPER_stop (h); + GNUNET_HELPER_stop (h, GNUNET_NO); return; } - stop_helper (h); + 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); 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)) { @@ -521,8 +640,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); } @@ -533,15 +652,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, @@ -567,15 +686,15 @@ GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h, sh); if (GNUNET_SCHEDULER_NO_TASK == 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. * @@ -591,12 +710,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; } - GNUNET_free (sh); } }