- fix
[oweals/gnunet.git] / src / util / helper.c
index 64b014994874502bf4eb790d916ef356c733c33b..d7a3cb271755fc01a2dfe80597593800ff159872 100644 (file)
@@ -131,12 +131,12 @@ struct GNUNET_HELPER_Handle
   /**
    * Binary to run.
    */
-  const char *binary_name;
+  char *binary_name;
 
   /**
    * NULL-terminated list of command-line arguments.
    */
-  char *const *binary_argv;
+  char **binary_argv;
                    
   /**
    * Task to read from the helper.
@@ -152,6 +152,12 @@ struct GNUNET_HELPER_Handle
    * Restart task.
    */
   GNUNET_SCHEDULER_TaskIdentifier restart_task;
+
+  /**
+   * Does the helper support the use of a control pipe for signalling?
+   */
+  int with_control_pipe;
+
 };
 
 
@@ -159,15 +165,26 @@ struct GNUNET_HELPER_Handle
  * 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)
+stop_helper (struct GNUNET_HELPER_Handle *h, int soft_kill)
 {
   struct GNUNET_HELPER_SendHandle *sh;
 
   if (NULL != h->helper_proc)
   {
-    GNUNET_break (0 == GNUNET_OS_process_kill (h->helper_proc, SIGTERM));
+    if (GNUNET_YES == soft_kill)
+    { 
+      /* soft-kill only possible with pipes */
+      GNUNET_assert (NULL != h->helper_in);
+      GNUNET_DISK_pipe_close (h->helper_in);
+      h->helper_in = NULL;
+      h->fh_to_helper = NULL;
+    }
+    else
+      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;
@@ -260,7 +277,7 @@ helper_read (void *cls,
       GNUNET_HELPER_stop (h);
       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);
@@ -270,8 +287,8 @@ helper_read (void *cls,
   {
     /* 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)
     {
@@ -279,15 +296,15 @@ helper_read (void *cls,
       GNUNET_HELPER_stop (h);
       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,
@@ -304,7 +321,7 @@ helper_read (void *cls,
       GNUNET_HELPER_stop (h);
       return;
     }     
-    stop_helper (h);
+    stop_helper (h, GNUNET_NO);
     /* Restart the helper */
     h->restart_task =
         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
@@ -327,28 +344,28 @@ 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);    
     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 (GNUNET_YES, GNUNET_OS_INHERIT_STD_ERR, 
-                                  h->helper_in, h->helper_out,
-                                  h->binary_name,
-                                  h->binary_argv);
+    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);    
@@ -385,6 +402,7 @@ restart_task (void *cls,
  * restarted when it dies except when it is stopped using GNUNET_HELPER_stop()
  * or when the exp_cb callback is not NULL.
  *
+ * @param with_control_pipe does the helper support the use of a control pipe for signalling?
  * @param binary_name name of the binary to run
  * @param binary_argv NULL-terminated list of arguments to give when starting the binary (this
  *                    argument must not be modified by the client for
@@ -396,17 +414,28 @@ restart_task (void *cls,
  * @return the new Handle, NULL on error
  */
 struct GNUNET_HELPER_Handle *
-GNUNET_HELPER_start (const char *binary_name,
+GNUNET_HELPER_start (int with_control_pipe,
+                    const char *binary_name,
                     char *const binary_argv[],
                     GNUNET_SERVER_MessageTokenizerCallback cb,
                     GNUNET_HELPER_ExceptionCallback exp_cb,
                     void *cb_cls)
 {
-  struct GNUNET_HELPER_Handle*h;
-
-  h =  GNUNET_malloc (sizeof (struct GNUNET_HELPER_Handle));
-  h->binary_name = binary_name;
-  h->binary_argv = binary_argv;
+  struct GNUNET_HELPER_Handle *h;
+  unsigned int c;
+
+  h = GNUNET_malloc (sizeof (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);
   h->exp_cb = exp_cb;
@@ -419,11 +448,14 @@ GNUNET_HELPER_start (const char *binary_name,
  * @brief Kills the helper, closes the pipe and frees the h
  *
  * @param h h 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)
+static void
+kill_helper (struct GNUNET_HELPER_Handle *h, int soft_kill)
 {
   struct GNUNET_HELPER_SendHandle *sh;
+  unsigned int c;
 
   h->exp_cb = NULL;
   /* signal pending writes that we were stopped */
@@ -436,12 +468,41 @@ GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h)
       sh->cont (sh->cont_cls, GNUNET_SYSERR);
     GNUNET_free (sh);
   }
-  stop_helper (h);
+  stop_helper (h, soft_kill);
   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);
   GNUNET_free (h);
 }
 
 
+/**
+ * Kills the helper, closes the pipe and frees the handle
+ *
+ * @param h handle to helper to stop
+ */
+void
+GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h)
+{
+  kill_helper (h, GNUNET_NO);
+}
+
+
+/**
+ * Kills the helper by closing its stdin (the helper is expected to catch the
+ * resulting SIGPIPE and shutdown), closes the pipe and frees the handle
+ *
+ * @param h handle to helper to stop
+ */
+void
+GNUNET_HELPER_soft_stop (struct GNUNET_HELPER_Handle *h)
+{
+  kill_helper (h, GNUNET_YES);
+}
+
+
 /**
  * Write to the helper-process
  *
@@ -476,7 +537,13 @@ helper_write (void *cls,
                 _("Error writing to `%s': %s\n"),
                h->binary_name,
                STRERROR (errno));
-    stop_helper (h);
+    if (NULL != h->exp_cb)
+    {
+      h->exp_cb (h->cb_cls);
+      GNUNET_HELPER_stop (h);
+      return;
+    }
+    stop_helper (h, GNUNET_NO);
     /* Restart the helper */
     h->restart_task =
       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,