2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief API for accessing the ARM service
24 * @author Christian Grothoff
27 #include "gnunet_arm_service.h"
28 #include "gnunet_client_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_server_lib.h"
37 * How long are we willing to wait for a service operation during the multi-operation
40 #define MULTI_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
44 * Handle for interacting with ARM.
46 struct GNUNET_ARM_Handle
50 * Our connection to the ARM service.
52 struct GNUNET_CLIENT_Connection *client;
55 * The configuration that we are using.
57 struct GNUNET_CONFIGURATION_Handle *cfg;
62 struct GNUNET_SCHEDULER_Handle *sched;
68 * Setup a context for communicating with ARM. Note that this
69 * can be done even if the ARM service is not yet running.
71 * @param cfg configuration to use (needed to contact ARM;
72 * the ARM service may internally use a different
73 * configuration to determine how to start the service).
74 * @param sched scheduler to use
75 * @param service service that *this* process is implementing/providing, can be NULL
76 * @return context to use for further ARM operations, NULL on error
78 struct GNUNET_ARM_Handle *
79 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
80 struct GNUNET_SCHEDULER_Handle *sched,
83 struct GNUNET_ARM_Handle *ret;
85 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
86 ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
93 * Disconnect from the ARM service.
95 * @param h the handle that was being used
98 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
100 if (h->client != NULL)
101 GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
102 GNUNET_CONFIGURATION_destroy (h->cfg);
106 struct ARM_ShutdownContext
109 * Callback to call once shutdown complete.
111 GNUNET_ARM_Callback cb;
114 * Closure for callback.
122 * Internal state for a request with ARM.
124 struct RequestContext
128 * Pointer to our handle with ARM.
130 struct GNUNET_ARM_Handle *h;
133 * Function to call with a status code for the requested operation.
135 GNUNET_ARM_Callback callback;
138 * Closure for "callback".
143 * Timeout for the operation.
145 struct GNUNET_TIME_Absolute timeout;
148 * Type of the request expressed as a message type (start or stop).
154 #include "do_start_process.c"
158 * A client specifically requested starting of ARM itself.
159 * This function is called with information about whether
160 * or not ARM is running; if it is, report success. If
161 * it is not, start the ARM process.
163 * @param cls the context for the request that we will report on (struct RequestContext*)
164 * @param tc why were we called (reason says if ARM is running)
167 arm_service_report (void *cls,
168 const struct GNUNET_SCHEDULER_TaskContext *tc)
170 struct RequestContext *pos = cls;
177 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181 "Looks like `%s' is already running.\n",
182 "gnunet-service-arm");
184 /* arm is running! */
185 if (pos->callback != NULL)
186 pos->callback (pos->cls, GNUNET_YES);
191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192 "Looks like `%s' is not running, will start it.\n",
193 "gnunet-service-arm");
195 /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
198 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
199 "arm", "PREFIX", &loprefix))
200 loprefix = GNUNET_strdup ("");
202 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
203 "arm", "OPTIONS", &lopostfix))
204 lopostfix = GNUNET_strdup ("");
206 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
211 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
212 _("Configuration failes to specify option `%s' in section `%s'!\n"),
215 if (pos->callback != NULL)
216 pos->callback (pos->cls, GNUNET_SYSERR);
218 GNUNET_free (loprefix);
219 GNUNET_free (lopostfix);
223 GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg,
224 "arm", "CONFIG", &config))
226 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
227 _("Configuration fails to specify option `%s' in section `%s'!\n"),
230 if (pos->callback != NULL)
231 pos->callback (pos->cls, GNUNET_SYSERR);
232 GNUNET_free (binary);
234 GNUNET_free (loprefix);
235 GNUNET_free (lopostfix);
238 pid = do_start_process (loprefix,
247 GNUNET_free (binary);
248 GNUNET_free (config);
249 GNUNET_free (loprefix);
250 GNUNET_free (lopostfix);
253 if (pos->callback != NULL)
254 pos->callback (pos->cls, GNUNET_SYSERR);
258 if (pos->callback != NULL)
259 pos->callback (pos->cls, GNUNET_YES);
265 * Process a response from ARM to a request for a change in service
268 * @param cls the request context
269 * @param msg the response
272 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
274 struct RequestContext *sc = cls;
279 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
280 _("Error receiving response to `%s' request from ARM for service `%s'\n"),
281 (sc->type == GNUNET_MESSAGE_TYPE_ARM_START)
284 (const char*) &sc[1]);
285 GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
286 sc->h->client = GNUNET_CLIENT_connect (sc->h->sched,
289 GNUNET_assert (NULL != sc->h->client);
290 GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
291 if (sc->callback != NULL)
292 sc->callback (sc->cls, GNUNET_SYSERR);
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298 "Received response from ARM for service `%s': %u\n",
299 (const char*) &sc[1],
302 switch (ntohs (msg->type))
304 case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
307 case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
310 case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
317 if (sc->callback != NULL)
318 sc->callback (sc->cls, ret);
324 * Start or stop a service.
326 * @param h handle to ARM
327 * @param service_name name of the service
328 * @param timeout how long to wait before failing for good
329 * @param cb callback to invoke when service is ready
330 * @param cb_cls closure for callback
331 * @param type type of the request
334 change_service (struct GNUNET_ARM_Handle *h,
335 const char *service_name,
336 struct GNUNET_TIME_Relative timeout,
337 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
339 struct RequestContext *sctx;
341 struct GNUNET_MessageHeader *msg;
343 slen = strlen (service_name) + 1;
344 if (slen + sizeof (struct GNUNET_MessageHeader) >
345 GNUNET_SERVER_MAX_MESSAGE_SIZE)
349 cb (cb_cls, GNUNET_NO);
353 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354 (type == GNUNET_MESSAGE_TYPE_ARM_START)
355 ? _("Requesting start of service `%s'.\n")
356 : _("Requesting termination of service `%s'.\n"),
359 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
363 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
365 memcpy (&sctx[1], service_name, slen);
366 msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
367 msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
368 msg->type = htons (sctx->type);
369 memcpy (&msg[1], service_name, slen);
371 GNUNET_CLIENT_transmit_and_get_response (sctx->h->client,
373 GNUNET_TIME_absolute_get_remaining (sctx->timeout),
378 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
379 (type == GNUNET_MESSAGE_TYPE_ARM_START)
380 ? _("Error while trying to transmit request to start `%s' to ARM\n")
381 : _("Error while trying to transmit request to stop `%s' to ARM\n"),
382 (const char*) &service_name);
384 cb (cb_cls, GNUNET_SYSERR);
396 * @param h handle to ARM
397 * @param service_name name of the service
398 * @param timeout how long to wait before failing for good
399 * @param cb callback to invoke when service is ready
400 * @param cb_cls closure for callback
403 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
404 const char *service_name,
405 struct GNUNET_TIME_Relative timeout,
406 GNUNET_ARM_Callback cb, void *cb_cls)
408 struct RequestContext *sctx;
409 struct GNUNET_CLIENT_Connection *client;
412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413 _("Asked to start service `%s' within %llu ms\n"), service_name,
414 (unsigned long long) timeout.value);
416 if (0 == strcasecmp ("arm", service_name))
418 slen = strlen ("arm") + 1;
419 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
423 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
424 memcpy (&sctx[1], service_name, slen);
425 GNUNET_CLIENT_service_test (h->sched,
427 h->cfg, timeout, &arm_service_report, sctx);
430 if (h->client == NULL)
432 client = GNUNET_CLIENT_connect (h->sched, "arm", h->cfg);
435 cb (cb_cls, GNUNET_SYSERR);
438 GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
441 change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
445 * Callback from the arm stop service call, indicates that the arm service
446 * is well and truly dead, won't die, or an error occurred.
448 * @param cls closure for the callback
449 * @param reason reason for callback, GNUNET_NO if arm is shutdown
450 * GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
452 void arm_shutdown_callback (void *cls,
455 struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
457 if (arm_shutdown_ctx->cb != NULL)
458 arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
460 GNUNET_free(arm_shutdown_ctx);
467 * @param h handle to ARM
468 * @param service_name name of the service
469 * @param timeout how long to wait before failing for good
470 * @param cb callback to invoke when service is ready
471 * @param cb_cls closure for callback
474 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
475 const char *service_name,
476 struct GNUNET_TIME_Relative timeout,
477 GNUNET_ARM_Callback cb, void *cb_cls)
479 struct ARM_ShutdownContext *arm_shutdown_ctx;
480 struct GNUNET_CLIENT_Connection *client;
482 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
483 _("Stopping service `%s' within %llu ms\n"), service_name,
484 (unsigned long long) timeout.value);
485 if (h->client == NULL)
487 client = GNUNET_CLIENT_connect (h->sched, "arm", h->cfg);
490 cb (cb_cls, GNUNET_SYSERR);
493 GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
496 if (0 == strcasecmp ("arm", service_name))
498 arm_shutdown_ctx = GNUNET_malloc(sizeof(struct ARM_ShutdownContext));
499 arm_shutdown_ctx->cb = cb;
500 arm_shutdown_ctx->cb_cls = cb_cls;
501 GNUNET_CLIENT_service_shutdown (h->sched, h->client, timeout, &arm_shutdown_callback, arm_shutdown_ctx);
505 change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
510 * Function to call for each service.
512 * @param h handle to ARM
513 * @param service_name name of the service
514 * @param timeout how long to wait before failing for good
515 * @param cb callback to invoke when service is ready
516 * @param cb_cls closure for callback
518 typedef void (*ServiceOperation) (struct GNUNET_ARM_Handle *h,
519 const char *service_name,
520 struct GNUNET_TIME_Relative timeout,
521 GNUNET_ARM_Callback cb, void *cb_cls);
525 * Context for starting or stopping multiple services.
530 * NULL-terminated array of services to start or stop.
537 struct GNUNET_ARM_Handle *h;
540 * Identifies the operation (start or stop).
545 * Current position in "services".
552 * Run the operation for the next service in the multi-service
555 * @param cls the "struct MultiContext" that is being processed
556 * @param success status of the previous operation (ignored)
559 next_operation (void *cls,
562 struct MultiContext *mc = cls;
565 if (NULL == (pos = mc->services[mc->pos]))
567 GNUNET_free (mc->services);
568 GNUNET_ARM_disconnect (mc->h);
573 mc->op (mc->h, pos, MULTI_TIMEOUT, &next_operation, mc);
579 * Run a multi-service request.
581 * @param cfg configuration to use (needed to contact ARM;
582 * the ARM service may internally use a different
583 * configuration to determine how to start the service).
584 * @param sched scheduler to use
585 * @param op the operation to perform for each service
586 * @param va NULL-terminated list of services
589 run_multi_request (const struct GNUNET_CONFIGURATION_Handle *cfg,
590 struct GNUNET_SCHEDULER_Handle *sched,
596 struct MultiContext *mc;
597 struct GNUNET_ARM_Handle *h;
600 h = GNUNET_ARM_connect (cfg, sched, NULL);
603 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
604 _("Error while trying to transmit to ARM service\n"));
609 while (NULL != (va_arg (cp, const char*))) total++;
611 mc = GNUNET_malloc (sizeof(struct MultiContext));
612 mc->services = GNUNET_malloc (total * sizeof (char*));
617 while (NULL != (c = va_arg (cp, const char*)))
618 mc->services[total++] = GNUNET_strdup (c);
620 next_operation (mc, GNUNET_YES);
625 * Start multiple services in the specified order. Convenience
626 * function. Works asynchronously, failures are not reported.
628 * @param cfg configuration to use (needed to contact ARM;
629 * the ARM service may internally use a different
630 * configuration to determine how to start the service).
631 * @param sched scheduler to use
632 * @param ... NULL-terminated list of service names (const char*)
635 GNUNET_ARM_start_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
636 struct GNUNET_SCHEDULER_Handle *sched,
641 va_start (ap, sched);
642 run_multi_request (cfg, sched, &GNUNET_ARM_start_service, ap);
648 * Stop multiple services in the specified order. Convenience
649 * function. Works asynchronously, failures are not reported.
650 * Should normally only be called from gnunet-arm or testcases,
651 * stopping a service is generally otherwise a bad idea.
653 * @param cfg configuration to use (needed to contact ARM;
654 * the ARM service may internally use a different
655 * configuration to determine how to start the service).
656 * @param sched scheduler to use
657 * @param ... NULL-terminated list of service names (const char*)
660 GNUNET_ARM_stop_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
661 struct GNUNET_SCHEDULER_Handle *sched,
666 va_start (ap, sched);
667 run_multi_request (cfg, sched, &GNUNET_ARM_stop_service, ap);
672 /* end of arm_api.c */