use c99
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
index ab1fe6481846126d3d2a671bb8c62809e817b414..0ccffa27ba03cde8a254929c1b64ec64c3513acd 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (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
@@ -36,6 +49,7 @@
  */
 #define MAX_NOTIFY_QUEUE 1024
 
+
 /**
  * List of our services.
  */
@@ -80,7 +94,7 @@ struct ServiceListeningInfo
   /**
    * Task doing the accepting.
    */
-  GNUNET_SCHEDULER_TaskIdentifier accept_task;
+  struct GNUNET_SCHEDULER_Task *accept_task;
 
 };
 
@@ -159,10 +173,10 @@ struct ServiceList
 
   /**
    * 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
+   * to start it)?  #GNUNET_NO if the service is started only upon 'accept' on a
    * listen socket or possibly explicitly by a client changing the value.
    */
-  int is_default;
+  int force_start;
 
   /**
    * Should we use pipes to signal this process? (YES for Java binaries and if we
@@ -199,13 +213,13 @@ static char *final_option;
 /**
  * ID of task called whenever we get a SIGCHILD.
  */
-static GNUNET_SCHEDULER_TaskIdentifier child_death_task;
+static struct GNUNET_SCHEDULER_Task *child_death_task;
 
 /**
  * ID of task called whenever the timeout for restarting a child
  * expires.
  */
-static GNUNET_SCHEDULER_TaskIdentifier child_restart_task;
+static struct GNUNET_SCHEDULER_Task *child_restart_task;
 
 /**
  * Pipe used to communicate shutdown via signal.
@@ -217,6 +231,16 @@ static struct GNUNET_DISK_PipeHandle *sigpipe;
  */
 static int in_shutdown;
 
+/**
+ * Are we starting user services?
+ */
+static int start_user = GNUNET_YES;
+
+/**
+ * Are we starting system services?
+ */
+static int start_system = GNUNET_YES;
+
 /**
  * Handle to our server instance.  Our server is a bit special in that
  * its service is not immediately stopped once we get a shutdown
@@ -235,8 +259,6 @@ static struct GNUNET_SERVER_Handle *server;
 static struct GNUNET_SERVER_NotificationContext *notifier;
 
 
-#include "do_start_process.c"
-
 /**
  * Transmit a status result message.
  *
@@ -251,7 +273,7 @@ write_result (void *cls, size_t size, void *buf)
   struct GNUNET_ARM_ResultMessage *msg = cls;
   size_t msize;
 
-  if (buf == NULL)
+  if (NULL == buf)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _("Could not send status result to client\n"));
@@ -259,7 +281,7 @@ write_result (void *cls, size_t size, void *buf)
     return 0;                  /* error, not much we can do */
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending status response %u to client\n", 
+             "Sending status response %u to client\n",
              (unsigned int) msg->result);
   msize = msg->arm_msg.header.size;
   GNUNET_assert (size >= msize);
@@ -275,7 +297,7 @@ write_result (void *cls, size_t size, void *buf)
 
 /**
  * Transmit the list of running services.
- * 
+ *
  * @param cls pointer to `struct GNUNET_ARM_ListResultMessage` with the message
  * @param size number of bytes available in @a buf
  * @param buf where to copy the message, NULL on error
@@ -286,22 +308,22 @@ write_list_result (void *cls, size_t size, void *buf)
 {
   struct GNUNET_ARM_ListResultMessage *msg = cls;
   size_t rslt_size;
-  
-  if (buf == NULL)
+
+  if (NULL == buf)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 _("Could not send list result to client\n"));
     GNUNET_free (msg);
     return 0;                   /* error, not much we can do */
   }
-  
+
   rslt_size = msg->arm_msg.header.size;
   GNUNET_assert (size >= rslt_size);
   msg->arm_msg.header.size = htons (msg->arm_msg.header.size);
   msg->arm_msg.header.type = htons (msg->arm_msg.header.type);
   msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id);
   msg->count = htons (msg->count);
-  
+
   memcpy (buf, msg, rslt_size);
   GNUNET_free (msg);
   return rslt_size;
@@ -319,7 +341,7 @@ write_list_result (void *cls, size_t size, void *buf)
  * @return NULL if it was not found
  */
 static void
-signal_result (struct GNUNET_SERVER_Client *client, 
+signal_result (struct GNUNET_SERVER_Client *client,
               const char *name,
               uint64_t request_id,
               enum GNUNET_ARM_Result result)
@@ -335,7 +357,7 @@ signal_result (struct GNUNET_SERVER_Client *client,
   msg->arm_msg.request_id = request_id;
 
   GNUNET_SERVER_notify_transmit_ready (client, msize,
-                                      GNUNET_TIME_UNIT_FOREVER_REL, 
+                                      GNUNET_TIME_UNIT_FOREVER_REL,
                                       write_result, msg);
 }
 
@@ -387,20 +409,19 @@ broadcast_status (const char *name,
  *                   being started. 0 if starting was not requested.
  */
 static void
-start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint64_t request_id)
+start_process (struct ServiceList *sl,
+               struct GNUNET_SERVER_Client *client,
+               uint64_t request_id)
 {
   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;
   char *binary;
+  char *quotedbinary;
 
   /* calculate listen socket list */
   lsocks = NULL;
@@ -409,10 +430,10 @@ start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint
     {
       GNUNET_array_append (lsocks, ls,
                           GNUNET_NETWORK_get_fd (sli->listen_socket));
-      if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK)
+      if (sli->accept_task != NULL)
        {
          GNUNET_SCHEDULER_cancel (sli->accept_task);
-         sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
+         sli->accept_task = NULL;
        }
     }
 #if WINDOWS
@@ -423,94 +444,174 @@ start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint
 
   /* 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);
+    }
+    else
+    {
+      /* only have "fin_options", use that */
+      options = fin_options;
     }
-  use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
+  }
+  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;
+  }
 
-  /* 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_assert (NULL == sl->proc);
-  if (GNUNET_YES == use_debug)
+  if (GNUNET_YES == is_simple_service)
   {
-    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);
+    /* 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 =
-       do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
-                         lsocks, loprefix, binary, 
-                         options, NULL);
+    /* 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,
+                                     "-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 =
-       do_start_process (sl->pipe_control, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
-                         lsocks, loprefix, binary, "-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);
   if (sl->proc == NULL)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to start service `%s'\n"),
                sl->name);
     if (client)
-      signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_START_FAILED);
+      signal_result (client,
+                     sl->name,
+                     request_id,
+                     GNUNET_ARM_RESULT_START_FAILED);
   }
   else
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"),
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                _("Starting service `%s'\n"),
                sl->name);
     broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL);
     if (client)
@@ -551,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 = GNUNET_SCHEDULER_NO_TASK;
+  sli->accept_task = NULL;
   GNUNET_assert (GNUNET_NO == in_shutdown);
-  if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
-    return;
   start_process (sl, NULL, 0);
 }
 
@@ -576,40 +674,45 @@ 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;
   struct GNUNET_NETWORK_Handle *sock;
   struct ServiceListeningInfo *sli;
+#ifndef WINDOWS
+  int match_uid;
+  int match_gid;
+#endif
 
   switch (sa->sa_family)
-    {
-    case AF_INET:
-      sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
-      break;
-    case AF_INET6:
-      sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
-      break;
-    case AF_UNIX:
-      if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0)        /* Do not bind to blank UNIX path! */
-       return;
-      sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
-      break;
-    default:
-      GNUNET_break (0);
-      sock = NULL;
-      errno = EAFNOSUPPORT;
-      break;
-    }
-  if (NULL == sock)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                 _("Unable to create socket for service `%s': %s\n"),
-                 sl->name, STRERROR (errno));
-      GNUNET_free (sa);
+  {
+  case AF_INET:
+    sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
+    break;
+  case AF_INET6:
+    sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
+    break;
+  case AF_UNIX:
+    if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0)  /* Do not bind to blank UNIX path! */
       return;
-    }
+    sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
+    break;
+  default:
+    GNUNET_break (0);
+    sock = NULL;
+    errno = EAFNOSUPPORT;
+    break;
+  }
+  if (NULL == sock)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Unable to create socket for service `%s': %s\n"),
+                sl->name, STRERROR (errno));
+    GNUNET_free (sa);
+    return;
+  }
   if (GNUNET_NETWORK_socket_setsockopt
       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
@@ -621,29 +724,53 @@ 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));
-      GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
-      GNUNET_free (sa);
-      return;
-    }
+  {
+    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));
+    GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+    GNUNET_free (sa);
+    return;
+  }
+#ifndef WINDOWS
+  if ((AF_UNIX == sa->sa_family)
+#ifdef LINUX
+      /* Permission settings are not required when abstract sockets are used */
+      && ('\0' != ((const struct sockaddr_un *)sa)->sun_path[0])
+#endif
+      )
+  {
+    match_uid =
+      GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name,
+                                            "UNIX_MATCH_UID");
+    match_gid =
+      GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name,
+                                            "UNIX_MATCH_GID");
+    GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sa)->sun_path,
+                                 match_uid,
+                                 match_gid);
+
+  }
+#endif
   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
-      GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
-      GNUNET_free (sa);
-      return;
-    }
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+    GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+    GNUNET_free (sa);
+    return;
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              _("ARM now monitors connections to service `%s' at `%s'\n"),
              sl->name, GNUNET_a2s (sa, addr_len));
-  sli = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
+  sli = GNUNET_new (struct ServiceListeningInfo);
   sli->service_addr = sa;
   sli->service_addr_len = addr_len;
   sli->listen_socket = sock;
@@ -651,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);
 }
 
 
@@ -684,7 +813,8 @@ free_service (struct ServiceList *sl)
  *         #GNUNET_SYSERR to close it (signal serious error)
  */
 static void
-handle_start (void *cls, struct GNUNET_SERVER_Client *client,
+handle_start (void *cls,
+              struct GNUNET_SERVER_Client *client,
              const struct GNUNET_MessageHeader *message)
 {
   const char *servicename;
@@ -693,37 +823,40 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client,
   uint64_t request_id;
   struct GNUNET_ARM_Message *amsg;
 
-  amsg = (struct GNUNET_ARM_Message *) message;  
+  amsg = (struct GNUNET_ARM_Message *) message;
   request_id = GNUNET_ntohll (amsg->request_id);
   size = ntohs (amsg->header.size);
   size -= sizeof (struct GNUNET_ARM_Message);
   servicename = (const char *) &amsg[1];
   if ((size == 0) || (servicename[size - 1] != '\0'))
-    {
-      GNUNET_break (0);
-      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-      return;
-    }
+  {
+    GNUNET_break (0);
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
   if (GNUNET_YES == in_shutdown)
-    {
-      signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN);
-      GNUNET_SERVER_receive_done (client, GNUNET_OK);
-      return;
-    }
+  {
+    signal_result (client, servicename, request_id,
+                  GNUNET_ARM_RESULT_IN_SHUTDOWN);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
   sl = find_service (servicename);
   if (NULL == sl)
-    {
-      signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN);
-      GNUNET_SERVER_receive_done (client, GNUNET_OK);
-      return;
-    }
-  sl->is_default = GNUNET_YES;
-  if (sl->proc != NULL)
-    {
-      signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
-      GNUNET_SERVER_receive_done (client, GNUNET_OK);
-      return;
-    }
+  {
+    signal_result (client, servicename, request_id,
+                  GNUNET_ARM_RESULT_IS_NOT_KNOWN);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
+  sl->force_start = GNUNET_YES;
+  if (NULL != sl->proc)
+  {
+    signal_result (client, servicename, request_id,
+                  GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
   start_process (sl, client, request_id);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
@@ -733,12 +866,12 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client,
  * 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");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Triggering shutdown\n");
   GNUNET_SCHEDULER_shutdown ();
 }
 
@@ -753,7 +886,8 @@ trigger_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  *         #GNUNET_SYSERR to close it (signal serious error)
  */
 static void
-handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
+handle_stop (void *cls,
+             struct GNUNET_SERVER_Client *client,
             const struct GNUNET_MessageHeader *message)
 {
   struct ServiceList *sl;
@@ -762,7 +896,7 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
   uint64_t request_id;
   struct GNUNET_ARM_Message *amsg;
 
-  amsg = (struct GNUNET_ARM_Message *) message;  
+  amsg = (struct GNUNET_ARM_Message *) message;
   request_id = GNUNET_ntohll (amsg->request_id);
   size = ntohs (amsg->header.size);
   size -= sizeof (struct GNUNET_ARM_Message);
@@ -774,54 +908,71 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
       return;
     }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-             _("Preparing to stop `%s'\n"), servicename);
+             _("Preparing to stop `%s'\n"),
+             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;
     }
-  sl->is_default = GNUNET_NO;
+  sl->force_start = GNUNET_NO;
   if (GNUNET_YES == in_shutdown)
     {
       /* shutdown in progress */
-      signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN);
-      GNUNET_SERVER_receive_done (client, GNUNET_OK);
-      return;
-    }
-  if (sl->killing_client != NULL)
-    {
-      /* killing already in progress */
-      signal_result (client, servicename, request_id,
-          GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
-      GNUNET_SERVER_receive_done (client, GNUNET_OK);
-      return;
-    }
-  if (sl->proc == NULL)
-    {
-      /* process is down */
-      signal_result (client, servicename, request_id,
-          GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
+      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,
+                  GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
+  if (NULL == sl->proc)
+  {
+    /* process is down */
+    signal_result (client,
+                  servicename,
+                  request_id,
+                  GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
   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, SIGTERM))
+  if (0 != GNUNET_OS_process_kill (sl->proc, GNUNET_TERM_SIG))
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
   sl->killing_client = client;
   sl->killing_client_request_id = request_id;
@@ -847,17 +998,18 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
   size_t total_size;
   struct ServiceList *sl;
   uint16_t count;
-  
+
   if (NULL == client)
     return;
-  
+
   request = (struct GNUNET_ARM_Message *) message;
+  GNUNET_break (0 == ntohl (request->reserved));
   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)
+  for (sl = running_head; NULL != sl; sl = sl->next)
   {
-    if (sl->proc != NULL)
+    if (NULL != sl->proc)
     {
       string_list_size += strlen (sl->name);
       string_list_size += strlen (sl->binary);
@@ -866,29 +1018,29 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
     }
   }
 
-  total_size = sizeof (struct GNUNET_ARM_ListResultMessage) 
+  total_size = sizeof (struct GNUNET_ARM_ListResultMessage)
                + string_list_size;
   msg = GNUNET_malloc (total_size);
   msg->arm_msg.header.size = total_size;
   msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT;
+  msg->arm_msg.reserved = htonl (0);
   msg->arm_msg.request_id = GNUNET_ntohll (request->request_id);
   msg->count = count;
-  
+
   char *pos = (char *)&msg[1];
-  for (sl = running_head; sl != NULL; sl = sl->next) 
+  for (sl = running_head; NULL != sl; sl = sl->next)
   {
-    if (sl->proc != NULL)
+    if (NULL != sl->proc)
     {
       size_t s = strlen (sl->name) + strlen (sl->binary) + 4;
-      GNUNET_snprintf(pos, s, "%s (%s)", sl->name, sl->binary);
+      GNUNET_snprintf (pos, s, "%s (%s)", sl->name, sl->binary);
       pos += s;
     }
   }
-  
   GNUNET_SERVER_notify_transmit_ready (client,
                                        total_size,
                                        GNUNET_TIME_UNIT_FOREVER_REL,
-                                       write_list_result, msg);
+                                       &write_list_result, msg);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -911,14 +1063,20 @@ do_shutdown ()
       GNUNET_SERVER_destroy (server);
       server = NULL;
     }
-  if (GNUNET_SCHEDULER_NO_TASK != child_death_task)
+  if (NULL != child_death_task)
     {
       GNUNET_SCHEDULER_cancel (child_death_task);
-      child_death_task = GNUNET_SCHEDULER_NO_TASK;
+      child_death_task = NULL;
     }
 }
 
 
+/**
+ * Count how many services are still active.
+ *
+ * @param running_head list of services
+ * @return number of active services found
+ */
 static unsigned int
 list_count (struct ServiceList *running_head)
 {
@@ -926,7 +1084,7 @@ list_count (struct ServiceList *running_head)
   unsigned int res = 0;
 
   for (res = 0, i = running_head; i; i = i->next, res++)
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "%s\n",
                i->name);
   return res;
@@ -937,21 +1095,20 @@ 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;
   struct ServiceListeningInfo *sli;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "First shutdown phase\n");
-  if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
+  if (NULL != child_restart_task)
   {
     GNUNET_SCHEDULER_cancel (child_restart_task);
-    child_restart_task = GNUNET_SCHEDULER_NO_TASK;
+    child_restart_task = NULL;
   }
   in_shutdown = GNUNET_YES;
   /* first, stop listening */
@@ -960,11 +1117,12 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     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)
+                                    pos->listen_tail,
+                                    sli);
+       if (NULL != sli->accept_task)
          {
            GNUNET_SCHEDULER_cancel (sli->accept_task);
-           sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
+           sli->accept_task = NULL;
          }
        GNUNET_break (GNUNET_OK ==
                      GNUNET_NETWORK_socket_close (sli->listen_socket));
@@ -977,13 +1135,13 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   while (NULL != (pos = nxt))
   {
     nxt = pos->next;
-    if (pos->proc != NULL)
+    if (NULL != pos->proc)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
+      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))
+      if (0 != GNUNET_OS_process_kill (pos->proc, GNUNET_TERM_SIG))
        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
     }
     else
@@ -992,11 +1150,11 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     }
   }
   /* 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,
-               "Delaying shutdown, have %u childs still running\n", 
+               "Delaying shutdown, have %u childs still running\n",
                list_count (running_head));
 }
 
@@ -1005,19 +1163,16 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * 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 = GNUNET_SCHEDULER_NO_TASK;
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-    return;
+  child_restart_task = NULL;
   GNUNET_assert (GNUNET_NO == in_shutdown);
   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
 
@@ -1031,23 +1186,25 @@ delayed_restart_task (void *cls,
     if (0 == GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value_us)
     {
       /* restart is now allowed */
-      if (sl->is_default)
+      if (sl->force_start)
       {
        /* process should run by default, start immediately */
        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                   _("Restarting service `%s'.\n"), sl->name);
+                   _("Restarting service `%s'.\n"),
+                    sl->name);
        start_process (sl, NULL, 0);
       }
       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)
+         if (NULL == 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,
+             (GNUNET_TIME_UNIT_FOREVER_REL,
+              sli->listen_socket,
               &accept_connection, sli);
          }
       }
@@ -1063,12 +1220,12 @@ delayed_restart_task (void *cls,
   }
   if (lowestRestartDelay.rel_value_us != GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+    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, 
+                                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
                                                  &delayed_restart_task, NULL);
   }
 }
@@ -1079,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;
@@ -1095,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);
-  child_death_task = GNUNET_SCHEDULER_NO_TASK;
-  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;
-    }
+  pr = GNUNET_DISK_pipe_handle (sigpipe,
+                               GNUNET_DISK_PIPE_END_READ);
+  child_death_task = NULL;
   /* 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)
         {
-          /* 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 (GNUNET_SCHEDULER_NO_TASK == sli->accept_task);
-            sli->accept_task =
-                GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                    sli->listen_socket, &accept_connection, sli);
-          }
-       }
-        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 %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 (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_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)
@@ -1237,7 +1466,8 @@ sighandler_child_death ()
  * @return #GNUNET_OK (continue)
  */
 static void
-setup_service (void *cls, const char *section)
+setup_service (void *cls,
+               const char *section)
 {
   struct ServiceList *sl;
   char *binary;
@@ -1252,10 +1482,29 @@ setup_service (void *cls, const char *section)
     return;
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary))
+  {
+    /* not a service section */
+    return;
+  }
+  if ((GNUNET_YES ==
+       GNUNET_CONFIGURATION_have_value (cfg, section, "USER_SERVICE")) &&
+      (GNUNET_YES ==
+       GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "USER_SERVICE")))
+  {
+    if (GNUNET_NO == start_user)
     {
-      /* not a service section */
-      return;
+      GNUNET_free (binary);
+      return; /* user service, and we don't deal with those */
+    }
+  }
+  else
+  {
+    if (GNUNET_NO == start_system)
+    {
+      GNUNET_free (binary);
+      return; /* system service, and we don't deal with those */
     }
+  }
   sl = find_service (section);
   if (NULL != sl)
   {
@@ -1266,16 +1515,19 @@ setup_service (void *cls, const char *section)
   }
   config = NULL;
   if (( (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG",
-                                                 &config)) &&
+        GNUNET_CONFIGURATION_get_value_filename (cfg, section,
+                                                  "CONFIG",
+                                                  &config)) &&
        (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", "DEFAULTCONFIG",
+        GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                                  "PATHS",
+                                                  "DEFAULTCONFIG",
                                                  &config)) ) ||
       (0 != STAT (config, &sbuf)))
   {
     if (NULL != config)
     {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 
+      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
                                 section, "CONFIG",
                                 STRERROR (errno));
       GNUNET_free (config);
@@ -1291,19 +1543,46 @@ setup_service (void *cls, const char *section)
 #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"))
-    return;
-  if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg,
-                                                      &addrs, &addr_lens)))
+  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,
+                                            "FORCESTART"))
+  {
+    sl->force_start = GNUNET_YES;
+    if (GNUNET_YES ==
+        GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                              section,
+                                              "NOARMBIND"))
+      return;
+  }
+  else
+  {
+    if (GNUNET_YES !=
+        GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                              section,
+                                              "AUTOSTART"))
+      return;
+  }
+  if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section,
+                                                       cfg,
+                                                      &addrs,
+                                                       &addr_lens)))
     return;
   /* this will free (or capture) addrs[i] */
   for (i = 0; i < ret; i++)
-    create_listen_socket (addrs[i], addr_lens[i], sl);
+    create_listen_socket (addrs[i],
+                          addr_lens[i],
+                          sl);
   GNUNET_free (addrs);
   GNUNET_free (addr_lens);
 }
@@ -1363,77 +1642,83 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
     {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
-    {&handle_monitor, NULL, GNUNET_MESSAGE_TYPE_ARM_MONITOR, 
+    {&handle_monitor, NULL, GNUNET_MESSAGE_TYPE_ARM_MONITOR,
      sizeof (struct GNUNET_MessageHeader)},
-    {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST, 
+    {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST,
      sizeof (struct GNUNET_ARM_Message)},
     {NULL, NULL, 0, 0}
   };
-  char *defaultservices;
-  const char *pos;
   struct ServiceList *sl;
 
   cfg = c;
   server = serv;
-  GNUNET_assert (serv != NULL);
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
-                               NULL);
+  GNUNET_assert (NULL != serv);
+  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_break (GNUNET_YES == start_user);
+    start_system = GNUNET_NO;
+  }
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                            "ARM",
+                                            "SYSTEM_ONLY"))
+  {
+    GNUNET_break (GNUNET_YES == start_system);
+    start_user = GNUNET_NO;
+  }
   GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL);
 
   /* start default services... */
-  if (GNUNET_OK ==
-      GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES",
-                                            &defaultservices))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                 _("Starting default services `%s'\n"), defaultservices);
-      if (0 < strlen (defaultservices))
-       {
-         for (pos = strtok (defaultservices, " "); NULL != pos;
-              pos = strtok (NULL, " "))
-           {
-             sl = find_service (pos);
-             if (NULL == sl)
-               {
-                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                             _
-                             ("Default service `%s' not configured correctly!\n"),
-                             pos);
-                 continue;
-               }
-             sl->is_default = GNUNET_YES;
-             start_process (sl, NULL, 0);
-           }
-       }
-      GNUNET_free (defaultservices);
-    }
-  else
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                 _
-                 ("No default services configured, GNUnet will not really start right now.\n"));
-    }
-
-  notifier =
-      GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
-  GNUNET_SERVER_connect_notify (server, handle_client_connecting, NULL);
+  for (sl = running_head; NULL != sl; sl = sl->next)
+    if (GNUNET_YES == sl->force_start)
+      start_process (sl, NULL, 0);
+  notifier
+    = GNUNET_SERVER_notification_context_create (server,
+                                                 MAX_NOTIFY_QUEUE);
+  GNUNET_SERVER_connect_notify (server,
+                                &handle_client_connecting, NULL);
   /* process client requests */
-  GNUNET_SERVER_add_handlers (server, handlers);
+  GNUNET_SERVER_add_handlers (server,
+                              handlers);
 }
 
 
@@ -1456,8 +1741,20 @@ 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_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);
@@ -1466,7 +1763,7 @@ main (int argc, char *const *argv)
 }
 
 
-#ifdef LINUX
+#if defined(LINUX) && defined(__GLIBC__)
 #include <malloc.h>
 
 /**