X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Farm%2Farm_api.c;h=15563e93398b840d64cf6ed0e3b53b50fbc6e66b;hb=d0b4927e6ab7e8b9874dd7807055e77fb4c5163f;hp=49e25b38ddb57804b28b5afd59e6945293f413d8;hpb=e37291924320dd72a9011056544b1ebbe9f5e05e;p=oweals%2Fgnunet.git diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index 49e25b38d..15563e933 100644 --- a/src/arm/arm_api.c +++ b/src/arm/arm_api.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2009 Christian Grothoff (and other contributing authors) + (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors) 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 @@ -25,58 +25,191 @@ */ #include "platform.h" #include "gnunet_arm_service.h" -#include "gnunet_client_lib.h" -#include "gnunet_getopt_lib.h" -#include "gnunet_os_lib.h" +#include "gnunet_util_lib.h" #include "gnunet_protocols.h" -#include "gnunet_server_lib.h" #include "arm.h" -/** - * How often do we re-try tranmsitting requests to ARM before - * giving up? Note that if we succeeded transmitting a request - * but failed to read a response, we do NOT re-try (since that - * might result in ARM getting a request twice). - */ -#define MAX_ATTEMPTS 4 +#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__) /** - * Minimum delay between attempts to talk to ARM. + * Handle for interacting with ARM. */ -#define MIN_RETRY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100) +struct GNUNET_ARM_Handle +{ + /** + * Our connection to the ARM service. + */ + struct GNUNET_CLIENT_Connection *client; -/** - * How long are we willing to wait for a service operation during the multi-operation - * request processing? - */ -#define MULTI_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + /** + * The configuration that we are using. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; +}; /** - * Handle for interacting with ARM. - */ -struct GNUNET_ARM_Handle + * Context for handling the shutdown of a service. + */ +struct ShutdownContext { + /** + * Connection to the service that is being shutdown. + */ + struct GNUNET_CLIENT_Connection *sock; /** - * Our connection to the ARM service. + * Time allowed for shutdown to happen. */ - struct GNUNET_CLIENT_Connection *client; + struct GNUNET_TIME_Absolute timeout; /** - * The configuration that we are using. + * Task set up to cancel the shutdown request on timeout. */ - const struct GNUNET_CONFIGURATION_Handle *cfg; + GNUNET_SCHEDULER_TaskIdentifier cancel_task; /** - * Scheduler to use. + * Task to call once shutdown complete */ - struct GNUNET_SCHEDULER_Handle *sched; + GNUNET_CLIENT_ShutdownTask cont; + + /** + * Closure for shutdown continuation + */ + void *cont_cls; + + /** + * Handle for transmission request. + */ + struct GNUNET_CLIENT_TransmitHandle *th; }; +/** + * Handler receiving response to service shutdown requests. + * First call with NULL: service misbehaving, or something. + * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK: + * - service will shutdown + * Second call with NULL: + * - service has now really shut down. + * + * @param cls closure + * @param msg NULL, indicating socket closure. + */ +static void +service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct ShutdownContext *shutdown_ctx = cls; + + if (NULL != msg) + { + /* We just expected a disconnect! Report the error and be done with it... */ + GNUNET_break (0); + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); + GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock); + GNUNET_free (shutdown_ctx); + return; + } + if (NULL != shutdown_ctx->cont) + /* shutdown is now complete, as we waited for the network disconnect... */ + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN); + GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock); + GNUNET_free (shutdown_ctx); +} + + +/** + * Shutting down took too long, cancel receive and return error. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +service_shutdown_cancel (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ShutdownContext *shutdown_ctx = cls; + + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock); + GNUNET_free (shutdown_ctx); +} + + +/** + * If possible, write a shutdown message to the target + * buffer and destroy the client connection. + * + * @param cls the "struct GNUNET_CLIENT_Connection" to destroy + * @param size number of bytes available in buf + * @param buf NULL on error, otherwise target buffer + * @return number of bytes written to buf + */ +static size_t +write_shutdown (void *cls, size_t size, void *buf) +{ + struct ShutdownContext *shutdown_ctx = cls; + struct GNUNET_MessageHeader *msg; + + shutdown_ctx->th = NULL; + if (size < sizeof (struct GNUNET_MessageHeader)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Failed to transmit shutdown request to client.\n")); + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock); + GNUNET_free (shutdown_ctx); + return 0; /* client disconnected */ + } + GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, + shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); + shutdown_ctx->cancel_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (shutdown_ctx->timeout), + &service_shutdown_cancel, shutdown_ctx); + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + return sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Request that the service should shutdown. + * Afterwards, the connection will automatically be + * disconnected. Hence the "sock" should not + * be used by the caller after this call + * (calling this function frees "sock" after a while). + * + * @param sock the socket connected to the service + * @param timeout how long to wait before giving up on transmission + * @param cont continuation to call once the service is really down + * @param cont_cls closure for continuation + * + */ +static void +arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, + struct GNUNET_TIME_Relative timeout, + GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) +{ + struct ShutdownContext *shutdown_ctx; + + shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext)); + shutdown_ctx->cont = cont; + shutdown_ctx->cont_cls = cont_cls; + shutdown_ctx->sock = sock; + shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + shutdown_ctx->th = GNUNET_CLIENT_notify_transmit_ready (sock, + sizeof (struct GNUNET_MessageHeader), + timeout, GNUNET_NO, &write_shutdown, + shutdown_ctx); +} + + /** * Setup a context for communicating with ARM. Note that this * can be done even if the ARM service is not yet running. @@ -84,25 +217,17 @@ struct GNUNET_ARM_Handle * @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 service service that *this* process is implementing/providing, can be NULL * @return context to use for further ARM operations, NULL on error */ struct GNUNET_ARM_Handle * GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_SCHEDULER_Handle *sched, const char *service) { struct GNUNET_ARM_Handle *ret; - struct GNUNET_CLIENT_Connection *client; - client = GNUNET_CLIENT_connect (sched, "arm", cfg); - if (client == NULL) - return NULL; ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle)); - ret->cfg = cfg; - ret->sched = sched; - ret->client = client; + ret->cfg = GNUNET_CONFIGURATION_dup (cfg); return ret; } @@ -117,10 +242,25 @@ GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) { if (h->client != NULL) GNUNET_CLIENT_disconnect (h->client); + GNUNET_CONFIGURATION_destroy (h->cfg); GNUNET_free (h); } +struct ARM_ShutdownContext +{ + /** + * Callback to call once shutdown complete. + */ + GNUNET_ARM_Callback cb; + + /** + * Closure for callback. + */ + void *cb_cls; +}; + + /** * Internal state for a request with ARM. */ @@ -142,35 +282,25 @@ struct RequestContext */ void *cls; - /** - * The service that is being manipulated. Do not free. - */ - const char *service_name; - /** * Timeout for the operation. */ struct GNUNET_TIME_Absolute timeout; /** - * Length of service_name plus one. - */ - size_t slen; - - /** - * Number of attempts left for transmitting the request to ARM. - * We may fail the first time (say because ARM is not yet up), - * in which case we wait a bit and re-try (timeout permitting). + * Type of the request expressed as a message type (start or stop). */ - unsigned int attempts_left; + uint16_t type; /** - * Type of the request expressed as a message type (start or stop). + * Flags for passing std descriptors to ARM (when starting ARM). */ - uint16_t type; + enum GNUNET_OS_InheritStdioFlags std_inheritance; }; +#include "do_start_process.c" + /** * A client specifically requested starting of ARM itself. @@ -182,69 +312,99 @@ struct RequestContext * @param tc why were we called (reason says if ARM is running) */ static void -arm_service_report (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct RequestContext *pos = cls; - pid_t pid; + struct GNUNET_OS_Process *proc; char *binary; char *config; + char *loprefix; + char *lopostfix; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) - { - /* arm is running! */ - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_YES); - GNUNET_free (pos); - return; - } - /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */ - /* start service */ + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n", + "gnunet-service-arm"); + /* arm is running! */ + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_ALREADY_RUNNING); + GNUNET_free (pos); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Looks like `%s' is not running, will start it.\n", + "gnunet-service-arm"); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX", + &loprefix)) + loprefix = GNUNET_strdup (""); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS", + &lopostfix)) + lopostfix = GNUNET_strdup (""); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, - "arm", - "BINARY", + GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY", &binary)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Configuration failes to specify option `%s' in section `%s'!\n"), - "BINARY", - "arm"); - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_SYSERR); - GNUNET_free (pos); - return; - } + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "arm", "BINARY"); + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN); + GNUNET_free (pos); + GNUNET_free (loprefix); + GNUNET_free (lopostfix); + return; + } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, - "arm", "CONFIG", &config)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Configuration fails to specify option `%s' in section `%s'!\n"), - "CONFIG", - "arm"); - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_SYSERR); - GNUNET_free (binary); - GNUNET_free (pos); - return; - } - pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config, -#if DEBUG_ARM - "-L", "DEBUG", -#endif - NULL); + GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG", + &config)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "arm", "CONFIG"); + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN); + GNUNET_free (binary); + GNUNET_free (pos); + GNUNET_free (loprefix); + GNUNET_free (lopostfix); + return; + } + if ((GNUNET_YES == + GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) + && (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", + "WEAKRANDOM")) + && (GNUNET_NO == + GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", + "HOSTFILE"))) + { + /* Means we are ONLY running locally */ + /* we're clearly running a test, don't daemonize */ + proc = do_start_process (GNUNET_NO, pos->std_inheritance, + NULL, loprefix, binary, "-c", config, + /* no daemonization! */ + lopostfix, NULL); + } + else + { + proc = do_start_process (GNUNET_NO, pos->std_inheritance, + NULL, loprefix, binary, "-c", config, + "-d", lopostfix, NULL); + } GNUNET_free (binary); GNUNET_free (config); - if (pid == -1) + GNUNET_free (loprefix); + GNUNET_free (lopostfix); + if (proc == NULL) { if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_SYSERR); + pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE); GNUNET_free (pos); return; } if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_YES); + pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING); + GNUNET_free (proc); GNUNET_free (pos); } @@ -253,170 +413,39 @@ arm_service_report (void *cls, * Process a response from ARM to a request for a change in service * status. * - * @param cls the request context + * @param cls the request context * @param msg the response */ static void handle_response (void *cls, const struct GNUNET_MessageHeader *msg) { struct RequestContext *sc = cls; - int ret; - - if (msg == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Error receiving response from ARM service\n")); - GNUNET_CLIENT_disconnect (sc->h->client); - sc->h->client = GNUNET_CLIENT_connect (sc->h->sched, - "arm", - sc->h->cfg); - GNUNET_assert (NULL != sc->h->client); - if (sc->callback != NULL) - sc->callback (sc->cls, GNUNET_SYSERR); - GNUNET_free (sc); - return; - } -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received response from ARM service: %u\n", - ntohs(msg->type)); -#endif - switch (ntohs (msg->type)) - { - case GNUNET_MESSAGE_TYPE_ARM_IS_UP: - ret = GNUNET_YES; - break; - case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN: - ret = GNUNET_NO; - break; - case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN: - ret = GNUNET_SYSERR; - break; - default: - GNUNET_break (0); - ret = GNUNET_SYSERR; - } + const struct GNUNET_ARM_ResultMessage *res; + enum GNUNET_ARM_ProcessStatus status; + + if ((msg == NULL) || + (ntohs (msg->size) != sizeof (struct GNUNET_ARM_ResultMessage))) + { + GNUNET_break (0); + GNUNET_CLIENT_disconnect (sc->h->client); + sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg); + GNUNET_assert (NULL != sc->h->client); + if (sc->callback != NULL) + sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); + GNUNET_free (sc); + return; + } + res = (const struct GNUNET_ARM_ResultMessage *) msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received response from ARM for service `%s': %u\n", + (const char *) &sc[1], ntohs (msg->type)); + status = (enum GNUNET_ARM_ProcessStatus) ntohl (res->status); if (sc->callback != NULL) - sc->callback (sc->cls, ret); + sc->callback (sc->cls, status); GNUNET_free (sc); } -/** - * We've failed to transmit the request to the ARM service. - * Report our failure and clean up the state. - * - * @param sctx the state of the (now failed) request - */ -static void -report_transmit_failure (struct RequestContext *sctx) -{ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Error while trying to transmit to ARM service\n")); - if (sctx->callback != NULL) - sctx->callback (sctx->cls, GNUNET_SYSERR); - GNUNET_free (sctx); -} - - -/** - * Transmit a request for a service status change to the - * ARM service. - * - * @param cls the "struct RequestContext" identifying the request - * @param size how many bytes are available in buf - * @param buf where to write the request, NULL on error - * @return number of bytes written to buf - */ -static size_t -send_service_msg (void *cls, size_t size, void *buf); - - -/** - * We've failed to transmit the request to the ARM service but - * are now going to try again. - * - * @param cls state of the request - * @param tc task context (unused) - */ -static void -retry_request (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct RequestContext *sctx = cls; - - if (NULL == - GNUNET_CLIENT_notify_transmit_ready (sctx->h->client, - sctx->slen + - sizeof (struct - GNUNET_MessageHeader), - GNUNET_TIME_absolute_get_remaining (sctx->timeout), - &send_service_msg, - sctx)) - { - report_transmit_failure (sctx); - return; - } -} - - -/** - * Transmit a request for a service status change to the - * ARM service. - * - * @param cls the "struct RequestContext" identifying the request - * @param size how many bytes are available in buf - * @param buf where to write the request, NULL on error - * @return number of bytes written to buf - */ -static size_t -send_service_msg (void *cls, size_t size, void *buf) -{ - struct RequestContext *sctx = cls; - struct GNUNET_MessageHeader *msg; - struct GNUNET_TIME_Relative rem; - - if (buf == NULL) - { - GNUNET_CLIENT_disconnect (sctx->h->client); - sctx->h->client = GNUNET_CLIENT_connect (sctx->h->sched, - "arm", - sctx->h->cfg); - GNUNET_assert (sctx->h->client != NULL); - rem = GNUNET_TIME_absolute_get_remaining (sctx->timeout); - if ( (sctx->attempts_left-- > 0) && - (rem.value > 0) ) - { - GNUNET_SCHEDULER_add_delayed (sctx->h->sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_relative_min (MIN_RETRY_DELAY, - rem), - &retry_request, - sctx); - return 0; - } - report_transmit_failure (sctx); - return 0; - } -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Transmitting service request to ARM.\n")); -#endif - GNUNET_assert (size >= sctx->slen); - msg = buf; - msg->size = htons (sizeof (struct GNUNET_MessageHeader) + sctx->slen); - msg->type = htons (sctx->type); - memcpy (&msg[1], sctx->service_name, sctx->slen); - GNUNET_CLIENT_receive (sctx->h->client, - &handle_response, - sctx, - GNUNET_TIME_absolute_get_remaining (sctx->timeout)); - return sctx->slen + sizeof (struct GNUNET_MessageHeader); -} - - /** * Start or stop a service. * @@ -425,43 +454,56 @@ send_service_msg (void *cls, size_t size, void *buf) * @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 - * @param type type of the request + * @param type type of the request */ static void -change_service (struct GNUNET_ARM_Handle *h, - const char *service_name, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type) +change_service (struct GNUNET_ARM_Handle *h, const char *service_name, + struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb, + void *cb_cls, uint16_t type) { struct RequestContext *sctx; size_t slen; + struct GNUNET_MessageHeader *msg; slen = strlen (service_name) + 1; - if (slen + sizeof (struct GNUNET_MessageHeader) > + if (slen + sizeof (struct GNUNET_MessageHeader) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) - { - GNUNET_break (0); - if (cb != NULL) - cb (cb_cls, GNUNET_NO); - return; - } -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("ARM requests starting of service `%s'.\n"), service_name); -#endif + { + GNUNET_break (0); + if (cb != NULL) + cb (cb_cls, GNUNET_NO); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + (type == + GNUNET_MESSAGE_TYPE_ARM_START) ? + "Requesting start of service `%s'.\n" : + "Requesting termination of service `%s'.\n", service_name); sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); sctx->h = h; sctx->callback = cb; sctx->cls = cb_cls; - sctx->service_name = (const char*) &sctx[1]; - memcpy (&sctx[1], - service_name, - slen); sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); - sctx->slen = slen; - sctx->attempts_left = MAX_ATTEMPTS; sctx->type = type; - retry_request (sctx, NULL); + memcpy (&sctx[1], service_name, slen); + msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen); + msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen); + msg->type = htons (sctx->type); + memcpy (&msg[1], service_name, slen); + if (GNUNET_OK != + GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg, + GNUNET_TIME_absolute_get_remaining + (sctx->timeout), GNUNET_YES, + &handle_response, sctx)) + { + GNUNET_break (0); + if (cb != NULL) + cb (cb_cls, GNUNET_SYSERR); + GNUNET_free (sctx); + GNUNET_free (msg); + return; + } + GNUNET_free (msg); } @@ -470,6 +512,7 @@ change_service (struct GNUNET_ARM_Handle *h, * * @param h handle to ARM * @param service_name name of the service + * @param std_inheritance inheritance of std streams * @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 @@ -477,26 +520,66 @@ change_service (struct GNUNET_ARM_Handle *h, void GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char *service_name, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_Callback cb, void *cb_cls) + enum GNUNET_OS_InheritStdioFlags std_inheritance, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls) { struct RequestContext *sctx; + struct GNUNET_CLIENT_Connection *client; + size_t slen; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Starting service `%s'\n"), service_name); - if (0 == strcmp ("arm", service_name)) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Asked to start service `%s' within %s\n", service_name, + GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); + if (0 == strcasecmp ("arm", service_name)) + { + slen = strlen ("arm") + 1; + sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); + sctx->h = h; + sctx->callback = cb; + sctx->cls = cb_cls; + sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + sctx->std_inheritance = std_inheritance; + memcpy (&sctx[1], service_name, slen); + GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report, + sctx); + return; + } + if (NULL == h->client) + { + client = GNUNET_CLIENT_connect ("arm", h->cfg); + if (client == NULL) { - sctx = GNUNET_malloc (sizeof (struct RequestContext)); - sctx->h = h; - sctx->callback = cb; - sctx->cls = cb_cls; - sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); - GNUNET_CLIENT_service_test (h->sched, - "arm", - h->cfg, timeout, &arm_service_report, sctx); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "arm_api, GNUNET_CLIENT_connect returned NULL\n"); + cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); return; } - change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); + h->client = client; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n"); + change_service (h, service_name, timeout, cb, cb_cls, + GNUNET_MESSAGE_TYPE_ARM_START); +} + + +/** + * Callback from the arm stop service call, indicates that the arm service + * is well and truly dead, won't die, or an error occurred. + * + * @param cls closure for the callback + * @param reason reason for callback + */ +static void +arm_shutdown_callback (void *cls, enum GNUNET_ARM_ProcessStatus reason) +{ + struct ARM_ShutdownContext *arm_shutdown_ctx = cls; + + if (arm_shutdown_ctx->cb != NULL) + arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason); + GNUNET_free (arm_shutdown_ctx); } @@ -512,181 +595,196 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, void GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, const char *service_name, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_Callback cb, void *cb_cls) + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Stopping service `%s'\n"), service_name); - if (0 == strcmp ("arm", service_name)) + struct ARM_ShutdownContext *arm_shutdown_ctx; + struct GNUNET_CLIENT_Connection *client; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Stopping service `%s' within %s\n", + service_name, + GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); + if (h->client == NULL) + { + client = GNUNET_CLIENT_connect ("arm", h->cfg); + if (client == NULL) { - GNUNET_CLIENT_service_shutdown (h->client); - if (cb != NULL) - cb (cb_cls, GNUNET_NO); + cb (cb_cls, GNUNET_SYSERR); return; } - change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP); + h->client = client; + } + if (0 == strcasecmp ("arm", service_name)) + { + arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext)); + arm_shutdown_ctx->cb = cb; + arm_shutdown_ctx->cb_cls = cb_cls; + arm_service_shutdown (h->client, timeout, &arm_shutdown_callback, + arm_shutdown_ctx); + h->client = NULL; + return; + } + change_service (h, service_name, timeout, cb, cb_cls, + GNUNET_MESSAGE_TYPE_ARM_STOP); } /** - * 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. + * Internal state for a list request with ARM. */ -struct MultiContext +struct ListRequestContext { + /** - * NULL-terminated array of services to start or stop. + * Pointer to our handle with ARM. */ - char **services; + struct GNUNET_ARM_Handle *h; /** - * Our handle to ARM. + * Function to call with a status code for the requested operation. */ - struct GNUNET_ARM_Handle *h; + GNUNET_ARM_List_Callback callback; /** - * Identifies the operation (start or stop). + * Closure for "callback". */ - ServiceOperation op; + void *cls; /** - * Current position in "services". + * Timeout for the operation. */ - unsigned int pos; + struct GNUNET_TIME_Absolute timeout; }; /** - * Run the operation for the next service in the multi-service - * request. + * Process a response from ARM for the list request. * - * @param cls the "struct MultiContext" that is being processed - * @param success status of the previous operation (ignored) + * @param cls the list request context + * @param msg the response */ static void -next_operation (void *cls, - int success) +handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg) { - struct MultiContext *mc = cls; - char *pos; + struct ListRequestContext *sc = cls; + const struct GNUNET_ARM_ListResultMessage *res; + const char *pos; + uint16_t size_check; + uint16_t rcount; + uint16_t msize; - if (NULL == (pos = mc->services[mc->pos])) + if (NULL == msg) + { + GNUNET_break (0); + GNUNET_CLIENT_disconnect (sc->h->client); + sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg); + GNUNET_assert (NULL != sc->h->client); + if (sc->callback != NULL) + sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL); + GNUNET_free (sc); + return; + } + + if (NULL == sc->callback) + { + GNUNET_break (0); + GNUNET_free (sc); + return; + } + msize = ntohs (msg->size); + if ( (msize < sizeof ( struct GNUNET_ARM_ListResultMessage)) || + (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT) ) + { + GNUNET_break (0); + sc->callback (sc->cls, GNUNET_NO, 0, NULL); + GNUNET_free (sc); + return; + } + size_check = 0; + res = (const struct GNUNET_ARM_ListResultMessage *) msg; + rcount = ntohs (res->count); + { + const char *list[rcount]; + unsigned int i; + + pos = (const char *)&res[1]; + for (i=0; iservices); - GNUNET_ARM_disconnect (mc->h); - GNUNET_free (mc); - return; + const char *end = memchr (pos, 0, msize - size_check); + if (NULL == end) + { + GNUNET_break (0); + sc->callback (sc->cls, GNUNET_NO, 0, NULL); + GNUNET_free (sc); + return; + } + list[i] = pos; + size_check += (end - pos) + 1; + pos = end + 1; } - mc->pos++; - mc->op (mc->h, pos, MULTI_TIMEOUT, &next_operation, mc); - GNUNET_free (pos); + sc->callback (sc->cls, GNUNET_YES, rcount, list); + } + GNUNET_free (sc); } /** - * 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 + * List all running services. + * + * @param h handle to ARM + * @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 */ -static void -run_multi_request (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_SCHEDULER_Handle *sched, - ServiceOperation op, - va_list va) +void +GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_List_Callback cb, void *cb_cls) { - va_list cp; - unsigned int total; - struct MultiContext *mc; - struct GNUNET_ARM_Handle *h; - const char *c; + struct ListRequestContext *sctx; + struct GNUNET_MessageHeader msg; + struct GNUNET_CLIENT_Connection *client; - h = GNUNET_ARM_connect (cfg, sched, NULL); - if (NULL == h) + if (h->client == NULL) + { + client = GNUNET_CLIENT_connect ("arm", h->cfg); + if (client == NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Error while trying to transmit to ARM service\n")); - return; + GNUNET_break (0); + cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL); + 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); + h->client = client; + } + + sctx = GNUNET_malloc (sizeof (struct RequestContext)); + sctx->h = h; + sctx->callback = cb; + sctx->cls = cb_cls; + sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + msg.size = htons (sizeof (struct GNUNET_MessageHeader)); + msg.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Requesting LIST from ARM service with timeout: %s\n", + GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES)); + + if (GNUNET_OK != + GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, + &msg, + GNUNET_TIME_absolute_get_remaining + (sctx->timeout), + GNUNET_YES, + &handle_list_response, + sctx)) + { + GNUNET_break (0); + if (cb != NULL) + cb (cb_cls, GNUNET_SYSERR, 0, NULL); + GNUNET_free (sctx); + return; + } } - /* end of arm_api.c */