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"
36 * How often do we re-try tranmsitting requests to ARM before
37 * giving up? Note that if we succeeded transmitting a request
38 * but failed to read a response, we do NOT re-try (since that
39 * might result in ARM getting a request twice).
41 #define MAX_ATTEMPTS 4
44 * Minimum delay between attempts to talk to ARM.
46 #define MIN_RETRY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
50 * How long are we willing to wait for a service operation during the multi-operation
53 #define MULTI_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
57 * Handle for interacting with ARM.
59 struct GNUNET_ARM_Handle
63 * Our connection to the ARM service.
65 struct GNUNET_CLIENT_Connection *client;
68 * The configuration that we are using.
70 const struct GNUNET_CONFIGURATION_Handle *cfg;
75 struct GNUNET_SCHEDULER_Handle *sched;
81 * Setup a context for communicating with ARM. Note that this
82 * can be done even if the ARM service is not yet running.
84 * @param cfg configuration to use (needed to contact ARM;
85 * the ARM service may internally use a different
86 * configuration to determine how to start the service).
87 * @param sched scheduler to use
88 * @param service service that *this* process is implementing/providing, can be NULL
89 * @return context to use for further ARM operations, NULL on error
91 struct GNUNET_ARM_Handle *
92 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
93 struct GNUNET_SCHEDULER_Handle *sched,
96 struct GNUNET_ARM_Handle *ret;
97 struct GNUNET_CLIENT_Connection *client;
99 client = GNUNET_CLIENT_connect (sched, "arm", cfg);
102 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
105 ret->client = client;
111 * Disconnect from the ARM service.
113 * @param h the handle that was being used
116 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
118 if (h->client != NULL)
119 GNUNET_CLIENT_disconnect (h->client);
125 * Internal state for a request with ARM.
127 struct RequestContext
131 * Pointer to our handle with ARM.
133 struct GNUNET_ARM_Handle *h;
136 * Function to call with a status code for the requested operation.
138 GNUNET_ARM_Callback callback;
141 * Closure for "callback".
146 * The service that is being manipulated. Do not free.
148 const char *service_name;
151 * Timeout for the operation.
153 struct GNUNET_TIME_Absolute timeout;
156 * Length of service_name plus one.
161 * Number of attempts left for transmitting the request to ARM.
162 * We may fail the first time (say because ARM is not yet up),
163 * in which case we wait a bit and re-try (timeout permitting).
165 unsigned int attempts_left;
168 * Type of the request expressed as a message type (start or stop).
176 * A client specifically requested starting of ARM itself.
177 * This function is called with information about whether
178 * or not ARM is running; if it is, report success. If
179 * it is not, start the ARM process.
181 * @param cls the context for the request that we will report on (struct RequestContext*)
182 * @param tc why were we called (reason says if ARM is running)
185 arm_service_report (void *cls,
186 const struct GNUNET_SCHEDULER_TaskContext *tc)
188 struct RequestContext *pos = cls;
193 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
195 /* arm is running! */
196 if (pos->callback != NULL)
197 pos->callback (pos->cls, GNUNET_YES);
201 /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
204 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
209 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
210 _("Configuration failes to specify option `%s' in section `%s'!\n"),
213 if (pos->callback != NULL)
214 pos->callback (pos->cls, GNUNET_SYSERR);
219 GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg,
220 "arm", "CONFIG", &config))
222 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
223 _("Configuration fails to specify option `%s' in section `%s'!\n"),
226 if (pos->callback != NULL)
227 pos->callback (pos->cls, GNUNET_SYSERR);
228 GNUNET_free (binary);
232 pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config,
237 GNUNET_free (binary);
238 GNUNET_free (config);
241 if (pos->callback != NULL)
242 pos->callback (pos->cls, GNUNET_SYSERR);
246 if (pos->callback != NULL)
247 pos->callback (pos->cls, GNUNET_YES);
253 * Process a response from ARM to a request for a change in service
256 * @param cls the request context
257 * @param msg the response
260 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
262 struct RequestContext *sc = cls;
267 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
268 _("Error receiving response from ARM service\n"));
269 GNUNET_CLIENT_disconnect (sc->h->client);
270 sc->h->client = GNUNET_CLIENT_connect (sc->h->sched,
273 GNUNET_assert (NULL != sc->h->client);
274 if (sc->callback != NULL)
275 sc->callback (sc->cls, GNUNET_SYSERR);
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Received response from ARM service: %u\n",
284 switch (ntohs (msg->type))
286 case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
289 case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
292 case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
299 if (sc->callback != NULL)
300 sc->callback (sc->cls, ret);
306 * We've failed to transmit the request to the ARM service.
307 * Report our failure and clean up the state.
309 * @param sctx the state of the (now failed) request
312 report_transmit_failure (struct RequestContext *sctx)
314 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
315 _("Error while trying to transmit to ARM service\n"));
316 if (sctx->callback != NULL)
317 sctx->callback (sctx->cls, GNUNET_SYSERR);
323 * Transmit a request for a service status change to the
326 * @param cls the "struct RequestContext" identifying the request
327 * @param size how many bytes are available in buf
328 * @param buf where to write the request, NULL on error
329 * @return number of bytes written to buf
332 send_service_msg (void *cls, size_t size, void *buf);
336 * We've failed to transmit the request to the ARM service but
337 * are now going to try again.
339 * @param cls state of the request
340 * @param tc task context (unused)
343 retry_request (void *cls,
344 const struct GNUNET_SCHEDULER_TaskContext *tc)
346 struct RequestContext *sctx = cls;
349 GNUNET_CLIENT_notify_transmit_ready (sctx->h->client,
352 GNUNET_MessageHeader),
353 GNUNET_TIME_absolute_get_remaining (sctx->timeout),
357 report_transmit_failure (sctx);
364 * Transmit a request for a service status change to the
367 * @param cls the "struct RequestContext" identifying the request
368 * @param size how many bytes are available in buf
369 * @param buf where to write the request, NULL on error
370 * @return number of bytes written to buf
373 send_service_msg (void *cls, size_t size, void *buf)
375 struct RequestContext *sctx = cls;
376 struct GNUNET_MessageHeader *msg;
377 struct GNUNET_TIME_Relative rem;
381 GNUNET_CLIENT_disconnect (sctx->h->client);
382 sctx->h->client = GNUNET_CLIENT_connect (sctx->h->sched,
385 GNUNET_assert (sctx->h->client != NULL);
386 rem = GNUNET_TIME_absolute_get_remaining (sctx->timeout);
387 if ( (sctx->attempts_left-- > 0) &&
390 GNUNET_SCHEDULER_add_delayed (sctx->h->sched,
392 GNUNET_SCHEDULER_PRIORITY_KEEP,
393 GNUNET_SCHEDULER_NO_TASK,
394 GNUNET_TIME_relative_min (MIN_RETRY_DELAY,
400 report_transmit_failure (sctx);
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 _("Transmitting service request to ARM.\n"));
407 GNUNET_assert (size >= sctx->slen);
409 msg->size = htons (sizeof (struct GNUNET_MessageHeader) + sctx->slen);
410 msg->type = htons (sctx->type);
411 memcpy (&msg[1], sctx->service_name, sctx->slen);
412 GNUNET_CLIENT_receive (sctx->h->client,
415 GNUNET_TIME_absolute_get_remaining (sctx->timeout));
416 return sctx->slen + sizeof (struct GNUNET_MessageHeader);
421 * Start or stop a service.
423 * @param h handle to ARM
424 * @param service_name name of the service
425 * @param timeout how long to wait before failing for good
426 * @param cb callback to invoke when service is ready
427 * @param cb_cls closure for callback
428 * @param type type of the request
431 change_service (struct GNUNET_ARM_Handle *h,
432 const char *service_name,
433 struct GNUNET_TIME_Relative timeout,
434 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
436 struct RequestContext *sctx;
439 slen = strlen (service_name) + 1;
440 if (slen + sizeof (struct GNUNET_MessageHeader) >
441 GNUNET_SERVER_MAX_MESSAGE_SIZE)
445 cb (cb_cls, GNUNET_NO);
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 _("ARM requests starting of service `%s'.\n"), service_name);
452 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
456 sctx->service_name = (const char*) &sctx[1];
460 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
462 sctx->attempts_left = MAX_ATTEMPTS;
464 retry_request (sctx, NULL);
471 * @param h handle to ARM
472 * @param service_name name of the service
473 * @param timeout how long to wait before failing for good
474 * @param cb callback to invoke when service is ready
475 * @param cb_cls closure for callback
478 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
479 const char *service_name,
480 struct GNUNET_TIME_Relative timeout,
481 GNUNET_ARM_Callback cb, void *cb_cls)
483 struct RequestContext *sctx;
485 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
486 _("Starting service `%s'\n"), service_name);
487 if (0 == strcmp ("arm", service_name))
489 sctx = GNUNET_malloc (sizeof (struct RequestContext));
493 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
494 GNUNET_CLIENT_service_test (h->sched,
496 h->cfg, timeout, &arm_service_report, sctx);
499 change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
506 * @param h handle to ARM
507 * @param service_name name of the service
508 * @param timeout how long to wait before failing for good
509 * @param cb callback to invoke when service is ready
510 * @param cb_cls closure for callback
513 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
514 const char *service_name,
515 struct GNUNET_TIME_Relative timeout,
516 GNUNET_ARM_Callback cb, void *cb_cls)
518 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
519 _("Stopping service `%s'\n"), service_name);
520 if (0 == strcmp ("arm", service_name))
522 GNUNET_CLIENT_service_shutdown (h->client);
524 cb (cb_cls, GNUNET_NO);
527 change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
532 * Function to call for each service.
534 * @param h handle to ARM
535 * @param service_name name of the service
536 * @param timeout how long to wait before failing for good
537 * @param cb callback to invoke when service is ready
538 * @param cb_cls closure for callback
540 typedef void (*ServiceOperation) (struct GNUNET_ARM_Handle *h,
541 const char *service_name,
542 struct GNUNET_TIME_Relative timeout,
543 GNUNET_ARM_Callback cb, void *cb_cls);
547 * Context for starting or stopping multiple services.
552 * NULL-terminated array of services to start or stop.
559 struct GNUNET_ARM_Handle *h;
562 * Identifies the operation (start or stop).
567 * Current position in "services".
574 * Run the operation for the next service in the multi-service
577 * @param cls the "struct MultiContext" that is being processed
578 * @param success status of the previous operation (ignored)
581 next_operation (void *cls,
584 struct MultiContext *mc = cls;
587 if (NULL == (pos = mc->services[mc->pos]))
589 GNUNET_free (mc->services);
590 GNUNET_ARM_disconnect (mc->h);
595 mc->op (mc->h, pos, MULTI_TIMEOUT, &next_operation, mc);
601 * Run a multi-service request.
603 * @param cfg configuration to use (needed to contact ARM;
604 * the ARM service may internally use a different
605 * configuration to determine how to start the service).
606 * @param sched scheduler to use
607 * @param op the operation to perform for each service
608 * @param va NULL-terminated list of services
611 run_multi_request (const struct GNUNET_CONFIGURATION_Handle *cfg,
612 struct GNUNET_SCHEDULER_Handle *sched,
618 struct MultiContext *mc;
619 struct GNUNET_ARM_Handle *h;
622 h = GNUNET_ARM_connect (cfg, sched, NULL);
625 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
626 _("Error while trying to transmit to ARM service\n"));
631 while (NULL != (va_arg (cp, const char*))) total++;
633 mc = GNUNET_malloc (sizeof(struct MultiContext));
634 mc->services = GNUNET_malloc (total * sizeof (char*));
639 while (NULL != (c = va_arg (cp, const char*)))
640 mc->services[total++] = GNUNET_strdup (c);
642 next_operation (mc, GNUNET_YES);
647 * Start multiple services in the specified order. Convenience
648 * function. Works asynchronously, failures are not reported.
650 * @param cfg configuration to use (needed to contact ARM;
651 * the ARM service may internally use a different
652 * configuration to determine how to start the service).
653 * @param sched scheduler to use
654 * @param ... NULL-terminated list of service names (const char*)
657 GNUNET_ARM_start_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
658 struct GNUNET_SCHEDULER_Handle *sched,
663 va_start (ap, sched);
664 run_multi_request (cfg, sched, &GNUNET_ARM_start_service, ap);
670 * Stop multiple services in the specified order. Convenience
671 * function. Works asynchronously, failures are not reported.
673 * @param cfg configuration to use (needed to contact ARM;
674 * the ARM service may internally use a different
675 * configuration to determine how to start the service).
676 * @param sched scheduler to use
677 * @param ... NULL-terminated list of service names (const char*)
680 GNUNET_ARM_stop_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
681 struct GNUNET_SCHEDULER_Handle *sched,
686 va_start (ap, sched);
687 run_multi_request (cfg, sched, &GNUNET_ARM_stop_service, ap);
692 /* end of arm_api.c */