+
+/**
+ * Function to call for each service.
+ *
+ * @param h handle to ARM
+ * @param service_name name of the service
+ * @param timeout how long to wait before failing for good
+ * @param cb callback to invoke when service is ready
+ * @param cb_cls closure for callback
+ */
+typedef void (*ServiceOperation) (struct GNUNET_ARM_Handle *h,
+ const char *service_name,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_ARM_Callback cb, void *cb_cls);
+
+
+/**
+ * Context for starting or stopping multiple services.
+ */
+struct MultiContext
+{
+ /**
+ * NULL-terminated array of services to start or stop.
+ */
+ char **services;
+
+ /**
+ * Our handle to ARM.
+ */
+ struct GNUNET_ARM_Handle *h;
+
+ /**
+ * Identifies the operation (start or stop).
+ */
+ ServiceOperation op;
+
+ /**
+ * Current position in "services".
+ */
+ unsigned int pos;
+};
+
+
+/**
+ * Run the operation for the next service in the multi-service
+ * request.
+ *
+ * @param cls the "struct MultiContext" that is being processed
+ * @param success status of the previous operation (ignored)
+ */
+static void
+next_operation (void *cls,
+ int success)
+{
+ struct MultiContext *mc = cls;
+ char *pos;
+
+ if (NULL == (pos = mc->services[mc->pos]))
+ {
+ GNUNET_free (mc->services);
+ GNUNET_ARM_disconnect (mc->h);
+ GNUNET_free (mc);
+ return;
+ }
+ mc->pos++;
+ mc->op (mc->h, pos, MULTI_TIMEOUT, &next_operation, mc);
+ GNUNET_free (pos);
+}
+
+
+/**
+ * Run a multi-service request.
+ *
+ * @param cfg configuration to use (needed to contact ARM;
+ * the ARM service may internally use a different
+ * configuration to determine how to start the service).
+ * @param sched scheduler to use
+ * @param op the operation to perform for each service
+ * @param va NULL-terminated list of services
+ */
+static void
+run_multi_request (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct GNUNET_SCHEDULER_Handle *sched,
+ ServiceOperation op,
+ va_list va)
+{
+ va_list cp;
+ unsigned int total;
+ struct MultiContext *mc;
+ struct GNUNET_ARM_Handle *h;
+ const char *c;
+
+ h = GNUNET_ARM_connect (cfg, sched, NULL);
+ if (NULL == h)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Error while trying to transmit to ARM service\n"));
+ return;
+ }
+ total = 1;
+ va_copy (cp, va);
+ while (NULL != (va_arg (cp, const char*))) total++;
+ va_end (cp);
+ mc = GNUNET_malloc (sizeof(struct MultiContext));
+ mc->services = GNUNET_malloc (total * sizeof (char*));
+ mc->h = h;
+ mc->op = op;
+ total = 0;
+ va_copy (cp, va);
+ while (NULL != (c = va_arg (cp, const char*)))
+ mc->services[total++] = GNUNET_strdup (c);
+ va_end (cp);
+ next_operation (mc, GNUNET_YES);
+}
+
+
+/**
+ * Start multiple services in the specified order. Convenience
+ * function. Works asynchronously, failures are not reported.
+ *
+ * @param cfg configuration to use (needed to contact ARM;
+ * the ARM service may internally use a different
+ * configuration to determine how to start the service).
+ * @param sched scheduler to use
+ * @param ... NULL-terminated list of service names (const char*)
+ */
+void
+GNUNET_ARM_start_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct GNUNET_SCHEDULER_Handle *sched,
+ ...)
+{
+ va_list ap;
+
+ va_start (ap, sched);
+ run_multi_request (cfg, sched, &GNUNET_ARM_start_service, ap);
+ va_end (ap);
+}
+
+
+/**
+ * Stop multiple services in the specified order. Convenience
+ * function. Works asynchronously, failures are not reported.
+ *
+ * @param cfg configuration to use (needed to contact ARM;
+ * the ARM service may internally use a different
+ * configuration to determine how to start the service).
+ * @param sched scheduler to use
+ * @param ... NULL-terminated list of service names (const char*)
+ */
+void
+GNUNET_ARM_stop_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct GNUNET_SCHEDULER_Handle *sched,
+ ...)
+{
+ va_list ap;
+
+ va_start (ap, sched);
+ run_multi_request (cfg, sched, &GNUNET_ARM_stop_service, ap);
+ va_end (ap);
+}
+
+