Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / arm / gnunet-arm.c
index d998e9d866fa6eb3c4481c0f462d3652abf0ba03..4c30985b1a6c04367bff1cac65446fc51d5b5c21 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2009 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2009, 2012, 2013 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
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -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
 
      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 "platform.h"
 #include "gnunet_arm_service.h"
  */
 #include "platform.h"
 #include "gnunet_arm_service.h"
-#include "gnunet_client_lib.h"
 #include "gnunet_constants.h"
 #include "gnunet_constants.h"
-#include "gnunet_getopt_lib.h"
-#include "gnunet_program_lib.h"
-#include "gnunet_time_lib.h"
-
-/**
- * Timeout for stopping services.  Long to give some services a real chance.
- */
-#define STOP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
-
-/**
- * Timeout for starting services, very short because of the strange way start works
- * (by checking if running before starting, so really this time is always waited on
- * startup (annoying)).
- */
-#define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 50)
-
-/**
- * Timeout for starting services, very short because of the strange way start works
- * (by checking if running before starting, so really this time is always waited on
- * startup (annoying)).
- */
-#define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
+#include "gnunet_util_lib.h"
 
 /**
  * Set if we are to shutdown all services (including ARM).
 
 /**
  * Set if we are to shutdown all services (including ARM).
@@ -75,6 +53,16 @@ static int delete;
  */
 static int quiet;
 
  */
 static int quiet;
 
+/**
+ * Monitor ARM activity.
+ */
+static int monitor;
+
+/**
+ * Set if we should print a list of currently running services.
+ */
+static int list;
+
 /**
  * Set to the name of a service to start.
  */
 /**
  * Set to the name of a service to start.
  */
@@ -85,11 +73,6 @@ static char *init;
  */
 static char *term;
 
  */
 static char *term;
 
-/**
- * Set to the name of a service to test.
- */
-static char *test;
-
 /**
  * Set to the name of the config file used.
  */
 /**
  * Set to the name of the config file used.
  */
@@ -111,251 +94,661 @@ static int ret;
 static struct GNUNET_ARM_Handle *h;
 
 /**
 static struct GNUNET_ARM_Handle *h;
 
 /**
- * Our scheduler.
+ * Monitor connection with ARM.
  */
  */
-static struct GNUNET_SCHEDULER_Handle *sched;
+static struct GNUNET_ARM_MonitorHandle *m;
 
 /**
  * Our configuration.
  */
 
 /**
  * Our configuration.
  */
-const struct GNUNET_CONFIGURATION_Handle *cfg;
+static struct GNUNET_CONFIGURATION_Handle *cfg;
 
 /**
  * Processing stage that we are in.  Simple counter.
  */
 static unsigned int phase;
 
 
 /**
  * Processing stage that we are in.  Simple counter.
  */
 static unsigned int phase;
 
+/**
+ * User defined timestamp for completing operations.
+ */
+static struct GNUNET_TIME_Relative timeout;
+
+/**
+ * Task to be run on timeout.
+ */
+static struct GNUNET_SCHEDULER_Task *timeout_task;
+
+/**
+ * Do we want to give our stdout to gnunet-service-arm?
+ */
+static int no_stdout;
+
+/**
+ * Do we want to give our stderr to gnunet-service-arm?
+ */
+static int no_stderr;
+
+/**
+ * Handle for the task running the #action_loop().
+ */
+static struct GNUNET_SCHEDULER_Task *al_task;
+
+/**
+ * Current operation.
+ */
+static struct GNUNET_ARM_Operation *op;
+
+/**
+ * Attempts to delete configuration file and GNUNET_HOME
+ * on ARM shutdown provided the end and delete options
+ * were specified when gnunet-arm was run.
+ */
+static void
+delete_files ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Will attempt to remove configuration file %s and service directory %s\n",
+             config_file, dir);
+
+  if (0 != UNLINK (config_file))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to remove configuration file %s\n"),
+                config_file);
+  }
+  if (GNUNET_OK != GNUNET_DISK_directory_remove (dir))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to remove servicehome directory %s\n"),
+                dir);
+
+  }
+}
+
 
 /**
  * Main continuation-passing-style loop.  Runs the various
  * jobs that we've been asked to do in order.
  *
  * @param cls closure, unused
 
 /**
  * Main continuation-passing-style loop.  Runs the various
  * jobs that we've been asked to do in order.
  *
  * @param cls closure, unused
- * @param tc context, unused
  */
 static void
  */
 static void
-cps_loop (void *cls,
-         const struct GNUNET_SCHEDULER_TaskContext *tc);
+shutdown_task (void *cls)
+{
+  if (NULL != al_task)
+  {
+    GNUNET_SCHEDULER_cancel (al_task);
+    al_task = NULL;
+  }
+  if (NULL != op)
+  {
+    GNUNET_ARM_operation_cancel (op);
+    op = NULL;
+  }
+  if (NULL != h)
+  {
+    GNUNET_ARM_disconnect (h);
+    h = NULL;
+  }
+  if (NULL != m)
+  {
+    GNUNET_ARM_monitor_stop (m);
+    m = NULL;
+  }
+  if (NULL != timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (timeout_task);
+    timeout_task = NULL;
+  }
+  if ((GNUNET_YES == end) && (GNUNET_YES == delete))
+    delete_files ();
+  GNUNET_CONFIGURATION_destroy (cfg);
+  cfg = NULL;
+}
 
 
 /**
 
 
 /**
- * Callback invoked with the status of the last operation.  Reports to the
- * user and then runs the next phase in the FSM.
+ * Returns a string interpretation of 'rs'
  *
  *
- * @param cls pointer to "const char*" identifying service that was manipulated
- * @param success GNUNET_OK if service is now running, GNUNET_NO if not, GNUNET_SYSERR on error
+ * @param rs the request status from ARM
+ * @return a string interpretation of the request status
  */
  */
-static void
-confirm_cb (void *cls, int success)
+static const char *
+req_string (enum GNUNET_ARM_RequestStatus rs)
 {
 {
-  const char *service = cls;
-  switch (success)
-    {
-    case GNUNET_OK:
-      if (quiet != GNUNET_YES)
-        fprintf(stdout, _("Service `%s' is now running.\n"), service);
-      if ((phase - 1 != 2) && (phase - 1 != 3))
-        {
-          if (quiet != GNUNET_YES)
-            fprintf(stdout, _("Failed to stop service `%s'!\n"), service);
-          ret = 1;
-        }
-      break;
-    case GNUNET_NO:
-      if (quiet != GNUNET_YES)
-        fprintf(stdout, _("Service `%s' is not running.\n"), service);
-      if ((phase - 1 != 0) && (phase - 1 != 1))
-        {
-          if (quiet != GNUNET_YES)
-            fprintf(stdout, _("Failed to start service `%s'!\n"), service);
-          ret = 1;
-        }
-      break;
-    case GNUNET_SYSERR:
-      if (quiet != GNUNET_YES)
-        fprintf(stdout,
-                _("Some error communicating with service `%s'.\n"), service);
-      ret = 1;
-      break;
-    }
+  switch (rs)
+  {
+  case GNUNET_ARM_REQUEST_SENT_OK:
+    return _("Message was sent successfully");
+  case GNUNET_ARM_REQUEST_DISCONNECTED:
+    return _("We disconnected from ARM before we could send a request");
+  }
+  return _("Unknown request status");
+}
 
 
-  GNUNET_SCHEDULER_add_continuation (sched,
-                                    &cps_loop,
-                                    NULL,
-                                    GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+
+/**
+ * Returns a string interpretation of the 'result'
+ *
+ * @param result the arm result
+ * @return a string interpretation
+ */
+static const char *
+ret_string (enum GNUNET_ARM_Result result)
+{
+  switch (result)
+  {
+  case GNUNET_ARM_RESULT_STOPPED:
+    return _("is stopped");
+  case GNUNET_ARM_RESULT_STARTING:
+    return _("is starting");
+  case GNUNET_ARM_RESULT_STOPPING:
+    return _("is stopping");
+  case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
+    return _("is starting already");
+  case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
+    return _("is stopping already");
+  case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
+    return _("is started already");
+  case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
+    return _("is stopped already");
+  case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
+    return _("service is not known to ARM");
+  case GNUNET_ARM_RESULT_START_FAILED:
+    return _("service failed to start");
+  case GNUNET_ARM_RESULT_IN_SHUTDOWN:
+    return _("service cannot be manipulated because ARM is shutting down");
+  }
+  return _("Unknown result code.");
 }
 
 
 /**
 }
 
 
 /**
- * Function called to confirm that a service is running (or that
- * it is not running).
+ * Main task that runs our various operations in order.
  *
  *
- * @param cls pointer to "const char*" identifying service that was manipulated
- * @param tc reason determines if service is now running
+ * @param cls closure
  */
 static void
  */
 static void
-confirm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+action_loop (void *cls);
+
+
+/**
+ * Function called whenever we connect to or disconnect from ARM.
+ * Termiantes the process if we fail to connect to the service on
+ * our first attempt.
+ *
+ * @param cls closure
+ * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
+ *                  #GNUNET_SYSERR on error.
+ */
+static void
+conn_status (void *cls,
+            int connected)
 {
 {
-  const char *service = cls;
+  static int once;
 
 
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
-    {
-      if (quiet != GNUNET_YES)
-        fprintf(stdout, _("Service `%s' is running.\n"), service);
-    }
-  else
-    {
-      if (quiet != GNUNET_YES)
-        fprintf(stdout, _("Service `%s' is not running.\n"), service);
-    }
-  GNUNET_SCHEDULER_add_continuation (sched,
-                                    &cps_loop,
-                                    NULL,
-                                    GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+  if ( (GNUNET_SYSERR == connected) &&
+       (0 == once) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("Fatal error initializing ARM API.\n"));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  once = 1;
 }
 
 
 /**
 }
 
 
 /**
- * Main function that will be run by the scheduler.
+ * We have requested ARM to be started, this function
+ * is called with the result of the operation.  Informs the
+ * use of the result; on success, we continue with the event
+ * loop, on failure we terminate the process.
  *
  *
- * @param cls closure
- * @param s the scheduler to use
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param c configuration
+ * @param cls closure unused
+ * @param rs what happened to our request
+ * @param result if the request was processed, this is the result
+ *               according to ARM
  */
 static void
  */
 static void
-run (void *cls,
-     struct GNUNET_SCHEDULER_Handle *s,
-     char *const *args,
-     const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *c)
+start_callback (void *cls,
+               enum GNUNET_ARM_RequestStatus rs,
+               enum GNUNET_ARM_Result result)
 {
 {
-  sched = s;
-  cfg = c;
-  config_file = cfgfile;
-  if (GNUNET_CONFIGURATION_get_value_string(cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                 _("Fatal configuration error: `%s' option in section `%s' missing.\n"),
-                 "SERVICEHOME",
-                 "PATHS");
-      return;
-    }
-  h = GNUNET_ARM_connect (cfg, sched, NULL);
-  if (h == NULL)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-              _("Fatal error initializing ARM API.\n"));
-      ret = 1;
-      return;
-    }
-  GNUNET_SCHEDULER_add_continuation (sched,
-                                    &cps_loop,
-                                    NULL,
-                                    GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+  op = NULL;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    FPRINTF (stdout,
+             _("Failed to start the ARM service: %s\n"),
+             req_string (rs));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if ( (GNUNET_ARM_RESULT_STARTING != result) &&
+       (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result) )
+  {
+    FPRINTF (stdout,
+             _("Failed to start the ARM service: %s\n"),
+             ret_string (result));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "ARM service [re]start successful\n");
+  start = 0;
+  al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
 }
 
 }
 
+
 /**
 /**
- * Attempts to delete configuration file and SERVICEHOME
- * on arm shutdown provided the end and delete options
- * were specified when gnunet-arm was run.
+ * We have requested ARM to be stopped, this function
+ * is called with the result of the operation.  Informs the
+ * use of the result; on success, we continue with the event
+ * loop, on failure we terminate the process.
+ *
+ * @param cls closure unused
+ * @param rs what happened to our request
+ * @param result if the request was processed, this is the result
+ *               according to ARM
  */
  */
-static void delete_files()
+static void
+stop_callback (void *cls,
+              enum GNUNET_ARM_RequestStatus rs,
+              enum GNUNET_ARM_Result result)
 {
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will attempt to remove configuration file %s and service directory %s\n", config_file, dir);
+  char *msg;
 
 
-  if (UNLINK(config_file) != 0)
+  op = NULL;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
   {
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-           _("Failed to remove configuration file %s\n"), config_file);
+    GNUNET_asprintf (&msg, "%s",
+                    _("Failed to send a stop request to the ARM service: %s\n"));
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
   }
   }
+  if ((GNUNET_ARM_RESULT_STOPPING != result) &&
+      (GNUNET_ARM_RESULT_STOPPED != result) &&
+      (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
+  {
+    FPRINTF (stdout,
+             _("Failed to stop the ARM service: %s\n"),
+             ret_string (result));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "ARM service shutdown successful\n");
+  end = 0;
+  if (restart)
+  {
+    restart = 0;
+    start = 1;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Initiating an ARM restart\n");
+  }
+  al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
+}
+
 
 
-  if (GNUNET_DISK_directory_remove(dir) != GNUNET_OK)
+/**
+ * We have requested a service to be started, this function
+ * is called with the result of the operation.  Informs the
+ * use of the result; on success, we continue with the event
+ * loop, on failure we terminate the process.
+ *
+ * @param cls closure unused
+ * @param rs what happened to our request
+ * @param result if the request was processed, this is the result
+ *               according to ARM
+ */
+static void
+init_callback (void *cls,
+              enum GNUNET_ARM_RequestStatus rs,
+              enum GNUNET_ARM_Result result)
+{
+  op = NULL;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
   {
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-        _("Failed to remove servicehome directory %s\n"), dir);
+    FPRINTF (stdout,
+             _("Failed to send a request to start the `%s' service: %s\n"),
+             init,
+             req_string (rs));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if ((GNUNET_ARM_RESULT_STARTING != result) &&
+      (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
+  {
+    FPRINTF (stdout,
+             _("Failed to start the `%s' service: %s\n"),
+             init,
+             ret_string (result));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Service %s [re]started successfully\n",
+             init);
+  GNUNET_free (init);
+  init = NULL;
+  al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
+}
 
 
+
+/**
+ * We have requested a service to be stopped, this function
+ * is called with the result of the operation.  Informs the
+ * use of the result; on success, we continue with the event
+ * loop, on failure we terminate the process.
+ *
+ * @param cls closure unused
+ * @param rs what happened to our request
+ * @param result if the request was processed, this is the result
+ *               according to ARM
+ */
+static void
+term_callback (void *cls,
+              enum GNUNET_ARM_RequestStatus rs,
+              enum GNUNET_ARM_Result result)
+{
+  char *msg;
+
+  op = NULL;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    GNUNET_asprintf (&msg,
+                     _("Failed to send a request to kill the `%s' service: %%s\n"),
+                     term);
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
   }
   }
+  if ((GNUNET_ARM_RESULT_STOPPED != result) &&
+      (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
+  {
+    FPRINTF (stdout,
+             _("Failed to kill the `%s' service: %s\n"),
+             term,
+             ret_string (result));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Service %s stopped successfully\n",
+              term);
+  GNUNET_free (term);
+  term = NULL;
+  al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
 }
 
 }
 
+
 /**
 /**
- * Main continuation-passing-style loop.  Runs the various
- * jobs that we've been asked to do in order.
+ * Function called with the list of running services. Prints
+ * the list to stdout, then starts the event loop again.
+ * Prints an error message and terminates the process on errors.
+ *
+ * @param cls closure (unused)
+ * @param rs request status (success, failure, etc.)
+ * @param count number of services in the list
+ * @param list list of services that are running
+ */
+static void
+list_callback (void *cls,
+              enum GNUNET_ARM_RequestStatus rs,
+              unsigned int count,
+              const char *const*list)
+{
+  unsigned int i;
+
+  op = NULL;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    char *msg;
+
+    GNUNET_asprintf (&msg, "%s",
+                    _("Failed to request a list of services: %s\n"));
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    ret = 3;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  if (NULL == list)
+  {
+    FPRINTF (stderr, "%s",
+            _("Error communicating with ARM. ARM not running?\n"));
+    GNUNET_SCHEDULER_shutdown ();
+    ret = 3;
+    return;
+  }
+  if (! quiet)
+    FPRINTF (stdout, "%s", _("Running services:\n"));
+  for (i = 0; i < count; i++)
+    FPRINTF (stdout, "%s\n", list[i]);
+  al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
+}
+
+
+/**
+ * Main action loop.  Runs the various jobs that we've been asked to
+ * do, in order.
  *
  * @param cls closure, unused
  *
  * @param cls closure, unused
- * @param tc context, unused
  */
 static void
  */
 static void
-cps_loop (void *cls,
-         const struct GNUNET_SCHEDULER_TaskContext *tc)
+action_loop (void *cls)
 {
 {
+  al_task = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Running requested actions\n");
   while (1)
   while (1)
+  {
+    switch (phase++)
     {
     {
-      switch (phase++)
+    case 0:
+      if (NULL != term)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "Termination action\n");
+        op = GNUNET_ARM_request_service_stop (h,
+                                              term,
+                                              &term_callback,
+                                              NULL);
+       return;
+      }
+      break;
+    case 1:
+      if (end || restart)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "End action\n");
+        op = GNUNET_ARM_request_service_stop (h,
+                                              "arm",
+                                              &stop_callback,
+                                              NULL);
+        return;
+      }
+      break;
+    case 2:
+      if (start)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "Start action\n");
+        op = GNUNET_ARM_request_service_start (h, "arm",
+                                               (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
+                                               (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
+                                               &start_callback,
+                                               NULL);
+        return;
+      }
+      break;
+    case 3:
+      if (NULL != init)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "Initialization action\n");
+        op = GNUNET_ARM_request_service_start (h, init,
+                                               GNUNET_OS_INHERIT_STD_NONE,
+                                               &init_callback,
+                                               NULL);
+        return;
+      }
+      break;
+    case 4:
+      if (list)
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "Going to list all running services controlled by ARM.\n");
+        op = GNUNET_ARM_request_service_list (h,
+                                              &list_callback,
+                                              &list);
+       return;
+      }
+      break;
+    case 5:
+      if (monitor)
        {
        {
-       case 0:
-         if (term != NULL)
-           {
-             GNUNET_ARM_stop_service (h, term, STOP_TIMEOUT, &confirm_cb, term);
-             return;
-           }
-         break;
-       case 1:
-         if ((end) || (restart))
-           {
-             GNUNET_ARM_stop_service (h, "arm", STOP_TIMEOUT, &confirm_cb, "arm");
-             return;
-           }
-         break;
-       case 2:
-         if (start)
-           {
-             GNUNET_ARM_start_service (h, "arm", START_TIMEOUT, &confirm_cb, "arm");
-             return;
-           }
-         break;
-       case 3:
-         if (init != NULL)
-           {
-             GNUNET_ARM_start_service (h, init, START_TIMEOUT, &confirm_cb, init);
-             return;
-           }
-         break;
-       case 4:
-         if (test != NULL)
-           {
-             GNUNET_CLIENT_service_test (sched, test, cfg, TEST_TIMEOUT, &confirm_task, test);
-             return;
-           }
-         break;
-       case 5:
-         if (restart)
-           {
-              GNUNET_ARM_disconnect (h);
-              phase = 0;
-              end = 0;
-              start = 1;
-              restart = 0;
-              h = GNUNET_ARM_connect (cfg, sched, NULL);
-              if (h == NULL)
-                {
-                  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                           _("Fatal error initializing ARM API.\n"));
-                  ret = 1;
-                  return;
-                }
-              GNUNET_SCHEDULER_add_now(sched, &cps_loop, NULL);
-              return;
-           }
-         /* Fall through */
-       default: /* last phase */
-         GNUNET_ARM_disconnect (h);
-         if ((end == GNUNET_YES) && (delete == GNUNET_YES))
-           delete_files();
-         return;
+         if (! quiet)
+           fprintf (stderr,
+                    _("Now only monitoring, press CTRL-C to stop.\n"));
+         quiet = 0; /* does not make sense to stay quiet in monitor mode at this time */
+         return; /* done with tasks, just monitor */
        }
        }
+      break;
+    default:           /* last phase */
+      GNUNET_SCHEDULER_shutdown ();
+      return;
     }
     }
+  }
+}
+
+
+/**
+ * Function called when a service starts or stops.
+ *
+ * @param cls closure
+ * @param service service name
+ * @param status status of the service
+ */
+static void
+srv_status (void *cls,
+           const char *service,
+           enum GNUNET_ARM_ServiceStatus status)
+{
+  const char *msg;
+
+  switch (status)
+  {
+  case GNUNET_ARM_SERVICE_MONITORING_STARTED:
+    return; /* this should be done silently */
+  case GNUNET_ARM_SERVICE_STOPPED:
+    msg = _("Stopped %s.\n");
+    break;
+  case GNUNET_ARM_SERVICE_STARTING:
+    msg = _("Starting %s...\n");
+    break;
+  case GNUNET_ARM_SERVICE_STOPPING:
+    msg = _("Stopping %s...\n");
+    break;
+  default:
+    msg = NULL;
+    break;
+  }
+  if (! quiet)
+  {
+    if (NULL != msg)
+      FPRINTF (stderr,
+               msg,
+               service);
+    else
+      FPRINTF (stderr,
+               _("Unknown status %u for service %s.\n"),
+               status,
+               service);
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Got service %s status %d\n",
+              service, (int)
+              status);
+}
+
+
+/**
+ * Task run on timeout (if -T is given).
+ */
+static void
+timeout_task_cb (void *cls)
+{
+  timeout_task = NULL;
+  ret = 2;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param c configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+{
+  char *armconfig;
+
+  cfg = GNUNET_CONFIGURATION_dup (c);
+  config_file = cfgfile;
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "PATHS",
+                                             "GNUNET_HOME",
+                                             &dir))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "PATHS",
+                               "GNUNET_HOME");
+    return;
+  }
+  if (NULL != cfgfile)
+  {
+    if (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                                 "arm",
+                                                 "CONFIG",
+                                                &armconfig))
+    {
+      GNUNET_CONFIGURATION_set_value_string (cfg,
+                                             "arm",
+                                             "CONFIG",
+                                             cfgfile);
+    }
+    else
+      GNUNET_free (armconfig);
+  }
+  if (NULL == (h = GNUNET_ARM_connect (cfg,
+                                       &conn_status,
+                                       NULL)))
+    return;
+  if (monitor)
+    m = GNUNET_ARM_monitor_start (cfg,
+                            &srv_status,
+                            NULL);
+  al_task = GNUNET_SCHEDULER_add_now (&action_loop,
+                                      NULL);
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+                                 NULL);
+  if (0 != timeout.rel_value_us)
+    timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
+                                                 &timeout_task_cb,
+                                                 NULL);
 }
 
 
 }
 
 
@@ -364,38 +757,93 @@ cps_loop (void *cls,
  *
  * @param argc number of arguments from the command line
  * @param argv command line arguments
  *
  * @param argc number of arguments from the command line
  * @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @return 0 ok, 1 on error, 2 on timeout
  */
 int
 main (int argc, char *const *argv)
 {
  */
 int
 main (int argc, char *const *argv)
 {
-  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
-    {'e', "end", NULL, gettext_noop ("stop all GNUnet services"),
-     GNUNET_NO, &GNUNET_GETOPT_set_one, &end},
-    {'i', "init", "SERVICE", gettext_noop ("start a particular service"),
-     GNUNET_YES, &GNUNET_GETOPT_set_string, &init},
-    {'k', "kill", "SERVICE", gettext_noop ("stop a particular service"),
-     GNUNET_YES, &GNUNET_GETOPT_set_string, &term},
-    {'s', "start", NULL, gettext_noop ("start all GNUnet default services"),
-     GNUNET_NO, &GNUNET_GETOPT_set_one, &start},
-    {'r', "restart", NULL, gettext_noop ("stop and start all GNUnet default services"),
-     GNUNET_NO, &GNUNET_GETOPT_set_one, &restart},
-    {'t', "test", "SERVICE",
-     gettext_noop ("test if a particular service is running"),
-     GNUNET_YES, &GNUNET_GETOPT_set_string, &test},
-    {'d', "delete", NULL, gettext_noop ("delete config file and directory on exit"),
-     GNUNET_NO, &GNUNET_GETOPT_set_one, &delete},
-    {'q', "quiet", NULL, gettext_noop ("don't print status messages"),
-     GNUNET_NO, &GNUNET_GETOPT_set_one, &quiet},
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+
+    GNUNET_GETOPT_option_flag ('e',
+                                  "end",
+                                  gettext_noop ("stop all GNUnet services"),
+                                  &end),
+
+    GNUNET_GETOPT_option_string ('i',
+                                 "init",
+                                 "SERVICE",
+                                 gettext_noop ("start a particular service"),
+                                 &init),
+
+    GNUNET_GETOPT_option_string ('k',
+                                 "kill",
+                                 "SERVICE",
+                                 gettext_noop ("stop a particular service"),
+                                 &term),
+
+    GNUNET_GETOPT_option_flag ('s',
+                                  "start",
+                                  gettext_noop ("start all GNUnet default services"),
+                                  &start),
+
+    GNUNET_GETOPT_option_flag ('r',
+                                  "restart",
+                                  gettext_noop ("stop and start all GNUnet default services"),
+                                  &restart),
+    GNUNET_GETOPT_option_flag ('d',
+                                  "delete",
+                                  gettext_noop ("delete config file and directory on exit"),
+                                  &delete),
+
+    GNUNET_GETOPT_option_flag ('m',
+                                  "monitor",
+                                  gettext_noop ("monitor ARM activities"),
+                                  &monitor),
+
+    GNUNET_GETOPT_option_flag ('q',
+                                  "quiet",
+                                  gettext_noop ("don't print status messages"),
+                                  &quiet),
+
+    GNUNET_GETOPT_option_relative_time ('T',
+                                            "timeout",
+                                            "DELAY",
+                                            gettext_noop ("exit with error status if operation does not finish after DELAY"),
+                                            &timeout),
+
+    GNUNET_GETOPT_option_flag ('I',
+                                  "info",
+                                  gettext_noop ("list currently running services"),
+                                  &list), 
+
+    GNUNET_GETOPT_option_flag ('O',
+                                  "no-stdout",
+                                  gettext_noop ("don't let gnunet-service-arm inherit standard output"),
+                                  &no_stdout),
+
+    GNUNET_GETOPT_option_flag ('E',
+                                  "no-stderr",
+                                  gettext_noop ("don't let gnunet-service-arm inherit standard error"),
+                                  &no_stderr),
+
     GNUNET_GETOPT_OPTION_END
   };
     GNUNET_GETOPT_OPTION_END
   };
-  return (GNUNET_OK ==
-          GNUNET_PROGRAM_run (argc,
-                              argv,
-                              "gnunet-arm",
-                              gettext_noop
-                              ("Control services and the Automated Restart Manager (ARM)"),
-                              options, &run, NULL)) ? ret : 1;
+
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
+                                                 &argc, &argv))
+    return 2;
+
+  if (GNUNET_OK ==
+      GNUNET_PROGRAM_run (argc, argv, "gnunet-arm",
+                         gettext_noop
+                         ("Control services and the Automated Restart Manager (ARM)"),
+                         options, &run, NULL))
+  {
+    GNUNET_free ((void *) argv);
+    return ret;
+  }
+  GNUNET_free ((void*) argv);
+  return 1;
 }
 
 /* end of gnunet-arm.c */
 }
 
 /* end of gnunet-arm.c */