+static void
+handle_watch (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ char *service;
+ char *name;
+ uint16_t msize;
+ uint16_t size;
+ struct StatsEntry *pos;
+ struct ClientEntry *ce;
+ struct WatchEntry *we;
+ size_t slen;
+
+ if (NULL == nc)
+ {
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ GNUNET_SERVER_client_mark_monitor (client);
+ ce = make_client_entry (client);
+ msize = ntohs (message->size);
+ if (msize < sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ size = msize - sizeof (struct GNUNET_MessageHeader);
+ if (size !=
+ GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], size, 2,
+ &service, &name))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received request to watch statistic on `%s:%s'\n", service,
+ name);
+ pos = start;
+ while (pos != NULL)
+ {
+ if (matches (pos, service, name))
+ break;
+ pos = pos->next;
+ }
+ if (pos == NULL)
+ {
+ pos =
+ GNUNET_malloc (sizeof (struct StatsEntry) +
+ sizeof (struct GNUNET_STATISTICS_SetMessage) + size);
+ pos->next = start;
+ pos->uid = uidgen++;
+ pos->set = GNUNET_NO;
+ pos->msg = (void *) &pos[1];
+ pos->msg->header.size =
+ htons (sizeof (struct GNUNET_STATISTICS_SetMessage) + size);
+ pos->msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
+ pos->service = (const char *) &pos->msg[1];
+ slen = strlen (service) + 1;
+ memcpy ((void *) pos->service, service, slen);
+ pos->name = &pos->service[slen];
+ memcpy ((void *) pos->name, name, strlen (name) + 1);
+ start = pos;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "New statistic on `%s:%s' with value %llu created.\n", service,
+ name, pos->value);
+ }
+ we = GNUNET_new (struct WatchEntry);
+ we->client = client;
+ we->last_value_set = GNUNET_NO;
+ we->wid = ce->max_wid++;
+ GNUNET_CONTAINER_DLL_insert (pos->we_head, pos->we_tail, we);
+ if (pos->value != 0)
+ notify_change (pos);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Actually perform the shutdown.
+ */
+static void
+do_shutdown ()
+{
+ struct WatchEntry *we;
+ struct StatsEntry *se;
+
+ if (NULL == nc)
+ return;
+ save ();
+ GNUNET_SERVER_notification_context_destroy (nc);
+ nc = NULL;
+ GNUNET_assert (NULL == client_head);
+ while (NULL != (se = start))
+ {
+ start = se->next;
+ while (NULL != (we = se->we_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (se->we_head, se->we_tail, we);
+ GNUNET_free (we);
+ }
+ GNUNET_free (se);
+ }
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ in_shutdown = GNUNET_YES;
+ if (NULL != client_head)
+ return;
+ do_shutdown ();
+}
+
+
+/**
+ * A client disconnected. Remove all of its data structure entries.
+ *
+ * @param cls closure, NULL
+ * @param client identification of the client
+ */
+static void
+handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
+{
+ struct ClientEntry *ce;
+ struct WatchEntry *we;
+ struct WatchEntry *wen;
+ struct StatsEntry *se;
+
+ ce = client_head;
+ while (NULL != ce)
+ {
+ if (ce->client == client)
+ {
+ GNUNET_CONTAINER_DLL_remove (client_head, client_tail, ce);
+ GNUNET_free (ce);
+ break;
+ }
+ ce = ce->next;
+ }
+ se = start;
+ while (NULL != se)
+ {
+ wen = se->we_head;
+ while (NULL != (we = wen))
+ {
+ wen = we->next;
+ if (we->client != client)
+ continue;
+ GNUNET_CONTAINER_DLL_remove (se->we_head, se->we_tail, we);
+ GNUNET_free (we);
+ }
+ se = se->next;
+ }
+ if ( (NULL == client_head) &&
+ (GNUNET_YES == in_shutdown) )
+ do_shutdown ();
+}