From cbf929c31e8afdd30b2ba6a6bf85c7942f9afac5 Mon Sep 17 00:00:00 2001 From: "Nathan S. Evans" Date: Thu, 6 May 2010 09:59:08 +0000 Subject: [PATCH] arm related changes to util things, enabling leaky sockets and shutdown messages from services --- src/util/client.c | 152 ++++++++++++++++++++++++++++++++++++++-- src/util/connection.c | 26 ++++++- src/util/server.c | 30 +++++++- src/util/service.c | 55 ++++++++++++++- src/util/test_server.c | 4 +- src/util/test_service.c | 17 +++-- 6 files changed, 264 insertions(+), 20 deletions(-) diff --git a/src/util/client.c b/src/util/client.c index 86b36bfbb..1b6f46d52 100644 --- a/src/util/client.c +++ b/src/util/client.c @@ -136,6 +136,46 @@ struct TransmitGetResponseContext void *rn_cls; }; +/** + * Context for handling the shutdown of a service. + */ +struct ShutdownContext +{ + /** + * Scheduler to be used to call continuation + */ + struct GNUNET_SCHEDULER_Handle *sched; + /** + * Connection to the service that is being shutdown. + */ + struct GNUNET_CLIENT_Connection *sock; + + /** + * Time allowed for shutdown to happen. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Task set up to cancel the shutdown request on timeout. + */ + GNUNET_SCHEDULER_TaskIdentifier cancel_task; + + /** + * Task to call once shutdown complete + */ + GNUNET_CLIENT_ShutdownTask cont; + + /** + * Closure for shutdown continuation + */ + void *cont_cls; + + /** + * We received a confirmation that the service will shut down. + */ + int confirmed; + +}; /** * Struct to refer to a GNUnet TCP connection. @@ -540,6 +580,85 @@ GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock, } +/** + * Handler receiving response to service shutdown requests. + * First call with NULL: service misbehaving, or something. + * First call with GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK: + * - service will shutdown + * First call with GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE: + * - service will not be stopped! + * + * Second call with NULL: + * - service has now really shut down. + * + * @param cls closure + * @param msg NULL, indicating socket closure. + */ +static void +service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct ShutdownContext *shutdown_ctx = cls; + + if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES)) /* Means the other side closed the connection and never confirmed a shutdown */ + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "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->sched, 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)) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); + if (shutdown_ctx->cont != NULL) + shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_NO); + + GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, 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_SHUTDOWN_ACK: + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Received confirmation for service shutdown.\n"); + shutdown_ctx->confirmed = GNUNET_YES; + GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); + break; + case GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE: + 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->sched, shutdown_ctx->cancel_task); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); + GNUNET_free(shutdown_ctx); + break; + } + } +} + +/** + * Shutting down took too long, cancel receive and return error. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +void service_shutdown_cancel (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + struct ShutdownContext *shutdown_ctx = cls; + fprintf(stderr, "service_shutdown_cancel called!\n"); + shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR); + GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO); + GNUNET_free(shutdown_ctx); +} /** * If possible, write a shutdown message to the target * buffer and destroy the client connection. @@ -553,15 +672,21 @@ static size_t write_shutdown (void *cls, size_t size, void *buf) { struct GNUNET_MessageHeader *msg; - struct GNUNET_CLIENT_Connection *sock = cls; + struct ShutdownContext *shutdown_ctx = cls; - GNUNET_CLIENT_disconnect (sock, GNUNET_YES); if (size < sizeof (struct GNUNET_MessageHeader)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("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->cancel_task = GNUNET_SCHEDULER_add_delayed (shutdown_ctx->sched, GNUNET_TIME_absolute_get_remaining(shutdown_ctx->timeout), &service_shutdown_cancel, shutdown_ctx); msg = (struct GNUNET_MessageHeader *) buf; msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN); msg->size = htons (sizeof (struct GNUNET_MessageHeader)); @@ -576,16 +701,33 @@ write_shutdown (void *cls, size_t size, void *buf) * be used by the caller after this call * (calling this function frees "sock" after a while). * + * @param sched the scheduler to use for calling shutdown continuation * @param sock the socket connected to the service + * @param timeout how long to wait before giving up on transmission + * @param cont continuation to call once the service is really down + * @param cont_cls closure for continuation + * */ void -GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock) +GNUNET_CLIENT_service_shutdown (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CLIENT_Connection *sock, + struct GNUNET_TIME_Relative timeout, + GNUNET_CLIENT_ShutdownTask cont, + void *cont_cls) { + struct ShutdownContext *shutdown_ctx; + shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext)); + shutdown_ctx->sched = sched; + shutdown_ctx->cont = cont; + shutdown_ctx->cont_cls = cont_cls; + shutdown_ctx->sock = sock; + shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute(timeout); + GNUNET_CONNECTION_notify_transmit_ready (sock->sock, sizeof (struct GNUNET_MessageHeader), - GNUNET_TIME_UNIT_FOREVER_REL, - &write_shutdown, sock); + timeout, + &write_shutdown, shutdown_ctx); } diff --git a/src/util/connection.c b/src/util/connection.c index 22a75691b..4a54aed1d 100644 --- a/src/util/connection.c +++ b/src/util/connection.c @@ -279,8 +279,27 @@ struct GNUNET_CONNECTION_Handle */ uint16_t port; + /** + * When shutdown, do not ever actually close the socket, but + * free resources. Only should ever be set if using program + * termination as a signal (because only then will the leaked + * socket be freed!) + */ + int persist; + }; +/** + * Set the persist option on this connection handle. Indicates + * that the underlying socket or fd should never really be closed. + * Used for indicating process death. + * + * @param sock the connection to set persistent + */ +void GNUNET_CONNECTION_persist_(struct GNUNET_CONNECTION_Handle *sock) +{ + sock->persist = GNUNET_YES; +} /** * Create a socket handle by boxing an existing OS socket. The OS @@ -486,7 +505,8 @@ destroy_continuation (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket (%p)\n", sock); #endif - GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR); + if (sock->persist != GNUNET_YES) + GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR); } if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) { @@ -518,8 +538,10 @@ destroy_continuation (void *cls, sock->nth.notify_ready = NULL; notify (sock->nth.notify_ready_cls, 0, NULL); } - if (sock->sock != NULL) + + if ((sock->sock != NULL) && (sock->persist != GNUNET_YES)) GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock)); + GNUNET_free_non_null (sock->addr); GNUNET_free_non_null (sock->hostname); #if DEBUG_CONNECTION diff --git a/src/util/server.c b/src/util/server.c index 1353d4c05..e34897f76 100644 --- a/src/util/server.c +++ b/src/util/server.c @@ -269,6 +269,13 @@ struct GNUNET_SERVER_Client * Are we currently trying to receive? */ int receive_pending; + + /** + * Persist the file handle for this client no matter what happens, + * force the OS to close once the process actually dies. Should only + * be used in special cases! + */ + int persist; }; @@ -933,11 +940,16 @@ sock_check (void *cls) * Destroy this socket (free resources). * * @param cls the socket + * @param persist set the socket to be persisted */ static void -sock_destroy (void *cls) +sock_destroy (void *cls, int persist) { - GNUNET_CONNECTION_destroy (cls, GNUNET_NO); + struct GNUNET_CONNECTION_Handle *sock = cls; + if (persist == GNUNET_YES) + GNUNET_CONNECTION_persist_ (sock); + + GNUNET_CONNECTION_destroy (sock, GNUNET_NO); } @@ -1168,6 +1180,7 @@ GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) client->receive_cancel (client->client_closure); client->receive_pending = GNUNET_NO; } + rc = client->reference_count; if (client->server != NULL) { @@ -1200,7 +1213,7 @@ GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) return; if (client->in_process_client_buffer == GNUNET_YES) return; - client->destroy (client->client_closure); + client->destroy (client->client_closure, client->persist); GNUNET_free (client); } @@ -1232,6 +1245,17 @@ GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, timeout, callback, callback_cls); } +/** + * Set the persistent flag on this client, used to setup client connection + * to only be killed when the service it's connected to is actually dead. + * + * @param client the client to set the persistent flag on + */ +void +GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client) +{ + client->persist = GNUNET_YES; +} /** * Resume receiving from this client, we are done processing the diff --git a/src/util/service.c b/src/util/service.c index e6fec7514..8597fddf6 100644 --- a/src/util/service.c +++ b/src/util/service.c @@ -570,6 +570,45 @@ handle_test (void *cls, } +static size_t +transmit_shutdown_deny (void *cls, size_t size, void *buf) +{ + struct GNUNET_SERVER_Client *client = cls; + struct GNUNET_MessageHeader *msg; + + if (size < sizeof (struct GNUNET_MessageHeader)) + { + return 0; /* client disconnected */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + } + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_SERVER_client_drop(client); + return sizeof (struct GNUNET_MessageHeader); +} + +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)) + { + return 0; /* client disconnected */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + } + + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_SERVER_client_drop(client); + return sizeof (struct GNUNET_MessageHeader); +} + /** * Handler for SHUTDOWN message. * @@ -583,19 +622,31 @@ handle_shutdown (void *cls, const struct GNUNET_MessageHeader *message) { struct GNUNET_SERVICE_Context *service = cls; + + /* FIXME: why is this call necessary???? */ + GNUNET_SERVER_client_keep(client); if (!service->allow_shutdown) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Received shutdown request, but configured to ignore!\n")); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + GNUNET_SERVER_notify_transmit_ready (client, + sizeof(struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_shutdown_deny, client); return; } 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); + GNUNET_assert (service->sched != NULL); + GNUNET_SERVER_client_persist_ (client); GNUNET_SCHEDULER_shutdown (service->sched); - GNUNET_SERVER_receive_done (client, GNUNET_OK); } diff --git a/src/util/test_server.c b/src/util/test_server.c index 901b78a08..5d714d6f9 100644 --- a/src/util/test_server.c +++ b/src/util/test_server.c @@ -152,7 +152,7 @@ my_check (void *cls) } -static void my_destroy (void *cls); +static void my_destroy (void *cls, int persist); struct CopyContext @@ -217,7 +217,7 @@ static struct GNUNET_SERVER_MessageHandler handlers[] = { static void -my_destroy (void *cls) +my_destroy (void *cls, int persist) { int *ok = cls; GNUNET_assert (5 == *ok); diff --git a/src/util/test_service.c b/src/util/test_service.c index a40630c64..544f86fba 100644 --- a/src/util/test_service.c +++ b/src/util/test_service.c @@ -43,6 +43,16 @@ static struct GNUNET_SERVICE_Context *sctx; static int ok = 1; +void +end_cont (void *cls, + int reason) +{ + if (sctx != NULL) + GNUNET_SERVICE_stop (sctx); + else + GNUNET_SCHEDULER_shutdown (sched); + ok = 0; +} static void end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) @@ -50,12 +60,7 @@ end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct GNUNET_CLIENT_Connection *client = cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down service\n"); - GNUNET_CLIENT_service_shutdown (client); - if (sctx != NULL) - GNUNET_SERVICE_stop (sctx); - else - GNUNET_SCHEDULER_shutdown (sched); - ok = 0; + GNUNET_CLIENT_service_shutdown (sched, client, GNUNET_TIME_UNIT_FOREVER_REL, &end_cont, NULL); } -- 2.25.1