From: LRN Date: Wed, 13 Mar 2013 17:49:26 +0000 (+0000) Subject: All-encompassing ARM update X-Git-Tag: initial-import-from-subversion-38251~9671 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=405f776bc08486af4edb80e18149c0829732b347;p=oweals%2Fgnunet.git All-encompassing ARM update --- diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am index 48d9955b8..dcc0f125f 100644 --- a/src/arm/Makefile.am +++ b/src/arm/Makefile.am @@ -19,7 +19,7 @@ endif lib_LTLIBRARIES = libgnunetarm.la libgnunetarm_la_SOURCES = \ - arm_api.c arm.h + arm_api.c arm_monitor_api.c arm.h libgnunetarm_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) $(XLIB) diff --git a/src/arm/arm.h b/src/arm/arm.h index 21884107c..aad16fd71 100644 --- a/src/arm/arm.h +++ b/src/arm/arm.h @@ -36,20 +36,60 @@ GNUNET_NETWORK_STRUCT_BEGIN /** - * Reply from ARM to client. + * Status update from ARM to client. */ -struct GNUNET_ARM_ResultMessage +struct GNUNET_ARM_StatusMessage { /** - * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT. + * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_STATUS. */ struct GNUNET_MessageHeader header; /** - * Status from the 'enum GNUNET_ARM_ProcessStatus' + * Status from the 'enum GNUNET_ARM_ServiceStatus' */ uint32_t status; + + /* followed by a 0-terminated service name */ +}; + +struct GNUNET_ARM_Message +{ + /** + * Reply to client, type is GNUNET_MESSAGE_TYPE_ARM_RESULT or + * GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT. + * OR + * Request from client, type is GNUNET_MESSAGE_TYPE_ARM_REQUEST + */ + struct GNUNET_MessageHeader header; + + /** + * ID of a request that is being replied to. + * OR + * ID of a request that is being sent. + */ + uint64_t request_id; + + /* For requests - followed by a 0-terminated service name */ +}; + + +/** + * Reply from ARM to client. + */ +struct GNUNET_ARM_ResultMessage +{ + + /** + * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT, with an ID. + */ + struct GNUNET_ARM_Message arm_msg; + + /** + * Result from the 'enum GNUNET_ARM_Result' + */ + uint32_t result; }; /** @@ -61,9 +101,10 @@ struct GNUNET_ARM_ResultMessage struct GNUNET_ARM_ListResultMessage { /** - * Reply to client is of type GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT + * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT, + * with an ID. */ - struct GNUNET_MessageHeader header; + struct GNUNET_ARM_Message arm_msg; /** * Number of '\0' terminated strings that follow diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index aee9afb24..d434ff3af 100644 --- a/src/arm/arm_api.c +++ b/src/arm/arm_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors) + (C) 2009, 2010, 2012, 2013 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 @@ -21,7 +21,7 @@ /** * @file arm/arm_api.c * @brief API for accessing the ARM service - * @author Christian Grothoff + * @author Christian Grothoff, LRN */ #include "platform.h" #include "gnunet_arm_service.h" @@ -38,7 +38,7 @@ struct GNUNET_ARM_Handle { /** - * Our connection to the ARM service. + * Our control connection to the ARM service. */ struct GNUNET_CLIENT_Connection *client; @@ -47,257 +47,469 @@ struct GNUNET_ARM_Handle */ struct GNUNET_CONFIGURATION_Handle *cfg; + /** + * Handle for our current transmission request. + */ + struct GNUNET_CLIENT_TransmitHandle *cth; + + /** + * Head of doubly-linked list of pending requests. + */ + struct ARMControlMessage *control_pending_head; + + /** + * Tail of doubly-linked list of pending requests. + */ + struct ARMControlMessage *control_pending_tail; + + /** + * Head of doubly-linked list of sent requests. + */ + struct ARMControlMessage *control_sent_head; + + /** + * Tail of doubly-linked list of sent requests. + */ + struct ARMControlMessage *control_sent_tail; + + /** + * ID of the reconnect task (if any). + */ + GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * Current delay we use for re-trying to connect to core. + */ + struct GNUNET_TIME_Relative retry_backoff; + + /** + * Are we currently disconnected and hence unable to send? + */ + unsigned char currently_down; + + /** + * Callback to invoke on connection/disconnection. + */ + GNUNET_ARM_ConnectionStatusCallback conn_status; + + /** + * Closure for conn_status. + */ + void *conn_status_cls; + + /** + * GNUNET_YES if we're running a service test. + */ + unsigned char service_test_is_active; + + /** + * Counter for request identifiers + */ + uint64_t request_id_counter; }; + /** - * Context for handling the shutdown of a service. + * Entry in a doubly-linked list of control messages to be transmitted + * to the arm service. + * + * The actual message is allocated at the end of this struct. */ -struct ShutdownContext +struct ARMControlMessage { /** - * Connection to the service that is being shutdown. + * This is a doubly-linked list. */ - struct GNUNET_CLIENT_Connection *sock; + struct ARMControlMessage *next; /** - * Time allowed for shutdown to happen. + * This is a doubly-linked list. */ - struct GNUNET_TIME_Absolute timeout; + struct ARMControlMessage *prev; /** - * Task set up to cancel the shutdown request on timeout. + * Callback for service state change requests. */ - GNUNET_SCHEDULER_TaskIdentifier cancel_task; + GNUNET_ARM_ResultCallback result_cont; /** - * Task to call once shutdown complete + * Callback for service list requests. */ - GNUNET_CLIENT_ShutdownTask cont; + GNUNET_ARM_ServiceListCallback list_cont; /** - * Closure for shutdown continuation + * Closure for 'result_cont' or 'list_cont'. */ void *cont_cls; /** - * Handle for transmission request. + * Timeout for the operation. */ - struct GNUNET_CLIENT_TransmitHandle *th; + struct GNUNET_TIME_Absolute timeout; + /** + * Type of the request expressed as a message type (start, stop or list). + */ + uint16_t type; + + /** + * Flags for passing std descriptors to ARM (when starting ARM). + */ + enum GNUNET_OS_InheritStdioFlags std_inheritance; + + /** + * ARM handle. + */ + struct GNUNET_ARM_Handle *h; + + /** + * Message to send. + */ + struct GNUNET_ARM_Message *msg; + + /** + * Task to run when request times out. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task_id; }; +static void +client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg); + +static void +reconnect_arm (struct GNUNET_ARM_Handle *h); + +static void +trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down); + /** - * 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. + * Task scheduled to try to re-connect to arm. * - * @param cls closure - * @param msg NULL, indicating socket closure. + * @param cls the 'struct GNUNET_ARM_Handle' + * @param tc task context */ static void -service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) +reconnect_arm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_ARM_Handle *h = cls; + + h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to ARM service after delay\n"); + reconnect_arm (h); +} + + +static void +clear_pending_messages (struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus result) { - struct ShutdownContext *shutdown_ctx = cls; + struct ARMControlMessage *cm; - if (NULL != msg) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Clearing pending messages\n"); + + while (NULL != (cm = h->control_pending_head)) { - /* 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; + GNUNET_CONTAINER_DLL_remove (h->control_pending_head, + h->control_pending_tail, cm); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id); + GNUNET_SCHEDULER_cancel (cm->timeout_task_id); + if (NULL != cm->result_cont) + cm->result_cont (cm->cont_cls, cm->h, result, NULL, 0); + GNUNET_free_non_null (cm->msg); + GNUNET_free (cm); } - 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. + * Close down any existing connection to the ARM service and + * try re-establishing it later. * - * @param cls closure - * @param tc context information (why was this task triggered now) + * @param h our handle */ static void -service_shutdown_cancel (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +reconnect_arm_later (struct GNUNET_ARM_Handle *h) { - struct ShutdownContext *shutdown_ctx = cls; + if (GNUNET_NO != h->currently_down) + return; - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock); - GNUNET_free (shutdown_ctx); -} + if (NULL != h->cth) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth); + h->cth = NULL; + } + + if (NULL != h->client) + { + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + } + + if (NULL != h->conn_status) + h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_NO); + + h->currently_down = GNUNET_YES; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task); + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h); + /* Don't clear pending messages on disconnection, deliver them later + clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED); + GNUNET_assert (NULL == h->control_pending_head); + */ + h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff); +} /** - * If possible, write a shutdown message to the target - * buffer and destroy the client connection. + * Transmit the next message to the arm service. * - * @param cls the "struct GNUNET_CLIENT_Connection" to destroy + * @param cls closure with the 'struct GNUNET_ARM_Handle' * @param size number of bytes available in buf - * @param buf NULL on error, otherwise target buffer - * @return number of bytes written to buf + * @param buf where the callee should write the message + * @return number of bytes written to buf */ static size_t -write_shutdown (void *cls, size_t size, void *buf) +transmit_arm_message (void *cls, size_t size, void *buf) { - struct ShutdownContext *shutdown_ctx = cls; - struct GNUNET_MessageHeader *msg; + struct GNUNET_ARM_Handle *h = cls; + struct ARMControlMessage *cm; + struct GNUNET_ARM_Message *arm_msg; + uint16_t msize; + uint64_t request_id; - shutdown_ctx->th = NULL; - if (size < sizeof (struct GNUNET_MessageHeader)) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "transmit_arm_message is running with %p buffer of size %lu. ARM is known to be %s\n", + buf, size, h->currently_down ? "unconnected" : "connected"); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task); + h->cth = NULL; + if ((GNUNET_YES == h->currently_down) && (NULL != buf)) + { + h->currently_down = GNUNET_NO; + if (NULL != h->conn_status) + h->conn_status (h->conn_status_cls, h, GNUNET_YES, GNUNET_NO); + h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + GNUNET_CLIENT_receive (h->client, &client_notify_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); + } + if (NULL == buf) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmission failed, initiating reconnect\n"); + reconnect_arm_later (h); + return 0; + } + if (NULL == (cm = h->control_pending_head)) { - 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 */ + LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue is empty, not sending anything\n"); + return 0; } - 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); + + GNUNET_assert (NULL != cm->msg); + msize = ntohs (cm->msg->header.size); + if (size < msize) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Request is too big (%u < %u), not sending it\n", size, msize); + trigger_next_request (h, GNUNET_NO); + return 0; + } + arm_msg = cm->msg; + if (0 == h->request_id_counter) + h->request_id_counter++; + request_id = h->request_id_counter++; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting control message with %u bytes of type %u to arm with id %llu\n", + (unsigned int) msize, (unsigned int) ntohs (cm->msg->header.type), request_id); + arm_msg->request_id = GNUNET_htonll (request_id); + memcpy (buf, cm->msg, msize); + /* Otherwise we won't be able to find it later! */ + arm_msg->request_id = request_id; + + GNUNET_CONTAINER_DLL_remove (h->control_pending_head, + h->control_pending_tail, cm); + GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head, + h->control_sent_tail, cm); + + /* Don't free msg, keep it around (kind of wasteful, but then we don't + * really have many messages to handle, and it'll be freed when it times + * out anyway. + */ + trigger_next_request (h, GNUNET_NO); + return msize; } /** - * 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). + * Check the list of pending requests, send the next + * one to the arm. * - * @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 + * @param h arm handle + * @param ignore_currently_down transmit message even if not initialized? + */ +static void +trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down) +{ + uint16_t msize; + + if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "ARM connection down, not processing queue\n"); + return; + } + if (NULL != h->cth) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n"); + return; + } + if (NULL != h->control_pending_head) + msize = + ntohs (((struct GNUNET_MessageHeader *) &h-> + control_pending_head[1])->size); + else if (GNUNET_NO == ignore_currently_down) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Request queue empty, not processing queue\n"); + return; /* no pending message */ + } + h->cth = + GNUNET_CLIENT_notify_transmit_ready (h->client, msize, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, &transmit_arm_message, h); +} + + +/** + * Connect to arm. * + * @param h arm handle */ static void -arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, - struct GNUNET_TIME_Relative timeout, - GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) +reconnect_arm (struct GNUNET_ARM_Handle *h) { - 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); + GNUNET_assert (NULL == h->client); + GNUNET_assert (GNUNET_YES == h->currently_down); + h->client = GNUNET_CLIENT_connect ("arm", h->cfg); + if (NULL == h->client) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "arm_api, GNUNET_CLIENT_connect returned NULL\n"); + if (NULL != h->conn_status) + h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_YES); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); + trigger_next_request (h, GNUNET_YES); } /** - * Setup a context for communicating with ARM. Note that this + * Set up a context for communicating with ARM. Note that this * can be done even if the ARM service is not yet running. + * Never fails. * * @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 service service that *this* process is implementing/providing, can be NULL - * @return context to use for further ARM operations, NULL on error + * @return context to use for further ARM operations */ struct GNUNET_ARM_Handle * -GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *service) +GNUNET_ARM_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_ARM_Handle *ret; ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle)); ret->cfg = GNUNET_CONFIGURATION_dup (cfg); + ret->currently_down = GNUNET_YES; + ret->reconnect_task = GNUNET_SCHEDULER_NO_TASK; return ret; } /** - * Disconnect from the ARM service. + * Start connecting to the ARM service using the context. * - * @param h the handle that was being used + * @param h ARM handle + * @param conn_status will be called when connecting/disconnecting + * @param cls closure for conn_status */ void -GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) +GNUNET_ARM_connect (struct GNUNET_ARM_Handle *h, + GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls) { - if (h->client != NULL) - GNUNET_CLIENT_disconnect (h->client); - GNUNET_CONFIGURATION_destroy (h->cfg); - GNUNET_free (h); + h->conn_status = conn_status; + h->conn_status_cls = cls; + reconnect_arm (h); } -struct ARM_ShutdownContext +/** + * Disconnect from the ARM service (if connected) and destroy the context. + * Don't call inside an ARM callback! + * + * @param h the handle that was being used + */ +void +GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *handle) { - /** - * Callback to call once shutdown complete. - */ - GNUNET_ARM_Callback cb; - - /** - * Closure for callback. - */ - void *cb_cls; -}; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n"); + if (NULL != handle->cth) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth); + handle->cth = NULL; + } + clear_pending_messages (handle, GNUNET_ARM_REQUEST_DISCONNECTED); + if (NULL != handle->client) + { + GNUNET_CLIENT_disconnect (handle->client); + handle->client = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != handle->reconnect_task) + { + GNUNET_SCHEDULER_cancel (handle->reconnect_task); + handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + } + if (GNUNET_NO == handle->service_test_is_active) + { + GNUNET_CONFIGURATION_destroy (handle->cfg); + GNUNET_free (handle); + } +} /** - * Internal state for a request with ARM. + * Message timed out. Remove it from the queue. + * + * @param cls the message (struct ARMControlMessage *) + * @param tc task context */ -struct RequestContext +static void +control_message_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + struct ARMControlMessage *cm = cls; + struct GNUNET_ARM_Message *arm_msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Control message timed out\n"); + arm_msg = cm->msg; + if ((NULL == arm_msg) || (0 == arm_msg->request_id)) + { + GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head, + cm->h->control_pending_tail, cm); + } + else + { + GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head, + cm->h->control_sent_tail, cm); + } + if (NULL != cm->result_cont) + cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, NULL, 0); + else if (NULL != cm->list_cont) + cm->list_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, 0, NULL); + GNUNET_free_non_null (cm->msg); + GNUNET_free (cm); +} - /** - * Pointer to our handle with ARM. - */ - struct GNUNET_ARM_Handle *h; - - /** - * Function to call with a status code for the requested operation. - */ - GNUNET_ARM_Callback callback; - - /** - * Closure for "callback". - */ - void *cls; - - /** - * Timeout for the operation. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Type of the request expressed as a message type (start or stop). - */ - uint16_t type; - - /** - * Flags for passing std descriptors to ARM (when starting ARM). - */ - enum GNUNET_OS_InheritStdioFlags std_inheritance; - -}; #include "do_start_process.c" @@ -308,78 +520,89 @@ struct RequestContext * or not ARM is running; if it is, report success. If * it is not, start the ARM process. * - * @param cls the context for the request that we will report on (struct RequestContext*) + * @param cls the context for the request that we will report on (struct ARMControlMessage *) * @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) { - struct RequestContext *pos = cls; + struct ARMControlMessage *cm = cls; struct GNUNET_OS_Process *proc; + unsigned char test_is_active; char *cbinary; char *binary; char *config; char *loprefix; char *lopostfix; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) + test_is_active = cm->h->service_test_is_active; + + /* FIXME: shouldn't we check for GNUNET_SCHEDULER_REASON_SHUTDOWN ? */ + if ((GNUNET_YES == test_is_active) && + (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))) { 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); + if (cm->result_cont) + cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY); + } + if (GNUNET_NO == test_is_active) + { + /* User disconnected & destroyed ARM handle in the middle of + * the service test, so we kept the handle around until now. + */ + GNUNET_CONFIGURATION_destroy (cm->h->cfg); + GNUNET_free (cm->h); + } + if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) || + (GNUNET_NO == test_is_active)) + { + GNUNET_free (cm); return; } + cm->h->service_test_is_active = GNUNET_NO; 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)) + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string ( + cm->h->cfg, "arm", "PREFIX", &loprefix)) loprefix = GNUNET_strdup (""); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS", - &lopostfix)) + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string ( + cm->h->cfg, "arm", "OPTIONS", &lopostfix)) lopostfix = GNUNET_strdup (""); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY", - &cbinary)) + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string ( + cm->h->cfg, "arm", "BINARY", &cbinary)) { - 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_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY"); + if (cm->result_cont) + cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_NOT_KNOWN); + GNUNET_free (cm); GNUNET_free (loprefix); GNUNET_free (lopostfix); return; } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG", - &config)) + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename ( + cm->h->cfg, "arm", "CONFIG", &config)) config = NULL; binary = GNUNET_OS_get_libexec_binary_path (cbinary); GNUNET_free (cbinary); - 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"))) + if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value ( + cm->h->cfg, "TESTING", "WEAKRANDOM")) && + (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno ( + cm->h->cfg, "TESTING", "WEAKRANDOM")) && + (GNUNET_NO == GNUNET_CONFIGURATION_have_value ( + cm->h->cfg, "TESTING", "HOSTFILE"))) { /* Means we are ONLY running locally */ /* we're clearly running a test, don't daemonize */ if (NULL == config) - proc = do_start_process (GNUNET_NO, pos->std_inheritance, + proc = do_start_process (GNUNET_NO, cm->std_inheritance, NULL, loprefix, binary, /* no daemonization! */ lopostfix, NULL); else - proc = do_start_process (GNUNET_NO, pos->std_inheritance, + proc = do_start_process (GNUNET_NO, cm->std_inheritance, NULL, loprefix, binary, "-c", config, /* no daemonization! */ lopostfix, NULL); @@ -387,11 +610,11 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) else { if (NULL == config) - proc = do_start_process (GNUNET_NO, pos->std_inheritance, + proc = do_start_process (GNUNET_NO, cm->std_inheritance, NULL, loprefix, binary, "-d", lopostfix, NULL); else - proc = do_start_process (GNUNET_NO, pos->std_inheritance, + proc = do_start_process (GNUNET_NO, cm->std_inheritance, NULL, loprefix, binary, "-c", config, "-d", lopostfix, NULL); } @@ -401,52 +624,18 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_free (lopostfix); if (NULL == proc) { - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE); - GNUNET_free (pos); + if (cm->result_cont) + cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", + GNUNET_ARM_RESULT_START_FAILED); + GNUNET_free (cm); return; } - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING); + if (cm->result_cont) + cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", + GNUNET_ARM_RESULT_STARTING); GNUNET_OS_process_destroy (proc); - GNUNET_free (pos); -} - - -/** - * Process a response from ARM to a request for a change in service - * status. - * - * @param cls the request context - * @param msg the response - */ -static void -handle_response (void *cls, const struct GNUNET_MessageHeader *msg) -{ - struct RequestContext *sc = cls; - 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, status); - GNUNET_free (sc); + reconnect_arm (cm->h); + GNUNET_free (cm); } @@ -462,74 +651,65 @@ handle_response (void *cls, const struct GNUNET_MessageHeader *msg) */ static void change_service (struct GNUNET_ARM_Handle *h, const char *service_name, - struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb, + struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cb, void *cb_cls, uint16_t type) { - struct RequestContext *sctx; + struct ARMControlMessage *cm; size_t slen; - struct GNUNET_MessageHeader *msg; + struct GNUNET_ARM_Message *msg; slen = strlen (service_name) + 1; - if (slen + sizeof (struct GNUNET_MessageHeader) >= + if (slen + sizeof (struct GNUNET_ARM_Message) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) { GNUNET_break (0); if (cb != NULL) - cb (cb_cls, GNUNET_NO); + cb (cb_cls, h, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0); 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->timeout = GNUNET_TIME_relative_to_absolute (timeout); - sctx->type = type; - 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); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n", + (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination", + service_name); + cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen); + cm->h = h; + cm->result_cont = cb; + cm->cont_cls = cb_cls; + cm->timeout = GNUNET_TIME_relative_to_absolute (timeout); + memcpy (&cm[1], service_name, slen); + msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen); + msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen); + msg->header.type = htons (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); + cm->msg = msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Inserting a control message into the queue. Timeout is %llu\n", + GNUNET_TIME_absolute_get_remaining (cm->timeout).rel_value); + GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head, + h->control_pending_tail, cm); + cm->timeout_task_id = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (cm->timeout), &control_message_timeout, cm); + trigger_next_request (h, GNUNET_NO); } /** - * Start a service. + * Request for a service to be started. * * @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 + * @param cont callback to invoke after request is sent or not sent + * @param cont_cls closure for callback */ void -GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, - const char *service_name, - enum GNUNET_OS_InheritStdioFlags std_inheritance, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_Callback cb, void *cb_cls) +GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h, + const char *service_name, enum GNUNET_OS_InheritStdioFlags std_inheritance, + struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cont, + void *cont_cls) { - struct RequestContext *sctx; - struct GNUNET_CLIENT_Connection *client; + struct ARMControlMessage *cm; size_t slen; LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -537,258 +717,266 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, 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) + /* Possible cases: + * 1) We're connected to ARM already. Invoke the callback immediately. + * 2) We're not connected to ARM. + * Cancel any reconnection attempts temporarily, then perform + * a service test. + */ + if (GNUNET_NO == h->currently_down) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n"); + if (NULL != cont) + cont (cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY); + } + else if (GNUNET_NO == h->service_test_is_active) { + if (NULL != h->cth) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth); + h->cth = NULL; + } + if (NULL != h->client) + { + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task) + { + GNUNET_SCHEDULER_cancel (h->reconnect_task); + h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, - "arm_api, GNUNET_CLIENT_connect returned NULL\n"); - cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); - return; + "Not connected to ARM, will do a service test\n"); + + slen = strlen ("arm") + 1; + cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen); + cm->h = h; + cm->result_cont = cont; + cm->cont_cls = cont_cls; + cm->timeout = GNUNET_TIME_relative_to_absolute (timeout); + cm->std_inheritance = std_inheritance; + memcpy (&cm[1], service_name, slen); + h->service_test_is_active = GNUNET_YES; + GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report, + cm); } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); - h->client = client; + else + { + /* Service test is already running - tell user to chill out and try + * again later. + */ + LOG (GNUNET_ERROR_TYPE_DEBUG, "Service test is already in progress, we're busy\n"); + if (NULL != cont) + cont (cont_cls, h, GNUNET_ARM_REQUEST_BUSY, NULL, 0); + } + return; } - LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n"); - change_service (h, service_name, timeout, cb, cb_cls, + change_service (h, service_name, timeout, cont, cont_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); -} - - -/** - * Stop a service. + * Request a service to be stopped. + * Stopping arm itself will not invalidate its handle, and + * ARM API will try to restore connection to the ARM service, + * even if ARM connection was lost because you asked for ARM to be stopped. + * Call GNUNET_ARM_disconnect () to free the handle and prevent + * further connection attempts. * * @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 + * @param cont callback to invoke after request is sent or is not sent + * @param cont_cls closure for callback */ 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) +GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h, + const char *service_name, struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_ResultCallback cont, void *cont_cls) { - 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) - { - cb (cb_cls, GNUNET_SYSERR); - return; - } - 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, + change_service (h, service_name, timeout, cont, cont_cls, GNUNET_MESSAGE_TYPE_ARM_STOP); } /** - * Internal state for a list request with ARM. + * Request a list of running services. + * + * @param h handle to ARM + * @param timeout how long to wait before failing for good + * @param cont callback to invoke after request is sent or is not sent + * @param cont_cls closure for callback */ -struct ListRequestContext +void +GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_ServiceListCallback cont, void *cont_cls) { + struct ARMControlMessage *cm; + struct GNUNET_ARM_Message *msg; - /** - * Pointer to our handle with ARM. - */ - struct GNUNET_ARM_Handle *h; - - /** - * Function to call with a status code for the requested operation. - */ - GNUNET_ARM_List_Callback callback; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Requesting LIST from ARM service with timeout: %s\n", + GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES)); - /** - * Closure for "callback". - */ - void *cls; + cm = GNUNET_malloc (sizeof (struct ARMControlMessage)); + cm->h = h; + cm->list_cont = cont; + cm->cont_cls = cont_cls; + cm->timeout = GNUNET_TIME_relative_to_absolute (timeout); + msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message)); + msg->header.size = htons (sizeof (struct GNUNET_ARM_Message)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST); + cm->msg = msg; + GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head, + h->control_pending_tail, cm); + cm->timeout_task_id = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (cm->timeout), &control_message_timeout, cm); + trigger_next_request (h, GNUNET_NO); +} - /** - * Timeout for the operation. - */ - struct GNUNET_TIME_Absolute timeout; -}; +static struct ARMControlMessage * +find_cm_by_id (struct GNUNET_ARM_Handle *h, uint64_t id) +{ + struct ARMControlMessage *result; + for (result = h->control_sent_head; result; result = result->next) + if (id == result->msg->request_id) + return result; + return NULL; +} /** - * Process a response from ARM for the list request. + * Handler for ARM replies. * - * @param cls the list request context - * @param msg the response + * @param cls our "struct GNUNET_ARM_Handle" + * @param msg the message received from the arm service */ static void -handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg) +client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg) { - struct ListRequestContext *sc = cls; - const struct GNUNET_ARM_ListResultMessage *res; + struct GNUNET_ARM_Handle *h = cls; + + uint16_t msize; + uint64_t id; + unsigned char fail; + + const struct GNUNET_ARM_Message *arm_msg; + const struct GNUNET_ARM_ResultMessage *res; + const struct GNUNET_ARM_ListResultMessage *lres; + enum GNUNET_ARM_Result result; + struct ARMControlMessage *cm; + const char *pos; uint16_t size_check; uint16_t rcount; - uint16_t msize; - + 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); + LOG (GNUNET_ERROR_TYPE_INFO, + _("Client was disconnected from arm service, trying to reconnect.\n")); + reconnect_arm_later (h); 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) ) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing message of type %u and size %u from arm service\n", + ntohs (msg->type), msize); + if (msize < sizeof (struct GNUNET_ARM_Message)) { GNUNET_break (0); - sc->callback (sc->cls, GNUNET_NO, 0, NULL); - GNUNET_free (sc); + reconnect_arm_later (h); return; } - size_check = 0; - res = (const struct GNUNET_ARM_ListResultMessage *) msg; - rcount = ntohs (res->count); + arm_msg = (const struct GNUNET_ARM_Message *) msg; + id = GNUNET_ntohll (arm_msg->request_id); + cm = find_cm_by_id (h, id); + if (NULL == cm) { - const char *list[rcount]; - unsigned int i; - - pos = (const char *)&res[1]; - for (i=0; icallback (sc->cls, GNUNET_NO, 0, NULL); - GNUNET_free (sc); - return; - } - list[i] = pos; - size_check += (end - pos) + 1; - pos = end + 1; - } - sc->callback (sc->cls, GNUNET_YES, rcount, list); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Message with unknown id %llu\n", id); + return; } - GNUNET_free (sc); -} - - -/** - * 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 - */ -void -GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_List_Callback cb, void *cb_cls) -{ - struct ListRequestContext *sctx; - struct GNUNET_MessageHeader msg; - struct GNUNET_CLIENT_Connection *client; - if (h->client == NULL) + fail = GNUNET_NO; + switch (ntohs (msg->type)) { - client = GNUNET_CLIENT_connect ("arm", h->cfg); - if (client == NULL) + case GNUNET_MESSAGE_TYPE_ARM_RESULT: + if (msize < sizeof (struct GNUNET_ARM_ResultMessage)) + { + GNUNET_assert (0); + fail = GNUNET_YES; + break; + } + res = (const struct GNUNET_ARM_ResultMessage *) msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received response from ARM for service `%s': %u\n", + (const char *) &cm->msg[1], ntohs (msg->type)); + result = (enum GNUNET_ARM_Result) ntohl (res->result); + if (NULL != cm->result_cont) + cm->result_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, (const char *) &cm->msg[1], result); + break; + case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT: + if (msize < sizeof (struct GNUNET_ARM_ListResultMessage)) { GNUNET_break (0); - cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL); + fail = GNUNET_YES; return; } - 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); + else + { + size_check = 0; + lres = (const struct GNUNET_ARM_ListResultMessage *) msg; + rcount = ntohs (lres->count); + { + const char *list[rcount]; + unsigned int i; + + pos = (const char *)&lres[1]; + for (i = 0; i < rcount; i++) + { + const char *end = memchr (pos, 0, msize - size_check); + if (NULL == end) + { + GNUNET_break (0); + fail = GNUNET_YES; + break; + } + list[i] = pos; + size_check += (end - pos) + 1; + pos = end + 1; + } + if (GNUNET_YES == fail) + break; + if (NULL != cm->list_cont) + cm->list_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, rcount, list); + } + } + break; + default: + fail = GNUNET_YES; return; } + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id); + GNUNET_SCHEDULER_cancel (cm->timeout_task_id); + GNUNET_CONTAINER_DLL_remove (h->control_sent_head, + h->control_sent_tail, cm); + GNUNET_free (cm->msg); + GNUNET_free (cm); + + if (GNUNET_YES == fail) + reconnect_arm_later (h); + else + GNUNET_CLIENT_receive (h->client, &client_notify_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); } /* end of arm_api.c */ diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c index 8a98ba06b..9eb64e316 100644 --- a/src/arm/gnunet-arm.c +++ b/src/arm/gnunet-arm.c @@ -110,6 +110,11 @@ static int ret; */ static struct GNUNET_ARM_Handle *h; +/** + * Monitor connection with ARM. + */ +static struct GNUNET_ARM_MonitorHandle *m; + /** * Our configuration. */ @@ -136,105 +141,6 @@ static unsigned int no_stdout; static unsigned int no_stderr; -/** - * Main continuation-passing-style loop. Runs the various - * jobs that we've been asked to do in order. - * - * @param cls closure, unused - * @param tc context, unused - */ -static void -cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); - - -/** - * Callback invoked with the status of the last operation. Reports to the - * user and then runs the next phase in the FSM. - * - * @param cls pointer to "const char*" identifying service that was manipulated - * @param result result of the operation - */ -static void -confirm_cb (void *cls, - enum GNUNET_ARM_ProcessStatus result) -{ - const char *service = cls; - - switch (result) - { - case GNUNET_ARM_PROCESS_UNKNOWN: - FPRINTF (stderr, _("Service `%s' is unknown to ARM.\n"), service); - ret = 1; - break; - case GNUNET_ARM_PROCESS_DOWN: - if (quiet != GNUNET_YES) - FPRINTF (stdout, _("Service `%s' has been stopped.\n"), service); - break; - case GNUNET_ARM_PROCESS_ALREADY_RUNNING: - FPRINTF (stderr, _("Service `%s' was already running.\n"), service); - ret = 1; - break; - case GNUNET_ARM_PROCESS_STARTING: - if (quiet != GNUNET_YES) - FPRINTF (stdout, _("Service `%s' has been started.\n"), service); - break; - case GNUNET_ARM_PROCESS_ALREADY_STOPPING: - FPRINTF (stderr, _("Service `%s' was already being stopped.\n"), service); - ret = 1; - break; - case GNUNET_ARM_PROCESS_ALREADY_DOWN: - FPRINTF (stderr, _("Service `%s' was already not running.\n"), service); - ret = 1; - break; - case GNUNET_ARM_PROCESS_SHUTDOWN: - FPRINTF (stderr, "%s", _("Request ignored as ARM is shutting down.\n")); - ret = 1; - break; - case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR: - FPRINTF (stderr, "%s", _("Error communicating with ARM service.\n")); - ret = 1; - break; - case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT: - FPRINTF (stderr, "%s", _("Timeout communicating with ARM service.\n")); - ret = 1; - break; - case GNUNET_ARM_PROCESS_FAILURE: - FPRINTF (stderr, "%s", _("Operation failed.\n")); - ret = 1; - break; - default: - FPRINTF (stderr, "%s", _("Unknown response code from ARM.\n")); - break; - } - GNUNET_SCHEDULER_add_now (&cps_loop, NULL); -} - - -/** - * Callback invoked with the list of running services. - * Reports to the user and then runs the next phase in the FSM. - * - * @param cls currently not used - * @param result result of the operation - * @param count number of running services - * @param list copy of the list of running services - */ -static void -list_cb (void *cls, int result, unsigned int count, const char *const*list) -{ - unsigned int i; - - if ( (result != GNUNET_YES) || (NULL == list) ) - { - FPRINTF (stderr, "%s", _("Error communicating with ARM. ARM not running?\n")); - return; - } - FPRINTF (stdout, "%s", _("Running services:\n")); - for (i=0; ireason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; while (1) @@ -352,82 +440,54 @@ cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) case 0: if (NULL != term) { - GNUNET_ARM_stop_service (h, term, - (0 == - timeout.rel_value) ? STOP_TIMEOUT : - timeout, &confirm_cb, term); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Termination action\n"); + GNUNET_ARM_request_service_stop (h, term, (0 == + timeout.rel_value) ? STOP_TIMEOUT : timeout, + term_callback, NULL); return; } break; case 1: - if ((end) || (restart)) + if (end || restart) { - GNUNET_ARM_stop_service (h, "arm", - (0 == - timeout.rel_value) ? STOP_TIMEOUT_ARM - : timeout, &confirm_cb, "arm"); - return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "End action\n"); + GNUNET_ARM_request_service_stop (h, "arm", (0 == + timeout.rel_value) ? STOP_TIMEOUT_ARM : timeout, + end_callback, NULL); + return; } break; case 2: if (start) { - GNUNET_ARM_start_service (h, "arm", - (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) | - (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR), - (0 == - timeout.rel_value) ? START_TIMEOUT : - timeout, &confirm_cb, "arm"); - return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start action\n"); + GNUNET_ARM_request_service_start (h, "arm", + (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) | + (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR), + (0 == timeout.rel_value) ? START_TIMEOUT: timeout, + start_callback, NULL); + return; } break; case 3: if (NULL != init) - { - GNUNET_ARM_start_service (h, init, - (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) | - (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR), - (0 == - timeout.rel_value) ? START_TIMEOUT : - timeout, &confirm_cb, init); - return; - } - break; - case 4: - if (restart) { - GNUNET_ARM_disconnect (h); - phase = 0; - end = 0; - start = 1; - restart = 0; - if (NULL == (h = GNUNET_ARM_connect (cfg, NULL))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Fatal error initializing ARM API.\n")); - ret = 1; - return; - } - GNUNET_SCHEDULER_add_now (&cps_loop, NULL); - return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initialization action\n"); + GNUNET_ARM_request_service_start (h, init, GNUNET_OS_INHERIT_STD_NONE, + (0 == timeout.rel_value) ? STOP_TIMEOUT : timeout, + init_callback, NULL); + return; } break; - case 5: + case 4: if (list) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Going to list all running services controlled by ARM.\n"); - if (NULL == h) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Fatal error initializing ARM API.\n")); - return; - } - GNUNET_ARM_list_running_services (h, - (0 == - timeout.rel_value) ? LIST_TIMEOUT : - timeout, &list_cb, NULL); + GNUNET_ARM_request_service_list (h, + (0 == timeout.rel_value) ? LIST_TIMEOUT : timeout, + list_callback, &list); return; } /* Fall through */ @@ -439,6 +499,89 @@ cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } +/** + * Function called when a service starts or stops. + * + * @param cls closure + * @param service service name + * @param status status of the service + */ +static void +srv_status (void *cls, struct GNUNET_ARM_MonitorHandle *arm, + const char *service, enum GNUNET_ARM_ServiceStatus status) +{ + const char *msg; + switch (status) + { + case GNUNET_ARM_SERVICE_MONITORING_STARTED: + msg = _("Began monitoring ARM for service status changes\n"); + break; + case GNUNET_ARM_SERVICE_STOPPED: + msg = _("Stopped %s.\n"); + break; + case GNUNET_ARM_SERVICE_STARTING: + msg = _("Starting %s...\n"); + break; + case GNUNET_ARM_SERVICE_STOPPING: + msg = _("Stopping %s...\n"); + break; + default: + msg = NULL; + break; + } + if (NULL != msg) + FPRINTF (stderr, msg, service); + else + FPRINTF (stderr, _("Unknown status %u for service %s.\n"), status, service); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got service %s status %u\n", service, status); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + char *armconfig; + + cfg = GNUNET_CONFIGURATION_dup (c); + config_file = cfgfile; + if (GNUNET_CONFIGURATION_get_value_string + (cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "PATHS", "SERVICEHOME"); + return; + } + if (NULL != cfgfile) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG", + &armconfig)) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG", + cfgfile); + } + else + GNUNET_free (armconfig); + } + h = GNUNET_ARM_alloc (cfg); + m = GNUNET_ARM_monitor_alloc (cfg); + GNUNET_ARM_connect (h, conn_status, NULL); + GNUNET_ARM_monitor (m, srv_status, NULL); + GNUNET_SCHEDULER_add_now (action_loop, NULL); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + shutdown_task, NULL); +} + + /** * The main function to obtain arm from gnunetd. * diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 7a99b5079..b1ba5af1b 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -29,6 +29,13 @@ #include "gnunet_protocols.h" #include "arm.h" +/** + * How many messages do we queue up at most for optional + * notifications to a client? (this can cause notifications + * about outgoing messages to be dropped). + */ +#define MAX_NOTIFY_QUEUE 1024 + /** * List of our services. */ @@ -124,6 +131,11 @@ struct ServiceList */ struct GNUNET_SERVER_Client *killing_client; + /** + * ID of the request that killed the service (for reporting back). + */ + uint64_t killing_client_request_id; + /** * Process structure pointer of the child. */ @@ -217,17 +229,154 @@ static int in_shutdown; */ static struct GNUNET_SERVER_Handle *server; +/** + * Context for notifications we need to send to our clients. + */ +static struct GNUNET_SERVER_NotificationContext *notifier; + #include "do_start_process.c" +/** + * Transmit a status result message. + * + * @param cls pointer to "unit16_t*" with message type + * @param size number of bytes available in buf + * @param buf where to copy the message, NULL on error + * @return number of bytes copied to buf + */ +static size_t +write_result (void *cls, size_t size, void *buf) +{ + struct GNUNET_ARM_ResultMessage *msg = cls; + size_t msize; + + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not send status result to client\n")); + GNUNET_free (msg); + return 0; /* error, not much we can do */ + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending status response %u to client\n", (unsigned int) msg->result); + msize = msg->arm_msg.header.size; + GNUNET_assert (size >= msize); + msg->arm_msg.header.size = htons (msg->arm_msg.header.size); + msg->arm_msg.header.type = htons (msg->arm_msg.header.type); + msg->result = htonl (msg->result); + msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id); + memcpy (buf, msg, msize); + GNUNET_free (msg); + return msize; +} + +/** + * Transmit the list of running services. + * + * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message + * @param size number of bytes available in buf + * @param buf where to copy the message, NULL on error + * @return number of bytes copied to buf + */ +static size_t +write_list_result (void *cls, size_t size, void *buf) +{ + struct GNUNET_ARM_ListResultMessage *msg = cls; + size_t rslt_size; + + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not send list result to client\n")); + GNUNET_free (msg); + return 0; /* error, not much we can do */ + } + + rslt_size = msg->arm_msg.header.size; + GNUNET_assert (size >= rslt_size); + msg->arm_msg.header.size = htons (msg->arm_msg.header.size); + msg->arm_msg.header.type = htons (msg->arm_msg.header.type); + msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id); + msg->count = htons (msg->count); + + memcpy (buf, msg, rslt_size); + GNUNET_free (msg); + return rslt_size; +} + + +/** + * Signal our client that we will start or stop the + * service. + * + * @param client who is being signalled + * @param name name of the service + * @param result message type to send + * @return NULL if it was not found + */ +static void +signal_result (struct GNUNET_SERVER_Client *client, const char *name, + uint64_t request_id, enum GNUNET_ARM_Result result) +{ + struct GNUNET_ARM_ResultMessage *msg; + size_t msize; + + msize = sizeof (struct GNUNET_ARM_ResultMessage); + msg = GNUNET_malloc (msize); + msg->arm_msg.header.size = msize; + msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_RESULT; + msg->result = result; + msg->arm_msg.request_id = request_id; + + GNUNET_SERVER_notify_transmit_ready (client, msize, + GNUNET_TIME_UNIT_FOREVER_REL, write_result, msg); +} + + +/** + * Tell all clients about status change of a service. + * + * @param name name of the service + * @param status message type to send + */ +static void +broadcast_status (const char *name, enum GNUNET_ARM_ServiceStatus status, + struct GNUNET_SERVER_Client *unicast) +{ + struct GNUNET_ARM_StatusMessage *msg; + size_t namelen; + + if (NULL == notifier) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending status %u of service `%s' to client\n", + (unsigned int) status, name); + namelen = strlen (name); + msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1); + msg->header.size = htons (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_STATUS); + msg->status = htonl ((uint32_t) (status)); + memcpy ((char *) &msg[1], name, namelen + 1); + + if (NULL == unicast) + GNUNET_SERVER_notification_context_broadcast (notifier, + (struct GNUNET_MessageHeader *) msg, GNUNET_YES); + else + GNUNET_SERVER_notification_context_unicast (notifier, unicast, + (const struct GNUNET_MessageHeader *) msg, GNUNET_NO); + GNUNET_free (msg); +} + /** * Actually start the process for the given service. * * @param sl identifies service to start + * @param client that asked to start the service (may be NULL) */ static void -start_process (struct ServiceList *sl) +start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint64_t request_id) { char *loprefix; char *options; @@ -342,11 +491,20 @@ start_process (struct ServiceList *sl) } GNUNET_free (binary); if (sl->proc == NULL) + { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"), sl->name); + if (client) + signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_START_FAILED); + } else + { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name); + broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL); + if (client) + signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_STARTING); + } /* clean up */ GNUNET_free (loprefix); GNUNET_free (options); @@ -354,104 +512,6 @@ start_process (struct ServiceList *sl) } -/** - * Transmit a status result message. - * - * @param cls pointer to "unit16_t*" with message type - * @param size number of bytes available in buf - * @param buf where to copy the message, NULL on error - * @return number of bytes copied to buf - */ -static size_t -write_result (void *cls, size_t size, void *buf) -{ - enum GNUNET_ARM_ProcessStatus *res = cls; - struct GNUNET_ARM_ResultMessage *msg; - - if (buf == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Could not send status result to client\n")); - return 0; /* error, not much we can do */ - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending status response %u to client\n", (unsigned int) *res); - GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage)); - msg = buf; - msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage)); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT); - msg->status = htonl ((uint32_t) (*res)); - GNUNET_free (res); - return sizeof (struct GNUNET_ARM_ResultMessage); -} - -/** - * Transmit the list of running services. - * - * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message - * @param size number of bytes available in buf - * @param buf where to copy the message, NULL on error - * @return number of bytes copied to buf - */ -static size_t -write_list_result (void *cls, size_t size, void *buf) -{ - struct GNUNET_ARM_ListResultMessage *msg = cls; - struct GNUNET_ARM_ListResultMessage *rslt; - size_t rslt_size; - - if (buf == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Could not send list result to client\n")); - return 0; /* error, not much we can do */ - } - - GNUNET_assert (size >= msg->header.size); - rslt = buf; - rslt->header.size = htons (msg->header.size); - rslt->header.type = htons (msg->header.type); - rslt->count = htons (msg->count); - - size_t list_size = msg->header.size - - sizeof (struct GNUNET_ARM_ListResultMessage); - memcpy (&rslt[1], &msg[1], list_size); - - rslt_size = msg->header.size; - GNUNET_free (msg); - return rslt_size; -} - - -/** - * Signal our client that we will start or stop the - * service. - * - * @param client who is being signalled - * @param name name of the service - * @param result message type to send - * @return NULL if it was not found - */ -static void -signal_result (struct GNUNET_SERVER_Client *client, const char *name, - enum GNUNET_ARM_ProcessStatus result) -{ - enum GNUNET_ARM_ProcessStatus *res; - - if (NULL == client) - return; - /* FIXME: this is not super-clean yet... */ - res = GNUNET_malloc (sizeof (enum GNUNET_ARM_ProcessStatus)); - *res = result; - GNUNET_SERVER_notify_transmit_ready (client, - sizeof (struct - GNUNET_ARM_ResultMessage), - GNUNET_TIME_UNIT_FOREVER_REL, - &write_result, res); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - /** * Find the process with the given service * name in the given list and return it. @@ -492,7 +552,7 @@ accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_assert (GNUNET_NO == in_shutdown); if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) return; - start_process (sl); + start_process (sl, NULL, 0); } @@ -619,10 +679,14 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client, const char *servicename; struct ServiceList *sl; uint16_t size; - - size = ntohs (message->size); - size -= sizeof (struct GNUNET_MessageHeader); - servicename = (const char *) &message[1]; + uint64_t request_id; + struct GNUNET_ARM_Message *amsg; + + amsg = (struct GNUNET_ARM_Message *) message; + request_id = GNUNET_ntohll (amsg->request_id); + size = ntohs (amsg->header.size); + size -= sizeof (struct GNUNET_ARM_Message); + servicename = (const char *) &amsg[1]; if ((size == 0) || (servicename[size - 1] != '\0')) { GNUNET_break (0); @@ -631,23 +695,40 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client, } if (GNUNET_YES == in_shutdown) { - signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); + signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } sl = find_service (servicename); if (NULL == sl) { - signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); + signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } sl->is_default = GNUNET_YES; if (sl->proc != NULL) { - signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_RUNNING); + signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_STARTED_ALREADY); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } - start_process (sl); - signal_result (client, servicename, GNUNET_ARM_PROCESS_STARTING); + start_process (sl, client, request_id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Start a shutdown sequence. + * + * @param cls closure (refers to service) + * @param tc task context + */ +static void +trigger_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering shutdown\n"); + GNUNET_SCHEDULER_shutdown (); } @@ -667,10 +748,14 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client, struct ServiceList *sl; const char *servicename; uint16_t size; - - size = ntohs (message->size); - size -= sizeof (struct GNUNET_MessageHeader); - servicename = (const char *) &message[1]; + uint64_t request_id; + struct GNUNET_ARM_Message *amsg; + + amsg = (struct GNUNET_ARM_Message *) message; + request_id = GNUNET_ntohll (amsg->request_id); + size = ntohs (amsg->header.size); + size -= sizeof (struct GNUNET_ARM_Message); + servicename = (const char *) &amsg[1]; if ((size == 0) || (servicename[size - 1] != '\0')) { GNUNET_break (0); @@ -679,40 +764,58 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client, } GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Preparing to stop `%s'\n"), servicename); + if (0 == strcasecmp (servicename, "arm")) + { + broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL); + signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_STOPPING); + GNUNET_SERVER_client_persist_ (client); + GNUNET_SCHEDULER_add_now (trigger_shutdown, NULL); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } sl = find_service (servicename); if (sl == NULL) { - signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); + signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } sl->is_default = GNUNET_NO; if (GNUNET_YES == in_shutdown) { /* shutdown in progress */ - signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); + signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } if (sl->killing_client != NULL) { /* killing already in progress */ - signal_result (client, servicename, - GNUNET_ARM_PROCESS_ALREADY_STOPPING); + signal_result (client, servicename, request_id, + GNUNET_ARM_RESULT_IS_STOPPING_ALREADY); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } if (sl->proc == NULL) { /* process is down */ - signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN); + signal_result (client, servicename, request_id, + GNUNET_ARM_RESULT_IS_STOPPED_ALREADY); + GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kill signal to service `%s', waiting for process to die.\n", servicename); + broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL); + /* no signal_start - only when it's STOPPED */ sl->killed_at = GNUNET_TIME_absolute_get (); if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM)) GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); sl->killing_client = client; + sl->killing_client_request_id = request_id; GNUNET_SERVER_client_keep (client); + GNUNET_SERVER_receive_done (client, GNUNET_YES); } /** @@ -727,6 +830,7 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) { struct GNUNET_ARM_ListResultMessage *msg; + struct GNUNET_ARM_Message *request; size_t string_list_size; size_t total_size; struct ServiceList *sl; @@ -735,6 +839,7 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, if (NULL == client) return; + request = (struct GNUNET_ARM_Message *) message; count = 0; string_list_size = 0; /* first count the running processes get their name's size */ @@ -748,11 +853,13 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, count++; } } + total_size = sizeof (struct GNUNET_ARM_ListResultMessage) + string_list_size; msg = GNUNET_malloc (total_size); - msg->header.size = total_size; - msg->header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT; + msg->arm_msg.header.size = total_size; + msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT; + msg->arm_msg.request_id = GNUNET_ntohll (request->request_id); msg->count = count; char *pos = (char *)&msg[1]; @@ -767,9 +874,9 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, } GNUNET_SERVER_notify_transmit_ready (client, - msg->header.size, + total_size, GNUNET_TIME_UNIT_FOREVER_REL, - &write_list_result, msg); + write_list_result, msg); GNUNET_SERVER_receive_done (client, GNUNET_OK); } @@ -780,6 +887,12 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client, static void do_shutdown () { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last shutdown phase\n"); + if (NULL != notifier) + { + GNUNET_SERVER_notification_context_destroy (notifier); + notifier = NULL; + } if (NULL != server) { GNUNET_SERVER_destroy (server); @@ -792,6 +905,15 @@ do_shutdown () } } +unsigned int +list_count (struct ServiceList *running_head) +{ + struct ServiceList *i; + unsigned int res = 0; + for (res = 0, i = running_head; i; i = i->next, res++) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", i->name); + return res; +} /** * Task run for shutdown. @@ -806,6 +928,7 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct ServiceList *nxt; struct ServiceListeningInfo *sli; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First shutdown phase\n"); if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) { GNUNET_SCHEDULER_cancel (child_restart_task); @@ -851,6 +974,9 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /* finally, should all service processes be already gone, terminate for real */ if (running_head == NULL) do_shutdown (); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delaying shutdown, have %u childs still running\n", list_count (running_head)); } @@ -890,7 +1016,7 @@ delayed_restart_task (void *cls, /* process should run by default, start immediately */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Restarting service `%s'.\n"), sl->name); - start_process (sl); + start_process (sl, NULL, 0); } else { @@ -1004,74 +1130,63 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } GNUNET_OS_process_destroy (pos->proc); pos->proc = NULL; + broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL); if (NULL != pos->killing_client) - { - signal_result (pos->killing_client, pos->name, - GNUNET_ARM_PROCESS_DOWN); - GNUNET_SERVER_client_drop (pos->killing_client); - pos->killing_client = NULL; - /* process can still be re-started on-demand, ensure it is re-started if there is demand */ - for (sli = pos->listen_head; NULL != sli; sli = sli->next) - { - GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task); - sli->accept_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - sli->listen_socket, - &accept_connection, sli); - } - } + { + signal_result (pos->killing_client, pos->name, + pos->killing_client_request_id, GNUNET_ARM_RESULT_STOPPED); + GNUNET_SERVER_client_drop (pos->killing_client); + pos->killing_client = NULL; + pos->killing_client_request_id = 0; + /* process can still be re-started on-demand, ensure it is re-started if there is demand */ + for (sli = pos->listen_head; NULL != sli; sli = sli->next) + { + GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task); + sli->accept_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + sli->listen_socket, &accept_connection, sli); + } + } if (GNUNET_YES != in_shutdown) - { - if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0)) - { - /* process terminated normally, allow restart at any time */ - pos->restart_at.abs_value = 0; - } - else - { - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Service `%s' terminated with status %s/%d, will restart in %s\n"), - pos->name, statstr, statcode, - GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES)); - /* schedule restart */ - pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); - pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff); - } - if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) - GNUNET_SCHEDULER_cancel (child_restart_task); - child_restart_task = - GNUNET_SCHEDULER_add_with_priority - (GNUNET_SCHEDULER_PRIORITY_IDLE, - &delayed_restart_task, NULL); + { + if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0)) + { + /* process terminated normally, allow restart at any time */ + pos->restart_at.abs_value = 0; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' terminated normally, will restart at any time\n"), + pos->name); } + else + { + if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' terminated with status %s/%d, will restart in %s\n"), + pos->name, statstr, statcode, + GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES)); + /* schedule restart */ + pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); + pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff); + } + if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) + GNUNET_SCHEDULER_cancel (child_restart_task); + child_restart_task = GNUNET_SCHEDULER_add_with_priority ( + GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL); + } else - { - free_service (pos); - } + { + free_service (pos); + } } - child_death_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, &maint_child_death, NULL); + child_death_task = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL); if ((NULL == running_head) && (GNUNET_YES == in_shutdown)) do_shutdown (); -} - + else if (GNUNET_YES == in_shutdown) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delaying shutdown after child's death, still have %u children\n", + list_count (running_head)); -/** - * Handler for SHUTDOWN message. - * - * @param cls closure (refers to service) - * @param client identification of the client - * @param message the actual message - */ -static void -handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_SCHEDULER_shutdown (); - GNUNET_SERVER_client_persist_ (client); } @@ -1173,6 +1288,43 @@ setup_service (void *cls, const char *section) } +/** + * A client connected, add it to the notification context. + * + * @param cls closure + * @param client identification of the client + */ +static void +handle_client_connecting (void *cls, struct GNUNET_SERVER_Client *client) +{ + /* All clients are considered to be of the "monitor" kind + * (that is, they don't affect ARM shutdown). + */ + if (NULL != client) + GNUNET_SERVER_client_mark_monitor (client); +} + +/** + * Handle MONITOR-message. + * + * @param cls closure (always NULL) + * @param client identification of the client + * @param message the actual message + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static void +handle_monitor (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + /* Removal is handled by the server implementation, internally. */ + if ((NULL != client) && (NULL != notifier)) + { + GNUNET_SERVER_notification_context_add (notifier, client); + broadcast_status ("arm", GNUNET_ARM_SERVICE_MONITORING_STARTED, client); + } +} + /** * Process arm requests. * @@ -1187,8 +1339,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv, static const struct GNUNET_SERVER_MessageHandler handlers[] = { {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0}, {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0}, - {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN, - sizeof (struct GNUNET_MessageHeader)}, + {&handle_monitor, NULL, GNUNET_MESSAGE_TYPE_ARM_MONITOR, 0}, {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST, sizeof (struct GNUNET_MessageHeader)}, {NULL, NULL, 0, 0} @@ -1241,7 +1392,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv, continue; } sl->is_default = GNUNET_YES; - start_process (sl); + start_process (sl, NULL, 0); } } GNUNET_free (defaultservices); @@ -1253,6 +1404,9 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv, ("No default services configured, GNUnet will not really start right now.\n")); } + notifier = + GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE); + GNUNET_SERVER_connect_notify (server, handle_client_connecting, NULL); /* process client requests */ GNUNET_SERVER_add_handlers (server, handlers); } diff --git a/src/arm/mockup-service.c b/src/arm/mockup-service.c index d9896997b..2e021627e 100644 --- a/src/arm/mockup-service.c +++ b/src/arm/mockup-service.c @@ -29,21 +29,27 @@ #include "gnunet_time_lib.h" +static int special_ret = 0; + /** - * Handler for SHUTDOWN message. + * Handler for STOP message. * * @param cls closure (refers to service) * @param client identification of the client * @param message the actual message */ static void -handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client, +handle_stop (void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Initiating shutdown as requested by client.\n")); GNUNET_SERVER_client_persist_ (client); GNUNET_SCHEDULER_shutdown (); + /* ARM won't exponentially increase restart delay if we + * terminate normally. This changes the return code. + */ + special_ret = 1; } @@ -52,7 +58,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, const struct GNUNET_CONFIGURATION_Handle *cfg) { static const struct GNUNET_SERVER_MessageHandler handlers[] = { - {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN, + {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, sizeof (struct GNUNET_MessageHeader)}, {NULL, NULL, 0, 0} }; @@ -70,5 +76,7 @@ main (int argc, char *const *argv) (GNUNET_OK == GNUNET_SERVICE_run (argc, argv, "do-nothing", GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1; + if (0 != special_ret) + return special_ret; return ret; } diff --git a/src/arm/test_arm_api.c b/src/arm/test_arm_api.c index 366d4e500..999bd77e6 100644 --- a/src/arm/test_arm_api.c +++ b/src/arm/test_arm_api.c @@ -42,77 +42,144 @@ static struct GNUNET_ARM_Handle *arm; static int ok = 1; +static int phase = 0; + static void -arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success) +arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) { - GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN); - if (success != GNUNET_ARM_PROCESS_DOWN) - ok = 3; - else if (ok == 1) - ok = 0; + /* (6), a stop request should be sent to ARM successfully */ + /* ARM should report that it is stopping */ + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING); + GNUNET_break (phase == 6); + phase++; + FPRINTF (stderr, "Sent 'STOP' request for arm to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully"); } - static void -arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus success) +resolver_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) { - GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN); + /* (5), a stop request should be sent to ARM successfully. + * ARM should report that resolver is stopped. + */ + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED); + GNUNET_break (phase == 5); + FPRINTF (stderr, "Sent 'STOP' request for resolver to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully"); + phase++; #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL); +#else + arm_stop_cb (NULL, GNUNET_ARM_STATUS_SENT_OK, "arm", GNUNET_ARM_SERVICE_STOPPING); + arm_conn (NULL, GNUNET_NO, GNUNET_NO); #endif } - static void dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen) { if (addr == NULL) { + /* (4), resolver should finish resolving localhost */ + GNUNET_break (phase == 4); + phase++; + FPRINTF (stderr, "%s", "Finished resolving localhost\n"); if (ok != 0) - { - GNUNET_break (0); - ok = 2; - } - GNUNET_ARM_stop_service (arm, "resolver", TIMEOUT, &arm_notify_stop, - NULL); + ok = 2; + GNUNET_ARM_request_service_stop (arm, "resolver", TIMEOUT, resolver_stop_cb, NULL); return; } + /* (3), resolver should resolve localhost */ + GNUNET_break (phase == 3); + FPRINTF (stderr, "%s", "Resolved localhost\n"); + phase++; GNUNET_break (addr != NULL); ok = 0; } - static void -resolver_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) +resolver_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) { - if (success != GNUNET_ARM_PROCESS_STARTING) - { - GNUNET_break (0); - ok = 2; -#if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); -#endif - return; - } + /* (2), the start request for resolver should be sent successfully + * ARM should report that resolver service is starting. + */ + GNUNET_assert (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (phase == 2); + GNUNET_break (result == GNUNET_ARM_RESULT_STARTING); + FPRINTF (stderr, "Sent 'START' request for resolver to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully"); + phase++; GNUNET_RESOLVER_ip_get ("localhost", AF_INET, TIMEOUT, &dns_notify, NULL); } - static void -arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) +trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (success != GNUNET_ARM_PROCESS_STARTING) - { - GNUNET_break (0); - ok = 2; + GNUNET_ARM_disconnect ((struct GNUNET_ARM_Handle *) cls); +} + + +void +arm_conn (void *cls, struct GNUNET_ARM_Handle *arm, unsigned char connected, unsigned char error) +{ + if (GNUNET_YES == error) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Fatal error initializing ARM API.\n")); + GNUNET_SCHEDULER_shutdown (); + GNUNET_assert (0); + return; + } + if (connected) + { + /* (1), arm connection should be established */ + FPRINTF (stderr, "%s", "Connected to ARM\n"); + GNUNET_break (phase == 1); + phase++; + GNUNET_ARM_request_service_start (arm, "resolver", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, resolver_start_cb, NULL); + } + else + { + /* (7), ARM should stop (we disconnect from it) */ + FPRINTF (stderr, "%s", "Disconnected from ARM\n"); + GNUNET_break (phase == 7); + if (phase != 7) + ok = 3; + else if (ok == 1) + ok = 0; + GNUNET_SCHEDULER_add_now (trigger_disconnect, arm); + } +} + +void +srv_status (void *cls, const char *service, enum GNUNET_ARM_ServiceStatus status) +{ + FPRINTF (stderr, "Service %s is %u\n", service, status); + switch (phase) + { + default: + FPRINTF (stderr, "Unexpectedly got status %u for service %s\n", service); + GNUNET_break (0); + ok = 2; #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, NULL, NULL); #endif - } - GNUNET_ARM_start_service (arm, "resolver", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, &resolver_notify, - NULL); + } } +static void +arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) +{ + /* (0) The request should be "sent" successfully + * ("sent", because it isn't going anywhere, ARM API starts ARM service + * by itself). + * ARM API should report that ARM service is starting. + */ + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (phase == 0); + FPRINTF (stderr, "Sent 'START' request for arm to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully"); + GNUNET_break (result == GNUNET_ARM_RESULT_STARTING); + phase++; +} static void task (void *cls, char *const *args, const char *cfgfile, @@ -133,11 +200,13 @@ task (void *cls, char *const *args, const char *cfgfile, else GNUNET_free (armconfig); } - arm = GNUNET_ARM_connect (cfg, NULL); + arm = GNUNET_ARM_alloc (cfg); + GNUNET_ARM_connect (arm, arm_conn, NULL); #if START_ARM - GNUNET_ARM_start_service (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, &arm_notify, NULL); + GNUNET_ARM_request_service_start (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, arm_start_cb, NULL); #else - arm_notify (NULL, GNUNET_YES); + arm_start_cb (NULL, arm, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_STARTING); + arm_conn (NULL, GNUNET_YES, GNUNET_NO); #endif } diff --git a/src/arm/test_exponential_backoff.c b/src/arm/test_exponential_backoff.c index 3395139ea..8a0dba117 100644 --- a/src/arm/test_exponential_backoff.c +++ b/src/arm/test_exponential_backoff.c @@ -43,14 +43,20 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg; static struct GNUNET_ARM_Handle *arm; +static struct GNUNET_ARM_MonitorHandle *mon; + static int ok = 1; +static int phase = 0; + static int trialCount; static struct GNUNET_TIME_Absolute startedWaitingAt; struct GNUNET_TIME_Relative waitedFor; +struct GNUNET_TIME_Relative waitedFor_prev; + #if LOG_BACKOFF static FILE *killLogFilePtr; @@ -97,11 +103,8 @@ struct ShutdownContext /** * Handler receiving response to service shutdown requests. - * First call with NULL: service misbehaving, or something. - * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN: - * - service will shutdown - * Second call with NULL: - * - service has now really shut down. + * We expect it to be called with NULL, since the service that + * we are shutting down will just die without replying. * * @param cls closure * @param msg NULL, indicating socket closure. @@ -111,7 +114,7 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) { struct ShutdownContext *shutdown_ctx = cls; - if (msg == NULL) + if (NULL == msg) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); if (shutdown_ctx->cont != NULL) @@ -122,29 +125,7 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) GNUNET_free (shutdown_ctx); return; } - GNUNET_assert (ntohs (msg->size) == - sizeof (struct GNUNET_MessageHeader)); - switch (ntohs (msg->type)) - { - case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received confirmation for service shutdown.\n"); - shutdown_ctx->confirmed = GNUNET_YES; - GNUNET_CLIENT_receive (shutdown_ctx->sock, - &service_shutdown_handler, shutdown_ctx, - GNUNET_TIME_UNIT_FOREVER_REL); - break; - default: /* Fall through */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Service shutdown refused!\n"); - if (shutdown_ctx->cont != NULL) - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_YES); - - GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock); - GNUNET_free (shutdown_ctx); - break; - } + GNUNET_assert (0); } @@ -183,25 +164,27 @@ write_shutdown (void *cls, size_t size, void *buf) struct ShutdownContext *shutdown_ctx = cls; if (size < sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Failed to transmit shutdown request to client.\n")); - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock); - GNUNET_free (shutdown_ctx); - return 0; /* client disconnected */ - } + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Failed to transmit shutdown request to client.\n")); + FPRINTF (stderr, "%s", "Failed to send a shutdown request\n"); + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); + 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); + 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->type = htons (GNUNET_MESSAGE_TYPE_ARM_STOP); msg->size = htons (sizeof (struct GNUNET_MessageHeader)); - return sizeof (struct GNUNET_MessageHeader); + strcpy ((char *) &msg[1], "do-nothing"); + FPRINTF (stderr, "%s", "Sent a shutdown request\n"); + return sizeof (struct GNUNET_MessageHeader) + strlen ("do-nothing") + 1; } @@ -219,7 +202,7 @@ write_shutdown (void *cls, size_t size, void *buf) * */ static void -arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, +do_nothing_service_shutdown (struct GNUNET_CLIENT_Connection *sock, struct GNUNET_TIME_Relative timeout, GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) { @@ -231,125 +214,132 @@ arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, shutdown_ctx->sock = sock; shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); GNUNET_CLIENT_notify_transmit_ready (sock, - sizeof (struct GNUNET_MessageHeader), + sizeof (struct GNUNET_MessageHeader) + strlen ("do-nothing") + 1, timeout, GNUNET_NO, &write_shutdown, shutdown_ctx); } - -static void -arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus status) -{ - GNUNET_assert ( (status == GNUNET_ARM_PROCESS_DOWN) || - (status == GNUNET_ARM_PROCESS_ALREADY_DOWN) ); -#if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, NULL, NULL); -#endif -} - - static void kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc); - static void -do_nothing_notify (void *cls, enum GNUNET_ARM_ProcessStatus status) -{ - GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING); - ok = 1; - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &kill_task, NULL); -} - -static void -arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus status) +shutdown_cont (void *cls, int reason) { - GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING); - GNUNET_ARM_start_service (arm, "do-nothing", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, TIMEOUT, &do_nothing_notify, - NULL); + if (GNUNET_NO != reason) + { + /* Re-try shutdown */ + FPRINTF (stderr, "%s", "do-nothing didn't die, trying again\n"); + GNUNET_SCHEDULER_add_now (kill_task, NULL); + return; + } + startedWaitingAt = GNUNET_TIME_absolute_get (); + FPRINTF (stderr, "%s", "do-nothing is dead, starting the countdown\n"); } - static void -kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc); - - -static void -do_nothing_restarted_notify_task (void *cls, - const struct GNUNET_SCHEDULER_TaskContext - *tc) +kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) { - static char a; - - trialCount++; + static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL; + if (NULL != cbData) + { + waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt); + FPRINTF (stderr, "Waited for: %llu ms\n", waitedFor.rel_value); #if LOG_BACKOFF - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - { - FPRINTF (killLogFilePtr, "%d.Reason is shutdown!\n", trialCount); - } - else if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0) - { - FPRINTF (killLogFilePtr, "%d.Reason is timeout!\n", trialCount); - } - else if ((tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE) != 0) - { - FPRINTF (killLogFilePtr, "%d.Service is running!\n", trialCount); - } + FPRINTF (killLogFilePtr, "Waited for: %llu ms\n", + (unsigned long long) waitedFor.rel_value); #endif - GNUNET_SCHEDULER_add_now (&kill_task, &a); + } + else + { + waitedFor.rel_value = 0; + } + /* Connect to the doNothing task */ + doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg); + GNUNET_assert (doNothingConnection != NULL); + if (trialCount == 12) + waitedFor_prev = waitedFor; + else if (trialCount == 13) + { + GNUNET_CLIENT_disconnect (doNothingConnection); + GNUNET_ARM_request_service_stop (arm, "do-nothing", TIMEOUT, NULL, NULL); + if (waitedFor_prev.rel_value >= waitedFor.rel_value) + ok = 9; + else + ok = 0; + trialCount += 1; + return; + } + trialCount += 1; + /* Use the created connection to kill the doNothingTask */ + do_nothing_service_shutdown (doNothingConnection, + TIMEOUT, &shutdown_cont, NULL); } - static void -do_test (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) +trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - GNUNET_CLIENT_service_test ("do-nothing", cfg, TIMEOUT, - &do_nothing_restarted_notify_task, NULL); + GNUNET_ARM_disconnect (arm); + GNUNET_ARM_monitor_disconnect (mon); } static void -shutdown_cont (void *cls, int reason) +arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) { - trialCount++; - startedWaitingAt = GNUNET_TIME_absolute_get (); - GNUNET_SCHEDULER_add_delayed (waitedFor, &do_test, NULL); + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING); + FPRINTF (stderr, "%s", "ARM service stopped\n"); + GNUNET_SCHEDULER_add_now (trigger_disconnect, NULL); } - -static void -kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) +void +srv_status (void *cls, struct GNUNET_ARM_MonitorHandle *mon, const char *service, enum GNUNET_ARM_ServiceStatus status) { - static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL; - - if (NULL != cbData) - { - waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt); - -#if LOG_BACKOFF - FPRINTF (killLogFilePtr, "Waited for: %llu ms\n", - (unsigned long long) waitedFor.rel_value); -#endif - } - else + FPRINTF (stderr, "Service %s is %u, phase %u\n", service, status, phase); + if (status == GNUNET_ARM_SERVICE_MONITORING_STARTED) + { + phase++; + GNUNET_ARM_request_service_start (arm, "do-nothing", + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, TIMEOUT, NULL, NULL); + return; + } + if (phase == 1) + { + GNUNET_break (status == GNUNET_ARM_SERVICE_STARTING); + GNUNET_break (0 == strcasecmp (service, "do-nothing")); + GNUNET_break (phase == 1); + FPRINTF (stderr, "%s", "do-nothing is starting\n"); + phase++; + ok = 1; + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &kill_task, NULL); + } + else if ((phase == 2) && (strcasecmp ("do-nothing", service) == 0)) + { + /* We passively monitor ARM for status updates. ARM should tell us + * when do-nothing dies (no need to run a service upness test ourselves). + */ + if (status == GNUNET_ARM_SERVICE_STARTING) { - waitedFor.rel_value = 0; + FPRINTF (stderr, "%s", "do-nothing is starting\n"); + GNUNET_SCHEDULER_add_now (kill_task, &ok); } - /* Connect to the doNothing task */ - doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg); - GNUNET_assert (doNothingConnection != NULL); - if (trialCount == 12) + else if ((status == GNUNET_ARM_SERVICE_STOPPED) && (trialCount == 14)) { - GNUNET_CLIENT_disconnect (doNothingConnection); - GNUNET_ARM_stop_service (arm, "do-nothing", TIMEOUT, &arm_notify_stop, - NULL); - ok = 0; - return; + phase++; + GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL); } - /* Use the created connection to kill the doNothingTask */ - arm_service_shutdown (doNothingConnection, TIMEOUT, &shutdown_cont, NULL); + } } +static void +arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) +{ + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (result == GNUNET_ARM_RESULT_STARTING); + GNUNET_break (phase == 0); + FPRINTF (stderr, "Sent 'START' request for arm to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully"); +} static void task (void *cls, char *const *args, const char *cfgfile, @@ -359,9 +349,8 @@ task (void *cls, char *const *args, const char *cfgfile, cfg = c; if (NULL != cfgfile) { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG", - &armconfig)) + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", + "CONFIG", &armconfig)) { GNUNET_CONFIGURATION_set_value_string ((struct GNUNET_CONFIGURATION_Handle *) cfg, "arm", "CONFIG", @@ -371,16 +360,18 @@ task (void *cls, char *const *args, const char *cfgfile, GNUNET_free (armconfig); } - arm = GNUNET_ARM_connect (cfg, NULL); + arm = GNUNET_ARM_alloc (cfg); + GNUNET_ARM_connect (arm, NULL, NULL); + mon = GNUNET_ARM_monitor_alloc (cfg); + GNUNET_ARM_monitor (mon, srv_status, NULL); #if START_ARM - GNUNET_ARM_start_service (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, GNUNET_TIME_UNIT_ZERO, &arm_notify, - NULL); + GNUNET_ARM_request_service_start (arm, "arm", + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, GNUNET_TIME_UNIT_ZERO, arm_start_cb, NULL); #else - arm_do_nothing (NULL, GNUNET_YES); + arm_start_cb (NULL, arm, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_SERVICE_STARTING); #endif } - static int check () { diff --git a/src/arm/test_gnunet_service_arm.c b/src/arm/test_gnunet_service_arm.c index 9e9286ac1..bd7fe5fa9 100644 --- a/src/arm/test_gnunet_service_arm.c +++ b/src/arm/test_gnunet_service_arm.c @@ -42,23 +42,22 @@ static int ret = 1; static struct GNUNET_ARM_Handle *arm; - static void -arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success) +trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (success != GNUNET_ARM_PROCESS_DOWN) - { - GNUNET_break (0); - ret = 4; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM stopped\n"); - } GNUNET_ARM_disconnect (arm); arm = NULL; } +static void +arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) +{ + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING); + if (result != GNUNET_ARM_RESULT_STOPPING) + ret = 4; + GNUNET_SCHEDULER_add_now (trigger_disconnect, NULL); +} static void hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen) @@ -66,43 +65,37 @@ hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen) if ((ret == 0) || (ret == 4)) return; if (NULL == addr) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n"); - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); - ret = 3; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resolved hostname, now stopping ARM\n"); - ret = 0; - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n"); + ret = 3; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolved hostname, now stopping ARM\n"); + ret = 0; + } + GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL); } - static void -arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) +arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result) { - if (success != GNUNET_ARM_PROCESS_STARTING) - { - GNUNET_break (0); - ret = 1; - return; - } + GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK); + GNUNET_break (result == GNUNET_ARM_RESULT_STARTING); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to resolve our own hostname!\n"); + "Trying to resolve our own hostname!\n"); /* connect to the resolver service */ - if (NULL == - GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, TIMEOUT, - &hostNameResolveCB, NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable initiate connection to resolver service\n"); - ret = 2; - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); - } + if (NULL == GNUNET_RESOLVER_hostname_resolve ( + AF_UNSPEC, TIMEOUT, &hostNameResolveCB, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable initiate connection to resolver service\n"); + ret = 2; + GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL); + } } - static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c) @@ -122,10 +115,10 @@ run (void *cls, char *const *args, const char *cfgfile, else GNUNET_free (armconfig); } - arm = GNUNET_ARM_connect (c, NULL); - GNUNET_ARM_start_service (arm, "arm", - GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, - &arm_notify, NULL); + arm = GNUNET_ARM_alloc (c); + GNUNET_ARM_connect (arm, NULL, NULL); + GNUNET_ARM_request_service_start (arm, "arm", + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, arm_start_cb, NULL); } diff --git a/src/include/gnunet_arm_service.h b/src/include/gnunet_arm_service.h index 0aa916bd4..be46831a4 100644 --- a/src/include/gnunet_arm_service.h +++ b/src/include/gnunet_arm_service.h @@ -43,112 +43,208 @@ extern "C" /** * Version of the arm API. */ -#define GNUNET_ARM_VERSION 0x00000001 +#define GNUNET_ARM_VERSION 0x00000002 /** - * Values characterizing GNUnet process states. + * Statuses of the requests that client can send to ARM. */ -enum GNUNET_ARM_ProcessStatus +enum GNUNET_ARM_RequestStatus { /** - * Service name is unknown to ARM. + * Message was sent successfully. */ - GNUNET_ARM_PROCESS_UNKNOWN = -1, + GNUNET_ARM_REQUEST_SENT_OK = 0, /** - * Service is now down (due to client request). + * Misconfiguration (can't connect to the ARM service). */ - GNUNET_ARM_PROCESS_DOWN = 0, + GNUNET_ARM_REQUEST_CONFIGURATION_ERROR = 1, /** - * Service is already running. + * We disconnected from ARM, and request was not sent. */ - GNUNET_ARM_PROCESS_ALREADY_RUNNING = 1, + GNUNET_ARM_REQUEST_DISCONNECTED = 2, /** - * Service is currently being started (due to client request). + * ARM API is busy (probably trying to connect to ARM), + * and request was not sent. Try again later. */ - GNUNET_ARM_PROCESS_STARTING = 2, - + GNUNET_ARM_REQUEST_BUSY = 3, + /** - * Service is already being stopped by some other client. + * It was discovered that the request would be too long to fit in a message, + * and thus it was not sent. */ - GNUNET_ARM_PROCESS_ALREADY_STOPPING = 3, + GNUNET_ARM_REQUEST_TOO_LONG = 4, /** - * Service is already down (no action taken) + * Request time ran out before we had a chance to send it. */ - GNUNET_ARM_PROCESS_ALREADY_DOWN = 4, + GNUNET_ARM_REQUEST_TIMEOUT = 5 + +}; + +/** + * Statuses of services. + */ +enum GNUNET_ARM_ServiceStatus +{ /** - * ARM is currently being shut down (no more process starts) + * Dummy message. */ - GNUNET_ARM_PROCESS_SHUTDOWN = 5, + GNUNET_ARM_SERVICE_MONITORING_STARTED = 0, /** - * Error in communication with ARM + * Service was stopped. */ - GNUNET_ARM_PROCESS_COMMUNICATION_ERROR = 6, + GNUNET_ARM_SERVICE_STOPPED = 1, /** - * Timeout in communication with ARM + * Service starting was initiated */ - GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT = 7, + GNUNET_ARM_SERVICE_STARTING = 2, /** - * Failure to perform operation + * Service stopping was initiated */ - GNUNET_ARM_PROCESS_FAILURE = 8 + GNUNET_ARM_SERVICE_STOPPING = 3 }; +/** + * Replies to ARM requests + */ +enum GNUNET_ARM_Result +{ + /** + * Service was stopped (never sent for ARM itself). + */ + GNUNET_ARM_RESULT_STOPPED = 0, + + /** + * ARM stopping was initiated (there's no "stopped" for ARM itself). + */ + GNUNET_ARM_RESULT_STOPPING = 1, + + /** + * Service starting was initiated + */ + GNUNET_ARM_RESULT_STARTING = 2, + + /** + * Asked to start it, but it's already starting. + */ + GNUNET_ARM_RESULT_IS_STARTING_ALREADY = 3, + + /** + * Asked to stop it, but it's already stopping. + */ + GNUNET_ARM_RESULT_IS_STOPPING_ALREADY = 4, + + /** + * Asked to start it, but it's already started. + */ + GNUNET_ARM_RESULT_IS_STARTED_ALREADY = 5, + + /** + * Asked to stop it, but it's already stopped. + */ + GNUNET_ARM_RESULT_IS_STOPPED_ALREADY = 6, + + /** + * Asked to start or stop a service, but it's not known. + */ + GNUNET_ARM_RESULT_IS_NOT_KNOWN = 7, + + /** + * Tried to start a service, but that failed for some reason. + */ + GNUNET_ARM_RESULT_START_FAILED = 8, + + /** + * Asked to start something, but ARM is shutting down and can't comply. + */ + GNUNET_ARM_RESULT_IN_SHUTDOWN = 9 +}; + + +/** + * Handle for interacting with ARM. + */ +struct GNUNET_ARM_Handle; + /** - * Callback function invoked when operation is complete. + * Function called whenever we connect to or disconnect from ARM. * * @param cls closure - * @param result outcome of the operation + * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected + * @param error GNUNET_YES if we encountered a permanent error, and there + * will be no re-connection. */ -typedef void (*GNUNET_ARM_Callback) (void *cls, - enum GNUNET_ARM_ProcessStatus result); +typedef void (*GNUNET_ARM_ConnectionStatusCallback) (void *cls, struct GNUNET_ARM_Handle *arm, unsigned char connected, unsigned char error); + /** - * Callback function invoked when list operation is complete. + * Function called in response to a start/stop request. + * Will be called when request was not sent successfully, + * or when a reply comes. If the request was not sent successfully, + * 'rs' will indicate that, and 'service' and 'result' will be undefined. * * @param cls closure - * @param result outcome of the operation (GNUNET_YES if successful) - * @param count number of strings in the list - * @param list list of running services + * @param arm handle to the arm connection + * @param rs status of the request + * @param service service name + * @param result result of the operation */ -typedef void (*GNUNET_ARM_List_Callback) (void *cls, - int result, - unsigned int count, - const char *const *list); +typedef void (*GNUNET_ARM_ResultCallback) (void *cls, struct GNUNET_ARM_Handle *arm, enum GNUNET_ARM_RequestStatus rs, const char *service, enum GNUNET_ARM_Result result); /** - * Handle for interacting with ARM. + * Callback function invoked when list operation is complete. + * Will be called when request was not sent successfully, + * or when a reply comes. If the request was not sent successfully, + * 'rs' will indicate that, and 'count' and 'list' will be undefined. + * + * @param cls closure + * @param arm handle to the arm connection + * @param rs status of the request + * @param count number of strings in the list + * @param list list of running services */ -struct GNUNET_ARM_Handle; +typedef void (*GNUNET_ARM_ServiceListCallback) (void *cls, struct GNUNET_ARM_Handle *arm, enum GNUNET_ARM_RequestStatus rs, unsigned int count, const char *const*list); /** * Setup a context for communicating with ARM. Note that this * can be done even if the ARM service is not yet running. + * Never fails. * * @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 service service that *this* process is implementing/providing, can be NULL - * @return context to use for further ARM operations, NULL on error + * @return context to use for further ARM operations */ struct GNUNET_ARM_Handle * -GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *service); +GNUNET_ARM_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Start connecting to the ARM service using the context. + * @param conn_status called when we (dis)connect from/to ARM. + * It's also called on connection errors. + * @param cls closure for conn_status + * + * @param h ARM handle + */ +void +GNUNET_ARM_connect (struct GNUNET_ARM_Handle *h, + GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls); /** - * Disconnect from the ARM service. + * Disconnect from the ARM service and destroy the handle. + * Don't call this from inside an ARM callback! * * @param h the handle that was being used */ @@ -157,60 +253,107 @@ GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h); /** - * Start a service. Note that this function merely asks ARM to start - * the service and that ARM merely confirms that it forked the - * respective process. The specified callback may thus return before - * the service has started to listen on the server socket and it may - * also be that the service has crashed in the meantime. Clients - * should repeatedly try to connect to the service at the respective - * port (with some delays in between) before assuming that the service - * actually failed to start. Note that if an error is returned to the - * callback, clients obviously should not bother with trying to - * contact the service. + * Request a list of running services. * * @param h handle to ARM - * @param service_name name of the service - * @param std_inheritance flags controlling std descriptors inheritance * @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 cont callback to invoke after request is sent or is not sent + * @param cont_cls closure for callback */ void -GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char *service_name, - enum GNUNET_OS_InheritStdioFlags std_inheritance, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_Callback cb, void *cb_cls); +GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_ServiceListCallback cont, void *cont_cls); /** - * Stop a service. Note that the callback is invoked as soon - * as ARM confirms that it will ask the service to terminate. - * The actual termination may still take some time. + * Request a service to be stopped. + * Stopping arm itself will not invalidate its handle, and + * ARM API will try to restore connection to the ARM service, + * even if ARM connection was lost because you asked for ARM to be stopped. + * Call GNUNET_ARM_disconnect () to free the handle and prevent + * further connection attempts. * * @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 + * @param cont callback to invoke after request is sent or is not sent + * @param cont_cls closure for callback */ 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); +GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h, + const char *service_name, struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_ResultCallback cont, void *cont_cls); /** - * List all running services. - * + * Request for a service to be started. + * * @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 + * @param cont callback to invoke after request is sent or not sent + * @param cont_cls closure for callback */ void -GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_List_Callback cb, void *cb_cls); +GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h, + const char *service_name, enum GNUNET_OS_InheritStdioFlags std_inheritance, + struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cont, + void *cont_cls); + + +/** + * Handle for monitoring ARM. + */ +struct GNUNET_ARM_MonitorHandle; + + +/** + * Function called in when a status update arrives. + * + * @param cls closure + * @param arm handle to the arm connection + * @param service service name + * @param status status of the service + */ +typedef void (*GNUNET_ARM_ServiceStatusCallback) (void *cls, struct GNUNET_ARM_MonitorHandle *arm, const char *service, enum GNUNET_ARM_ServiceStatus status); + + +/** + * Setup a context for monitoring ARM. Note that this + * can be done even if the ARM service is not yet running. + * Never fails. + * + * @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). + * @return context to use for further ARM monitor operations + */ +struct GNUNET_ARM_MonitorHandle * +GNUNET_ARM_monitor_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Start connecting to the ARM service for monitoring using the context. + * + * @param h ARM monitor handle + * @param cont callback to invoke on status updates + * @param cont_cls closure + */ +void +GNUNET_ARM_monitor (struct GNUNET_ARM_MonitorHandle *h, + GNUNET_ARM_ServiceStatusCallback cont, void *cont_cls); + + +/** + * Disconnect from the ARM service and destroy the handle. + * Don't call this from inside an ARM callback! + * + * @param h the handle that was being used + */ +void +GNUNET_ARM_monitor_disconnect (struct GNUNET_ARM_MonitorHandle *h); + #if 0 /* keep Emacsens' auto-indent happy */ { diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 222924cf8..9d65c66c2 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -78,14 +78,14 @@ extern "C" #define GNUNET_MESSAGE_TYPE_ARM_STOP 9 /** - * Request ARM service itself to shutdown. + * Response from ARM. */ -#define GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN 10 +#define GNUNET_MESSAGE_TYPE_ARM_RESULT 10 /** - * Response from ARM. + * Status update from ARM. */ -#define GNUNET_MESSAGE_TYPE_ARM_RESULT 11 +#define GNUNET_MESSAGE_TYPE_ARM_STATUS 11 /** * Request to ARM to list all currently running services @@ -97,6 +97,11 @@ extern "C" */ #define GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT 13 +/** + * Request to ARM to notify client of service status changes + */ +#define GNUNET_MESSAGE_TYPE_ARM_MONITOR 14 + /******************************************************************************* * HELLO message types ******************************************************************************/ diff --git a/src/include/gnunet_server_lib.h b/src/include/gnunet_server_lib.h index f59e97015..f7b526416 100644 --- a/src/include/gnunet_server_lib.h +++ b/src/include/gnunet_server_lib.h @@ -395,6 +395,17 @@ typedef void (*GNUNET_SERVER_DisconnectCallback) (void *cls, struct GNUNET_SERVER_Client * client); +/** + * Functions with this signature are called whenever a client + * is connected on the network level. + * + * @param cls closure + * @param client identification of the client + */ +typedef void (*GNUNET_SERVER_ConnectCallback) (void *cls, + struct GNUNET_SERVER_Client *client); + + /** * Ask the server to notify us whenever a client disconnects. @@ -416,6 +427,20 @@ GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, void *callback_cls); +/** + * Ask the server to notify us whenever a client connects. + * This function is called whenever the actual network connection + * is opened. + * + * @param server the server manageing the clients + * @param callback function to call on sconnect + * @param callback_cls closure for callback + */ +void +GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_ConnectCallback callback, void *callback_cls); + + /** * Ask the server to stop notifying us whenever a client disconnects. * @@ -429,6 +454,18 @@ GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server, callback, void *callback_cls); +/** + * Ask the server to stop notifying us whenever a client connects. + * + * @param server the server manageing the clients + * @param callback function to call on connect + * @param callback_cls closure for callback + */ +void +GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_ConnectCallback callback, void *callback_cls); + + /** * Ask the server to disconnect from the given client. * This is the same as returning GNUNET_SYSERR from a message diff --git a/src/regex/gnunet-regex-profiler.c b/src/regex/gnunet-regex-profiler.c index 0c104fab4..303f1fffc 100644 --- a/src/regex/gnunet-regex-profiler.c +++ b/src/regex/gnunet-regex-profiler.c @@ -997,6 +997,17 @@ find_next_string (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } + +/** + * Start announcing the next regex in the DHT. + * + * @param cls Index of the next peer in the peers array. + * @param tc TaskContext. + */ +void +announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + /** * ARM connect adapter. Opens a connection to the ARM service. * @@ -1010,7 +1021,8 @@ arm_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct RegexPeer *peer = cls; - peer->arm_handle = GNUNET_ARM_connect (cfg, NULL); + peer->arm_handle = GNUNET_ARM_alloc (cfg); + GNUNET_ARM_connect (peer->arm_handle, NULL, NULL); return peer->arm_handle; } @@ -1036,104 +1048,52 @@ arm_da (void *cls, void *op_result) } } - -/** - * Start announcing the next regex in the DHT. - * - * @param cls Index of the next peer in the peers array. - * @param tc TaskContext. - */ -void -announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); - - -/** - * Callback function invoked when ARM peration is complete: deamon is started. - * - * @param cls Closure (RegexPeer). - * @param result Outcome of the operation. - */ static void -arm_start_cb (void *cls, enum GNUNET_ARM_ProcessStatus result) +regexprofiler_start_cb (void *cls, struct GNUNET_ARM_Handle *arm, + enum GNUNET_ARM_RequestStatus rs, const char *service, + enum GNUNET_ARM_Result result) { struct RegexPeer *peer = (struct RegexPeer *) cls; - static unsigned int peer_cnt; unsigned int next_p; - switch (result) + if (rs != GNUNET_ARM_REQUEST_SENT_OK) { - /** - * Service is currently being started (due to client request). - */ - case GNUNET_ARM_PROCESS_STARTING: - GNUNET_TESTBED_operation_done (peer->op_handle); - peer->op_handle = NULL; - - if (peer_cnt < (num_peers - 1)) - { - next_p = (++peer_cnt % num_peers); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply( - GNUNET_TIME_UNIT_MILLISECONDS, - 400), - &announce_next_regex, - (void *) (long) next_p); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "All daemons started." - " Waiting %s to start string searches\n", - GNUNET_STRINGS_relative_time_to_string (search_delay, - GNUNET_NO)); - GNUNET_SCHEDULER_add_delayed (search_delay, - do_connect_by_string, - NULL); - } - break; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM request was not sent: %u\n", rs); + GNUNET_abort (); + } + else if (result != GNUNET_ARM_RESULT_STARTING) + { + /* FIXME: maybe check for other acceptable results (already starting, + * already started)? + */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM failed to start regexprofiler: %u\n", result); + GNUNET_abort (); + } + GNUNET_TESTBED_operation_done (peer->op_handle); + peer->op_handle = NULL; - /** - * Service name is unknown to ARM. - */ - case GNUNET_ARM_PROCESS_UNKNOWN: - /** - * Service is now down (due to client request). - */ - case GNUNET_ARM_PROCESS_DOWN: - /** - * Service is already running. - */ - case GNUNET_ARM_PROCESS_ALREADY_RUNNING: - /** - * Service is already being stopped by some other client. - */ - case GNUNET_ARM_PROCESS_ALREADY_STOPPING: - /** - * Service is already down (no action taken) - */ - case GNUNET_ARM_PROCESS_ALREADY_DOWN: - /** - * ARM is currently being shut down (no more process starts) - */ - case GNUNET_ARM_PROCESS_SHUTDOWN: - /** - * Error in communication with ARM - */ - case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR: - /** - * Timeout in communication with ARM - */ - case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT: - /** - * Failure to perform operation - */ - case GNUNET_ARM_PROCESS_FAILURE: - default: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM returned %d\n", result); - GNUNET_abort (); + if (peer_cnt < (num_peers - 1)) + { + next_p = (++peer_cnt % num_peers); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply( + GNUNET_TIME_UNIT_MILLISECONDS, + 400), + &announce_next_regex, + (void *) (long) next_p); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "All daemons started." + " Waiting %s to start string searches\n", + GNUNET_STRINGS_relative_time_to_string (search_delay, + GNUNET_NO)); + GNUNET_SCHEDULER_add_delayed (search_delay, + do_connect_by_string, + NULL); } } - /** * ARM connect callback. Called when we are connected to the arm service for * the peer in 'cls'. If successfull we start the regex deamon to start @@ -1160,11 +1120,10 @@ arm_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op, GNUNET_assert (peer->op_handle == op); GNUNET_assert (peer->arm_handle == ca_result); - GNUNET_ARM_start_service (ca_result, "regexprofiler", - GNUNET_OS_INHERIT_STD_NONE, - GNUNET_TIME_UNIT_FOREVER_REL, - &arm_start_cb, - peer); + GNUNET_ARM_request_service_start (ca_result, "regexprofiler", + GNUNET_OS_INHERIT_STD_NONE, + GNUNET_TIME_UNIT_FOREVER_REL, + regexprofiler_start_cb, cls); } diff --git a/src/regex/regex_test_lib.c b/src/regex/regex_test_lib.c index 57c6b13e8..56ccb3496 100644 --- a/src/regex/regex_test_lib.c +++ b/src/regex/regex_test_lib.c @@ -267,7 +267,7 @@ GNUNET_REGEX_read_from_file (const char *filename) struct GNUNET_DISK_FileHandle *f; unsigned int nr; unsigned int offset; - off_t size; + uint64_t size; size_t len; char *buffer; char *regex; diff --git a/src/util/client.c b/src/util/client.c index 69380c9b0..8b4776201 100644 --- a/src/util/client.c +++ b/src/util/client.c @@ -1007,6 +1007,7 @@ client_notify (void *cls, size_t size, void *buf) size_t ret; struct GNUNET_TIME_Relative delay; + LOG (GNUNET_ERROR_TYPE_DEBUG, "client_notify is running\n"); th->th = NULL; client->th = NULL; if (NULL == buf) diff --git a/src/util/connection.c b/src/util/connection.c index c4795cebe..ea35b04e1 100644 --- a/src/util/connection.c +++ b/src/util/connection.c @@ -1153,14 +1153,22 @@ process_notify (struct GNUNET_CONNECTION_Handle *connection) size_t size; GNUNET_CONNECTION_TransmitReadyNotify notify; + LOG (GNUNET_ERROR_TYPE_DEBUG, "process_notify is running\n"); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == connection->write_task); if (NULL == (notify = connection->nth.notify_ready)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Noone to notify\n"); return GNUNET_NO; + } used = connection->write_buffer_off - connection->write_buffer_pos; avail = connection->write_buffer_size - used; size = connection->nth.notify_size; if (size > avail) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Not enough buffer\n"); return GNUNET_NO; + } connection->nth.notify_ready = NULL; if (connection->write_buffer_size - connection->write_buffer_off < size) { diff --git a/src/util/server.c b/src/util/server.c index fc5f263bf..526821477 100644 --- a/src/util/server.c +++ b/src/util/server.c @@ -110,6 +110,16 @@ struct GNUNET_SERVER_Handle */ struct NotifyList *disconnect_notify_list_tail; + /** + * Head of linked list of functions to call on connects by clients. + */ + struct NotifyList *connect_notify_list_head; + + /** + * Tail of linked list of functions to call on connects by clients. + */ + struct NotifyList *connect_notify_list_tail; + /** * Function to call for access control. */ @@ -756,6 +766,14 @@ GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server) npos); GNUNET_free (npos); } + while (NULL != (npos = server->connect_notify_list_head)) + { + npos->callback (npos->callback_cls, NULL); + GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head, + server->connect_notify_list_tail, + npos); + GNUNET_free (npos); + } GNUNET_free (server); } @@ -1161,6 +1179,7 @@ GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, struct GNUNET_CONNECTION_Handle *connection) { struct GNUNET_SERVER_Client *client; + struct NotifyList *n; client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); client->connection = connection; @@ -1178,6 +1197,9 @@ GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, client->mst = GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, server); GNUNET_assert (NULL != client->mst); + for (n = server->connect_notify_list_head; NULL != n; n = n->next) + n->callback (n->callback_cls, client); + client->receive_pending = GNUNET_YES; GNUNET_CONNECTION_receive (client->connection, GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, @@ -1277,10 +1299,34 @@ GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, /** - * Ask the server to stop notifying us whenever a client disconnects. + * Ask the server to notify us whenever a client connects. + * This function is called whenever the actual network connection + * is opened. * * @param server the server manageing the clients - * @param callback function to call on disconnect + * @param callback function to call on sconnect + * @param callback_cls closure for callback + */ +void +GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_ConnectCallback callback, void *callback_cls) +{ + struct NotifyList *n; + + n = GNUNET_malloc (sizeof (struct NotifyList)); + n->callback = callback; + n->callback_cls = callback_cls; + GNUNET_CONTAINER_DLL_insert (server->connect_notify_list_head, + server->connect_notify_list_tail, + n); +} + + +/** + * Ask the server to stop notifying us whenever a client connects. + * + * @param server the server manageing the clients + * @param callback function to call on connect * @param callback_cls closure for callback */ void @@ -1305,6 +1351,34 @@ GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server, } +/** + * Ask the server to stop notifying us whenever a client disconnects. + * + * @param server the server manageing the clients + * @param callback function to call on disconnect + * @param callback_cls closure for callback + */ +void +GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_ConnectCallback callback, void *callback_cls) +{ + struct NotifyList *pos; + + for (pos = server->connect_notify_list_head; NULL != pos; pos = pos->next) + if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) + break; + if (NULL == pos) + { + GNUNET_break (0); + return; + } + GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head, + server->connect_notify_list_tail, + pos); + GNUNET_free (pos); +} + + /** * Destroy the connection that is passed in via 'cls'. Used * as calling 'GNUNET_CONNECTION_destroy' from within a function