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, 5)
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;
84 struct GNUNET_CLIENT_Connection *client;
86 client = GNUNET_CLIENT_connect (sched, "arm", cfg);
89 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
90 ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
98 * Disconnect from the ARM service.
100 * @param h the handle that was being used
103 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
105 if (h->client != NULL)
106 GNUNET_CLIENT_disconnect (h->client);
107 GNUNET_CONFIGURATION_destroy (h->cfg);
113 * Internal state for a request with ARM.
115 struct RequestContext
119 * Pointer to our handle with ARM.
121 struct GNUNET_ARM_Handle *h;
124 * Function to call with a status code for the requested operation.
126 GNUNET_ARM_Callback callback;
129 * Closure for "callback".
134 * Timeout for the operation.
136 struct GNUNET_TIME_Absolute timeout;
139 * Type of the request expressed as a message type (start or stop).
147 * A client specifically requested starting of ARM itself.
148 * This function is called with information about whether
149 * or not ARM is running; if it is, report success. If
150 * it is not, start the ARM process.
152 * @param cls the context for the request that we will report on (struct RequestContext*)
153 * @param tc why were we called (reason says if ARM is running)
156 arm_service_report (void *cls,
157 const struct GNUNET_SCHEDULER_TaskContext *tc)
159 struct RequestContext *pos = cls;
164 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
166 /* arm is running! */
167 if (pos->callback != NULL)
168 pos->callback (pos->cls, GNUNET_YES);
172 /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
175 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
180 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
181 _("Configuration failes to specify option `%s' in section `%s'!\n"),
184 if (pos->callback != NULL)
185 pos->callback (pos->cls, GNUNET_SYSERR);
190 GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg,
191 "arm", "CONFIG", &config))
193 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
194 _("Configuration fails to specify option `%s' in section `%s'!\n"),
197 if (pos->callback != NULL)
198 pos->callback (pos->cls, GNUNET_SYSERR);
199 GNUNET_free (binary);
203 pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config,
208 GNUNET_free (binary);
209 GNUNET_free (config);
212 if (pos->callback != NULL)
213 pos->callback (pos->cls, GNUNET_SYSERR);
217 if (pos->callback != NULL)
218 pos->callback (pos->cls, GNUNET_YES);
224 * Process a response from ARM to a request for a change in service
227 * @param cls the request context
228 * @param msg the response
231 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
233 struct RequestContext *sc = cls;
238 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
239 _("Error receiving response from ARM service\n"));
240 GNUNET_CLIENT_disconnect (sc->h->client);
241 sc->h->client = GNUNET_CLIENT_connect (sc->h->sched,
244 GNUNET_assert (NULL != sc->h->client);
245 if (sc->callback != NULL)
246 sc->callback (sc->cls, GNUNET_SYSERR);
251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
252 "Received response from ARM service: %u\n",
255 switch (ntohs (msg->type))
257 case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
260 case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
263 case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
270 if (sc->callback != NULL)
271 sc->callback (sc->cls, ret);
277 * Start or stop a service.
279 * @param h handle to ARM
280 * @param service_name name of the service
281 * @param timeout how long to wait before failing for good
282 * @param cb callback to invoke when service is ready
283 * @param cb_cls closure for callback
284 * @param type type of the request
287 change_service (struct GNUNET_ARM_Handle *h,
288 const char *service_name,
289 struct GNUNET_TIME_Relative timeout,
290 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
292 struct RequestContext *sctx;
294 struct GNUNET_MessageHeader *msg;
296 slen = strlen (service_name) + 1;
297 if (slen + sizeof (struct GNUNET_MessageHeader) >
298 GNUNET_SERVER_MAX_MESSAGE_SIZE)
302 cb (cb_cls, GNUNET_NO);
306 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
307 _("ARM requests starting of service `%s'.\n"), service_name);
309 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
313 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
315 msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
316 msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
317 msg->type = htons (sctx->type);
318 memcpy (&msg[1], service_name, slen);
320 GNUNET_CLIENT_transmit_and_get_response (sctx->h->client,
322 GNUNET_TIME_absolute_get_remaining (sctx->timeout),
327 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
328 _("Error while trying to transmit to ARM service\n"));
330 cb (cb_cls, GNUNET_SYSERR);
342 * @param h handle to ARM
343 * @param service_name name of the service
344 * @param timeout how long to wait before failing for good
345 * @param cb callback to invoke when service is ready
346 * @param cb_cls closure for callback
349 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
350 const char *service_name,
351 struct GNUNET_TIME_Relative timeout,
352 GNUNET_ARM_Callback cb, void *cb_cls)
354 struct RequestContext *sctx;
356 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
357 _("Starting service `%s'\n"), service_name);
358 if (0 == strcmp ("arm", service_name))
360 sctx = GNUNET_malloc (sizeof (struct RequestContext));
364 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
365 GNUNET_CLIENT_service_test (h->sched,
367 h->cfg, timeout, &arm_service_report, sctx);
370 change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
377 * @param h handle to ARM
378 * @param service_name name of the service
379 * @param timeout how long to wait before failing for good
380 * @param cb callback to invoke when service is ready
381 * @param cb_cls closure for callback
384 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
385 const char *service_name,
386 struct GNUNET_TIME_Relative timeout,
387 GNUNET_ARM_Callback cb, void *cb_cls)
389 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
390 _("Stopping service `%s'\n"), service_name);
391 if (0 == strcmp ("arm", service_name))
393 GNUNET_CLIENT_service_shutdown (h->client);
396 cb (cb_cls, GNUNET_NO);
399 change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
404 * Function to call for each service.
406 * @param h handle to ARM
407 * @param service_name name of the service
408 * @param timeout how long to wait before failing for good
409 * @param cb callback to invoke when service is ready
410 * @param cb_cls closure for callback
412 typedef void (*ServiceOperation) (struct GNUNET_ARM_Handle *h,
413 const char *service_name,
414 struct GNUNET_TIME_Relative timeout,
415 GNUNET_ARM_Callback cb, void *cb_cls);
419 * Context for starting or stopping multiple services.
424 * NULL-terminated array of services to start or stop.
431 struct GNUNET_ARM_Handle *h;
434 * Identifies the operation (start or stop).
439 * Current position in "services".
446 * Run the operation for the next service in the multi-service
449 * @param cls the "struct MultiContext" that is being processed
450 * @param success status of the previous operation (ignored)
453 next_operation (void *cls,
456 struct MultiContext *mc = cls;
459 if (NULL == (pos = mc->services[mc->pos]))
461 GNUNET_free (mc->services);
462 GNUNET_ARM_disconnect (mc->h);
467 mc->op (mc->h, pos, MULTI_TIMEOUT, &next_operation, mc);
473 * Run a multi-service request.
475 * @param cfg configuration to use (needed to contact ARM;
476 * the ARM service may internally use a different
477 * configuration to determine how to start the service).
478 * @param sched scheduler to use
479 * @param op the operation to perform for each service
480 * @param va NULL-terminated list of services
483 run_multi_request (const struct GNUNET_CONFIGURATION_Handle *cfg,
484 struct GNUNET_SCHEDULER_Handle *sched,
490 struct MultiContext *mc;
491 struct GNUNET_ARM_Handle *h;
494 h = GNUNET_ARM_connect (cfg, sched, NULL);
497 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
498 _("Error while trying to transmit to ARM service\n"));
503 while (NULL != (va_arg (cp, const char*))) total++;
505 mc = GNUNET_malloc (sizeof(struct MultiContext));
506 mc->services = GNUNET_malloc (total * sizeof (char*));
511 while (NULL != (c = va_arg (cp, const char*)))
512 mc->services[total++] = GNUNET_strdup (c);
514 next_operation (mc, GNUNET_YES);
519 * Start multiple services in the specified order. Convenience
520 * function. Works asynchronously, failures are not reported.
522 * @param cfg configuration to use (needed to contact ARM;
523 * the ARM service may internally use a different
524 * configuration to determine how to start the service).
525 * @param sched scheduler to use
526 * @param ... NULL-terminated list of service names (const char*)
529 GNUNET_ARM_start_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
530 struct GNUNET_SCHEDULER_Handle *sched,
535 va_start (ap, sched);
536 run_multi_request (cfg, sched, &GNUNET_ARM_start_service, ap);
542 * Stop multiple services in the specified order. Convenience
543 * function. Works asynchronously, failures are not reported.
545 * @param cfg configuration to use (needed to contact ARM;
546 * the ARM service may internally use a different
547 * configuration to determine how to start the service).
548 * @param sched scheduler to use
549 * @param ... NULL-terminated list of service names (const char*)
552 GNUNET_ARM_stop_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
553 struct GNUNET_SCHEDULER_Handle *sched,
558 va_start (ap, sched);
559 run_multi_request (cfg, sched, &GNUNET_ARM_stop_service, ap);
564 /* end of arm_api.c */