+/**
+ * Handler receiving response to service shutdown requests.
+ * First call with NULL: service misbehaving, or something.
+ * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
+ * - service will shutdown
+ * Second call with NULL:
+ * - service has now really shut down.
+ *
+ * @param cls closure
+ * @param msg NULL, indicating socket closure.
+ */
+static void
+service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+ struct ShutdownContext *shutdown_ctx = cls;
+
+ if (NULL != msg)
+ {
+ /* We just expected a disconnect! Report the error and be done with it... */
+ GNUNET_break (0);
+ shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
+ GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
+ GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+ GNUNET_free (shutdown_ctx);
+ return;
+ }
+ if (NULL != shutdown_ctx->cont)
+ /* shutdown is now complete, as we waited for the network disconnect... */
+ shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN);
+ GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
+ GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+ GNUNET_free (shutdown_ctx);
+}
+
+
+/**
+ * Shutting down took too long, cancel receive and return error.
+ *
+ * @param cls closure
+ * @param tc context information (why was this task triggered now)
+ */
+static void
+service_shutdown_cancel (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ShutdownContext *shutdown_ctx = cls;
+
+ shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT);
+ GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+ GNUNET_free (shutdown_ctx);
+}
+
+
+/**
+ * If possible, write a shutdown message to the target
+ * buffer and destroy the client connection.
+ *
+ * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
+ * @param size number of bytes available in buf
+ * @param buf NULL on error, otherwise target buffer
+ * @return number of bytes written to buf
+ */
+static size_t
+write_shutdown (void *cls, size_t size, void *buf)
+{
+ struct ShutdownContext *shutdown_ctx = cls;
+ struct GNUNET_MessageHeader *msg;
+
+ shutdown_ctx->th = NULL;
+ if (size < sizeof (struct GNUNET_MessageHeader))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to transmit shutdown request to client.\n"));
+ shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
+ GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+ GNUNET_free (shutdown_ctx);
+ return 0; /* client disconnected */
+ }
+ GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
+ shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
+ shutdown_ctx->cancel_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
+ (shutdown_ctx->timeout),
+ &service_shutdown_cancel, shutdown_ctx);
+ msg = (struct GNUNET_MessageHeader *) buf;
+ msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
+ msg->size = htons (sizeof (struct GNUNET_MessageHeader));
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+/**
+ * Request that the service should shutdown.
+ * Afterwards, the connection will automatically be
+ * disconnected. Hence the "sock" should not
+ * be used by the caller after this call
+ * (calling this function frees "sock" after a while).
+ *
+ * @param sock the socket connected to the service
+ * @param timeout how long to wait before giving up on transmission
+ * @param cont continuation to call once the service is really down
+ * @param cont_cls closure for continuation
+ *
+ */
+static void
+arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
+{
+ struct ShutdownContext *shutdown_ctx;
+
+ shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
+ shutdown_ctx->cont = cont;
+ shutdown_ctx->cont_cls = cont_cls;
+ shutdown_ctx->sock = sock;
+ shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ shutdown_ctx->th = GNUNET_CLIENT_notify_transmit_ready (sock,
+ sizeof (struct GNUNET_MessageHeader),
+ timeout, GNUNET_NO, &write_shutdown,
+ shutdown_ctx);
+}
+
+