use c99
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
index d60000202434c8938037774c9621161d6a957931..0ccffa27ba03cde8a254929c1b64ec64c3513acd 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     Copyright (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2009, 2010, 2011, 2015 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -14,8 +14,8 @@
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
 #include "gnunet_protocols.h"
 #include "arm.h"
 
+#if HAVE_WAIT4
+/**
+ * Name of the file for writing resource utilization summaries to.
+ */
+static char *wait_filename;
+
+/**
+ * Handle for the file for writing resource summaries.
+ */
+static FILE *wait_file;
+#endif
+
+
 /**
  * How many messages do we queue up at most for optional
  * notifications to a client?  (this can cause notifications
@@ -81,7 +94,7 @@ struct ServiceListeningInfo
   /**
    * Task doing the accepting.
    */
-  struct GNUNET_SCHEDULER_Task * accept_task;
+  struct GNUNET_SCHEDULER_Task *accept_task;
 
 };
 
@@ -200,13 +213,13 @@ static char *final_option;
 /**
  * ID of task called whenever we get a SIGCHILD.
  */
-static struct GNUNET_SCHEDULER_Task * child_death_task;
+static struct GNUNET_SCHEDULER_Task *child_death_task;
 
 /**
  * ID of task called whenever the timeout for restarting a child
  * expires.
  */
-static struct GNUNET_SCHEDULER_Task * child_restart_task;
+static struct GNUNET_SCHEDULER_Task *child_restart_task;
 
 /**
  * Pipe used to communicate shutdown via signal.
@@ -402,12 +415,8 @@ start_process (struct ServiceList *sl,
 {
   char *loprefix;
   char *options;
-  char *optpos;
-  char *optend;
-  const char *next;
   int use_debug;
-  char b;
-  char *val;
+  int is_simple_service;
   struct ServiceListeningInfo *sli;
   SOCKTYPE *lsocks;
   unsigned int ls;
@@ -435,91 +444,156 @@ start_process (struct ServiceList *sl,
 
   /* obtain configuration */
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX",
-                                            &loprefix))
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             sl->name,
+                                             "PREFIX",
+                                             &loprefix))
     loprefix = GNUNET_strdup (prefix_command);
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS",
-                                            &options))
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             sl->name,
+                                             "OPTIONS",
+                                             &options))
+    options = NULL;
+
+  {
+    char *new_options;
+    char *optpos;
+    char *fin_options;
+
+    fin_options = GNUNET_strdup (final_option);
+    /* replace '{}' with service name */
+    while (NULL != (optpos = strstr (fin_options, "{}")))
+      {
+        /* terminate string at opening parenthesis */
+        *optpos = 0;
+        GNUNET_asprintf (&new_options,
+                         "%s%s%s",
+                         fin_options,
+                         sl->name,
+                         optpos + 2);
+        GNUNET_free (fin_options);
+        fin_options = new_options;
+      }
+    if (NULL != options)
     {
-      options = GNUNET_strdup (final_option);
-      if (NULL == strstr (options, "%"))
-       {
-         /* replace '{}' with service name */
-         while (NULL != (optpos = strstr (options, "{}")))
-           {
-             optpos[0] = '%';
-             optpos[1] = 's';
-             GNUNET_asprintf (&optpos, options, sl->name);
-             GNUNET_free (options);
-             options = optpos;
-           }
-         /* replace '$PATH' with value associated with "PATH" */
-         while (NULL != (optpos = strstr (options, "$")))
-           {
-             optend = optpos + 1;
-             while (isupper ((unsigned char) *optend))
-               optend++;
-             b = *optend;
-             if ('\0' == b)
-               next = "";
-             else
-               next = optend + 1;
-             *optend = '\0';
-             if (GNUNET_OK !=
-                 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
-                                                        optpos + 1, &val))
-               val = GNUNET_strdup ("");
-             *optpos = '\0';
-             GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next);
-             GNUNET_free (options);
-             GNUNET_free (val);
-             options = optpos;
-           }
-       }
+      /* combine "fin_options" with "options" */
+      optpos = options;
+      GNUNET_asprintf (&options,
+                       "%s %s",
+                       fin_options,
+                       optpos);
+      GNUNET_free (optpos);
     }
-  use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
-
-  /* actually start process */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Starting service `%s' using binary `%s' and configuration `%s'\n",
-             sl->name, sl->binary, sl->config);
-  binary = GNUNET_OS_get_libexec_binary_path (sl->binary);
-  GNUNET_asprintf (&quotedbinary,
-                  "\"%s\"",
-                  binary);
+    else
+    {
+      /* only have "fin_options", use that */
+      options = fin_options;
+    }
+  }
+  options = GNUNET_CONFIGURATION_expand_dollar (cfg,
+                                                options);
+  use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                                    sl->name,
+                                                    "DEBUG");
+  {
+    const char *service_type = NULL;
+    const char *choices[] = { "GNUNET", "SIMPLE", NULL };
+
+    is_simple_service = GNUNET_NO;
+    if ( (GNUNET_OK ==
+          GNUNET_CONFIGURATION_get_value_choice (cfg,
+                                                 sl->name,
+                                                 "TYPE",
+                                                 choices,
+                                                 &service_type)) &&
+         (0 == strcasecmp (service_type, "SIMPLE")) )
+      is_simple_service = GNUNET_YES;
+  }
 
   GNUNET_assert (NULL == sl->proc);
-  if (GNUNET_YES == use_debug)
+  if (GNUNET_YES == is_simple_service)
   {
-    if (NULL == sl->config)
-      sl->proc =
-       GNUNET_OS_start_process_s (sl->pipe_control,
-                                   GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
-                                   lsocks, loprefix, quotedbinary, "-L",
-                                   "DEBUG", options, NULL);
-    else
-      sl->proc =
-          GNUNET_OS_start_process_s (sl->pipe_control,
-                                     GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
-                                     lsocks, loprefix, quotedbinary, "-c",
-                                     sl->config, "-L",
-                                     "DEBUG", options, NULL);
+    /* A simple service will receive no GNUnet specific
+       command line options. */
+    binary = GNUNET_strdup (sl->binary);
+    binary = GNUNET_CONFIGURATION_expand_dollar (cfg, binary);
+    GNUNET_asprintf (&quotedbinary,
+                     "\"%s\"",
+                     sl->binary);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Starting simple service `%s' using binary `%s'\n",
+                sl->name, sl->binary);
+    /* FIXME: dollar expansion should only be done outside
+     * of ''-quoted strings, escaping should be considered. */
+    if (NULL != options)
+      options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
+    sl->proc =
+      GNUNET_OS_start_process_s (sl->pipe_control,
+                                 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                                 lsocks,
+                                 loprefix,
+                                 quotedbinary,
+                                 options,
+                                 NULL);
   }
   else
   {
-    if (NULL == sl->config)
-      sl->proc =
+    /* actually start process */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Starting service `%s' using binary `%s' and configuration `%s'\n",
+                sl->name, sl->binary, sl->config);
+    binary = GNUNET_OS_get_libexec_binary_path (sl->binary);
+    GNUNET_asprintf (&quotedbinary,
+                     "\"%s\"",
+                     binary);
+
+    if (GNUNET_YES == use_debug)
+    {
+      if (NULL == sl->config)
+        sl->proc =
           GNUNET_OS_start_process_s (sl->pipe_control,
                                      GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
-                                     lsocks, loprefix, quotedbinary,
-                                     options, NULL);
+                                     lsocks,
+                                     loprefix,
+                                     quotedbinary,
+                                     "-L", "DEBUG",
+                                     options,
+                                     NULL);
+      else
+        sl->proc =
+            GNUNET_OS_start_process_s (sl->pipe_control,
+                                       GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                                       lsocks,
+                                       loprefix,
+                                       quotedbinary,
+                                       "-c", sl->config,
+                                       "-L", "DEBUG",
+                                       options,
+                                       NULL);
+    }
     else
-      sl->proc =
-          GNUNET_OS_start_process_s (sl->pipe_control,
-                                     GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
-                                     lsocks, loprefix, quotedbinary, "-c",
-                                     sl->config, options, NULL);
+    {
+      if (NULL == sl->config)
+        sl->proc =
+            GNUNET_OS_start_process_s (sl->pipe_control,
+                                       GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                                       lsocks,
+                                       loprefix,
+                                       quotedbinary,
+                                       options,
+                                       NULL);
+      else
+        sl->proc =
+            GNUNET_OS_start_process_s (sl->pipe_control,
+                                       GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                                       lsocks,
+                                       loprefix,
+                                       quotedbinary,
+                                       "-c", sl->config,
+                                       options,
+                                       NULL);
+    }
   }
   GNUNET_free (binary);
   GNUNET_free (quotedbinary);
@@ -578,18 +652,15 @@ find_service (const char *name)
  * create the service in order to relay the incoming connection to it
  *
  * @param cls callback data, `struct ServiceListeningInfo` describing a listen socket
- * @param tc context
  */
 static void
-accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+accept_connection (void *cls)
 {
   struct ServiceListeningInfo *sli = cls;
   struct ServiceList *sl = sli->sl;
 
   sli->accept_task = NULL;
   GNUNET_assert (GNUNET_NO == in_shutdown);
-  if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
-    return;
   start_process (sl, NULL, 0);
 }
 
@@ -603,7 +674,8 @@ accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * @param sl service entry for the service in question
  */
 static void
-create_listen_socket (struct sockaddr *sa, socklen_t addr_len,
+create_listen_socket (struct sockaddr *sa,
+                      socklen_t addr_len,
                      struct ServiceList *sl)
 {
   static int on = 1;
@@ -652,14 +724,18 @@ create_listen_socket (struct sockaddr *sa, socklen_t addr_len,
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
                         "setsockopt");
 #endif
-
+#ifndef WINDOWS
+  if (AF_UNIX == sa->sa_family)
+    GNUNET_NETWORK_unix_precheck ((struct sockaddr_un *) sa);
+#endif
   if (GNUNET_OK !=
       GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                _
-                ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
-                sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno));
+                _("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
+                sl->name,
+                GNUNET_a2s (sa, addr_len),
+                STRERROR (errno));
     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
     GNUNET_free (sa);
     return;
@@ -702,7 +778,9 @@ create_listen_socket (struct sockaddr *sa, socklen_t addr_len,
   sli->accept_task =
     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
                                   &accept_connection, sli);
-  GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli);
+  GNUNET_CONTAINER_DLL_insert (sl->listen_head,
+                              sl->listen_tail,
+                              sli);
 }
 
 
@@ -788,11 +866,9 @@ handle_start (void *cls,
  * Start a shutdown sequence.
  *
  * @param cls closure (refers to service)
- * @param tc task context
  */
 static void
-trigger_shutdown (void *cls,
-                  const struct GNUNET_SCHEDULER_TaskContext *tc)
+trigger_shutdown (void *cls)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Triggering shutdown\n");
@@ -836,17 +912,24 @@ handle_stop (void *cls,
              servicename);
   if (0 == strcasecmp (servicename, "arm"))
   {
-    broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
-    signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_STOPPING);
+    broadcast_status (servicename,
+                     GNUNET_ARM_SERVICE_STOPPING, NULL);
+    signal_result (client,
+                  servicename,
+                  request_id,
+                  GNUNET_ARM_RESULT_STOPPING);
     GNUNET_SERVER_client_persist_ (client);
-    GNUNET_SCHEDULER_add_now (trigger_shutdown, NULL);
+    GNUNET_SCHEDULER_add_now (&trigger_shutdown, NULL);
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }
   sl = find_service (servicename);
   if (sl == NULL)
     {
-      signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN);
+      signal_result (client,
+                    servicename,
+                    request_id,
+                    GNUNET_ARM_RESULT_IS_NOT_KNOWN);
       GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
@@ -854,14 +937,19 @@ handle_stop (void *cls,
   if (GNUNET_YES == in_shutdown)
     {
       /* shutdown in progress */
-      signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN);
+      signal_result (client,
+                    servicename,
+                    request_id,
+                    GNUNET_ARM_RESULT_IN_SHUTDOWN);
       GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   if (NULL != sl->killing_client)
   {
     /* killing already in progress */
-    signal_result (client, servicename, request_id,
+    signal_result (client,
+                  servicename,
+                  request_id,
                   GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
@@ -869,7 +957,9 @@ handle_stop (void *cls,
   if (NULL == sl->proc)
   {
     /* process is down */
-    signal_result (client, servicename, request_id,
+    signal_result (client,
+                  servicename,
+                  request_id,
                   GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
@@ -877,7 +967,9 @@ handle_stop (void *cls,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Sending kill signal to service `%s', waiting for process to die.\n",
              servicename);
-  broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
+  broadcast_status (servicename,
+                   GNUNET_ARM_SERVICE_STOPPING,
+                   NULL);
   /* no signal_start - only when it's STOPPED */
   sl->killed_at = GNUNET_TIME_absolute_get ();
   if (0 != GNUNET_OS_process_kill (sl->proc, GNUNET_TERM_SIG))
@@ -1003,11 +1095,9 @@ list_count (struct ServiceList *running_head)
  * Task run for shutdown.
  *
  * @param cls closure, NULL if we need to self-restart
- * @param tc context
  */
 static void
-shutdown_task (void *cls,
-               const struct GNUNET_SCHEDULER_TaskContext *tc)
+shutdown_task (void *cls)
 {
   struct ServiceList *pos;
   struct ServiceList *nxt;
@@ -1027,8 +1117,9 @@ shutdown_task (void *cls,
     while (NULL != (sli = pos->listen_head))
       {
        GNUNET_CONTAINER_DLL_remove (pos->listen_head,
-                                    pos->listen_tail, sli);
-       if (sli->accept_task != NULL)
+                                    pos->listen_tail,
+                                    sli);
+       if (NULL != sli->accept_task)
          {
            GNUNET_SCHEDULER_cancel (sli->accept_task);
            sli->accept_task = NULL;
@@ -1044,7 +1135,7 @@ shutdown_task (void *cls,
   while (NULL != (pos = nxt))
   {
     nxt = pos->next;
-    if (pos->proc != NULL)
+    if (NULL != pos->proc)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Stopping service `%s'\n",
@@ -1059,7 +1150,7 @@ shutdown_task (void *cls,
     }
   }
   /* finally, should all service processes be already gone, terminate for real */
-  if (running_head == NULL)
+  if (NULL == running_head)
     do_shutdown ();
   else
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1072,19 +1163,16 @@ shutdown_task (void *cls,
  * Task run whenever it is time to restart a child that died.
  *
  * @param cls closure, always NULL
- * @param tc context
  */
 static void
-delayed_restart_task (void *cls,
-                     const struct GNUNET_SCHEDULER_TaskContext *tc)
+delayed_restart_task (void *cls)
+
 {
   struct ServiceList *sl;
   struct GNUNET_TIME_Relative lowestRestartDelay;
   struct ServiceListeningInfo *sli;
 
   child_restart_task = NULL;
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-    return;
   GNUNET_assert (GNUNET_NO == in_shutdown);
   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
 
@@ -1115,7 +1203,8 @@ delayed_restart_task (void *cls,
            /* accept was actually paused, so start it again */
            sli->accept_task =
              GNUNET_SCHEDULER_add_read_net
-             (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket,
+             (GNUNET_TIME_UNIT_FOREVER_REL,
+              sli->listen_socket,
               &accept_connection, sli);
          }
       }
@@ -1147,10 +1236,9 @@ delayed_restart_task (void *cls,
  * process died).
  *
  * @param cls closure, NULL if we need to self-restart
- * @param tc context
  */
 static void
-maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+maint_child_death (void *cls)
 {
   struct ServiceList *pos;
   struct ServiceList *next;
@@ -1163,111 +1251,184 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   unsigned long statusCode;
   const struct GNUNET_DISK_FileHandle *pr;
 
-  pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
+  pr = GNUNET_DISK_pipe_handle (sigpipe,
+                               GNUNET_DISK_PIPE_END_READ);
   child_death_task = NULL;
-  if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
-    {
-      /* shutdown scheduled us, ignore! */
-      child_death_task =
-       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                       pr, &maint_child_death, NULL);
-      return;
-    }
   /* consume the signal */
   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
 
   /* check for services that died (WAITPID) */
   next = running_head;
   while (NULL != (pos = next))
-    {
-      next = pos->next;
+  {
+    next = pos->next;
 
-      if (pos->proc == NULL)
+    if (NULL == pos->proc)
+    {
+      if (GNUNET_YES == in_shutdown)
+        free_service (pos);
+      continue;
+    }
+#if HAVE_WAIT4
+    if (NULL != wait_file)
+    {
+      /* need to use 'wait4()' to obtain and log performance data */
+      struct rusage ru;
+      int status;
+      pid_t pid;
+
+      pid = GNUNET_OS_process_get_pid (pos->proc);
+      ret = wait4 (pid,
+                   &status,
+                   WNOHANG,
+                   &ru);
+      if (ret <= 0)
+        continue; /* no process done */
+      if (WIFEXITED (status))
       {
-       if (GNUNET_YES == in_shutdown)
-         free_service (pos);
-       continue;
+        statusType = GNUNET_OS_PROCESS_EXITED;
+        statusCode = WEXITSTATUS (status);
       }
-      if ((GNUNET_SYSERR ==
-          (ret =
-           GNUNET_OS_process_status (pos->proc, &statusType, &statusCode)))
-         || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED)
-             || (statusType == GNUNET_OS_PROCESS_RUNNING)))
-       continue;
-      if (statusType == GNUNET_OS_PROCESS_EXITED)
+      else if (WIFSIGNALED (status))
       {
-       statstr = _( /* process termination method */ "exit");
-       statcode = statusCode;
+        statusType = GNUNET_OS_PROCESS_SIGNALED;
+        statusCode = WTERMSIG (status);
       }
-      else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
+      else if (WIFSTOPPED (status))
       {
-       statstr = _( /* process termination method */ "signal");
-       statcode = statusCode;
+        statusType = GNUNET_OS_PROCESS_SIGNALED;
+        statusCode = WSTOPSIG (status);
       }
-      else
+#ifdef WIFCONTINUED
+      else if (WIFCONTINUED (status))
       {
-       statstr = _( /* process termination method */ "unknown");
-       statcode = 0;
+        statusType = GNUNET_OS_PROCESS_RUNNING;
+        statusCode = 0;
       }
-      if (0 != pos->killed_at.abs_value_us)
+#endif
+      else
       {
-       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));
+        statusType = GNUNET_OS_PROCESS_UNKNOWN;
+        statusCode = 0;
       }
-      GNUNET_OS_process_destroy (pos->proc);
-      pos->proc = NULL;
-      broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL);
-      if (NULL != pos->killing_client)
+      if ( (GNUNET_OS_PROCESS_EXITED == statusType) ||
+           (GNUNET_OS_PROCESS_SIGNALED == statusType) )
       {
-        signal_result (pos->killing_client, pos->name,
-            pos->killing_client_request_id, GNUNET_ARM_RESULT_STOPPED);
-        GNUNET_SERVER_client_drop (pos->killing_client);
-        pos->killing_client = NULL;
-        pos->killing_client_request_id = 0;
+        double utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 10e6);
+        double stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 10e6);
+        fprintf (wait_file,
+                 "%s(%u) %.3f %.3f %llu %llu %llu %llu %llu\n",
+                 pos->binary,
+                 (unsigned int) pid,
+                 utime,
+                 stime,
+                 (unsigned long long) ru.ru_maxrss,
+                 (unsigned long long) ru.ru_inblock,
+                 (unsigned long long) ru.ru_oublock,
+                 (unsigned long long) ru.ru_nvcsw,
+                 (unsigned long long) ru.ru_nivcsw);
       }
-      if (GNUNET_YES != in_shutdown)
+    }
+    else /* continue with JUST this "if" as "else" (intentionally no brackets!) */
+#endif
+    if ( (GNUNET_SYSERR ==
+          (ret =
+           GNUNET_OS_process_status (pos->proc,
+                                     &statusType,
+                                     &statusCode))) ||
+         (ret == GNUNET_NO) ||
+         (statusType == GNUNET_OS_PROCESS_STOPPED) ||
+         (statusType == GNUNET_OS_PROCESS_UNKNOWN) ||
+         (statusType == GNUNET_OS_PROCESS_RUNNING) )
+      continue;
+
+    if (statusType == GNUNET_OS_PROCESS_EXITED)
+    {
+      statstr = _( /* process termination method */ "exit");
+      statcode = statusCode;
+    }
+    else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
+    {
+      statstr = _( /* process termination method */ "signal");
+      statcode = statusCode;
+    }
+    else
+    {
+      statstr = _( /* process termination method */ "unknown");
+      statcode = 0;
+    }
+    if (0 != pos->killed_at.abs_value_us)
+    {
+      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;
+    broadcast_status (pos->name,
+                      GNUNET_ARM_SERVICE_STOPPED,
+                      NULL);
+    if (NULL != pos->killing_client)
+    {
+      signal_result (pos->killing_client, pos->name,
+                     pos->killing_client_request_id,
+                     GNUNET_ARM_RESULT_STOPPED);
+      GNUNET_SERVER_client_drop (pos->killing_client);
+      pos->killing_client = NULL;
+      pos->killing_client_request_id = 0;
+    }
+    if (GNUNET_YES != in_shutdown)
+    {
+      if ( (statusType == GNUNET_OS_PROCESS_EXITED) &&
+           (statcode == 0) )
       {
-        if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
-        {
-          /* process terminated normally, allow restart at any time */
-          pos->restart_at.abs_value_us = 0;
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              _("Service `%s' terminated normally, will restart at any time\n"),
-              pos->name);
-          /* process can still be re-started on-demand, ensure it is re-started if there is demand */
-          for (sli = pos->listen_head; NULL != sli; sli = sli->next)
-          {
-            GNUNET_break (NULL == sli->accept_task);
-            sli->accept_task =
-                GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                    sli->listen_socket, &accept_connection, sli);
-          }
-       }
-        else
+        /* process terminated normally, allow restart at any time */
+        pos->restart_at.abs_value_us = 0;
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    _("Service `%s' terminated normally, will restart at any time\n"),
+                    pos->name);
+        /* process can still be re-started on-demand, ensure it is re-started if there is demand */
+        for (sli = pos->listen_head; NULL != sli; sli = sli->next)
         {
-         if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-               _("Service `%s' terminated with status %s/%d, will restart in %s\n"),
-                pos->name, statstr, statcode,
-                GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES));
-         /* schedule restart */
-         pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
-         pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
-          if (NULL != 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_break (NULL == sli->accept_task);
+          sli->accept_task =
+            GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                           sli->listen_socket,
+                                           &accept_connection,
+                                           sli);
         }
       }
       else
       {
-        free_service (pos);
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Service `%s' terminated with status %s/%d, will restart in %s\n"),
+                   pos->name,
+                   statstr,
+                   statcode,
+                   GNUNET_STRINGS_relative_time_to_string (pos->backoff,
+                                                           GNUNET_YES));
+        /* schedule restart */
+        pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
+        pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
+        if (NULL != 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);
       }
     }
+    else
+    {
+      free_service (pos);
+    }
+  }
   child_death_task = GNUNET_SCHEDULER_add_read_file (
-      GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL);
+      GNUNET_TIME_UNIT_FOREVER_REL,
+      pr,
+      &maint_child_death, NULL);
   if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
     do_shutdown ();
   else if (GNUNET_YES == in_shutdown)
@@ -1356,7 +1517,7 @@ setup_service (void *cls,
   if (( (GNUNET_OK !=
         GNUNET_CONFIGURATION_get_value_filename (cfg, section,
                                                   "CONFIG",
-                                                 &config)) &&
+                                                  &config)) &&
        (GNUNET_OK !=
         GNUNET_CONFIGURATION_get_value_filename (cfg,
                                                   "PATHS",
@@ -1382,8 +1543,12 @@ setup_service (void *cls,
 #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");
+  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,
@@ -1394,11 +1559,11 @@ setup_service (void *cls,
                                             "FORCESTART"))
   {
     sl->force_start = GNUNET_YES;
-    /* FIXME: we might like the pre-binding even for
-       _certain_ services that have force_start set,
-       otherwise interdependencies may again force
-       client's to retry connections during startup. */
-    return;
+    if (GNUNET_YES ==
+        GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                              section,
+                                              "NOARMBIND"))
+      return;
   }
   else
   {
@@ -1488,31 +1653,54 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
   cfg = c;
   server = serv;
   GNUNET_assert (NULL != serv);
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
-                                &shutdown_task,
-                               NULL);
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+                                NULL);
   child_death_task =
     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
                                    GNUNET_DISK_pipe_handle (sigpipe,
                                                             GNUNET_DISK_PIPE_END_READ),
                                    &maint_child_death, NULL);
-
+#if HAVE_WAIT4
+  if (GNUNET_OK ==
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               "ARM",
+                                               "RESOURCE_DIAGNOSTICS",
+                                               &wait_filename))
+  {
+    wait_file = fopen (wait_filename,
+                       "w");
+    if (NULL == wait_file)
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "fopen",
+                                wait_filename);
+    }
+  }
+#endif
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX",
-                                            &prefix_command))
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "ARM",
+                                             "GLOBAL_PREFIX",
+                                             &prefix_command))
     prefix_command = GNUNET_strdup ("");
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX",
-                                            &final_option))
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "ARM",
+                                             "GLOBAL_POSTFIX",
+                                             &final_option))
     final_option = GNUNET_strdup ("");
   if (GNUNET_YES ==
-      GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "USER_ONLY"))
+      GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                            "ARM",
+                                            "USER_ONLY"))
   {
     GNUNET_break (GNUNET_YES == start_user);
     start_system = GNUNET_NO;
   }
   if (GNUNET_YES ==
-      GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "SYSTEM_ONLY"))
+      GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                            "ARM",
+                                            "SYSTEM_ONLY"))
   {
     GNUNET_break (GNUNET_YES == start_system);
     start_user = GNUNET_NO;
@@ -1555,6 +1743,18 @@ main (int argc, char *const *argv)
     (GNUNET_OK ==
      GNUNET_SERVICE_run (argc, argv, "arm",
                         GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN, &run, NULL)) ? 0 : 1;
+#if HAVE_WAIT4
+  if (NULL != wait_file)
+  {
+    fclose (wait_file);
+    wait_file = NULL;
+  }
+  if (NULL != wait_filename)
+  {
+    GNUNET_free (wait_filename);
+    wait_filename = NULL;
+  }
+#endif
   GNUNET_SIGNAL_handler_uninstall (shc_chld);
   shc_chld = NULL;
   GNUNET_DISK_pipe_close (sigpipe);