- assert hard
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
index cfe164fa6bf2112980b25cf391d644e61ad0e2ba..f16fd5265c9392b4bb38313c69517069fb5c8e1f 100644 (file)
 #include "gnunet_protocols.h"
 #include "arm.h"
 
-/**
- * Threshold after which exponential backoff shouldn't increase (in ms); 30m
- */
-#define EXPONENTIAL_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
-
-
 /**
  * List of our services.
  */
@@ -145,6 +139,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
@@ -240,6 +240,7 @@ start_process (struct ServiceList *sl)
   struct ServiceListeningInfo *sli;
   SOCKTYPE *lsocks;
   unsigned int ls;
+  char *binary;
 
   /* calculate listen socket list */
   lsocks = NULL;
@@ -308,22 +309,38 @@ start_process (struct ServiceList *sl)
   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
 
   /* actually start process */
-#if DEBUG_ARM
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Starting service `%s' using binary `%s' and configuration `%s'\n",
              sl->name, sl->binary, sl->config);
-#endif
+  binary = GNUNET_OS_get_libexec_binary_path (sl->binary);
   GNUNET_assert (NULL == sl->proc);
   if (GNUNET_YES == use_debug)
-    sl->proc =
-      do_start_process (sl->pipe_control,
-                       lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
-                       "DEBUG", options, NULL);
+  {
+    if (NULL == sl->config)
+      sl->proc =
+       do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                         lsocks, loprefix, binary, "-L",
+                         "DEBUG", options, NULL);
+    else
+      sl->proc =
+       do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                         lsocks, loprefix, binary, "-c", sl->config, "-L",
+                         "DEBUG", options, NULL);
+  }
   else
-    sl->proc =
-      do_start_process (sl->pipe_control,
-                       lsocks, loprefix, sl->binary, "-c", sl->config,
-                       options, NULL);
+  {
+    if (NULL == sl->config)
+      sl->proc =
+       do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                         lsocks, loprefix, binary, 
+                         options, NULL);
+    else
+      sl->proc =
+       do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                         lsocks, loprefix, binary, "-c", sl->config,
+                         options, NULL);
+  }
+  GNUNET_free (binary);
   if (sl->proc == NULL)
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
                sl->name);
@@ -357,10 +374,8 @@ write_result (void *cls, size_t size, void *buf)
                _("Could not send status result to client\n"));
     return 0;                  /* error, not much we can do */
   }
-#if DEBUG_ARM
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Sending status response %u to client\n", (unsigned int) *res);
-#endif
   GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage));
   msg = buf;
   msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage));
@@ -474,6 +489,7 @@ accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct ServiceList *sl = sli->sl;
 
   sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_assert (GNUNET_NO == in_shutdown);
   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
     return;
   start_process (sl);
@@ -492,7 +508,7 @@ static void
 create_listen_socket (struct sockaddr *sa, socklen_t addr_len,
                      struct ServiceList *sl)
 {
-  const static int on = 1;
+  static int on = 1;
   struct GNUNET_NETWORK_Handle *sock;
   struct ServiceListeningInfo *sli;
 
@@ -689,11 +705,10 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
       signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN);
       return;
     }
-#if DEBUG_ARM
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "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;
@@ -728,8 +743,8 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
     if (sl->proc != NULL)
     {
       string_list_size += strlen (sl->name);
-      string_list_size += 3;
-      string_list_size += strlen (sl->binary) + 1;
+      string_list_size += strlen (sl->binary);
+      string_list_size += 4;
       count++;
     }
   }
@@ -745,9 +760,8 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
   {
     if (sl->proc != NULL)
     {
-      //memcpy (pos, sl->name, strlen (sl->name) + 1);
       size_t s = strlen (sl->name) + strlen (sl->binary) + 4;
-      snprintf(pos, s, "%s (%s)", sl->name, sl->binary);
+      GNUNET_snprintf(pos, s, "%s (%s)", sl->name, sl->binary);
       pos += s;
     }
   }
@@ -793,46 +807,47 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct ServiceListeningInfo *sli;
 
   if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
-    {
-      GNUNET_SCHEDULER_cancel (child_restart_task);
-      child_restart_task = GNUNET_SCHEDULER_NO_TASK;
-    }
+  {
+    GNUNET_SCHEDULER_cancel (child_restart_task);
+    child_restart_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   in_shutdown = GNUNET_YES;
   /* first, stop listening */
   for (pos = running_head; NULL != pos; pos = pos->next)
-    {
-      while (NULL != (sli = pos->listen_head))
-       {
-         GNUNET_CONTAINER_DLL_remove (pos->listen_head,
-                                      pos->listen_tail, sli);
-         if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK)
-           {
-             GNUNET_SCHEDULER_cancel (sli->accept_task);
-             sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
-           }
-         GNUNET_break (GNUNET_OK ==
-                       GNUNET_NETWORK_socket_close (sli->listen_socket));
-         GNUNET_free (sli->service_addr);
-         GNUNET_free (sli);
-       }
-    }
+  {
+    while (NULL != (sli = pos->listen_head))
+      {
+       GNUNET_CONTAINER_DLL_remove (pos->listen_head,
+                                    pos->listen_tail, sli);
+       if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK)
+         {
+           GNUNET_SCHEDULER_cancel (sli->accept_task);
+           sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
+         }
+       GNUNET_break (GNUNET_OK ==
+                     GNUNET_NETWORK_socket_close (sli->listen_socket));
+       GNUNET_free (sli->service_addr);
+       GNUNET_free (sli);
+      }
+  }
   /* then, shutdown all existing service processes */
   nxt = running_head;
   while (NULL != (pos = nxt))
+  {
+    nxt = pos->next;
+    if (pos->proc != NULL)
     {
-      nxt = pos->next;
-      if (pos->proc != NULL)
-       {
-         GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n",
-                     pos->name);
-         if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
-           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
-       }
-      else
-       {
-         free_service (pos);
-       }
+      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");
+    }
+    else
+    {
+      free_service (pos);
     }
+  }
   /* finally, should all service processes be already gone, terminate for real */
   if (running_head == NULL)
     do_shutdown ();
@@ -862,56 +877,54 @@ delayed_restart_task (void *cls,
   /* check for services that need to be restarted due to
    * configuration changes or because the last restart failed */
   for (sl = running_head; NULL != sl; sl = sl->next)
+  {
+    if (NULL != sl->proc)
+      continue;
+    /* service is currently not running */
+    if (GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value ==
+       0)
     {
-      if (sl->proc == NULL)
-       {
-         /* service is currently not running */
-         if (GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value ==
-             0)
-           {
-             /* restart is now allowed */
-             if (sl->is_default)
-               {
-                 /* process should run by default, start immediately */
-                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                             _("Restarting service `%s'.\n"), sl->name);
-                 start_process (sl);
-               }
-             else
-               {
-                 /* process is run on-demand, ensure it is re-started if there is demand */
-                 for (sli = sl->listen_head; NULL != sli; sli = sli->next)
-                   if (GNUNET_SCHEDULER_NO_TASK == sli->accept_task)
-                     {
-                       /* accept was actually paused, so start it again */
-                       sli->accept_task =
-                         GNUNET_SCHEDULER_add_read_net
-                         (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket,
-                          &accept_connection, sli);
-                     }
-               }
-           }
-         else
-           {
-             /* update calculation for earliest time to reactivate a service */
-             lowestRestartDelay =
-               GNUNET_TIME_relative_min (lowestRestartDelay,
-                                         GNUNET_TIME_absolute_get_remaining
-                                         (sl->restart_at));
-           }
-       }
+      /* restart is now allowed */
+      if (sl->is_default)
+      {
+       /* process should run by default, start immediately */
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Restarting service `%s'.\n"), sl->name);
+       start_process (sl);
+      }
+      else
+      {
+       /* process is run on-demand, ensure it is re-started if there is demand */
+       for (sli = sl->listen_head; NULL != sli; sli = sli->next)
+         if (GNUNET_SCHEDULER_NO_TASK == sli->accept_task)
+         {
+           /* accept was actually paused, so start it again */
+           sli->accept_task =
+             GNUNET_SCHEDULER_add_read_net
+             (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket,
+              &accept_connection, sli);
+         }
+      }
     }
-  if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+    else
     {
-#if DEBUG_ARM
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n",
-                 (unsigned long long) lowestRestartDelay.rel_value);
-#endif
-      child_restart_task =
-       GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
-                                                   GNUNET_SCHEDULER_PRIORITY_IDLE, 
-                                                   &delayed_restart_task, NULL);
+      /* update calculation for earliest time to reactivate a service */
+      lowestRestartDelay =
+       GNUNET_TIME_relative_min (lowestRestartDelay,
+                                 GNUNET_TIME_absolute_get_remaining
+                                 (sl->restart_at));
     }
+  }
+  if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+               "Will restart process in %s\n",
+               GNUNET_STRINGS_relative_time_to_string (lowestRestartDelay, GNUNET_YES));
+    child_restart_task =
+      GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
+                                                 GNUNET_SCHEDULER_PRIORITY_IDLE, 
+                                                 &delayed_restart_task, NULL);
+  }
 }
 
 
@@ -956,11 +969,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)))
@@ -968,21 +981,28 @@ 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;
-       }
-      GNUNET_OS_process_close (pos->proc);
+      {
+       statstr = _( /* process termination method */ "unknown");
+       statcode = 0;
+      }
+      if (0 != pos->killed_at.abs_value)
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Service `%s' took %s to terminate\n"),
+                   pos->name,
+                   GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->killed_at), GNUNET_YES));
+      }
+      GNUNET_OS_process_destroy (pos->proc);
       pos->proc = NULL;
       if (NULL != pos->killing_client)
        {
@@ -1017,10 +1037,7 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                            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));
+             pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
             }
          if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
            GNUNET_SCHEDULER_cancel (child_restart_task);
@@ -1042,41 +1059,6 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 }
 
 
-/**
- * Transmit our shutdown acknowledgement to the client.
- *
- * @param cls the 'struct GNUNET_SERVER_Client'
- * @param size number of bytes available in buf
- * @param buf where to write the message
- * @return number of bytes written
- */
-static size_t
-transmit_shutdown_ack (void *cls, size_t size, void *buf)
-{
-  struct GNUNET_SERVER_Client *client = cls;
-  struct GNUNET_ARM_ResultMessage *msg;
-
-  if (size < sizeof (struct GNUNET_ARM_ResultMessage))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                 _("Failed to transmit shutdown ACK.\n"));
-      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-      return 0;                        /* client disconnected */
-    }
-  /* Make the connection flushing for the purpose of ACK transmitting,
-   * needed on W32 to ensure that the message is even received, harmless
-   * on other platforms... */
-  GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client));
-  msg = (struct GNUNET_ARM_ResultMessage *) buf;
-  msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT);
-  msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage));
-  msg->status = htonl ((uint32_t) GNUNET_ARM_PROCESS_SHUTDOWN);
-  GNUNET_SERVER_receive_done (client, GNUNET_OK);
-  GNUNET_SERVER_client_drop (client);
-  return sizeof (struct GNUNET_ARM_ResultMessage);
-}
-
-
 /**
  * Handler for SHUTDOWN message.
  *
@@ -1089,14 +1071,10 @@ handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
                 const struct GNUNET_MessageHeader *message)
 {
   GNUNET_SCHEDULER_shutdown ();
-  GNUNET_SERVER_client_keep (client);
-  GNUNET_SERVER_notify_transmit_ready (client,
-                                      sizeof (struct GNUNET_ARM_ResultMessage),
-                                      GNUNET_TIME_UNIT_FOREVER_REL,
-                                      &transmit_shutdown_ack, client);
   GNUNET_SERVER_client_persist_ (client);
 }
 
+
 /**
  * Signal handler called for SIGCHLD.  Triggers the
  * respective handler by writing to the trigger pipe.
@@ -1151,20 +1129,23 @@ setup_service (void *cls, const char *section)
     return;
   }
   config = NULL;
-  if ((GNUNET_OK !=
-       GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG",
-                                               &config)) ||
+  if (( (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG",
+                                                 &config)) &&
+       (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", "DEFAULTCONFIG",
+                                                 &config)) ) ||
       (0 != STAT (config, &sbuf)))
+  {
+    if (NULL != config)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _
-                 ("Configuration file `%s' for service `%s' not valid: %s\n"),
-                 config, section,
-                 (config == NULL) ? _("option missing") : STRERROR (errno));
-      GNUNET_free (binary);
-      GNUNET_free_non_null (config);
-      return;
+      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 
+                                section, "CONFIG",
+                                STRERROR (errno));
+      GNUNET_free (config);
+      config = NULL;
     }
+  }
   sl = GNUNET_malloc (sizeof (struct ServiceList));
   sl->name = GNUNET_strdup (section);
   sl->binary = binary;
@@ -1219,7 +1200,6 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
   cfg = c;
   server = serv;
   GNUNET_assert (serv != NULL);
-  GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
                                NULL);
   child_death_task =
@@ -1297,7 +1277,8 @@ main (int argc, char *const *argv)
     GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
   ret =
     (GNUNET_OK ==
-     GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
+     GNUNET_SERVICE_run (argc, argv, "arm", 
+                        GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN, &run, NULL)) ? 0 : 1;
   GNUNET_SIGNAL_handler_uninstall (shc_chld);
   shc_chld = NULL;
   GNUNET_DISK_pipe_close (sigpipe);