From cbd09ba94fde1331cb4fe6ea29f5f164adc8db25 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 8 Dec 2011 15:45:10 +0000 Subject: major rewrite of ARM service and a bit of the ARM IPC to take advantage of the simplifications possible now that we no longer intercept traffic; the new code in particular is better at communicating what exactly ARM was doing in response to requests. A major change is that gnunet-arm -i/-k now only impacts if a service is running by-default, on-demand starting is no longer impacted, option -t from gnunet-arm was removed --- doc/man/gnunet-arm.1 | 16 +- po/POTFILES.in | 2 - src/arm/Makefile.am | 2 +- src/arm/arm.h | 19 + src/arm/arm_api.c | 446 +++++----- src/arm/do_start_process.c | 56 +- src/arm/gnunet-arm.c | 319 +++---- src/arm/gnunet-service-arm.c | 1518 ++++++++++++++------------------- src/arm/mockup-service.c | 2 +- src/arm/test_arm_api.c | 73 +- src/arm/test_exponential_backoff.c | 221 ++--- src/arm/test_gnunet_arm.sh | 39 - src/arm/test_gnunet_service_manager.c | 103 +-- src/include/gnunet_arm_service.h | 67 +- src/include/gnunet_protocols.h | 24 +- 15 files changed, 1344 insertions(+), 1563 deletions(-) diff --git a/doc/man/gnunet-arm.1 b/doc/man/gnunet-arm.1 index 30e0082ef..e8b7c7a61 100644 --- a/doc/man/gnunet-arm.1 +++ b/doc/man/gnunet-arm.1 @@ -9,7 +9,7 @@ gnunet\-arm \- control GNUnet services .br .SH DESCRIPTION -\fBgnunet\-arm\fP can be used to start or stop GNUnet services, including the ARM service itself. +\fBgnunet\-arm\fP can be used to start or stop GNUnet services, including the ARM service itself. The ARM service is a supervisor for GNUnet's service processes. ARM starts services on-demand or as configured and re-starts them if they crash. .SH OPTIONS @@ -19,7 +19,7 @@ Use the configuration file FILENAME. .TP .IP "\-e, \-\-end" -Shutdown all GNUnet services (including ARM). +Shutdown all GNUnet services (including ARM itself). Running "gnunet-arm -e" is the usual way to shutdown a GNUnet peer. .TP .IP "\-h, \-\-help" @@ -31,19 +31,15 @@ Use LOGLEVEL for logging. Valid values are DEBUG, INFO, WARNING and ERROR. .TP .IP "\-i SERVICE, \-\-init=SERVICE" -Starts the specified SERVICE if it is not already running. +Starts the specified SERVICE if it is not already running. More specifically, this makes the service behave as if it were in the default services list. .TP .IP "\-k SERVICE, \-\-kill=SERVICE" -Stop the specified SERVICE if it is running. +Stop the specified SERVICE if it is running. While this will kill the service right now, the service may be restarted immediately if other services depend on it (service is then started 'on-demand'). If the service used to be a 'default' service, its default-service status will be revoked. If the service was not a default service, it will just be (temporarily) stopped, but could be re-started on-demand at any time. .TP .IP "\-s, \-\-start" -Start all GNUnet default services on this system (including ARM). - -.TP -.IP "\-t SERVICE, \-\-test=SERVICE" -Report if the specified SERVICE is running. +Start all GNUnet default services on this system (and also ARM). Naturally, if a service is demanded by a default service, it will then also be started. Running "gnunet-arm -s" is the usual way to start a GNUnet peer. .TP .IP "\-v, \-\-version" @@ -51,7 +47,7 @@ Print GNUnet version number. .SH BUGS -Report bugs by using Mantis or by sending electronic mail to +Report bugs by using Mantis or by sending electronic mail to .SH SEE ALSO gnunet\-service\-arm(1) diff --git a/po/POTFILES.in b/po/POTFILES.in index dc8e00a3f..c008d61dd 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -95,7 +95,6 @@ src/arm/gnunet-arm.c src/arm/mockup-service.c src/arm/gnunet-service-arm.c src/arm/arm_api.c -src/arm/gnunet-service-arm_interceptor.c src/arm/do_start_process.c src/ats/gnunet-service-ats_reservations.c src/ats/ats_api.c @@ -219,7 +218,6 @@ src/dht/gnunet-service-dht.h src/dht/gnunet-service-dht_hello.h src/dht/gnunet-service-dht_nse.h src/arm/arm.h -src/arm/gnunet-service-arm.h src/ats/gnunet-service-ats_performance.h src/ats/gnunet-service-ats_math.h src/ats/gnunet-service-ats.h diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am index 7d96ba594..2bc81653c 100644 --- a/src/arm/Makefile.am +++ b/src/arm/Makefile.am @@ -23,7 +23,7 @@ libgnunetarm_la_LIBADD = \ $(GN_LIBINTL) $(XLIB) libgnunetarm_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) $(WINFLAGS) \ - -version-info 0:0:0 + -version-info 1:0:0 bin_PROGRAMS = \ diff --git a/src/arm/arm.h b/src/arm/arm.h index f20562c20..615079b7c 100644 --- a/src/arm/arm.h +++ b/src/arm/arm.h @@ -33,4 +33,23 @@ */ #define DEBUG_ARM GNUNET_EXTRA_LOGGING + +/** + * Reply from ARM to client. + */ +struct GNUNET_ARM_ResultMessage +{ + + /** + * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT. + */ + struct GNUNET_MessageHeader header; + + /** + * Status from the 'enum GNUNET_ARM_ProcessStatus' + */ + uint32_t status; +}; + + #endif diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index c15c59875..12395fea9 100644 --- a/src/arm/arm_api.c +++ b/src/arm/arm_api.c @@ -84,12 +84,13 @@ struct ShutdownContext void *cont_cls; /** - * We received a confirmation that the service will shut down. + * Result of the operation */ - int confirmed; + enum GNUNET_ARM_ProcessStatus confirmed; }; + /** * Handler receiving response to service shutdown requests. * First call with NULL: service misbehaving, or something. @@ -105,76 +106,61 @@ static void service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) { struct ShutdownContext *shutdown_ctx = cls; + const struct GNUNET_ARM_ResultMessage *rmsg; - if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES)) - { -#if DEBUG_ARM - /* Means the other side closed the connection and never confirmed a shutdown */ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Service handle shutdown before ACK!\n"); -#endif - if (shutdown_ctx->cont != NULL) - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); - GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); - GNUNET_free (shutdown_ctx); - } - else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES)) + if (msg == NULL) { -#if DEBUG_ARM - LOG (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); -#endif if (shutdown_ctx->cont != NULL) - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_NO); - + { + if (shutdown_ctx->confirmed == GNUNET_ARM_PROCESS_SHUTDOWN) + { + /* shutdown is now complete, as we waited for the network disconnect... */ + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN); + } + else + { + /* communication error */ + 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_NO); GNUNET_free (shutdown_ctx); + return; } - else + if (ntohs (msg->size) == + sizeof (struct GNUNET_ARM_ResultMessage)) { - GNUNET_assert (ntohs (msg->size) == sizeof (struct GNUNET_MessageHeader)); - switch (ntohs (msg->type)) + rmsg = (const struct GNUNET_ARM_ResultMessage*) msg; + shutdown_ctx->confirmed = (enum GNUNET_ARM_ProcessStatus) ntohl (rmsg->status); + if (shutdown_ctx->confirmed != GNUNET_ARM_PROCESS_SHUTDOWN) { - case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK: -#if DEBUG_ARM - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received confirmation for service shutdown.\n"); -#endif - shutdown_ctx->confirmed = GNUNET_YES; - GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, - shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); - break; - default: -#if DEBUG_ARM - LOG (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown refused!\n"); -#endif - if (shutdown_ctx->cont != NULL) - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_YES); - + /* ARM is not shutting down, well, report the error and be done with it... */ + shutdown_ctx->cont (shutdown_ctx->cont_cls, shutdown_ctx->confirmed); GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); GNUNET_free (shutdown_ctx); - break; + return; } } + GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, + shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); } + /** * Shutting down took too long, cancel receive and return error. * * @param cls closure * @param tc context information (why was this task triggered now) */ -void +static void service_shutdown_cancel (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ShutdownContext *shutdown_ctx = cls; -#if DEBUG_ARM - LOG (GNUNET_ERROR_TYPE_DEBUG, "service_shutdown_cancel called!\n"); -#endif - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT); GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); GNUNET_free (shutdown_ctx); } @@ -196,21 +182,21 @@ write_shutdown (void *cls, size_t size, void *buf) struct ShutdownContext *shutdown_ctx = cls; if (size < sizeof (struct GNUNET_MessageHeader)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Failed to transmit shutdown request to client.\n")); - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); - GNUNET_free (shutdown_ctx); - return 0; /* client disconnected */ - } + { + 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_NO); + 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, 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); + 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)); @@ -233,8 +219,8 @@ write_shutdown (void *cls, size_t size, void *buf) */ static void arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, - struct GNUNET_TIME_Relative timeout, - GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) + struct GNUNET_TIME_Relative timeout, + GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) { struct ShutdownContext *shutdown_ctx; @@ -243,10 +229,12 @@ arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, shutdown_ctx->cont_cls = cont_cls; shutdown_ctx->sock = sock; shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + shutdown_ctx->confirmed = GNUNET_ARM_PROCESS_COMMUNICATION_ERROR; + /* FIXME: store return value? */ GNUNET_CLIENT_notify_transmit_ready (sock, - sizeof (struct GNUNET_MessageHeader), - timeout, GNUNET_YES, &write_shutdown, - shutdown_ctx); + sizeof (struct GNUNET_MessageHeader), + timeout, GNUNET_YES, &write_shutdown, + shutdown_ctx); } @@ -262,7 +250,7 @@ arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, */ struct GNUNET_ARM_Handle * GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *service) + const char *service) { struct GNUNET_ARM_Handle *ret; @@ -286,6 +274,7 @@ GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) GNUNET_free (h); } + struct ARM_ShutdownContext { /** @@ -300,7 +289,6 @@ struct ARM_ShutdownContext }; - /** * Internal state for a request with ARM. */ @@ -357,17 +345,17 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) char *lopostfix; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) - { + { #if DEBUG_ARM - LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n", - "gnunet-service-arm"); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n", + "gnunet-service-arm"); #endif - /* arm is running! */ - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_YES); - GNUNET_free (pos); - return; - } + /* arm is running! */ + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_ALREADY_RUNNING); + GNUNET_free (pos); + return; + } #if DEBUG_ARM LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is not running, will start it.\n", @@ -375,73 +363,81 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) #endif if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX", - &loprefix)) + &loprefix)) loprefix = GNUNET_strdup (""); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS", - &lopostfix)) + &lopostfix)) lopostfix = GNUNET_strdup (""); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY", - &binary)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Configuration failes to specify option `%s' in section `%s'!\n"), - "BINARY", "arm"); - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_SYSERR); - GNUNET_free (pos); - GNUNET_free (loprefix); - GNUNET_free (lopostfix); - return; - } + &binary)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _ + ("Configuration failes to specify option `%s' in section `%s'!\n"), + "BINARY", "arm"); + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN); + GNUNET_free (pos); + GNUNET_free (loprefix); + GNUNET_free (lopostfix); + return; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG", - &config)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Configuration fails to specify option `%s' in section `%s'!\n"), - "CONFIG", "arm"); - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_SYSERR); - GNUNET_free (binary); - GNUNET_free (pos); - GNUNET_free (loprefix); - GNUNET_free (lopostfix); - return; - } - if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) && (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", "WEAKRANDOM")) && (GNUNET_NO == GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "HOSTFILE")) /* Means we are ONLY running locally */ - ) - { - /* we're clearly running a test, don't daemonize */ - proc = do_start_process (NULL, loprefix, binary, "-c", config, + &config)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Configuration fails to specify option `%s' in section `%s'!\n"), + "CONFIG", "arm"); + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN); + GNUNET_free (binary); + GNUNET_free (pos); + GNUNET_free (loprefix); + GNUNET_free (lopostfix); + return; + } + if ((GNUNET_YES == + GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) + && (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", + "WEAKRANDOM")) + && (GNUNET_NO == + GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", + "HOSTFILE"))) + { + /* Means we are ONLY running locally */ + /* we're clearly running a test, don't daemonize */ + proc = do_start_process (NULL, loprefix, binary, "-c", config, #if DEBUG_ARM - "-L", "DEBUG", + "-L", "DEBUG", #endif - /* no daemonization! */ - lopostfix, NULL); - } + /* no daemonization! */ + lopostfix, NULL); + } else - { - proc = do_start_process (NULL, loprefix, binary, "-c", config, + { + proc = do_start_process (NULL, loprefix, binary, "-c", config, #if DEBUG_ARM - "-L", "DEBUG", + "-L", "DEBUG", #endif - "-d", lopostfix, NULL); - } + "-d", lopostfix, NULL); + } GNUNET_free (binary); GNUNET_free (config); GNUNET_free (loprefix); GNUNET_free (lopostfix); if (proc == NULL) - { - if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_SYSERR); - GNUNET_free (pos); - return; - } + { + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE); + GNUNET_free (pos); + return; + } if (pos->callback != NULL) - pos->callback (pos->cls, GNUNET_YES); + pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING); GNUNET_free (proc); GNUNET_free (pos); } @@ -458,45 +454,34 @@ static void handle_response (void *cls, const struct GNUNET_MessageHeader *msg) { struct RequestContext *sc = cls; - int ret; + const struct GNUNET_ARM_ResultMessage *res; + enum GNUNET_ARM_ProcessStatus status; - if (msg == NULL) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _ - ("Error receiving response to `%s' request from ARM for service `%s'\n"), - (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) ? "START" : "STOP", - (const char *) &sc[1]); - GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO); - sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg); - GNUNET_assert (NULL != sc->h->client); - if (sc->callback != NULL) - sc->callback (sc->cls, GNUNET_SYSERR); - GNUNET_free (sc); - return; - } + if ((msg == NULL) || + (ntohs (msg->size) != sizeof (struct GNUNET_ARM_ResultMessage))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _ + ("Error receiving response to `%s' request from ARM for service `%s'\n"), + (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) ? "START" : "STOP", + (const char *) &sc[1]); + GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO); + sc->h->client = GNUNET_CLIENT_connect ("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; #if DEBUG_ARM LOG (GNUNET_ERROR_TYPE_DEBUG, "Received response from ARM for service `%s': %u\n", (const char *) &sc[1], ntohs (msg->type)); #endif - switch (ntohs (msg->type)) - { - case GNUNET_MESSAGE_TYPE_ARM_IS_UP: - ret = GNUNET_YES; - break; - case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN: - ret = GNUNET_NO; - break; - case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN: - ret = GNUNET_SYSERR; - break; - default: - GNUNET_break (0); - ret = GNUNET_SYSERR; - } + status = (enum GNUNET_ARM_ProcessStatus) ntohl (res->status); if (sc->callback != NULL) - sc->callback (sc->cls, ret); + sc->callback (sc->cls, status); GNUNET_free (sc); } @@ -513,8 +498,8 @@ 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, - void *cb_cls, uint16_t type) + struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb, + void *cb_cls, uint16_t type) { struct RequestContext *sctx; size_t slen; @@ -523,16 +508,16 @@ change_service (struct GNUNET_ARM_Handle *h, const char *service_name, slen = strlen (service_name) + 1; if (slen + sizeof (struct GNUNET_MessageHeader) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) - { - GNUNET_break (0); - if (cb != NULL) - cb (cb_cls, GNUNET_NO); - return; - } + { + GNUNET_break (0); + if (cb != NULL) + cb (cb_cls, GNUNET_NO); + return; + } #if DEBUG_ARM LOG (GNUNET_ERROR_TYPE_DEBUG, (type == - GNUNET_MESSAGE_TYPE_ARM_START) ? + GNUNET_MESSAGE_TYPE_ARM_START) ? _("Requesting start of service `%s'.\n") : _("Requesting termination of service `%s'.\n"), service_name); #endif @@ -549,22 +534,23 @@ change_service (struct GNUNET_ARM_Handle *h, const char *service_name, 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)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - (type == - GNUNET_MESSAGE_TYPE_ARM_START) ? - _("Error while trying to transmit request to start `%s' to ARM\n") : - _("Error while trying to transmit request to stop `%s' to ARM\n"), - (const char *) &service_name); - if (cb != NULL) - cb (cb_cls, GNUNET_SYSERR); - GNUNET_free (sctx); - GNUNET_free (msg); - return; - } + GNUNET_TIME_absolute_get_remaining + (sctx->timeout), GNUNET_YES, + &handle_response, sctx)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + (type == + GNUNET_MESSAGE_TYPE_ARM_START) ? + _("Error while trying to transmit request to start `%s' to ARM\n") + : + _("Error while trying to transmit request to stop `%s' to ARM\n"), + (const char *) &service_name); + if (cb != NULL) + cb (cb_cls, GNUNET_SYSERR); + GNUNET_free (sctx); + GNUNET_free (msg); + return; + } GNUNET_free (msg); } @@ -579,9 +565,10 @@ change_service (struct GNUNET_ARM_Handle *h, const char *service_name, * @param cb_cls closure for callback */ void -GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char *service_name, - struct GNUNET_TIME_Relative timeout, - GNUNET_ARM_Callback cb, void *cb_cls) +GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, + const char *service_name, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls) { struct RequestContext *sctx; struct GNUNET_CLIENT_Connection *client; @@ -593,47 +580,47 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char *service_name, (unsigned long long) timeout.rel_value); #endif 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); - memcpy (&sctx[1], service_name, slen); - GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report, - sctx); - return; - } + { + slen = strlen ("arm") + 1; + sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); + sctx->h = h; + sctx->callback = cb; + sctx->cls = cb_cls; + sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + memcpy (&sctx[1], service_name, slen); + GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report, + sctx); + return; + } if (h->client == NULL) - { - client = GNUNET_CLIENT_connect ("arm", h->cfg); - if (client == NULL) { + client = GNUNET_CLIENT_connect ("arm", h->cfg); + if (client == NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "arm_api, GNUNET_CLIENT_connect returned NULL\n"); + cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); + return; + } LOG (GNUNET_ERROR_TYPE_DEBUG, - "arm_api, GNUNET_CLIENT_connect returned NULL\n"); - cb (cb_cls, GNUNET_SYSERR); - return; + "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); + h->client = client; } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); - h->client = client; - } LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n"); change_service (h, service_name, timeout, cb, cb_cls, - GNUNET_MESSAGE_TYPE_ARM_START); + 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, GNUNET_NO if arm is shutdown - * GNUNET_YES if arm remains running, and GNUNET_SYSERR on error + * @param reason reason for callback */ -void -arm_shutdown_callback (void *cls, int reason) +static void +arm_shutdown_callback (void *cls, enum GNUNET_ARM_ProcessStatus reason) { struct ARM_ShutdownContext *arm_shutdown_ctx = cls; @@ -654,9 +641,10 @@ arm_shutdown_callback (void *cls, int reason) * @param cb_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_stop_service (struct GNUNET_ARM_Handle *h, + const char *service_name, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls) { struct ARM_ShutdownContext *arm_shutdown_ctx; struct GNUNET_CLIENT_Connection *client; @@ -664,27 +652,27 @@ GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, const char *service_name, LOG (GNUNET_ERROR_TYPE_INFO, _("Stopping service `%s' within %llu ms\n"), service_name, (unsigned long long) timeout.rel_value); if (h->client == NULL) - { - client = GNUNET_CLIENT_connect ("arm", h->cfg); - if (client == NULL) { - cb (cb_cls, GNUNET_SYSERR); - return; + client = GNUNET_CLIENT_connect ("arm", h->cfg); + if (client == NULL) + { + cb (cb_cls, GNUNET_SYSERR); + return; + } + h->client = client; } - 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; - } + { + arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext)); + arm_shutdown_ctx->cb = cb; + arm_shutdown_ctx->cb_cls = cb_cls; + arm_service_shutdown (h->client, timeout, &arm_shutdown_callback, + arm_shutdown_ctx); + h->client = NULL; + return; + } change_service (h, service_name, timeout, cb, cb_cls, - GNUNET_MESSAGE_TYPE_ARM_STOP); + GNUNET_MESSAGE_TYPE_ARM_STOP); } diff --git a/src/arm/do_start_process.c b/src/arm/do_start_process.c index f4d3424ff..fd7fc17ed 100644 --- a/src/arm/do_start_process.c +++ b/src/arm/do_start_process.c @@ -10,10 +10,10 @@ * @param lsocks array of listen sockets to dup starting at fd3 (systemd-style), or NULL * @param first_arg first argument for argv (may be an empty string) * @param ... more arguments, NULL terminated - * @return PID of the started process, -1 on error + * @return handle of the started process, NULL on error */ static struct GNUNET_OS_Process * -do_start_process (const SOCKTYPE *lsocks, const char *first_arg, ...) +do_start_process (const SOCKTYPE * lsocks, const char *first_arg, ...) { va_list ap; char **argv; @@ -35,20 +35,20 @@ do_start_process (const SOCKTYPE *lsocks, const char *first_arg, ...) /* *INDENT-ON* */ rpos = arg; while ('\0' != *rpos) - { - if (' ' == *rpos) { - if (last != NULL) - argv_size++; - last = NULL; - while (' ' == *rpos) - rpos++; + if (' ' == *rpos) + { + if (last != NULL) + argv_size++; + last = NULL; + while (' ' == *rpos) + rpos++; + } + if ((last == NULL) && (*rpos != '\0')) + last = rpos; + if (*rpos != '\0') + rpos++; } - if ((last == NULL) && (*rpos != '\0')) - last = rpos; - if (*rpos != '\0') - rpos++; - } if (last != NULL) argv_size++; /* *INDENT-OFF* */ @@ -69,22 +69,22 @@ do_start_process (const SOCKTYPE *lsocks, const char *first_arg, ...) cp = GNUNET_strdup (arg); pos = cp; while ('\0' != *pos) - { - if (' ' == *pos) { - *pos = '\0'; - if (last != NULL) - argv[argv_size++] = GNUNET_strdup (last); - last = NULL; - pos++; - while (' ' == *pos) - pos++; + if (' ' == *pos) + { + *pos = '\0'; + if (last != NULL) + argv[argv_size++] = GNUNET_strdup (last); + last = NULL; + pos++; + while (' ' == *pos) + pos++; + } + if ((last == NULL) && (*pos != '\0')) + last = pos; + if (*pos != '\0') + pos++; } - if ((last == NULL) && (*pos != '\0')) - last = pos; - if (*pos != '\0') - pos++; - } if (last != NULL) argv[argv_size++] = GNUNET_strdup (last); last = NULL; diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c index db5687dbc..13b1fdc17 100644 --- a/src/arm/gnunet-arm.c +++ b/src/arm/gnunet-arm.c @@ -48,13 +48,6 @@ */ #define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) -/** - * Timeout for starting services, very short because of the strange way start works - * (by checking if running before starting, so really this time is always waited on - * startup (annoying)). - */ -#define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2) - /** * Set if we are to shutdown all services (including ARM). */ @@ -90,11 +83,6 @@ static char *init; */ static char *term; -/** - * Set to the name of a service to test. - */ -static char *test; - /** * Set to the name of the config file used. */ @@ -118,7 +106,7 @@ static struct GNUNET_ARM_Handle *h; /** * Our configuration. */ -const struct GNUNET_CONFIGURATION_Handle *cfg; +static const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Processing stage that we are in. Simple counter. @@ -147,72 +135,62 @@ cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); * user and then runs the next phase in the FSM. * * @param cls pointer to "const char*" identifying service that was manipulated - * @param success GNUNET_OK if service is now running, GNUNET_NO if not, GNUNET_SYSERR on error + * @param result result of the operation */ static void -confirm_cb (void *cls, int success) +confirm_cb (void *cls, + enum GNUNET_ARM_ProcessStatus result) { const char *service = cls; - switch (success) + switch (result) { - case GNUNET_OK: - if (quiet != GNUNET_YES) - fprintf (stdout, _("Service `%s' has been started.\n"), service); - if ((phase - 1 != 2) && (phase - 1 != 3)) - { - if (quiet != GNUNET_YES) - fprintf (stdout, _("Failed to stop service `%s'!\n"), service); - ret = 1; - } + case GNUNET_ARM_PROCESS_UNKNOWN: + fprintf (stderr, _("Service `%s' is unknown to ARM.\n"), service); + ret = 1; break; - case GNUNET_NO: + case GNUNET_ARM_PROCESS_DOWN: if (quiet != GNUNET_YES) fprintf (stdout, _("Service `%s' has been stopped.\n"), service); - if ((phase - 1 != 0) && (phase - 1 != 1)) - { - if (quiet != GNUNET_YES) - fprintf (stdout, _("Failed to start service `%s'!\n"), service); - ret = 1; - } break; - case GNUNET_SYSERR: - if (quiet != GNUNET_YES) - fprintf (stdout, _("Some error communicating with service `%s'.\n"), - service); + case GNUNET_ARM_PROCESS_ALREADY_RUNNING: + fprintf (stderr, _("Service `%s' was already running.\n"), service); ret = 1; break; - } - - GNUNET_SCHEDULER_add_continuation (&cps_loop, NULL, - GNUNET_SCHEDULER_REASON_PREREQ_DONE); -} - - -/** - * Function called to confirm that a service is running (or that - * it is not running). - * - * @param cls pointer to "const char*" identifying service that was manipulated - * @param tc reason determines if service is now running - */ -static void -confirm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - const char *service = cls; - - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) - { - if (quiet != GNUNET_YES) - fprintf (stdout, _("Service `%s' is running.\n"), service); - } - else - { + case GNUNET_ARM_PROCESS_STARTING: if (quiet != GNUNET_YES) - fprintf (stdout, _("Service `%s' is not running.\n"), service); + 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, _("Request ignored as ARM is shutting down.\n")); + ret = 1; + break; + case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR: + fprintf (stderr, _("Error communicating with ARM service.\n")); + ret = 1; + break; + case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT: + fprintf (stderr, _("Timeout communicating with ARM service.\n")); + ret = 1; + break; + case GNUNET_ARM_PROCESS_FAILURE: + fprintf (stderr, _("Operation failed.\n")); + ret = 1; + break; + default: + fprintf (stderr, _("Unknown response code from ARM.\n")); + break; } GNUNET_SCHEDULER_add_continuation (&cps_loop, NULL, - GNUNET_SCHEDULER_REASON_PREREQ_DONE); + GNUNET_SCHEDULER_REASON_PREREQ_DONE); } @@ -230,25 +208,25 @@ run (void *cls, char *const *args, const char *cfgfile, { cfg = c; config_file = cfgfile; - if (GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME", &dir) - != GNUNET_OK) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Fatal configuration error: `%s' option in section `%s' missing.\n"), - "SERVICEHOME", "PATHS"); - return; - } + if (GNUNET_CONFIGURATION_get_value_string + (cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Fatal configuration error: `%s' option in section `%s' missing.\n"), + "SERVICEHOME", "PATHS"); + return; + } h = GNUNET_ARM_connect (cfg, NULL); if (h == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Fatal error initializing ARM API.\n")); - ret = 1; - return; - } + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Fatal error initializing ARM API.\n")); + ret = 1; + return; + } GNUNET_SCHEDULER_add_continuation (&cps_loop, NULL, - GNUNET_SCHEDULER_REASON_PREREQ_DONE); + GNUNET_SCHEDULER_REASON_PREREQ_DONE); } /** @@ -260,21 +238,21 @@ static void delete_files () { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Will attempt to remove configuration file %s and service directory %s\n", - config_file, dir); + "Will attempt to remove configuration file %s and service directory %s\n", + config_file, dir); if (UNLINK (config_file) != 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Failed to remove configuration file %s\n"), config_file); - } + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to remove configuration file %s\n"), config_file); + } if (GNUNET_DISK_directory_remove (dir) != GNUNET_OK) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Failed to remove servicehome directory %s\n"), dir); + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to remove servicehome directory %s\n"), dir); - } + } } /** @@ -288,86 +266,76 @@ static void cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { while (1) - { - switch (phase++) { - case 0: - if (term != NULL) - { - GNUNET_ARM_stop_service (h, term, - (0 == - timeout.rel_value) ? STOP_TIMEOUT : timeout, - &confirm_cb, term); - return; - } - break; - case 1: - if ((end) || (restart)) - { - GNUNET_ARM_stop_service (h, "arm", - (0 == - timeout.rel_value) ? STOP_TIMEOUT_ARM : - timeout, &confirm_cb, "arm"); - return; - } - break; - case 2: - if (start) - { - GNUNET_ARM_start_service (h, "arm", - (0 == - timeout.rel_value) ? START_TIMEOUT : timeout, - &confirm_cb, "arm"); - return; - } - break; - case 3: - if (init != NULL) - { - GNUNET_ARM_start_service (h, init, - (0 == - timeout.rel_value) ? START_TIMEOUT : timeout, - &confirm_cb, init); - return; - } - break; - case 4: - if (test != NULL) - { - GNUNET_CLIENT_service_test (test, cfg, - (0 == - timeout.rel_value) ? TEST_TIMEOUT : - timeout, &confirm_task, test); - return; - } - break; - case 5: - if (restart) - { - GNUNET_ARM_disconnect (h); - phase = 0; - end = 0; - start = 1; - restart = 0; - h = GNUNET_ARM_connect (cfg, NULL); - if (h == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Fatal error initializing ARM API.\n")); - ret = 1; - return; - } - GNUNET_SCHEDULER_add_now (&cps_loop, NULL); - return; - } - /* Fall through */ - default: /* last phase */ - GNUNET_ARM_disconnect (h); - if ((end == GNUNET_YES) && (delete == GNUNET_YES)) - delete_files (); - return; + switch (phase++) + { + case 0: + if (term != NULL) + { + GNUNET_ARM_stop_service (h, term, + (0 == + timeout.rel_value) ? STOP_TIMEOUT : + timeout, &confirm_cb, term); + return; + } + break; + case 1: + if ((end) || (restart)) + { + GNUNET_ARM_stop_service (h, "arm", + (0 == + timeout.rel_value) ? STOP_TIMEOUT_ARM + : timeout, &confirm_cb, "arm"); + return; + } + break; + case 2: + if (start) + { + GNUNET_ARM_start_service (h, "arm", + (0 == + timeout.rel_value) ? START_TIMEOUT : + timeout, &confirm_cb, "arm"); + return; + } + break; + case 3: + if (init != NULL) + { + GNUNET_ARM_start_service (h, init, + (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; + h = GNUNET_ARM_connect (cfg, NULL); + if (h == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Fatal error initializing ARM API.\n")); + ret = 1; + return; + } + GNUNET_SCHEDULER_add_now (&cps_loop, NULL); + return; + } + /* Fall through */ + default: /* last phase */ + GNUNET_ARM_disconnect (h); + if ((end == GNUNET_YES) && (delete == GNUNET_YES)) + delete_files (); + return; + } } - } } @@ -395,9 +363,6 @@ main (int argc, char *const *argv) {'r', "restart", NULL, gettext_noop ("stop and start all GNUnet default services"), GNUNET_NO, &GNUNET_GETOPT_set_one, &restart}, - {'t', "test", "SERVICE", - gettext_noop ("test if a particular service is running"), - GNUNET_YES, &GNUNET_GETOPT_set_string, &test}, {'d', "delete", NULL, gettext_noop ("delete config file and directory on exit"), GNUNET_NO, &GNUNET_GETOPT_set_one, &delete}, @@ -414,12 +379,12 @@ main (int argc, char *const *argv) if (GNUNET_OK == GNUNET_PROGRAM_run (argc, argv, "gnunet-arm", - gettext_noop - ("Control services and the Automated Restart Manager (ARM)"), - options, &run, NULL)) - { - return ret; - } + gettext_noop + ("Control services and the Automated Restart Manager (ARM)"), + options, &run, NULL)) + { + return ret; + } return 1; } diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 74bbd76c2..733eac6c6 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009, 2010 Christian Grothoff (and other contributing authors) + (C) 2009, 2010, 2011 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 @@ -22,31 +22,18 @@ * @file arm/gnunet-service-arm.c * @brief the automated restart manager service * @author Christian Grothoff - * - * TODO: - * - need to test auto-restart code on configuration changes; - * - should refine restart code to check if *relevant* parts of the - * configuration were changed (anything in the section for the service) - * - should have a way to specify dependencies between services and - * manage restarts of groups of services - * - * + install handler for disconnecting clients!? */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_arm_service.h" #include "gnunet_protocols.h" #include "arm.h" - -/** - * Check for configuration file changes every 5s. - */ -#define MAINT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - /** * Threshold after which exponential backoff shouldn't increase (in ms); 30m */ -#define EXPONENTIAL_BACKOFF_THRESHOLD (1000 * 60 * 30) +#define EXPONENTIAL_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) + /** * List of our services. @@ -54,6 +41,49 @@ struct ServiceList; +/** + * Record with information about a listen socket we have open. + */ +struct ServiceListeningInfo +{ + /** + * This is a linked list. + */ + struct ServiceListeningInfo *next; + + /** + * This is a linked list. + */ + struct ServiceListeningInfo *prev; + + /** + * Address this socket is listening on. + */ + struct sockaddr *service_addr; + + /** + * Service this listen socket is for. + */ + struct ServiceList *sl; + + /** + * Number of bytes in 'service_addr' + */ + socklen_t service_addr_len; + + /** + * Our listening socket. + */ + struct GNUNET_NETWORK_Handle *listen_socket; + + /** + * Task doing the accepting. + */ + GNUNET_SCHEDULER_TaskIdentifier accept_task; + +}; + + /** * List of our services. */ @@ -69,6 +99,16 @@ struct ServiceList */ struct ServiceList *prev; + /** + * Linked list of listen sockets associated with this service. + */ + struct ServiceListeningInfo *listen_head; + + /** + * Linked list of listen sockets associated with this service. + */ + struct ServiceListeningInfo *listen_tail; + /** * Name of the service. */ @@ -95,12 +135,6 @@ struct ServiceList */ struct GNUNET_OS_Process *proc; - /** - * Last time the config of this service was - * modified. - */ - time_t mtime; - /** * Process exponential backoff time */ @@ -109,7 +143,14 @@ struct ServiceList /** * Absolute time at which the process is scheduled to restart in case of death */ - struct GNUNET_TIME_Absolute restartAt; + struct GNUNET_TIME_Absolute restart_at; + + /** + * Is this service to be started by default (or did a client tell us explicitly + * to start it)? GNUNET_NO if the service is started only upon 'accept' on a + * listen socket or possibly explicitly by a client changing the value. + */ + int is_default; }; @@ -149,85 +190,16 @@ static GNUNET_SCHEDULER_TaskIdentifier child_death_task; */ static GNUNET_SCHEDULER_TaskIdentifier child_restart_task; -/** - * - */ -struct ServiceListeningInfo -{ - /** - * This is a linked list. - */ - struct ServiceListeningInfo *next; - - /** - * This is a linked list. - */ - struct ServiceListeningInfo *prev; - - /** - * Name of the service being forwarded. - */ - char *serviceName; - - /** - * - */ - struct sockaddr *service_addr; - - /** - * - */ - socklen_t service_addr_len; - - /** - * Our listening socket. - */ - struct GNUNET_NETWORK_Handle *listeningSocket; - - /** - * Task doing the accepting. - */ - GNUNET_SCHEDULER_TaskIdentifier acceptTask; -}; - - -/** - * Array with the names of the services started by default. - */ -static char **defaultServicesList; - -/** - * Size of the defaultServicesList array. - */ -static unsigned int numDefaultServices; - -/** - * - */ -static struct ServiceListeningInfo *serviceListeningInfoList_head; - -/** - * - */ -static struct ServiceListeningInfo *serviceListeningInfoList_tail; - - /** * Pipe used to communicate shutdown via signal. */ static struct GNUNET_DISK_PipeHandle *sigpipe; -/** - * Reading end of the signal pipe. - */ -static const struct GNUNET_DISK_FileHandle *pr; - /** * Are we in shutdown mode? */ static int in_shutdown; - /** * Handle to our server instance. Our server is a bit special in that * its service is not immediately stopped once we get a shutdown @@ -248,10 +220,9 @@ static struct GNUNET_SERVER_Handle *server; * Actually start the process for the given service. * * @param sl identifies service to start - * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL */ static void -start_process (struct ServiceList *sl, const SOCKTYPE *lsocks) +start_process (struct ServiceList *sl) { char *loprefix; char *options; @@ -261,161 +232,100 @@ start_process (struct ServiceList *sl, const SOCKTYPE *lsocks) int use_debug; char b; char *val; + struct ServiceListeningInfo *sli; + SOCKTYPE *lsocks; + unsigned int ls; - /* start service */ + /* calculate listen socket list */ + lsocks = NULL; + ls = 0; + for (sli = sl->listen_head; NULL != sli; sli = sli->next) + { + GNUNET_array_append (lsocks, ls, + GNUNET_NETWORK_get_fd (sli->listen_socket)); + if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (sli->accept_task); + sli->accept_task = GNUNET_SCHEDULER_NO_TASK; + } + } +#if WINDOWS + GNUNET_array_append (lsocks, ls, INVALID_SOCKET); +#else + GNUNET_array_append (lsocks, ls, -1); +#endif + + /* obtain configuration */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX", - &loprefix)) + &loprefix)) loprefix = GNUNET_strdup (prefix_command); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS", - &options)) - { - options = GNUNET_strdup (final_option); - if (NULL == strstr (options, "%")) + &options)) { - /* replace '{}' with service name */ - while (NULL != (optpos = strstr (options, "{}"))) - { - optpos[0] = '%'; - optpos[1] = 's'; - GNUNET_asprintf (&optpos, options, sl->name); - GNUNET_free (options); - options = optpos; - } - /* replace '$PATH' with value associated with "PATH" */ - while (NULL != (optpos = strstr (options, "$"))) - { - optend = optpos + 1; - while (isupper ((unsigned char) *optend)) - optend++; - b = *optend; - if ('\0' == b) - next = ""; - else - next = optend + 1; - *optend = '\0'; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", optpos + 1, - &val)) - val = GNUNET_strdup (""); - *optpos = '\0'; - GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next); - GNUNET_free (options); - GNUNET_free (val); - options = optpos; - } + options = GNUNET_strdup (final_option); + if (NULL == strstr (options, "%")) + { + /* replace '{}' with service name */ + while (NULL != (optpos = strstr (options, "{}"))) + { + optpos[0] = '%'; + optpos[1] = 's'; + GNUNET_asprintf (&optpos, options, sl->name); + GNUNET_free (options); + options = optpos; + } + /* replace '$PATH' with value associated with "PATH" */ + while (NULL != (optpos = strstr (options, "$"))) + { + optend = optpos + 1; + while (isupper ((unsigned char) *optend)) + optend++; + b = *optend; + if ('\0' == b) + next = ""; + else + next = optend + 1; + *optend = '\0'; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", + optpos + 1, &val)) + val = GNUNET_strdup (""); + *optpos = '\0'; + GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next); + GNUNET_free (options); + GNUNET_free (val); + options = optpos; + } + } } - } use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG"); + /* actually start process */ #if DEBUG_ARM GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting service `%s' using binary `%s' and configuration `%s'\n", - sl->name, sl->binary, sl->config); + "Starting service `%s' using binary `%s' and configuration `%s'\n", + sl->name, sl->binary, sl->config); #endif if (GNUNET_YES == use_debug) sl->proc = - do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L", - "DEBUG", options, NULL); + do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L", + "DEBUG", options, NULL); else sl->proc = - do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, - options, NULL); + do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, + options, NULL); if (sl->proc == NULL) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"), - sl->name); + sl->name); else - GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), + sl->name); + /* clean up */ GNUNET_free (loprefix); GNUNET_free (options); -} - - -/** - * Put the default services represented by a space separated string into an array of strings - * - * @param services space separated string of default services - */ -static void -addDefaultServicesToList (const char *services) -{ - unsigned int i; - const char *token; - char *s; - - if (strlen (services) == 0) - return; - s = GNUNET_strdup (services); - token = strtok (s, " "); - while (NULL != token) - { - numDefaultServices++; - token = strtok (NULL, " "); - } - GNUNET_free (s); - - defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *)); - i = 0; - s = GNUNET_strdup (services); - token = strtok (s, " "); - while (NULL != token) - { - defaultServicesList[i++] = GNUNET_strdup (token); - token = strtok (NULL, " "); - } - GNUNET_free (s); - GNUNET_assert (i == numDefaultServices); -} - - -/** - * Checks whether the serviceName is in the list of default services - * - * @param serviceName string to check its existance in the list - * @return GNUNET_YES if the service is started by default - */ -static int -isInDefaultList (const char *serviceName) -{ - unsigned int i; - - for (i = 0; i < numDefaultServices; i++) - if (strcmp (serviceName, defaultServicesList[i]) == 0) - return GNUNET_YES; - return GNUNET_NO; -} - - -/** - * - */ -static int -stop_listening (const char *serviceName) -{ - struct ServiceListeningInfo *pos; - struct ServiceListeningInfo *next; - int ret; - - ret = GNUNET_NO; - next = serviceListeningInfoList_head; - while (NULL != (pos = next)) - { - next = pos->next; - if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0)) - continue; - if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (pos->acceptTask); - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (pos->listeningSocket)); - GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, - serviceListeningInfoList_tail, pos); - GNUNET_free (pos->serviceName); - GNUNET_free (pos->service_addr); - GNUNET_free (pos); - ret = GNUNET_OK; - } - return ret; + GNUNET_array_grow (lsocks, ls, 0); } @@ -430,25 +340,26 @@ stop_listening (const char *serviceName) static size_t write_result (void *cls, size_t size, void *buf) { - uint16_t *res = cls; - struct GNUNET_MessageHeader *msg; + 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 */ + _("Could not send status result to client\n")); + return 0; /* error, not much we can do */ } #if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending status response %u to client\n", - (unsigned int) *res); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending status response %u to client\n", (unsigned int) *res); #endif - GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); + GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage)); msg = buf; - msg->size = htons (sizeof (struct GNUNET_MessageHeader)); - msg->type = htons (*res); + 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_MessageHeader); + return sizeof (struct GNUNET_ARM_ResultMessage); } @@ -463,27 +374,21 @@ write_result (void *cls, size_t size, void *buf) */ static void signal_result (struct GNUNET_SERVER_Client *client, const char *name, - uint16_t result) + enum GNUNET_ARM_ProcessStatus result) { - uint16_t *res; + enum GNUNET_ARM_ProcessStatus *res; if (NULL == client) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Not sending status result to client: no client known\n")); return; - } -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling client that service `%s' is now %s\n", name, - result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up"); -#endif - res = GNUNET_malloc (sizeof (uint16_t)); + /* 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_MessageHeader), - GNUNET_TIME_UNIT_FOREVER_REL, - &write_result, res); + sizeof (struct + GNUNET_ARM_ResultMessage), + GNUNET_TIME_UNIT_FOREVER_REL, + &write_result, res); + GNUNET_SERVER_receive_done (client, GNUNET_OK); } @@ -496,418 +401,144 @@ signal_result (struct GNUNET_SERVER_Client *client, const char *name, */ static struct ServiceList * find_service (const char *name) -{ - struct ServiceList *pos; - - pos = running_head; - while (pos != NULL) - { - if (0 == strcmp (pos->name, name)) - return pos; - pos = pos->next; - } - return NULL; -} - - -/** - * Start the specified service. - * - * @param client who is asking for this - * @param servicename name of the service to start - * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL - * @return GNUNET_OK on success, GNUNET_SYSERR on error - */ -static int -start_service (struct GNUNET_SERVER_Client *client, const char *servicename, - const SOCKTYPE *lsocks) { struct ServiceList *sl; - char *binary; - char *config; - struct stat sbuf; - if (GNUNET_YES == in_shutdown) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("ARM is shutting down, service `%s' not started.\n"), - servicename); - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - return GNUNET_SYSERR; - } - sl = find_service (servicename); - if (sl != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Service `%s' already running.\n"), - servicename); - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, servicename, "BINARY", - &binary)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Binary implementing service `%s' not known!\n"), - servicename); - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - return GNUNET_SYSERR; - } - if ((GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, servicename, "CONFIG", - &config)) || - (0 != STAT (config, &sbuf))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Configuration file `%s' for service `%s' not known!\n"), - config, servicename); - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - GNUNET_free (binary); - GNUNET_free_non_null (config); - return GNUNET_SYSERR; - } - (void) stop_listening (servicename); - sl = GNUNET_malloc (sizeof (struct ServiceList)); - sl->name = GNUNET_strdup (servicename); - sl->binary = binary; - sl->config = config; - sl->mtime = sbuf.st_mtime; - sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS; - sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS; - GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl); - start_process (sl, lsocks); - if (NULL != client) - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); - return GNUNET_OK; -} - - -/** - * First connection has come to the listening socket associated with the service, - * create the service in order to relay the incoming connection to it - * - * @param cls callback data, struct ServiceListeningInfo describing a listen socket - * @param tc context - */ -static void -acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct ServiceListeningInfo *sli = cls; - struct ServiceListeningInfo *pos; - struct ServiceListeningInfo *next; - SOCKTYPE *lsocks; - unsigned int ls; - - sli->acceptTask = GNUNET_SCHEDULER_NO_TASK; - if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) - return; - GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, - serviceListeningInfoList_tail, sli); - lsocks = NULL; - ls = 0; - next = serviceListeningInfoList_head; - while (NULL != (pos = next)) - { - next = pos->next; - if (0 == strcmp (pos->serviceName, sli->serviceName)) + sl = running_head; + while (sl != NULL) { - GNUNET_array_append (lsocks, ls, - GNUNET_NETWORK_get_fd (pos->listeningSocket)); - GNUNET_free (pos->listeningSocket); /* deliberately no closing! */ - GNUNET_free (pos->service_addr); - GNUNET_free (pos->serviceName); - GNUNET_SCHEDULER_cancel (pos->acceptTask); - GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, - serviceListeningInfoList_tail, pos); - GNUNET_free (pos); + if (0 == strcmp (sl->name, name)) + return sl; + sl = sl->next; } - } - GNUNET_array_append (lsocks, ls, - GNUNET_NETWORK_get_fd (sli->listeningSocket)); - GNUNET_free (sli->listeningSocket); /* deliberately no closing! */ - GNUNET_free (sli->service_addr); -#if WINDOWS - GNUNET_array_append (lsocks, ls, INVALID_SOCKET); -#else - GNUNET_array_append (lsocks, ls, -1); -#endif - start_service (NULL, sli->serviceName, lsocks); - ls = 0; - while (lsocks[ls] != -1) -#if WINDOWS - GNUNET_break (0 == closesocket (lsocks[ls++])); -#else - GNUNET_break (0 == close (lsocks[ls++])); -#endif - GNUNET_array_grow (lsocks, ls, 0); - GNUNET_free (sli->serviceName); - GNUNET_free (sli); -} - - -/** - * Creating a listening socket for each of the service's addresses and - * wait for the first incoming connection to it - * - * @param sa address associated with the service - * @param addr_len length of sa - * @param serviceName the name of the service in question - */ -static void -createListeningSocket (struct sockaddr *sa, socklen_t addr_len, - const char *serviceName) -{ - const static int on = 1; - struct GNUNET_NETWORK_Handle *sock; - struct ServiceListeningInfo *serviceListeningInfo; - - switch (sa->sa_family) - { - case AF_INET: - sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); - break; - case AF_INET6: - sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); - break; - case AF_UNIX: - if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */ - return; - sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); - break; - default: - GNUNET_break (0); - sock = NULL; - errno = EAFNOSUPPORT; - break; - } - if (NULL == sock) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Unable to create socket for service `%s': %s\n"), - serviceName, STRERROR (errno)); - GNUNET_free (sa); - return; - } - if (GNUNET_NETWORK_socket_setsockopt - (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "setsockopt"); -#ifdef IPV6_V6ONLY - if ((sa->sa_family == AF_INET6) && - (GNUNET_NETWORK_socket_setsockopt - (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "setsockopt"); -#endif - - if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len) - != GNUNET_OK) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ - ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), - serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno)); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - GNUNET_free (sa); - return; - } - if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - GNUNET_free (sa); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("ARM now monitors connections to service `%s' at `%s'\n"), - serviceName, GNUNET_a2s (sa, addr_len)); - serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); - serviceListeningInfo->serviceName = GNUNET_strdup (serviceName); - serviceListeningInfo->service_addr = sa; - serviceListeningInfo->service_addr_len = addr_len; - serviceListeningInfo->listeningSocket = sock; - serviceListeningInfo->acceptTask = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock, - &acceptConnection, serviceListeningInfo); - GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head, - serviceListeningInfoList_tail, - serviceListeningInfo); -} - - -/** - * Callback function, checks whether the current tokens are representing a service, - * gets its addresses and create listening socket for it. - * - * @param cls callback data, not used - * @param section configuration section - * @param option configuration option - * @param value the option's value - */ -static void -checkPortNumberCB (void *cls, const char *section, const char *option, - const char *value) -{ - struct sockaddr **addrs; - socklen_t *addr_lens; - int ret; - unsigned int i; - - if ((strcasecmp (section, "arm") == 0) || - (strcasecmp (option, "AUTOSTART") != 0) || - (strcasecmp (value, "YES") != 0) || - (isInDefaultList (section) == GNUNET_YES)) - return; - if (0 >= - (ret = - GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens))) - return; - /* this will free (or capture) addrs[i] */ - for (i = 0; i < ret; i++) - createListeningSocket (addrs[i], addr_lens[i], section); - GNUNET_free (addrs); - GNUNET_free (addr_lens); -} - - -/** - * Entry point to the Service Manager - * - * @param configurationHandle configuration to use to get services - */ -static void -prepare_services (const struct GNUNET_CONFIGURATION_Handle *configurationHandle) -{ - char *defaultServicesString; - - cfg = configurationHandle; - /* Split the default services into a list */ - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES", - &defaultServicesString)) - { - addDefaultServicesToList (defaultServicesString); - GNUNET_free (defaultServicesString); - } - /* Spot the services from the configuration and create a listening - * socket for each */ - GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL); + return NULL; } - /** - * If the configuration file changes, restart tasks that depended on that - * option. + * First connection has come to the listening socket associated with the service, + * create the service in order to relay the incoming connection to it * - * @param cls closure, NULL if we need to self-restart + * @param cls callback data, struct ServiceListeningInfo describing a listen socket * @param tc context */ static void -config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct ServiceList *pos; - struct stat sbuf; + struct ServiceListeningInfo *sli = cls; + struct ServiceList *sl = sli->sl; - pos = running_head; - while (pos != NULL) - { - /* FIXME: this test for config change may be a bit too coarse grained */ - if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime) && - (pos->proc != NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Restarting service `%s' due to configuration file change.\n")); - if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - else - pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS; - } - pos = pos->next; - } + sli->accept_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) + return; + start_process (sl); } /** - * Remove and free an entry in the service list. + * Creating a listening socket for each of the service's addresses and + * wait for the first incoming connection to it * - * @param pos entry to free + * @param sa address associated with the service + * @param addr_len length of sa + * @param sl service entry for the service in question */ static void -free_service (struct ServiceList *pos) +create_listen_socket (struct sockaddr *sa, socklen_t addr_len, + struct ServiceList *sl) { - GNUNET_CONTAINER_DLL_remove (running_head, running_tail, pos); - GNUNET_free_non_null (pos->config); - GNUNET_free_non_null (pos->binary); - GNUNET_free (pos->name); - GNUNET_free (pos); + const static int on = 1; + struct GNUNET_NETWORK_Handle *sock; + struct ServiceListeningInfo *sli; + + switch (sa->sa_family) + { + case AF_INET: + sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); + break; + case AF_INET6: + sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); + break; + case AF_UNIX: + if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */ + return; + sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); + break; + default: + GNUNET_break (0); + sock = NULL; + errno = EAFNOSUPPORT; + break; + } + if (NULL == sock) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Unable to create socket for service `%s': %s\n"), + sl->name, STRERROR (errno)); + GNUNET_free (sa); + return; + } + if (GNUNET_NETWORK_socket_setsockopt + (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); +#ifdef IPV6_V6ONLY + if ((sa->sa_family == AF_INET6) && + (GNUNET_NETWORK_socket_setsockopt + (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); +#endif + + if (GNUNET_NETWORK_socket_bind + (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), + sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno)); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + GNUNET_free (sa); + return; + } + if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + GNUNET_free (sa); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("ARM now monitors connections to service `%s' at `%s'\n"), + sl->name, GNUNET_a2s (sa, addr_len)); + sli = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); + sli->service_addr = sa; + sli->service_addr_len = addr_len; + sli->listen_socket = sock; + sli->sl = sl; + sli->accept_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock, + &accept_connection, sli); + GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli); } /** - * Stop the specified service. + * Remove and free an entry in the service list. Listen sockets + * must have already been cleaned up. Only to be called during shutdown. * - * @param client who is asking for this - * @param servicename name of the service to stop + * @param sl entry to free */ static void -stop_service (struct GNUNET_SERVER_Client *client, const char *servicename) +free_service (struct ServiceList *sl) { - struct ServiceList *pos; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Preparing to stop `%s'\n"), - servicename); - pos = find_service (servicename); - if (pos == NULL) - { - if (GNUNET_OK == stop_listening (servicename)) - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - else - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - if (pos->killing_client != NULL) - { - /* killing already in progress */ -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' is already down\n", - servicename); -#endif - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - - if (GNUNET_YES == in_shutdown) - { -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Termination request already sent to `%s' (since ARM is in shutdown).\n", - servicename); -#endif - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - if (pos->proc == NULL) - { - /* process is in delayed restart, simply remove it! */ - free_service (pos); - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending kill signal to service `%s', waiting for process to die.\n", - servicename); -#endif - if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - pos->killing_client = client; - GNUNET_SERVER_client_keep (client); + GNUNET_assert (GNUNET_YES == in_shutdown); + GNUNET_CONTAINER_DLL_remove (running_head, running_tail, sl); + GNUNET_assert (NULL == sl->listen_head); + GNUNET_free_non_null (sl->config); + GNUNET_free_non_null (sl->binary); + GNUNET_free (sl->name); + GNUNET_free (sl); } @@ -922,22 +553,40 @@ stop_service (struct GNUNET_SERVER_Client *client, const char *servicename) */ static void handle_start (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) + const struct GNUNET_MessageHeader *message) { const char *servicename; + struct ServiceList *sl; uint16_t size; size = ntohs (message->size); size -= sizeof (struct GNUNET_MessageHeader); servicename = (const char *) &message[1]; if ((size == 0) || (servicename[size - 1] != '\0')) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - start_service (client, servicename, NULL); - GNUNET_SERVER_receive_done (client, GNUNET_OK); + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (GNUNET_YES == in_shutdown) + { + signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); + return; + } + sl = find_service (servicename); + if (NULL == sl) + { + signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); + return; + } + sl->is_default = GNUNET_YES; + if (sl->proc != NULL) + { + signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_RUNNING); + return; + } + start_process (sl); + signal_result (client, servicename, GNUNET_ARM_PROCESS_STARTING); } @@ -952,8 +601,9 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client, */ static void handle_stop (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) + const struct GNUNET_MessageHeader *message) { + struct ServiceList *sl; const char *servicename; uint16_t size; @@ -961,33 +611,48 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client, size -= sizeof (struct GNUNET_MessageHeader); servicename = (const char *) &message[1]; if ((size == 0) || (servicename[size - 1] != '\0')) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - stop_service (client, servicename); -} - - -/** - * Remove all entries for tasks that are not running - * (proc = NULL) from the running list (they will no longer - * be restarted since we are shutting down). - */ -static void -clean_up_running () -{ - struct ServiceList *pos; - struct ServiceList *next; - - next = running_head; - while (NULL != (pos = next)) - { - next = pos->next; - if (pos->proc == NULL) - free_service (pos); - } + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Preparing to stop `%s'\n"), servicename); + sl = find_service (servicename); + if (sl == NULL) + { + signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); + return; + } + sl->is_default = GNUNET_NO; + if (GNUNET_YES == in_shutdown) + { + /* shutdown in progress */ + signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); + return; + } + if (sl->killing_client != NULL) + { + /* killing already in progress */ + signal_result (client, servicename, + GNUNET_ARM_PROCESS_ALREADY_STOPPING); + return; + } + if (sl->proc == NULL) + { + /* process is down */ + signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN); + return; + } +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending kill signal to service `%s', waiting for process to die.\n", + servicename); +#endif + if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + sl->killing_client = client; + GNUNET_SERVER_client_keep (client); } @@ -999,15 +664,15 @@ static void do_shutdown () { if (NULL != server) - { - GNUNET_SERVER_destroy (server); - server = NULL; - } + { + GNUNET_SERVER_destroy (server); + server = NULL; + } if (GNUNET_SCHEDULER_NO_TASK != child_death_task) - { - GNUNET_SCHEDULER_cancel (child_death_task); - child_death_task = GNUNET_SCHEDULER_NO_TASK; - } + { + GNUNET_SCHEDULER_cancel (child_death_task); + child_death_task = GNUNET_SCHEDULER_NO_TASK; + } } @@ -1022,33 +687,50 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ServiceList *pos; struct ServiceList *nxt; + struct ServiceListeningInfo *sli; -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n")); -#endif if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) - { - GNUNET_SCHEDULER_cancel (child_restart_task); - child_restart_task = GNUNET_SCHEDULER_NO_TASK; - } + { + GNUNET_SCHEDULER_cancel (child_restart_task); + child_restart_task = GNUNET_SCHEDULER_NO_TASK; + } in_shutdown = GNUNET_YES; - stop_listening (NULL); - pos = running_head; - while (NULL != pos) - { - nxt = pos->next; - if (pos->proc != NULL) + /* first, stop listening */ + for (pos = running_head; NULL != pos; pos = pos->next) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", pos->name); - if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + while (NULL != (sli = pos->listen_head)) + { + GNUNET_CONTAINER_DLL_remove (pos->listen_head, + pos->listen_tail, sli); + if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (sli->accept_task); + sli->accept_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (sli->listen_socket)); + GNUNET_free (sli->service_addr); + GNUNET_free (sli); + } } - else + /* then, shutdown all existing service processes */ + nxt = running_head; + while (NULL != (pos = nxt)) { - free_service (pos); + nxt = pos->next; + if (pos->proc != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", + pos->name); + if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + } + else + { + free_service (pos); + } } - pos = nxt; - } + /* finally, should all service processes be already gone, terminate for real */ if (running_head == NULL) do_shutdown (); } @@ -1061,10 +743,12 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * @param tc context */ static void -delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +delayed_restart_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct ServiceList *pos; + struct ServiceList *sl; struct GNUNET_TIME_Relative lowestRestartDelay; + struct ServiceListeningInfo *sli; child_restart_task = GNUNET_SCHEDULER_NO_TASK; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) @@ -1074,37 +758,56 @@ delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /* check for services that need to be restarted due to * configuration changes or because the last restart failed */ - pos = running_head; - while (pos != NULL) - { - if (pos->proc == NULL) + for (sl = running_head; NULL != sl; sl = sl->next) { - if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).rel_value == 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Restarting service `%s'.\n"), - pos->name); - start_process (pos, NULL); - } - else - { - lowestRestartDelay = - GNUNET_TIME_relative_min (lowestRestartDelay, - GNUNET_TIME_absolute_get_remaining - (pos->restartAt)); - } + if (sl->proc == NULL) + { + /* service is currently not running */ + if (GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value == + 0) + { + /* restart is now allowed */ + if (sl->is_default) + { + /* process should run by default, start immediately */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Restarting service `%s'.\n"), sl->name); + start_process (sl); + } + else + { + /* process is run on-demand, ensure it is re-started if there is demand */ + for (sli = sl->listen_head; NULL != sli; sli = sli->next) + if (GNUNET_SCHEDULER_NO_TASK == sli->accept_task) + { + /* accept was actually paused, so start it again */ + sli->accept_task = + GNUNET_SCHEDULER_add_read_net + (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket, + &accept_connection, sli); + } + } + } + else + { + /* update calculation for earliest time to reactivate a service */ + lowestRestartDelay = + GNUNET_TIME_relative_min (lowestRestartDelay, + GNUNET_TIME_absolute_get_remaining + (sl->restart_at)); + } + } } - pos = pos->next; - } if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value) - { + { #if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n", - (unsigned long long) lowestRestartDelay.rel_value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n", + (unsigned long long) lowestRestartDelay.rel_value); #endif - child_restart_task = - GNUNET_SCHEDULER_add_delayed (lowestRestartDelay, &delayed_restart_task, - NULL); - } + child_restart_task = + GNUNET_SCHEDULER_add_delayed (lowestRestartDelay, + &delayed_restart_task, NULL); + } } @@ -1120,126 +823,150 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ServiceList *pos; struct ServiceList *next; + struct ServiceListeningInfo *sli; const char *statstr; int statcode; int ret; char c[16]; enum GNUNET_OS_ProcessStatusType statusType; unsigned long statusCode; + const struct GNUNET_DISK_FileHandle *pr; + pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); child_death_task = GNUNET_SCHEDULER_NO_TASK; if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) - { - /* shutdown scheduled us, ignore! */ - child_death_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, - &maint_child_death, NULL); - return; - } + { + /* shutdown scheduled us, ignore! */ + child_death_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + pr, &maint_child_death, NULL); + return; + } /* consume the signal */ GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); /* check for services that died (WAITPID) */ next = running_head; while (NULL != (pos = next)) - { - next = pos->next; - if (pos->proc == NULL) - continue; - if ((GNUNET_SYSERR == - (ret = GNUNET_OS_process_status (pos->proc, &statusType, &statusCode))) - || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) || - (statusType == GNUNET_OS_PROCESS_RUNNING))) - continue; - - if (statusType == GNUNET_OS_PROCESS_EXITED) - { - statstr = _( /* process termination method */ "exit"); - statcode = statusCode; - } - else if (statusType == GNUNET_OS_PROCESS_SIGNALED) - { - statstr = _( /* process termination method */ "signal"); - statcode = statusCode; - } - else - { - statstr = _( /* process termination method */ "unknown"); - statcode = 0; - } - GNUNET_OS_process_close (pos->proc); - pos->proc = NULL; - if (NULL != pos->killing_client) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' stopped\n"), - pos->name); - signal_result (pos->killing_client, pos->name, - GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); - GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK); - GNUNET_SERVER_client_drop (pos->killing_client); - free_service (pos); - continue; - } - if (GNUNET_YES != in_shutdown) - { - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ - ("Service `%s' terminated with status %s/%d, will try to restart it!\n"), - pos->name, statstr, statcode); - /* schedule restart */ - pos->restartAt = GNUNET_TIME_relative_to_absolute (pos->backoff); - if (pos->backoff.rel_value < EXPONENTIAL_BACKOFF_THRESHOLD) - pos->backoff = GNUNET_TIME_relative_multiply (pos->backoff, 2); - 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); + next = pos->next; + + if (pos->proc == NULL) + { + if (GNUNET_YES == in_shutdown) + free_service (pos); + continue; + } + if ((GNUNET_SYSERR == + (ret = + GNUNET_OS_process_status (pos->proc, &statusType, &statusCode))) + || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) + || (statusType == GNUNET_OS_PROCESS_RUNNING))) + continue; + if (statusType == GNUNET_OS_PROCESS_EXITED) + { + statstr = _( /* process termination method */ "exit"); + statcode = statusCode; + } + else if (statusType == GNUNET_OS_PROCESS_SIGNALED) + { + statstr = _( /* process termination method */ "signal"); + statcode = statusCode; + } + else + { + statstr = _( /* process termination method */ "unknown"); + statcode = 0; + } + GNUNET_OS_process_close (pos->proc); + pos->proc = 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); + } + continue; + } + 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; + continue; + } + if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Service `%s' terminated with status %s/%d, will restart in %llu ms\n"), + pos->name, statstr, statcode, pos->backoff.rel_value); + /* schedule restart */ + pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); + pos->backoff = + GNUNET_TIME_relative_min (EXPONENTIAL_BACKOFF_THRESHOLD, + GNUNET_TIME_relative_multiply + (pos->backoff, 2)); + 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); + } } -#if DEBUG_ARM - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Service `%s' terminated with status %s/%d\n", pos->name, - statstr, statcode); -#endif - } child_death_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, - &maint_child_death, NULL); - if (GNUNET_YES == in_shutdown) - clean_up_running (); + 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 (); } +/** + * Transmit our shutdown acknowledgement to the client. + * + * @param cls the 'struct GNUNET_SERVER_Client' + * @param size number of bytes available in buf + * @param buf where to write the message + * @return number of bytes written + */ static size_t transmit_shutdown_ack (void *cls, size_t size, void *buf) { struct GNUNET_SERVER_Client *client = cls; - struct GNUNET_MessageHeader *msg; - - if (size < sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Failed to transmit shutdown ACK.\n")); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return 0; /* client disconnected */ - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting shutdown ACK.\n")); + struct GNUNET_ARM_ResultMessage *msg; + if (size < sizeof (struct GNUNET_ARM_ResultMessage)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Failed to transmit shutdown ACK.\n")); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return 0; /* client disconnected */ + } /* Make the connection flushing for the purpose of ACK transmitting, * needed on W32 to ensure that the message is even received, harmless * on other platforms... */ GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client)); - msg = (struct GNUNET_MessageHeader *) buf; - msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK); - msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + msg = (struct GNUNET_ARM_ResultMessage *) buf; + msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT); + msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage)); + msg->status = htonl ((uint32_t) GNUNET_ARM_PROCESS_SHUTDOWN); GNUNET_SERVER_receive_done (client, GNUNET_OK); GNUNET_SERVER_client_drop (client); - return sizeof (struct GNUNET_MessageHeader); + return sizeof (struct GNUNET_ARM_ResultMessage); } @@ -1252,17 +979,15 @@ transmit_shutdown_ack (void *cls, size_t size, void *buf) */ static void handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) + const struct GNUNET_MessageHeader *message) { + GNUNET_SCHEDULER_shutdown (); GNUNET_SERVER_client_keep (client); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Initiating shutdown as requested by client.\n")); GNUNET_SERVER_notify_transmit_ready (client, - sizeof (struct GNUNET_MessageHeader), - GNUNET_TIME_UNIT_FOREVER_REL, - &transmit_shutdown_ack, client); + sizeof (struct GNUNET_ARM_ResultMessage), + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_shutdown_ack, client); GNUNET_SERVER_client_persist_ (client); - GNUNET_SCHEDULER_shutdown (); } @@ -1274,13 +999,77 @@ static void sighandler_child_death () { static char c; - int old_errno = errno; /* back-up errno */ + int old_errno = errno; /* back-up errno */ GNUNET_break (1 == - GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle - (sigpipe, GNUNET_DISK_PIPE_END_WRITE), - &c, sizeof (c))); - errno = old_errno; /* restore errno */ + GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle + (sigpipe, GNUNET_DISK_PIPE_END_WRITE), + &c, sizeof (c))); + errno = old_errno; /* restore errno */ +} + + +/** + * Setup our service record for the given section in the configuration file + * (assuming the section is for a service). + * + * @param cls unused + * @param section a section in the configuration file + * @return GNUNET_OK (continue) + */ +static void +setup_service (void *cls, const char *section) +{ + struct ServiceList *sl; + char *binary; + char *config; + struct stat sbuf; + struct sockaddr **addrs; + socklen_t *addr_lens; + int ret; + unsigned int i; + + if (strcasecmp (section, "arm") == 0) + return; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary)) + { + /* not a service section */ + return; + } + config = NULL; + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG", + &config)) || + (0 != STAT (config, &sbuf))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Configuration file `%s' for service `%s' not valid: %s\n"), + config, section, + (config == NULL) ? _("option missing") : STRERROR (errno)); + GNUNET_free (binary); + GNUNET_free_non_null (config); + return; + } + sl = GNUNET_malloc (sizeof (struct ServiceList)); + sl->name = GNUNET_strdup (section); + sl->binary = binary; + sl->config = config; + sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS; + sl->restart_at = GNUNET_TIME_UNIT_FOREVER_ABS; + GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl); + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "AUTOSTART")) + return; + if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, + &addrs, &addr_lens))) + return; + /* this will free (or capture) addrs[i] */ + for (i = 0; i < ret; i++) + create_listen_socket (addrs[i], addr_lens[i], sl); + GNUNET_free (addrs); + GNUNET_free (addr_lens); } @@ -1303,64 +1092,68 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv, {NULL, NULL, 0, 0} }; char *defaultservices; - char *pos; + const char *pos; + struct ServiceList *sl; cfg = c; server = serv; GNUNET_assert (serv != NULL); - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - GNUNET_assert (pr != NULL); GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES); GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, - NULL); + NULL); child_death_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, - &maint_child_death, NULL); + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_READ), + &maint_child_death, NULL); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX", - &prefix_command)) + &prefix_command)) prefix_command = GNUNET_strdup (""); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX", - &final_option)) + &final_option)) final_option = GNUNET_strdup (""); + + GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL); + /* start default services... */ if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES", - &defaultservices)) - { -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting default services `%s'\n", - defaultservices); -#endif - if (0 < strlen (defaultservices)) + &defaultservices)) { - pos = strtok (defaultservices, " "); - while (pos != NULL) - { - start_service (NULL, pos, NULL); - pos = strtok (NULL, " "); - } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Starting default services `%s'\n"), defaultservices); + if (0 < strlen (defaultservices)) + { + for (pos = strtok (defaultservices, " "); NULL != pos; + pos = strtok (NULL, " ")) + { + sl = find_service (pos); + if (NULL == sl) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Default service `%s' not configured correctly!\n"), + pos); + continue; + } + sl->is_default = GNUNET_YES; + start_process (sl); + } + } + GNUNET_free (defaultservices); } - GNUNET_free (defaultservices); - } else - { -#if DEBUG_ARM - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No default services configured.\n"); -#endif - } - - /* create listening sockets for future services */ - prepare_services (cfg); + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("No default services configured, GNUnet will not really start right now.\n")); + } /* process client requests */ GNUNET_SERVER_add_handlers (server, handlers); - - /* manage services */ - GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, - &config_change_task, NULL); } @@ -1380,10 +1173,10 @@ main (int argc, char *const *argv) sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO); GNUNET_assert (sigpipe != NULL); shc_chld = - GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); + GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); ret = - (GNUNET_OK == - GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1; + (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1; GNUNET_SIGNAL_handler_uninstall (shc_chld); shc_chld = NULL; GNUNET_DISK_pipe_close (sigpipe); @@ -1391,6 +1184,7 @@ main (int argc, char *const *argv) return ret; } + #ifdef LINUX #include diff --git a/src/arm/mockup-service.c b/src/arm/mockup-service.c index 53a502fa8..1e9748809 100644 --- a/src/arm/mockup-service.c +++ b/src/arm/mockup-service.c @@ -46,7 +46,7 @@ transmit_shutdown_ack (void *cls, size_t size, void *buf) GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting shutdown ACK.\n")); msg = (struct GNUNET_MessageHeader *) buf; - msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK); + msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN); msg->size = htons (sizeof (struct GNUNET_MessageHeader)); GNUNET_SERVER_receive_done (client, GNUNET_OK); GNUNET_SERVER_client_drop (client); diff --git a/src/arm/test_arm_api.c b/src/arm/test_arm_api.c index 5fbbaa6b1..74e225809 100644 --- a/src/arm/test_arm_api.c +++ b/src/arm/test_arm_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009 Christian Grothoff (and other contributing authors) + (C) 2009, 2011 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 @@ -45,18 +45,20 @@ static struct GNUNET_ARM_Handle *arm; static int ok = 1; static void -arm_stopped (void *cls, int success) +arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success) { - if (success != GNUNET_NO) + GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN); + if (success != GNUNET_ARM_PROCESS_DOWN) ok = 3; else if (ok == 1) ok = 0; } + static void -arm_notify_stop (void *cls, int success) +arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus success) { - GNUNET_assert (success == GNUNET_NO); + GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN); #if START_ARM GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); #endif @@ -67,49 +69,50 @@ static void dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen) { if (addr == NULL) - { - if (ok != 0) { - GNUNET_break (0); - ok = 2; + if (ok != 0) + { + GNUNET_break (0); + ok = 2; + } + GNUNET_ARM_stop_service (arm, "resolver", TIMEOUT, &arm_notify_stop, + NULL); + return; } - GNUNET_ARM_stop_service (arm, "resolver", TIMEOUT, &arm_notify_stop, NULL); - return; - } - GNUNET_assert (addr != NULL); + GNUNET_break (addr != NULL); ok = 0; } static void -resolver_notify (void *cls, int success) +resolver_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) { - if (success != GNUNET_YES) - { - GNUNET_break (0); - ok = 2; + if (success != GNUNET_ARM_PROCESS_STARTING) + { + GNUNET_break (0); + ok = 2; #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); #endif - return; - } + return; + } GNUNET_RESOLVER_ip_get ("localhost", AF_INET, TIMEOUT, &dns_notify, NULL); } static void -arm_notify (void *cls, int success) +arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) { - if (success != GNUNET_YES) - { - GNUNET_break (0); - ok = 2; + if (success != GNUNET_ARM_PROCESS_STARTING) + { + GNUNET_break (0); + ok = 2; #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); #endif - } + } GNUNET_ARM_start_service (arm, "resolver", START_TIMEOUT, &resolver_notify, - NULL); + NULL); } @@ -143,9 +146,9 @@ check () GNUNET_GETOPT_OPTION_END }; GNUNET_assert (GNUNET_OK == - GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, - argv, "test-arm-api", "nohelp", options, - &task, NULL)); + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-arm-api", "nohelp", options, + &task, NULL)); return ok; } @@ -157,11 +160,11 @@ main (int argc, char *argv[]) GNUNET_log_setup ("test-arm-api", #if VERBOSE - "DEBUG", + "DEBUG", #else - "WARNING", + "WARNING", #endif - NULL); + NULL); ret = check (); return ret; diff --git a/src/arm/test_exponential_backoff.c b/src/arm/test_exponential_backoff.c index 3ff4d284f..029ed145a 100644 --- a/src/arm/test_exponential_backoff.c +++ b/src/arm/test_exponential_backoff.c @@ -29,22 +29,33 @@ #include "gnunet_protocols.h" #define VERBOSE GNUNET_EXTRA_LOGGING + #define START_ARM GNUNET_YES + #define LOG_BACKOFF GNUNET_NO + #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + #define SERVICE_TEST_TIMEOUT GNUNET_TIME_UNIT_FOREVER_REL + #define FIVE_MILLISECONDS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 5) + static const struct GNUNET_CONFIGURATION_Handle *cfg; + static struct GNUNET_ARM_Handle *arm; + static int ok = 1; static int trialCount; + static struct GNUNET_TIME_Absolute startedWaitingAt; + struct GNUNET_TIME_Relative waitedFor; #if LOG_BACKOFF static FILE *killLogFilePtr; + static char *killLogFileName; #endif @@ -89,7 +100,7 @@ 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_ACK: + * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN: * - service will shutdown * Second call with NULL: * - service has now really shut down. @@ -103,53 +114,56 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) struct ShutdownContext *shutdown_ctx = cls; if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES)) - { - /* Means the other side closed the connection and never confirmed a shutdown */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Service handle shutdown before ACK!\n"); - if (shutdown_ctx->cont != NULL) - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); - GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); - GNUNET_free (shutdown_ctx); - } + { + /* Means the other side closed the connection and never confirmed a shutdown */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Service handle shutdown before ACK!\n"); + if (shutdown_ctx->cont != NULL) + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); + GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); + GNUNET_free (shutdown_ctx); + } else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES)) - { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); -#endif - if (shutdown_ctx->cont != NULL) - shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_NO); - - GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); - GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); - GNUNET_free (shutdown_ctx); - } - else - { - GNUNET_assert (ntohs (msg->size) == sizeof (struct GNUNET_MessageHeader)); - switch (ntohs (msg->type)) { - case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK: #if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received confirmation for service shutdown.\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); #endif - 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); + shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_NO); GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); GNUNET_free (shutdown_ctx); - break; } - } + else + { + GNUNET_assert (ntohs (msg->size) == + sizeof (struct GNUNET_MessageHeader)); + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN: +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received confirmation for service shutdown.\n"); +#endif + 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_NO); + GNUNET_free (shutdown_ctx); + break; + } + } } /** @@ -160,7 +174,7 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) */ void service_shutdown_cancel (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ShutdownContext *shutdown_ctx = cls; @@ -187,21 +201,21 @@ 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_NO); - GNUNET_free (shutdown_ctx); - return 0; /* client disconnected */ - } + { + 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_NO); + 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, 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); + 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)); @@ -224,8 +238,8 @@ write_shutdown (void *cls, size_t size, void *buf) */ static void arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, - struct GNUNET_TIME_Relative timeout, - GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) + struct GNUNET_TIME_Relative timeout, + GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) { struct ShutdownContext *shutdown_ctx; @@ -235,16 +249,17 @@ 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), - timeout, GNUNET_NO, &write_shutdown, - shutdown_ctx); + sizeof (struct GNUNET_MessageHeader), + timeout, GNUNET_NO, &write_shutdown, + shutdown_ctx); } static void -arm_notify_stop (void *cls, int success) +arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus status) { - GNUNET_assert (success == GNUNET_NO); + 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 @@ -256,20 +271,19 @@ kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc); static void -do_nothing_notify (void *cls, int success) +do_nothing_notify (void *cls, enum GNUNET_ARM_ProcessStatus status) { - GNUNET_assert (success == GNUNET_YES); + 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, int success) +arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus status) { - GNUNET_assert (success == GNUNET_YES); + GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING); GNUNET_ARM_start_service (arm, "do-nothing", TIMEOUT, &do_nothing_notify, - NULL); + NULL); } @@ -279,7 +293,8 @@ 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) + const struct GNUNET_SCHEDULER_TaskContext + *tc) { static char a; @@ -287,17 +302,17 @@ do_nothing_restarted_notify_task (void *cls, #if LOG_BACKOFF if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - { - fprintf (killLogFilePtr, "%d.Reason is shutdown!\n", trialCount); - } + { + 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); - } + { + 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, "%d.Service is running!\n", trialCount); + } #endif GNUNET_SCHEDULER_add_now (&kill_task, &a); } @@ -307,7 +322,7 @@ static void do_test (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_CLIENT_service_test ("do-nothing", cfg, TIMEOUT, - &do_nothing_restarted_notify_task, NULL); + &do_nothing_restarted_notify_task, NULL); } @@ -326,29 +341,29 @@ kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL; if (NULL != cbData) - { - waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt); + { + waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt); #if LOG_BACKOFF - fprintf (killLogFilePtr, "Waited for: %llu ms\n", - (unsigned long long) waitedFor.rel_value); + fprintf (killLogFilePtr, "Waited for: %llu ms\n", + (unsigned long long) waitedFor.rel_value); #endif - } + } else - { - waitedFor.rel_value = 0; - } + { + waitedFor.rel_value = 0; + } /* Connect to the doNothing task */ doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg); GNUNET_assert (doNothingConnection != NULL); if (trialCount == 12) - { - GNUNET_CLIENT_disconnect (doNothingConnection, GNUNET_NO); - GNUNET_ARM_stop_service (arm, "do-nothing", TIMEOUT, &arm_notify_stop, - NULL); - ok = 0; - return; - } + { + GNUNET_CLIENT_disconnect (doNothingConnection, GNUNET_NO); + GNUNET_ARM_stop_service (arm, "do-nothing", TIMEOUT, &arm_notify_stop, + NULL); + ok = 0; + return; + } /* Use the created connection to kill the doNothingTask */ arm_service_shutdown (doNothingConnection, TIMEOUT, &shutdown_cont, NULL); } @@ -363,7 +378,7 @@ task (void *cls, char *const *args, const char *cfgfile, arm = GNUNET_ARM_connect (cfg, NULL); #if START_ARM GNUNET_ARM_start_service (arm, "arm", GNUNET_TIME_UNIT_ZERO, &arm_notify, - NULL); + NULL); #else arm_do_nothing (NULL, GNUNET_YES); #endif @@ -387,9 +402,9 @@ check () /* Running ARM and running the do_nothing task */ GNUNET_assert (GNUNET_OK == - GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, - argv, "test-exponential-backoff", "nohelp", - options, &task, NULL)); + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-exponential-backoff", + "nohelp", options, &task, NULL)); return ok; @@ -401,12 +416,12 @@ init () #if LOG_BACKOFF killLogFileName = GNUNET_DISK_mktemp ("exponential-backoff-waiting.log"); if (NULL == (killLogFilePtr = FOPEN (killLogFileName, "w"))) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", - killLogFileName); - GNUNET_free (killLogFileName); - return GNUNET_SYSERR; - } + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", + killLogFileName); + GNUNET_free (killLogFileName); + return GNUNET_SYSERR; + } #endif return GNUNET_OK; } @@ -429,11 +444,11 @@ main (int argc, char *argv[]) GNUNET_log_setup ("test-exponential-backoff", #if VERBOSE - "DEBUG", + "DEBUG", #else - "WARNING", + "WARNING", #endif - NULL); + NULL); init (); ret = check (); diff --git a/src/arm/test_gnunet_arm.sh b/src/arm/test_gnunet_arm.sh index d36c2ca38..4a5b7266d 100755 --- a/src/arm/test_gnunet_arm.sh +++ b/src/arm/test_gnunet_arm.sh @@ -39,28 +39,6 @@ fi sleep 1 echo "PASS" -# ---------------------------------------------------------------------------------- -echo -n "TEST: Test -t on running service... " -if ! $exe $DEBUG -t resolver > $out; then - echo "FAIL: error running $exe" - exit 1 -fi -LINES=`cat $out | grep resolver | grep not | wc -l` -if test $LINES -ne 0; then - echo "FAIL: unexpected output:" - cat $out - $exe -e - exit 1 -fi -LINES=`cat $out | grep resolver | grep -v not | wc -l` -if test $LINES -ne 1; then - echo "FAIL: unexpected output" - cat $out - $exe -e - exit 1 -fi -echo "PASS" - # ---------------------------------------------------------------------------------- echo -n "TEST: Stop a service... " @@ -72,23 +50,6 @@ fi sleep 1 echo "PASS" -# ---------------------------------------------------------------------------------- -echo -n "TEST: Test -t on stopped service... " -if ! $exe $DEBUG -t resolver > $out; then - echo "FAIL: error running $exe" - cat $out - $exe -e > /dev/null - exit 1 -fi -LINES=`cat $out | grep resolver | grep not | wc -l` -if test $LINES -ne 1; then - echo "FAIL: unexpected output" - cat $out - $exe -e > /dev/null - exit 1 -fi -echo "PASS" - # ---------------------------------------------------------------------------------- echo -n "TEST: Stop ARM... " diff --git a/src/arm/test_gnunet_service_manager.c b/src/arm/test_gnunet_service_manager.c index 100e1569c..2be86e79f 100644 --- a/src/arm/test_gnunet_service_manager.c +++ b/src/arm/test_gnunet_service_manager.c @@ -51,17 +51,17 @@ static struct GNUNET_ARM_Handle *arm; #endif static void -arm_stopped (void *cls, int success) +arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success) { - if (success != GNUNET_NO) - { - GNUNET_break (0); - ret = 4; - } + if (success != GNUNET_ARM_PROCESS_DOWN) + { + GNUNET_break (0); + ret = 4; + } else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM stopped\n"); - } + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM stopped\n"); + } #if START_ARM GNUNET_ARM_disconnect (arm); arm = NULL; @@ -74,15 +74,16 @@ 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_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n"); #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); #endif - ret = 3; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resolved hostname, now stopping ARM\n"); + ret = 3; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolved hostname, now stopping ARM\n"); ret = 0; #if START_ARM GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); @@ -91,27 +92,28 @@ hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen) static void -arm_notify (void *cls, int success) +arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) { - if (success != GNUNET_YES) - { - GNUNET_break (0); - ret = 1; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to resolve our own hostname!\n"); + if (success != GNUNET_ARM_PROCESS_STARTING) + { + GNUNET_break (0); + ret = 1; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "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_RESOLVER_hostname_resolve (AF_UNSPEC, TIMEOUT, + &hostNameResolveCB, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable initiate connection to resolver service\n"); + ret = 2; #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); + GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); #endif - } + } } @@ -144,9 +146,9 @@ check () GNUNET_GETOPT_OPTION_END }; GNUNET_assert (GNUNET_OK == - GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, - argv, "test-gnunet-service-manager", - "nohelp", options, &run, NULL)); + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-gnunet-service-manager", + "nohelp", options, &run, NULL)); } @@ -156,27 +158,28 @@ main (int argc, char *argv[]) char hostname[GNUNET_OS_get_hostname_max_length () + 1]; if (0 != gethostname (hostname, sizeof (hostname) - 1)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "gethostname"); - fprintf (stderr, - "Failed to determine my own hostname, testcase not run.\n"); - return 0; - } + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "gethostname"); + fprintf (stderr, + "Failed to determine my own hostname, testcase not run.\n"); + return 0; + } if (NULL == gethostbyname (hostname)) - { - fprintf (stderr, "Failed to resolve my hostname `%s', testcase not run.\n", - hostname); - return 0; - } + { + fprintf (stderr, + "Failed to resolve my hostname `%s', testcase not run.\n", + hostname); + return 0; + } GNUNET_log_setup ("test-gnunet-service-manager", #if VERBOSE - "DEBUG", + "DEBUG", #else - "WARNING", + "WARNING", #endif - NULL); + NULL); check (); return ret; } diff --git a/src/include/gnunet_arm_service.h b/src/include/gnunet_arm_service.h index 6d52773de..af1c8cd94 100644 --- a/src/include/gnunet_arm_service.h +++ b/src/include/gnunet_arm_service.h @@ -42,19 +42,74 @@ extern "C" /** * Version of the arm API. */ -#define GNUNET_ARM_VERSION 0x00000000 +#define GNUNET_ARM_VERSION 0x00000001 + + +/** + * Values characterizing GNUnet process states. + */ +enum GNUNET_ARM_ProcessStatus +{ + /** + * Service name is unknown to ARM. + */ + GNUNET_ARM_PROCESS_UNKNOWN = -1, + + /** + * Service is now down (due to client request). + */ + GNUNET_ARM_PROCESS_DOWN = 0, + + /** + * Service is already running. + */ + GNUNET_ARM_PROCESS_ALREADY_RUNNING = 1, + + /** + * Service is currently being started (due to client request). + */ + GNUNET_ARM_PROCESS_STARTING = 2, + + /** + * Service is already being stopped by some other client. + */ + GNUNET_ARM_PROCESS_ALREADY_STOPPING = 3, + + /** + * Service is already down (no action taken) + */ + GNUNET_ARM_PROCESS_ALREADY_DOWN = 4, + + /** + * ARM is currently being shut down (no more process starts) + */ + GNUNET_ARM_PROCESS_SHUTDOWN = 5, + + /** + * Error in communication with ARM + */ + GNUNET_ARM_PROCESS_COMMUNICATION_ERROR = 6, + + /** + * Timeout in communication with ARM + */ + GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT = 7, + + /** + * Failure to perform operation + */ + GNUNET_ARM_PROCESS_FAILURE = 8 +}; /** * Callback function invoked when operation is complete. * * @param cls closure - * @param success GNUNET_YES if we think the service is running - * GNUNET_NO if we think the service is stopped - * GNUNET_SYSERR if we think ARM was not running or - * if the service status is unknown + * @param result outcome of the operation */ -typedef void (*GNUNET_ARM_Callback) (void *cls, int success); +typedef void (*GNUNET_ARM_Callback) (void *cls, + enum GNUNET_ARM_ProcessStatus result); /** diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index e959a5cf9..5b9f0612a 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -78,31 +78,15 @@ extern "C" #define GNUNET_MESSAGE_TYPE_ARM_STOP 9 /** - * Response from ARM: service is now up. + * Request ARM service itself to shutdown. */ -#define GNUNET_MESSAGE_TYPE_ARM_IS_UP 10 +#define GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN 10 /** - * Response from ARM: service is now down. - * (failed to start it or shut it down). + * Response from ARM. */ -#define GNUNET_MESSAGE_TYPE_ARM_IS_DOWN 11 +#define GNUNET_MESSAGE_TYPE_ARM_RESULT 11 -/** - * Response from ARM: service status is unknown. - */ -#define GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN 12 - -/** - * Request ARM service shutdown. - */ -#define GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN 13 - -/** - * Acknowledge service shutting down, disconnect - * indicates service stopped. - */ -#define GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK 14 /******************************************************************************* * HELLO message types -- cgit v1.2.3