aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/util/client.c152
-rw-r--r--src/util/connection.c26
-rw-r--r--src/util/server.c30
-rw-r--r--src/util/service.c55
-rw-r--r--src/util/test_server.c4
-rw-r--r--src/util/test_service.c17
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
136 void *rn_cls; 136 void *rn_cls;
137}; 137};
138 138
139/**
140 * Context for handling the shutdown of a service.
141 */
142struct ShutdownContext
143{
144 /**
145 * Scheduler to be used to call continuation
146 */
147 struct GNUNET_SCHEDULER_Handle *sched;
148 /**
149 * Connection to the service that is being shutdown.
150 */
151 struct GNUNET_CLIENT_Connection *sock;
152
153 /**
154 * Time allowed for shutdown to happen.
155 */
156 struct GNUNET_TIME_Absolute timeout;
157
158 /**
159 * Task set up to cancel the shutdown request on timeout.
160 */
161 GNUNET_SCHEDULER_TaskIdentifier cancel_task;
162
163 /**
164 * Task to call once shutdown complete
165 */
166 GNUNET_CLIENT_ShutdownTask cont;
167
168 /**
169 * Closure for shutdown continuation
170 */
171 void *cont_cls;
172
173 /**
174 * We received a confirmation that the service will shut down.
175 */
176 int confirmed;
177
178};
139 179
140/** 180/**
141 * Struct to refer to a GNUnet TCP connection. 181 * Struct to refer to a GNUnet TCP connection.
@@ -541,6 +581,85 @@ GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
541 581
542 582
543/** 583/**
584 * Handler receiving response to service shutdown requests.
585 * First call with NULL: service misbehaving, or something.
586 * First call with GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK:
587 * - service will shutdown
588 * First call with GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE:
589 * - service will not be stopped!
590 *
591 * Second call with NULL:
592 * - service has now really shut down.
593 *
594 * @param cls closure
595 * @param msg NULL, indicating socket closure.
596 */
597static void
598service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
599{
600 struct ShutdownContext *shutdown_ctx = cls;
601
602 if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES)) /* Means the other side closed the connection and never confirmed a shutdown */
603 {
604 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Service handle shutdown before ACK!\n");
605 if (shutdown_ctx->cont != NULL)
606 shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
607
608 GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
609 GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
610 GNUNET_free(shutdown_ctx);
611 }
612 else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES))
613 {
614 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
615 if (shutdown_ctx->cont != NULL)
616 shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_NO);
617
618 GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
619 GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
620 GNUNET_free(shutdown_ctx);
621 }
622 else
623 {
624 GNUNET_assert(ntohs(msg->size) == sizeof(struct GNUNET_MessageHeader));
625
626 switch (ntohs(msg->type))
627 {
628 case GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK:
629 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Received confirmation for service shutdown.\n");
630 shutdown_ctx->confirmed = GNUNET_YES;
631 GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
632 break;
633 case GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE:
634 default: /* Fall through */
635 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Service shutdown refused!\n");
636 if (shutdown_ctx->cont != NULL)
637 shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_YES);
638
639 GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
640 GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
641 GNUNET_free(shutdown_ctx);
642 break;
643 }
644 }
645}
646
647/**
648 * Shutting down took too long, cancel receive and return error.
649 *
650 * @param cls closure
651 * @param tc context information (why was this task triggered now)
652 */
653void service_shutdown_cancel (void *cls,
654 const struct GNUNET_SCHEDULER_TaskContext * tc)
655{
656 struct ShutdownContext *shutdown_ctx = cls;
657 fprintf(stderr, "service_shutdown_cancel called!\n");
658 shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
659 GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
660 GNUNET_free(shutdown_ctx);
661}
662/**
544 * If possible, write a shutdown message to the target 663 * If possible, write a shutdown message to the target
545 * buffer and destroy the client connection. 664 * buffer and destroy the client connection.
546 * 665 *
@@ -553,15 +672,21 @@ static size_t
553write_shutdown (void *cls, size_t size, void *buf) 672write_shutdown (void *cls, size_t size, void *buf)
554{ 673{
555 struct GNUNET_MessageHeader *msg; 674 struct GNUNET_MessageHeader *msg;
556 struct GNUNET_CLIENT_Connection *sock = cls; 675 struct ShutdownContext *shutdown_ctx = cls;
557 676
558 GNUNET_CLIENT_disconnect (sock, GNUNET_YES);
559 if (size < sizeof (struct GNUNET_MessageHeader)) 677 if (size < sizeof (struct GNUNET_MessageHeader))
560 { 678 {
561 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 679 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
562 _("Failed to transmit shutdown request to client.\n")); 680 _("Failed to transmit shutdown request to client.\n"));
681
682 shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
683 GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
684 GNUNET_free(shutdown_ctx);
563 return 0; /* client disconnected */ 685 return 0; /* client disconnected */
564 } 686 }
687
688 GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
689 shutdown_ctx->cancel_task = GNUNET_SCHEDULER_add_delayed (shutdown_ctx->sched, GNUNET_TIME_absolute_get_remaining(shutdown_ctx->timeout), &service_shutdown_cancel, shutdown_ctx);
565 msg = (struct GNUNET_MessageHeader *) buf; 690 msg = (struct GNUNET_MessageHeader *) buf;
566 msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN); 691 msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN);
567 msg->size = htons (sizeof (struct GNUNET_MessageHeader)); 692 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
@@ -576,16 +701,33 @@ write_shutdown (void *cls, size_t size, void *buf)
576 * be used by the caller after this call 701 * be used by the caller after this call
577 * (calling this function frees "sock" after a while). 702 * (calling this function frees "sock" after a while).
578 * 703 *
704 * @param sched the scheduler to use for calling shutdown continuation
579 * @param sock the socket connected to the service 705 * @param sock the socket connected to the service
706 * @param timeout how long to wait before giving up on transmission
707 * @param cont continuation to call once the service is really down
708 * @param cont_cls closure for continuation
709 *
580 */ 710 */
581void 711void
582GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock) 712GNUNET_CLIENT_service_shutdown (struct GNUNET_SCHEDULER_Handle *sched,
713 struct GNUNET_CLIENT_Connection *sock,
714 struct GNUNET_TIME_Relative timeout,
715 GNUNET_CLIENT_ShutdownTask cont,
716 void *cont_cls)
583{ 717{
718 struct ShutdownContext *shutdown_ctx;
719 shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext));
720 shutdown_ctx->sched = sched;
721 shutdown_ctx->cont = cont;
722 shutdown_ctx->cont_cls = cont_cls;
723 shutdown_ctx->sock = sock;
724 shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute(timeout);
725
584 GNUNET_CONNECTION_notify_transmit_ready (sock->sock, 726 GNUNET_CONNECTION_notify_transmit_ready (sock->sock,
585 sizeof (struct 727 sizeof (struct
586 GNUNET_MessageHeader), 728 GNUNET_MessageHeader),
587 GNUNET_TIME_UNIT_FOREVER_REL, 729 timeout,
588 &write_shutdown, sock); 730 &write_shutdown, shutdown_ctx);
589} 731}
590 732
591 733
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
279 */ 279 */
280 uint16_t port; 280 uint16_t port;
281 281
282 /**
283 * When shutdown, do not ever actually close the socket, but
284 * free resources. Only should ever be set if using program
285 * termination as a signal (because only then will the leaked
286 * socket be freed!)
287 */
288 int persist;
289
282}; 290};
283 291
292/**
293 * Set the persist option on this connection handle. Indicates
294 * that the underlying socket or fd should never really be closed.
295 * Used for indicating process death.
296 *
297 * @param sock the connection to set persistent
298 */
299void GNUNET_CONNECTION_persist_(struct GNUNET_CONNECTION_Handle *sock)
300{
301 sock->persist = GNUNET_YES;
302}
284 303
285/** 304/**
286 * Create a socket handle by boxing an existing OS socket. The OS 305 * Create a socket handle by boxing an existing OS socket. The OS
@@ -486,7 +505,8 @@ destroy_continuation (void *cls,
486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
487 "Shutting down socket (%p)\n", sock); 506 "Shutting down socket (%p)\n", sock);
488#endif 507#endif
489 GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR); 508 if (sock->persist != GNUNET_YES)
509 GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR);
490 } 510 }
491 if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) 511 if (sock->read_task != GNUNET_SCHEDULER_NO_TASK)
492 { 512 {
@@ -518,8 +538,10 @@ destroy_continuation (void *cls,
518 sock->nth.notify_ready = NULL; 538 sock->nth.notify_ready = NULL;
519 notify (sock->nth.notify_ready_cls, 0, NULL); 539 notify (sock->nth.notify_ready_cls, 0, NULL);
520 } 540 }
521 if (sock->sock != NULL) 541
542 if ((sock->sock != NULL) && (sock->persist != GNUNET_YES))
522 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock)); 543 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock));
544
523 GNUNET_free_non_null (sock->addr); 545 GNUNET_free_non_null (sock->addr);
524 GNUNET_free_non_null (sock->hostname); 546 GNUNET_free_non_null (sock->hostname);
525#if DEBUG_CONNECTION 547#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
269 * Are we currently trying to receive? 269 * Are we currently trying to receive?
270 */ 270 */
271 int receive_pending; 271 int receive_pending;
272
273 /**
274 * Persist the file handle for this client no matter what happens,
275 * force the OS to close once the process actually dies. Should only
276 * be used in special cases!
277 */
278 int persist;
272}; 279};
273 280
274 281
@@ -933,11 +940,16 @@ sock_check (void *cls)
933 * Destroy this socket (free resources). 940 * Destroy this socket (free resources).
934 * 941 *
935 * @param cls the socket 942 * @param cls the socket
943 * @param persist set the socket to be persisted
936 */ 944 */
937static void 945static void
938sock_destroy (void *cls) 946sock_destroy (void *cls, int persist)
939{ 947{
940 GNUNET_CONNECTION_destroy (cls, GNUNET_NO); 948 struct GNUNET_CONNECTION_Handle *sock = cls;
949 if (persist == GNUNET_YES)
950 GNUNET_CONNECTION_persist_ (sock);
951
952 GNUNET_CONNECTION_destroy (sock, GNUNET_NO);
941} 953}
942 954
943 955
@@ -1168,6 +1180,7 @@ GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
1168 client->receive_cancel (client->client_closure); 1180 client->receive_cancel (client->client_closure);
1169 client->receive_pending = GNUNET_NO; 1181 client->receive_pending = GNUNET_NO;
1170 } 1182 }
1183
1171 rc = client->reference_count; 1184 rc = client->reference_count;
1172 if (client->server != NULL) 1185 if (client->server != NULL)
1173 { 1186 {
@@ -1200,7 +1213,7 @@ GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
1200 return; 1213 return;
1201 if (client->in_process_client_buffer == GNUNET_YES) 1214 if (client->in_process_client_buffer == GNUNET_YES)
1202 return; 1215 return;
1203 client->destroy (client->client_closure); 1216 client->destroy (client->client_closure, client->persist);
1204 GNUNET_free (client); 1217 GNUNET_free (client);
1205} 1218}
1206 1219
@@ -1232,6 +1245,17 @@ GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
1232 timeout, callback, callback_cls); 1245 timeout, callback, callback_cls);
1233} 1246}
1234 1247
1248/**
1249 * Set the persistent flag on this client, used to setup client connection
1250 * to only be killed when the service it's connected to is actually dead.
1251 *
1252 * @param client the client to set the persistent flag on
1253 */
1254void
1255GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client)
1256{
1257 client->persist = GNUNET_YES;
1258}
1235 1259
1236/** 1260/**
1237 * Resume receiving from this client, we are done processing the 1261 * 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,
570} 570}
571 571
572 572
573static size_t
574transmit_shutdown_deny (void *cls, size_t size, void *buf)
575{
576 struct GNUNET_SERVER_Client *client = cls;
577 struct GNUNET_MessageHeader *msg;
578
579 if (size < sizeof (struct GNUNET_MessageHeader))
580 {
581 return 0; /* client disconnected */
582 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
583 }
584 msg = (struct GNUNET_MessageHeader *) buf;
585 msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN_REFUSE);
586 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
587 GNUNET_SERVER_receive_done (client, GNUNET_OK);
588 GNUNET_SERVER_client_drop(client);
589 return sizeof (struct GNUNET_MessageHeader);
590}
591
592static size_t
593transmit_shutdown_ack (void *cls, size_t size, void *buf)
594{
595 struct GNUNET_SERVER_Client *client = cls;
596 struct GNUNET_MessageHeader *msg;
597
598 if (size < sizeof (struct GNUNET_MessageHeader))
599 {
600 return 0; /* client disconnected */
601 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
602 }
603
604 msg = (struct GNUNET_MessageHeader *) buf;
605 msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN_ACK);
606 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
607 GNUNET_SERVER_receive_done (client, GNUNET_OK);
608 GNUNET_SERVER_client_drop(client);
609 return sizeof (struct GNUNET_MessageHeader);
610}
611
573/** 612/**
574 * Handler for SHUTDOWN message. 613 * Handler for SHUTDOWN message.
575 * 614 *
@@ -583,19 +622,31 @@ handle_shutdown (void *cls,
583 const struct GNUNET_MessageHeader *message) 622 const struct GNUNET_MessageHeader *message)
584{ 623{
585 struct GNUNET_SERVICE_Context *service = cls; 624 struct GNUNET_SERVICE_Context *service = cls;
625
626 /* FIXME: why is this call necessary???? */
627 GNUNET_SERVER_client_keep(client);
586 if (!service->allow_shutdown) 628 if (!service->allow_shutdown)
587 { 629 {
588 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 630 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
589 _ 631 _
590 ("Received shutdown request, but configured to ignore!\n")); 632 ("Received shutdown request, but configured to ignore!\n"));
591 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 633 GNUNET_SERVER_notify_transmit_ready (client,
634 sizeof(struct GNUNET_MessageHeader),
635 GNUNET_TIME_UNIT_FOREVER_REL,
636 &transmit_shutdown_deny, client);
592 return; 637 return;
593 } 638 }
594 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 639 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
595 _("Initiating shutdown as requested by client.\n")); 640 _("Initiating shutdown as requested by client.\n"));
641
642 GNUNET_SERVER_notify_transmit_ready (client,
643 sizeof(struct GNUNET_MessageHeader),
644 GNUNET_TIME_UNIT_FOREVER_REL,
645 &transmit_shutdown_ack, client);
646
596 GNUNET_assert (service->sched != NULL); 647 GNUNET_assert (service->sched != NULL);
648 GNUNET_SERVER_client_persist_ (client);
597 GNUNET_SCHEDULER_shutdown (service->sched); 649 GNUNET_SCHEDULER_shutdown (service->sched);
598 GNUNET_SERVER_receive_done (client, GNUNET_OK);
599} 650}
600 651
601 652
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)
152} 152}
153 153
154 154
155static void my_destroy (void *cls); 155static void my_destroy (void *cls, int persist);
156 156
157 157
158struct CopyContext 158struct CopyContext
@@ -217,7 +217,7 @@ static struct GNUNET_SERVER_MessageHandler handlers[] = {
217 217
218 218
219static void 219static void
220my_destroy (void *cls) 220my_destroy (void *cls, int persist)
221{ 221{
222 int *ok = cls; 222 int *ok = cls;
223 GNUNET_assert (5 == *ok); 223 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;
43 43
44static int ok = 1; 44static int ok = 1;
45 45
46void
47end_cont (void *cls,
48 int reason)
49{
50 if (sctx != NULL)
51 GNUNET_SERVICE_stop (sctx);
52 else
53 GNUNET_SCHEDULER_shutdown (sched);
54 ok = 0;
55}
46 56
47static void 57static void
48end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 58end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
@@ -50,12 +60,7 @@ end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
50 struct GNUNET_CLIENT_Connection *client = cls; 60 struct GNUNET_CLIENT_Connection *client = cls;
51 61
52 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down service\n"); 62 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down service\n");
53 GNUNET_CLIENT_service_shutdown (client); 63 GNUNET_CLIENT_service_shutdown (sched, client, GNUNET_TIME_UNIT_FOREVER_REL, &end_cont, NULL);
54 if (sctx != NULL)
55 GNUNET_SERVICE_stop (sctx);
56 else
57 GNUNET_SCHEDULER_shutdown (sched);
58 ok = 0;
59} 64}
60 65
61 66