-fixing #2546
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
index 45927f12d326e9b3e4a64aa89d8ada228b86d81d..00dda263eb3ab9252f88c417c2f7464aace32cc4 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
@@ -308,20 +314,18 @@ 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
   GNUNET_assert (NULL == sl->proc);
   if (GNUNET_YES == use_debug)
     sl->proc =
-      do_start_process (sl->pipe_control,
+      do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
                        lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
                        "DEBUG", options, NULL);
   else
     sl->proc =
-      do_start_process (sl->pipe_control,
+      do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
                        lsocks, loprefix, sl->binary, "-c", sl->config,
                        options, NULL);
   if (sl->proc == NULL)
@@ -357,10 +361,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));
@@ -492,7 +494,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 +691,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;
@@ -792,46 +793,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 ();
@@ -861,56 +863,53 @@ 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 %llums\n",
+               (unsigned long long) lowestRestartDelay.rel_value);
+    child_restart_task =
+      GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
+                                                 GNUNET_SCHEDULER_PRIORITY_IDLE, 
+                                                 &delayed_restart_task, NULL);
+  }
 }
 
 
@@ -955,11 +954,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)))
@@ -967,21 +966,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 %llu ms to terminate\n"),
+                   pos->name,
+                   GNUNET_TIME_absolute_get_duration (pos->killed_at).rel_value);
+      }
+      GNUNET_OS_process_destroy (pos->proc);
       pos->proc = NULL;
       if (NULL != pos->killing_client)
        {
@@ -1041,41 +1047,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.
  *
@@ -1088,14 +1059,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.
@@ -1218,7 +1185,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 =
@@ -1296,7 +1262,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);