-extending defaults
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
index 733eac6c613492f460c33f5523dffcbc557ac966..091d5ae54550f0e8b99d833c8250abc41a26ab34 100644 (file)
@@ -145,6 +145,12 @@ struct ServiceList
    */
   struct GNUNET_TIME_Absolute restart_at;
 
+  /**
+   * Time we asked the service to shut down (used to calculate time it took
+   * the service to terminate).
+   */
+  struct GNUNET_TIME_Absolute killed_at;
+
   /**
    * Is this service to be started by default (or did a client tell us explicitly
    * to start it)?  GNUNET_NO if the service is started only upon 'accept' on a
@@ -152,6 +158,11 @@ struct ServiceList
    */
   int is_default;
 
+  /**
+   * Should we use pipes to signal this process? (YES for Java binaries and if we
+   * are on Windoze).
+   */
+  int pipe_control;
 };
 
 /**
@@ -308,13 +319,16 @@ start_process (struct ServiceList *sl)
              "Starting service `%s' using binary `%s' and configuration `%s'\n",
              sl->name, sl->binary, sl->config);
 #endif
+  GNUNET_assert (NULL == sl->proc);
   if (GNUNET_YES == use_debug)
     sl->proc =
-      do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
+      do_start_process (sl->pipe_control,
+                       lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
                        "DEBUG", options, NULL);
   else
     sl->proc =
-      do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config,
+      do_start_process (sl->pipe_control,
+                       lsocks, loprefix, sl->binary, "-c", sl->config,
                        options, NULL);
   if (sl->proc == NULL)
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
@@ -362,6 +376,43 @@ write_result (void *cls, size_t size, void *buf)
   return sizeof (struct GNUNET_ARM_ResultMessage);
 }
 
+/**
+ * Transmit the list of running services.
+ * 
+ * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message
+ * @param size number of bytes available in buf
+ * @param buf where to copy the message, NULL on error
+ * @return number of bytes copied to buf
+ */
+static size_t
+write_list_result (void *cls, size_t size, void *buf)
+{
+  struct GNUNET_ARM_ListResultMessage *msg = cls;
+  struct GNUNET_ARM_ListResultMessage *rslt;
+  size_t rslt_size;
+  
+  if (buf == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Could not send list result to client\n"));
+    return 0;                   /* error, not much we can do */
+  }
+  
+  GNUNET_assert (size >= msg->header.size);
+  rslt = buf;
+  rslt->header.size = htons (msg->header.size);
+  rslt->header.type = htons (msg->header.type);
+  rslt->count = htons (msg->count);
+  
+  size_t list_size = msg->header.size 
+                     - sizeof (struct GNUNET_ARM_ListResultMessage);  
+  memcpy (&rslt[1], &msg[1], list_size);
+
+  rslt_size = msg->header.size;
+  GNUNET_free (msg);
+  return rslt_size;
+}
+
 
 /**
  * Signal our client that we will start or stop the
@@ -407,7 +458,7 @@ find_service (const char *name)
   sl = running_head;
   while (sl != NULL)
     {
-      if (0 == strcmp (sl->name, name))
+      if (0 == strcasecmp (sl->name, name))
        return sl;
       sl = sl->next;
     }
@@ -558,7 +609,7 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client,
   const char *servicename;
   struct ServiceList *sl;
   uint16_t size;
-
+  
   size = ntohs (message->size);
   size -= sizeof (struct GNUNET_MessageHeader);
   servicename = (const char *) &message[1];
@@ -649,12 +700,70 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
              "Sending kill signal to service `%s', waiting for process to die.\n",
              servicename);
 #endif
+  sl->killed_at = GNUNET_TIME_absolute_get ();
   if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM))
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
   sl->killing_client = client;
   GNUNET_SERVER_client_keep (client);
 }
 
+/**
+ * Handle LIST-message.
+ *
+ * @param cls closure (always NULL)
+ * @param client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_list (void *cls, struct GNUNET_SERVER_Client *client,
+             const struct GNUNET_MessageHeader *message)
+{
+  struct GNUNET_ARM_ListResultMessage *msg;
+  size_t string_list_size;
+  size_t total_size;
+  struct ServiceList *sl;
+  uint16_t count;
+  
+  if (NULL == client)
+    return;
+  
+  count = 0;
+  string_list_size = 0;
+  /* first count the running processes get their name's size */
+  for (sl = running_head; sl != NULL; sl = sl->next)
+  {
+    if (sl->proc != NULL)
+    {
+      string_list_size += strlen (sl->name);
+      string_list_size += strlen (sl->binary);
+      string_list_size += 4;
+      count++;
+    }
+  }
+  total_size = sizeof (struct GNUNET_ARM_ListResultMessage) 
+               + string_list_size;
+  msg = GNUNET_malloc (total_size);
+  msg->header.size = total_size;
+  msg->header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT;
+  msg->count = count;
+  
+  char *pos = (char *)&msg[1];
+  for (sl = running_head; sl != NULL; sl = sl->next) 
+  {
+    if (sl->proc != NULL)
+    {
+      size_t s = strlen (sl->name) + strlen (sl->binary) + 4;
+      GNUNET_snprintf(pos, s, "%s (%s)", sl->name, sl->binary);
+      pos += s;
+    }
+  }
+  
+  GNUNET_SERVER_notify_transmit_ready (client,
+                                       msg->header.size,
+                                       GNUNET_TIME_UNIT_FOREVER_REL,
+                                       &write_list_result, msg);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
 
 /**
  * We are done with everything.  Stop remaining
@@ -722,6 +831,7 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n",
                      pos->name);
+         pos->killed_at = GNUNET_TIME_absolute_get ();
          if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
            GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
        }
@@ -805,8 +915,9 @@ delayed_restart_task (void *cls,
                  (unsigned long long) lowestRestartDelay.rel_value);
 #endif
       child_restart_task =
-       GNUNET_SCHEDULER_add_delayed (lowestRestartDelay,
-                                     &delayed_restart_task, NULL);
+       GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
+                                                   GNUNET_SCHEDULER_PRIORITY_IDLE, 
+                                                   &delayed_restart_task, NULL);
     }
 }
 
@@ -852,11 +963,11 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       next = pos->next;
 
       if (pos->proc == NULL)
-       {
-         if (GNUNET_YES == in_shutdown)
-           free_service (pos);
-         continue;
-       }
+      {
+       if (GNUNET_YES == in_shutdown)
+         free_service (pos);
+       continue;
+      }
       if ((GNUNET_SYSERR ==
           (ret =
            GNUNET_OS_process_status (pos->proc, &statusType, &statusCode)))
@@ -864,20 +975,27 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
              || (statusType == GNUNET_OS_PROCESS_RUNNING)))
        continue;
       if (statusType == GNUNET_OS_PROCESS_EXITED)
-       {
-         statstr = _( /* process termination method */ "exit");
-         statcode = statusCode;
-       }
+      {
+       statstr = _( /* process termination method */ "exit");
+       statcode = statusCode;
+      }
       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
-       {
-         statstr = _( /* process termination method */ "signal");
-         statcode = statusCode;
-       }
+      {
+       statstr = _( /* process termination method */ "signal");
+       statcode = statusCode;
+      }
       else
-       {
-         statstr = _( /* process termination method */ "unknown");
-         statcode = 0;
-       }
+      {
+       statstr = _( /* process termination method */ "unknown");
+       statcode = 0;
+      }
+      if (0 != pos->killed_at.abs_value)
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Service `%s' took %llu ms to terminate\n"),
+                   pos->name,
+                   GNUNET_TIME_absolute_get_duration (pos->killed_at).rel_value);
+      }
       GNUNET_OS_process_close (pos->proc);
       pos->proc = NULL;
       if (NULL != pos->killing_client)
@@ -903,24 +1021,27 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
            {
              /* process terminated normally, allow restart at any time */
              pos->restart_at.abs_value = 0;
-             continue;
            }
-         if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                       _
-                       ("Service `%s' terminated with status %s/%d, will restart in %llu ms\n"),
-                       pos->name, statstr, statcode, pos->backoff.rel_value);
-         /* schedule restart */
-         pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
-         pos->backoff =
-           GNUNET_TIME_relative_min (EXPONENTIAL_BACKOFF_THRESHOLD,
-                                     GNUNET_TIME_relative_multiply
-                                     (pos->backoff, 2));
+          else
+            {
+             if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                           _
+                           ("Service `%s' terminated with status %s/%d, will restart in %llu ms\n"),
+                           pos->name, statstr, statcode, pos->backoff.rel_value);
+             /* schedule restart */
+             pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
+             pos->backoff =
+               GNUNET_TIME_relative_min (EXPONENTIAL_BACKOFF_THRESHOLD,
+                                         GNUNET_TIME_relative_multiply
+                                         (pos->backoff, 2));
+            }
          if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
            GNUNET_SCHEDULER_cancel (child_restart_task);
          child_restart_task =
            GNUNET_SCHEDULER_add_with_priority
-           (GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL);
+           (GNUNET_SCHEDULER_PRIORITY_IDLE, 
+            &delayed_restart_task, NULL);
        }
       else
        {
@@ -990,7 +1111,6 @@ handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
   GNUNET_SERVER_client_persist_ (client);
 }
 
-
 /**
  * Signal handler called for SIGCHLD.  Triggers the
  * respective handler by writing to the trigger pipe.
@@ -1037,6 +1157,13 @@ setup_service (void *cls, const char *section)
       /* not a service section */
       return;
     }
+  sl = find_service (section);
+  if (NULL != sl)
+  {
+    /* got the same section twice!? */
+    GNUNET_break (0);
+    return;
+  }
   config = NULL;
   if ((GNUNET_OK !=
        GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG",
@@ -1058,6 +1185,12 @@ setup_service (void *cls, const char *section)
   sl->config = config;
   sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
   sl->restart_at = GNUNET_TIME_UNIT_FOREVER_ABS;
+#if WINDOWS
+  sl->pipe_control = GNUNET_YES;
+#else
+  if (GNUNET_CONFIGURATION_have_value (cfg, section, "PIPECONTROL"))
+    sl->pipe_control = GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "PIPECONTROL");
+#endif  
   GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl);
   if (GNUNET_YES !=
       GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "AUTOSTART"))
@@ -1089,6 +1222,8 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
     {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
      sizeof (struct GNUNET_MessageHeader)},
+    {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST, 
+     sizeof (struct GNUNET_MessageHeader)},
     {NULL, NULL, 0, 0}
   };
   char *defaultservices;
@@ -1170,7 +1305,7 @@ main (int argc, char *const *argv)
   int ret;
   struct GNUNET_SIGNAL_Context *shc_chld;
 
-  sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
+  sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
   GNUNET_assert (sigpipe != NULL);
   shc_chld =
     GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);