X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Farm%2Farm_api.c;h=15563e93398b840d64cf6ed0e3b53b50fbc6e66b;hb=d0b4927e6ab7e8b9874dd7807055e77fb4c5163f;hp=69e6ea99eb8711c78ac33e7c325b23fea9993eb2;hpb=ac715837a25032b460c00a97d957d1eeafc36c48;p=oweals%2Fgnunet.git diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index 69e6ea99e..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,24 +25,15 @@ */ #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 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, 1) - +#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__) /** * Handle for interacting with ARM. - */ + */ struct GNUNET_ARM_Handle { @@ -56,14 +47,169 @@ struct GNUNET_ARM_Handle */ struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +/** + * Context for handling the shutdown of a service. + */ +struct ShutdownContext +{ + /** + * Connection to the service that is being shutdown. + */ + struct GNUNET_CLIENT_Connection *sock; + + /** + * Time allowed for shutdown to happen. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Task set up to cancel the shutdown request on timeout. + */ + GNUNET_SCHEDULER_TaskIdentifier cancel_task; + + /** + * Task to call once shutdown complete + */ + GNUNET_CLIENT_ShutdownTask cont; + /** - * Scheduler to use. + * Closure for shutdown continuation */ - struct GNUNET_SCHEDULER_Handle *sched; + 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. @@ -71,26 +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; - GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES); ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle)); ret->cfg = GNUNET_CONFIGURATION_dup (cfg); - ret->sched = sched; - ret->client = client; return ret; } @@ -104,12 +241,26 @@ void GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) { if (h->client != NULL) - GNUNET_CLIENT_disconnect (h->client, GNUNET_NO); + 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. */ @@ -141,6 +292,11 @@ struct RequestContext */ uint16_t type; + /** + * Flags for passing std descriptors to ARM (when starting ARM). + */ + enum GNUNET_OS_InheritStdioFlags std_inheritance; + }; #include "do_start_process.c" @@ -156,99 +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)) - { -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Looks like `%s' is already running.\n", - "gnunet-service-arm"); -#endif - /* arm is running! */ - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_YES); - GNUNET_free (pos); - return; - } -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Looks like `%s' is not running, will start it.\n", - "gnunet-service-arm"); -#endif - /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */ - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, - "arm", "PREFIX", &loprefix)) + { + 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)) + 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); - GNUNET_free (loprefix); - GNUNET_free (lopostfix); - 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); - GNUNET_free (loprefix); - GNUNET_free (lopostfix); - return; - } - pid = do_start_process (loprefix, - binary, - "-c", config, -#if DEBUG_ARM - "-L", "DEBUG", -#endif - "-d", - lopostfix, - 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); GNUNET_free (loprefix); GNUNET_free (lopostfix); - if (pid == -1) + 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); } @@ -257,57 +413,35 @@ 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 to `%s' request from ARM for service `%s'\n"), - (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) - ? "START" - : "STOP", - (const char*) &sc[1]); - GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO); - sc->h->client = GNUNET_CLIENT_connect (sc->h->sched, - "arm", - sc->h->cfg); - GNUNET_assert (NULL != sc->h->client); - GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES); - 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 for service `%s': %u\n", - (const char*) &sc[1], - 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); } @@ -320,34 +454,31 @@ handle_response (void *cls, const struct GNUNET_MessageHeader *msg) * @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, - (type == GNUNET_MESSAGE_TYPE_ARM_START) - ? _("Requesting start of service `%s'.\n") - : _("Requesting termination 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; @@ -360,24 +491,18 @@ change_service (struct GNUNET_ARM_Handle *h, 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_log (GNUNET_ERROR_TYPE_WARNING, - (type == GNUNET_MESSAGE_TYPE_ARM_START) - ? _("Error while trying to transmit request to start `%s' to ARM\n") - : _("Error while trying to transmit request to stop `%s' to ARM\n"), - (const char*) &service_name); - if (cb != NULL) - cb (cb_cls, GNUNET_SYSERR); - GNUNET_free (sctx); - GNUNET_free (msg); - return; - } + 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); } @@ -387,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 @@ -394,31 +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; -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Asked to start service `%s' within %llu ms\n"), service_name, - (unsigned long long) timeout.value); -#endif + + 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) { - 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); - memcpy (&sctx[1], service_name, slen); - 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); } @@ -434,185 +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' within %llu ms\n"), service_name, - (unsigned long long) timeout.value); - if (0 == strcasecmp ("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); - h->client = NULL; - 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 + * Internal state for a list request with ARM. */ -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 +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. - * Should normally only be called from gnunet-arm or testcases, - * stopping a service is generally otherwise a bad idea. - * - * @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 */