From 1948cedcb5e7c5017dcd18ef087f0f7e5aa5f27f Mon Sep 17 00:00:00 2001 From: "Nathan S. Evans" Date: Thu, 6 May 2010 09:54:08 +0000 Subject: [PATCH] main change for gnunet-arm is that gnunet-arm now gets proper confirmation when called to start or stop services. This means that calling gnunet-arm to stop/start a process will return 0 only if the process is stopped/started. For gnunet-service-arm, this is implemented by leaking the client connection socket on a shutdown request, so that it is only closed when the arm service actually shuts down (indicating everything is really down). --- src/arm/arm_api.c | 40 +++++++++++++++-- src/arm/gnunet-arm.c | 71 ++++++++++++++++++++++++++---- src/arm/gnunet-service-arm.c | 17 ++++++- src/arm/test_arm_api.c | 18 ++++++-- src/arm/test_exponential_backoff.c | 30 ++++++++----- 5 files changed, 146 insertions(+), 30 deletions(-) diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c index 69e6ea99e..2f3524d67 100644 --- a/src/arm/arm_api.c +++ b/src/arm/arm_api.c @@ -109,6 +109,20 @@ GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) GNUNET_free (h); } +struct ARM_ShutdownContext +{ + /** + * Callback to call once shutdown complete. + */ + GNUNET_ARM_Callback cb; + + /** + * Closure for callback. + */ + void *cb_cls; +}; + + /** * Internal state for a request with ARM. @@ -421,6 +435,22 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START); } +/** + * Callback from the arm stop service call, indicates that the arm service + * is well and truly dead. + * + * @param cls closure for the callback + * @param tc scheduler context + */ +void arm_shutdown_callback (void *cls, + int reason) +{ + struct ARM_ShutdownContext *arm_shutdown_ctx = cls; + + if (arm_shutdown_ctx->cb != NULL) + arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason); +} + /** * Stop a service. @@ -437,15 +467,19 @@ GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb, void *cb_cls) { + struct ARM_ShutdownContext *arm_shutdown_ctx; + + arm_shutdown_ctx = GNUNET_malloc(sizeof(struct ARM_ShutdownContext)); + arm_shutdown_ctx->cb = cb; + arm_shutdown_ctx->cb_cls = cb_cls; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopping service `%s' within %llu ms\n"), service_name, (unsigned long long) timeout.value); if (0 == strcasecmp ("arm", service_name)) { - GNUNET_CLIENT_service_shutdown (h->client); + GNUNET_CLIENT_service_shutdown (h->sched, h->client, timeout, &arm_shutdown_callback, arm_shutdown_ctx); h->client = NULL; - if (cb != NULL) - cb (cb_cls, GNUNET_NO); return; } change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP); diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c index 2b438b30d..652c74e11 100644 --- a/src/arm/gnunet-arm.c +++ b/src/arm/gnunet-arm.c @@ -31,9 +31,16 @@ #include "gnunet_time_lib.h" /** - * Timeout for all operations. + * Timeout for stopping services. Long to give some services a real chance. */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 50) +#define STOP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20) + +/** + * 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 START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 50) /** * Set if we are to shutdown all services (including ARM). @@ -45,6 +52,11 @@ static int end; */ static int start; +/** + * Set if we are to stop/start default services (including ARM). + */ +static int restart; + /** * Set if we should delete configuration and temp directory on exit. */ @@ -134,17 +146,31 @@ confirm_cb (void *cls, int success) case GNUNET_OK: if (quiet != GNUNET_YES) fprintf(stdout, _("Service `%s' is now running.\n"), service); + if ((phase - 1 != 2) && (phase - 1 != 3)) + { + if (quiet != GNUNET_YES) + fprintf(stdout, _("Failed to stop service `%s'!\n"), service); + ret = 1; + } break; case GNUNET_NO: if (quiet != GNUNET_YES) fprintf(stdout, _("Service `%s' is not running.\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, - _("Error updating service `%s': ARM not running\n"), service); + _("Some error communicating with service `%s'.\n"), service); + ret = 1; break; } + GNUNET_SCHEDULER_add_continuation (sched, &cps_loop, NULL, @@ -260,38 +286,63 @@ cps_loop (void *cls, case 0: if (term != NULL) { - GNUNET_ARM_stop_service (h, term, TIMEOUT, &confirm_cb, term); + GNUNET_ARM_stop_service (h, term, STOP_TIMEOUT, &confirm_cb, term); return; } break; case 1: - if (end) + if ((end) || (restart)) { - GNUNET_ARM_stop_service (h, "arm", TIMEOUT, &confirm_cb, "arm"); + GNUNET_ARM_stop_service (h, "arm", STOP_TIMEOUT, &confirm_cb, "arm"); return; } break; case 2: if (start) { - GNUNET_ARM_start_service (h, "arm", TIMEOUT, &confirm_cb, "arm"); + GNUNET_ARM_start_service (h, "arm", START_TIMEOUT, &confirm_cb, "arm"); return; } break; case 3: if (init != NULL) { - GNUNET_ARM_start_service (h, init, TIMEOUT, &confirm_cb, init); + GNUNET_ARM_start_service (h, init, START_TIMEOUT, &confirm_cb, init); return; } break; case 4: if (test != NULL) { - GNUNET_CLIENT_service_test (sched, test, cfg, TIMEOUT, &confirm_task, test); + GNUNET_CLIENT_service_test (sched, test, cfg, START_TIMEOUT, &confirm_task, test); return; } break; + case 5: + if (restart) /* FIXME: + * Restart should be a legal option but this is a hack. + * The proper thing to do would be have gnunet-service-arm + * signal us when actually shut down, and then initiate + * the startup. Instead we just sleep for two seconds + * and hope that's enough time for shutdown to have happened. + */ + { + GNUNET_ARM_disconnect (h); + phase = 0; + end = 0; + start = 1; + restart = 0; + h = GNUNET_ARM_connect (cfg, sched, NULL); + if (h == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Fatal error initializing ARM API.\n")); + ret = 1; + return; + } + GNUNET_SCHEDULER_add_now(sched, &cps_loop, NULL); + return; + } default: /* last phase */ GNUNET_ARM_disconnect (h); if ((end == GNUNET_YES) && (delete == GNUNET_YES)) @@ -314,6 +365,8 @@ static struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_YES, &GNUNET_GETOPT_set_string, &term}, {'s', "start", NULL, gettext_noop ("start all GNUnet default services"), GNUNET_NO, &GNUNET_GETOPT_set_one, &start}, + {'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}, diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 9de610a01..36fabf7f4 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -54,6 +54,7 @@ */ #define EXPONENTIAL_BACKOFF_THRESHOLD (1000 * 60 * 30) +#define DELAY_SHUTDOWN GNUNET_NO /** * List of our services. @@ -720,6 +721,17 @@ do_shutdown () child_death_task = GNUNET_SCHEDULER_NO_TASK; } +#if DELAY_SHUTDOWN +/** + * Dummy task to delay arm shutdown. + */ +void dummy_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Dummy task executing\n"); + return; +} +#endif /** * Task run for shutdown. @@ -731,7 +743,6 @@ static void shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ServiceList *pos; - #if DEBUG_ARM GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n")); #endif @@ -750,6 +761,9 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } pos = pos->next; } +#if DELAY_SHUTDOWN + GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 2), &dummy_task, NULL); +#endif if (running == NULL) do_shutdown (); } @@ -965,7 +979,6 @@ static void sighandler_child_death () { static char c; - GNUNET_break (1 == GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c, diff --git a/src/arm/test_arm_api.c b/src/arm/test_arm_api.c index 22fa3716e..7fab89ca7 100644 --- a/src/arm/test_arm_api.c +++ b/src/arm/test_arm_api.c @@ -34,7 +34,9 @@ #define START_ARM GNUNET_YES -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) +#define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 50) + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) static struct GNUNET_SCHEDULER_Handle *sched; @@ -44,13 +46,21 @@ static struct GNUNET_ARM_Handle *arm; static int ok = 1; +static void +arm_stopped (void *cls, int success) +{ + if (success != GNUNET_NO) + ok = 1; + else + ok = 0; +} static void arm_notify_stop (void *cls, int success) { GNUNET_assert (success == GNUNET_NO); #if START_ARM - GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, NULL, NULL); + GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); #endif } @@ -83,7 +93,7 @@ static void arm_notify (void *cls, int success) { GNUNET_assert (success == GNUNET_YES); - GNUNET_ARM_start_service (arm, "resolver", TIMEOUT, &resolver_notify, NULL); + GNUNET_ARM_start_service (arm, "resolver", START_TIMEOUT, &resolver_notify, NULL); } @@ -98,7 +108,7 @@ task (void *cls, sched = s; arm = GNUNET_ARM_connect (cfg, sched, NULL); #if START_ARM - GNUNET_ARM_start_service (arm, "arm", TIMEOUT, &arm_notify, NULL); + GNUNET_ARM_start_service (arm, "arm", START_TIMEOUT, &arm_notify, NULL); #else arm_notify (NULL, GNUNET_YES); #endif diff --git a/src/arm/test_exponential_backoff.c b/src/arm/test_exponential_backoff.c index 425e6bd8d..357a5e71a 100644 --- a/src/arm/test_exponential_backoff.c +++ b/src/arm/test_exponential_backoff.c @@ -39,6 +39,10 @@ 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; @@ -90,7 +94,6 @@ do_nothing_restarted_notify_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { static char a; - static int trialCount = 0; trialCount++; @@ -128,17 +131,25 @@ do_test (void *cbData, } +static void +shutdown_cont (void *cls, int reason) +{ + trialCount++; + startedWaitingAt = GNUNET_TIME_absolute_get(); + GNUNET_SCHEDULER_add_delayed (sched, + waitedFor, + &do_test, + NULL); +} static void kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) { static struct GNUNET_CLIENT_Connection * doNothingConnection = NULL; - static struct GNUNET_TIME_Absolute startedWaitingAt; - struct GNUNET_TIME_Relative waitedFor; - static int trialCount = 0; - + if (NULL != cbData) { waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt); + #if LOG_BACKOFF fprintf(killLogFilePtr, "Waited for: %llu ms\n", @@ -164,13 +175,8 @@ kill_task (void *cbData, } /* Use the created connection to kill the doNothingTask */ - GNUNET_CLIENT_service_shutdown(doNothingConnection); - trialCount++; - startedWaitingAt = GNUNET_TIME_absolute_get(); - GNUNET_SCHEDULER_add_delayed (sched, - waitedFor, - &do_test, - NULL); + GNUNET_CLIENT_service_shutdown(sched, doNothingConnection, TIMEOUT, &shutdown_cont, NULL); + } -- 2.25.1