diff options
Diffstat (limited to 'src/service/statistics')
-rw-r--r-- | src/service/statistics/.gitignore | 7 | ||||
-rw-r--r-- | src/service/statistics/Makefile.am | 88 | ||||
-rw-r--r-- | src/service/statistics/gnunet-service-statistics.c | 1059 | ||||
-rw-r--r-- | src/service/statistics/meson.build | 35 | ||||
-rw-r--r-- | src/service/statistics/statistics.conf.in | 21 | ||||
-rw-r--r-- | src/service/statistics/statistics.h | 149 | ||||
-rw-r--r-- | src/service/statistics/statistics_api.c | 1342 | ||||
-rw-r--r-- | src/service/statistics/test_gnunet_statistics.py.in | 171 | ||||
-rw-r--r-- | src/service/statistics/test_statistics_api.c | 253 | ||||
-rw-r--r-- | src/service/statistics/test_statistics_api_data.conf | 5 | ||||
-rw-r--r-- | src/service/statistics/test_statistics_api_loop.c | 123 | ||||
-rw-r--r-- | src/service/statistics/test_statistics_api_watch.c | 156 | ||||
-rw-r--r-- | src/service/statistics/test_statistics_api_watch_zero_value.c | 197 |
13 files changed, 3606 insertions, 0 deletions
diff --git a/src/service/statistics/.gitignore b/src/service/statistics/.gitignore new file mode 100644 index 000000000..f1f567149 --- /dev/null +++ b/src/service/statistics/.gitignore | |||
@@ -0,0 +1,7 @@ | |||
1 | gnunet-statistics | ||
2 | gnunet-service-statistics | ||
3 | test_gnunet_statistics.py | ||
4 | test_statistics_api | ||
5 | test_statistics_api_loop | ||
6 | test_statistics_api_watch | ||
7 | test_statistics_api_watch_zero_value | ||
diff --git a/src/service/statistics/Makefile.am b/src/service/statistics/Makefile.am new file mode 100644 index 000000000..42c010c54 --- /dev/null +++ b/src/service/statistics/Makefile.am | |||
@@ -0,0 +1,88 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | pkgcfg_DATA = \ | ||
14 | statistics.conf | ||
15 | |||
16 | lib_LTLIBRARIES = libgnunetstatistics.la | ||
17 | |||
18 | libgnunetstatistics_la_SOURCES = \ | ||
19 | statistics_api.c statistics.h | ||
20 | libgnunetstatistics_la_LIBADD = \ | ||
21 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
22 | $(GN_LIBINTL) $(XLIB) | ||
23 | libgnunetstatistics_la_LDFLAGS = \ | ||
24 | $(GN_LIB_LDFLAGS) \ | ||
25 | -version-info 2:0:0 | ||
26 | |||
27 | libexec_PROGRAMS = \ | ||
28 | gnunet-service-statistics | ||
29 | |||
30 | gnunet_service_statistics_SOURCES = \ | ||
31 | gnunet-service-statistics.c | ||
32 | gnunet_service_statistics_LDADD = \ | ||
33 | libgnunetstatistics.la \ | ||
34 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
35 | $(GN_LIBINTL) | ||
36 | |||
37 | check_PROGRAMS = \ | ||
38 | test_statistics_api \ | ||
39 | test_statistics_api_loop \ | ||
40 | test_statistics_api_watch \ | ||
41 | test_statistics_api_watch_zero_value | ||
42 | |||
43 | if ENABLE_TEST_RUN | ||
44 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
45 | TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||
46 | endif | ||
47 | |||
48 | test_statistics_api_SOURCES = \ | ||
49 | test_statistics_api.c | ||
50 | test_statistics_api_LDADD = \ | ||
51 | libgnunetstatistics.la \ | ||
52 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
53 | |||
54 | test_statistics_api_loop_SOURCES = \ | ||
55 | test_statistics_api_loop.c | ||
56 | test_statistics_api_loop_LDADD = \ | ||
57 | libgnunetstatistics.la \ | ||
58 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
59 | |||
60 | test_statistics_api_watch_SOURCES = \ | ||
61 | test_statistics_api_watch.c | ||
62 | test_statistics_api_watch_LDADD = \ | ||
63 | libgnunetstatistics.la \ | ||
64 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
65 | |||
66 | test_statistics_api_watch_zero_value_SOURCES = \ | ||
67 | test_statistics_api_watch_zero_value.c | ||
68 | test_statistics_api_watch_zero_value_LDADD = \ | ||
69 | libgnunetstatistics.la \ | ||
70 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
71 | |||
72 | # Needs to be done with CLI | ||
73 | #if HAVE_PYTHON | ||
74 | #check_SCRIPTS = \ | ||
75 | # test_gnunet_statistics.py | ||
76 | #endif | ||
77 | # | ||
78 | #SUFFIXES = .py.in .py | ||
79 | #.py.in.py: | ||
80 | # $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/$< > $@ | ||
81 | # chmod +x $@ | ||
82 | # | ||
83 | #test_gnunet_statistics.py: test_gnunet_statistics.py.in Makefile | ||
84 | # $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $(srcdir)/test_gnunet_statistics.py.in > test_gnunet_statistics.py | ||
85 | # chmod +x test_gnunet_statistics.py | ||
86 | # | ||
87 | EXTRA_DIST = \ | ||
88 | test_statistics_api_data.conf | ||
diff --git a/src/service/statistics/gnunet-service-statistics.c b/src/service/statistics/gnunet-service-statistics.c new file mode 100644 index 000000000..a6c897a79 --- /dev/null +++ b/src/service/statistics/gnunet-service-statistics.c | |||
@@ -0,0 +1,1059 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2012, 2014, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file statistics/gnunet-service-statistics.c | ||
23 | * @brief program that tracks statistics | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_protocols.h" | ||
29 | #include "gnunet_statistics_service.h" | ||
30 | #include "gnunet_time_lib.h" | ||
31 | #include "statistics.h" | ||
32 | |||
33 | /** | ||
34 | * Watch entry. | ||
35 | */ | ||
36 | struct WatchEntry | ||
37 | { | ||
38 | /** | ||
39 | * Watch entries are kept in a linked list. | ||
40 | */ | ||
41 | struct WatchEntry *next; | ||
42 | |||
43 | /** | ||
44 | * Watch entries are kept in a linked list. | ||
45 | */ | ||
46 | struct WatchEntry *prev; | ||
47 | |||
48 | /** | ||
49 | * For which client is this watch entry? | ||
50 | */ | ||
51 | struct ClientEntry *ce; | ||
52 | |||
53 | /** | ||
54 | * Last value we communicated to the client for this watch entry. | ||
55 | */ | ||
56 | uint64_t last_value; | ||
57 | |||
58 | /** | ||
59 | * Unique watch number for this client and this watched value. | ||
60 | */ | ||
61 | uint32_t wid; | ||
62 | |||
63 | /** | ||
64 | * Is last_value valid | ||
65 | * #GNUNET_NO : last_value is n/a, #GNUNET_YES: last_value is valid | ||
66 | */ | ||
67 | int last_value_set; | ||
68 | }; | ||
69 | |||
70 | |||
71 | /** | ||
72 | * We keep the statistics organized by subsystem for faster | ||
73 | * lookup during SET operations. | ||
74 | */ | ||
75 | struct SubsystemEntry; | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Entry in the statistics list. | ||
80 | */ | ||
81 | struct StatsEntry | ||
82 | { | ||
83 | /** | ||
84 | * This is a linked list. | ||
85 | */ | ||
86 | struct StatsEntry *next; | ||
87 | |||
88 | /** | ||
89 | * This is a linked list. | ||
90 | */ | ||
91 | struct StatsEntry *prev; | ||
92 | |||
93 | /** | ||
94 | * Subsystem this entry belongs to. | ||
95 | */ | ||
96 | struct SubsystemEntry *subsystem; | ||
97 | |||
98 | /** | ||
99 | * Name for the value stored by this entry, allocated at the end of | ||
100 | * this struct. | ||
101 | */ | ||
102 | const char *name; | ||
103 | |||
104 | /** | ||
105 | * Watch context for changes to this value, or NULL for none. | ||
106 | */ | ||
107 | struct WatchEntry *we_head; | ||
108 | |||
109 | /** | ||
110 | * Watch context for changes to this value, or NULL for none. | ||
111 | */ | ||
112 | struct WatchEntry *we_tail; | ||
113 | |||
114 | /** | ||
115 | * Our value. | ||
116 | */ | ||
117 | uint64_t value; | ||
118 | |||
119 | /** | ||
120 | * Unique ID. | ||
121 | */ | ||
122 | uint32_t uid; | ||
123 | |||
124 | /** | ||
125 | * Is this value persistent? | ||
126 | */ | ||
127 | int persistent; | ||
128 | |||
129 | /** | ||
130 | * Is this value set? | ||
131 | * #GNUNET_NO: value is n/a, #GNUNET_YES: value is valid | ||
132 | */ | ||
133 | int set; | ||
134 | }; | ||
135 | |||
136 | |||
137 | /** | ||
138 | * We keep the statistics organized by subsystem for faster | ||
139 | * lookup during SET operations. | ||
140 | */ | ||
141 | struct SubsystemEntry | ||
142 | { | ||
143 | /** | ||
144 | * Subsystems are kept in a DLL. | ||
145 | */ | ||
146 | struct SubsystemEntry *next; | ||
147 | |||
148 | /** | ||
149 | * Subsystems are kept in a DLL. | ||
150 | */ | ||
151 | struct SubsystemEntry *prev; | ||
152 | |||
153 | /** | ||
154 | * Head of list of values kept for this subsystem. | ||
155 | */ | ||
156 | struct StatsEntry *stat_head; | ||
157 | |||
158 | /** | ||
159 | * Tail of list of values kept for this subsystem. | ||
160 | */ | ||
161 | struct StatsEntry *stat_tail; | ||
162 | |||
163 | /** | ||
164 | * Name of the subsystem this entry is for, allocated at | ||
165 | * the end of this struct, do not free(). | ||
166 | */ | ||
167 | const char *service; | ||
168 | }; | ||
169 | |||
170 | |||
171 | /** | ||
172 | * Client entry. | ||
173 | */ | ||
174 | struct ClientEntry | ||
175 | { | ||
176 | /** | ||
177 | * Corresponding server handle. | ||
178 | */ | ||
179 | struct GNUNET_SERVICE_Client *client; | ||
180 | |||
181 | /** | ||
182 | * Corresponding message queue. | ||
183 | */ | ||
184 | struct GNUNET_MQ_Handle *mq; | ||
185 | |||
186 | /** | ||
187 | * Which subsystem is this client writing to (SET/UPDATE)? | ||
188 | */ | ||
189 | struct SubsystemEntry *subsystem; | ||
190 | |||
191 | /** | ||
192 | * Maximum watch ID used by this client so far. | ||
193 | */ | ||
194 | uint32_t max_wid; | ||
195 | }; | ||
196 | |||
197 | |||
198 | /** | ||
199 | * Our configuration. | ||
200 | */ | ||
201 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
202 | |||
203 | /** | ||
204 | * Head of linked list of subsystems with active statistics. | ||
205 | */ | ||
206 | static struct SubsystemEntry *sub_head; | ||
207 | |||
208 | /** | ||
209 | * Tail of linked list of subsystems with active statistics. | ||
210 | */ | ||
211 | static struct SubsystemEntry *sub_tail; | ||
212 | |||
213 | /** | ||
214 | * Number of connected clients. | ||
215 | */ | ||
216 | static unsigned int client_count; | ||
217 | |||
218 | /** | ||
219 | * Our notification context. | ||
220 | */ | ||
221 | static struct GNUNET_NotificationContext *nc; | ||
222 | |||
223 | /** | ||
224 | * Counter used to generate unique values. | ||
225 | */ | ||
226 | static uint32_t uidgen; | ||
227 | |||
228 | /** | ||
229 | * Set to #GNUNET_YES if we are shutting down as soon as possible. | ||
230 | */ | ||
231 | static int in_shutdown; | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Write persistent statistics to disk. | ||
236 | */ | ||
237 | static void | ||
238 | save () | ||
239 | { | ||
240 | struct SubsystemEntry *se; | ||
241 | struct StatsEntry *pos; | ||
242 | char *fn; | ||
243 | struct GNUNET_BIO_WriteHandle *wh; | ||
244 | uint16_t size; | ||
245 | unsigned long long total; | ||
246 | size_t nlen; | ||
247 | size_t slen; | ||
248 | struct GNUNET_STATISTICS_SetMessage *msg; | ||
249 | |||
250 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
251 | "STATISTICS", | ||
252 | "DATABASE", | ||
253 | &fn)) | ||
254 | { | ||
255 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
256 | "STATISTICS", | ||
257 | "DATABASE"); | ||
258 | return; | ||
259 | } | ||
260 | (void) GNUNET_DISK_directory_create_for_file (fn); | ||
261 | wh = GNUNET_BIO_write_open_file (fn); | ||
262 | total = 0; | ||
263 | while (NULL != (se = sub_head)) | ||
264 | { | ||
265 | GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se); | ||
266 | slen = strlen (se->service) + 1; | ||
267 | while (NULL != (pos = se->stat_head)) | ||
268 | { | ||
269 | GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); | ||
270 | if ((pos->persistent) && (NULL != wh)) | ||
271 | { | ||
272 | nlen = strlen (pos->name) + 1; | ||
273 | size = sizeof(struct GNUNET_STATISTICS_SetMessage) + nlen + slen; | ||
274 | GNUNET_assert (size < UINT16_MAX); | ||
275 | msg = GNUNET_malloc (size); | ||
276 | |||
277 | msg->header.size = htons ((uint16_t) size); | ||
278 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET); | ||
279 | GNUNET_assert (nlen + slen == | ||
280 | GNUNET_STRINGS_buffer_fill ((char *) &msg[1], | ||
281 | nlen + slen, | ||
282 | 2, | ||
283 | se->service, | ||
284 | pos->name)); | ||
285 | msg->flags = | ||
286 | htonl (pos->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0); | ||
287 | msg->value = GNUNET_htonll (pos->value); | ||
288 | if (GNUNET_OK != GNUNET_BIO_write (wh, "statistics-save-msg", msg, | ||
289 | size)) | ||
290 | { | ||
291 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); | ||
292 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
293 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); | ||
294 | wh = NULL; | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | total += size; | ||
299 | } | ||
300 | GNUNET_free (msg); | ||
301 | } | ||
302 | GNUNET_free (pos); | ||
303 | } | ||
304 | GNUNET_free (se); | ||
305 | } | ||
306 | if (NULL != wh) | ||
307 | { | ||
308 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
309 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); | ||
310 | if (0 == total) | ||
311 | GNUNET_break (0 == unlink (fn)); | ||
312 | else | ||
313 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
314 | _ ("Wrote %llu bytes of statistics to `%s'\n"), | ||
315 | total, | ||
316 | fn); | ||
317 | } | ||
318 | GNUNET_free (fn); | ||
319 | } | ||
320 | |||
321 | |||
322 | /** | ||
323 | * Transmit the given stats value. | ||
324 | * | ||
325 | * @param ce receiver of the value | ||
326 | * @param e value to transmit | ||
327 | */ | ||
328 | static void | ||
329 | transmit (struct ClientEntry *ce, const struct StatsEntry *e) | ||
330 | { | ||
331 | struct GNUNET_MQ_Envelope *env; | ||
332 | struct GNUNET_STATISTICS_ReplyMessage *m; | ||
333 | size_t size; | ||
334 | |||
335 | size = strlen (e->subsystem->service) + 1 + strlen (e->name) + 1; | ||
336 | GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE); | ||
337 | env = GNUNET_MQ_msg_extra (m, size, GNUNET_MESSAGE_TYPE_STATISTICS_VALUE); | ||
338 | m->uid = htonl (e->uid); | ||
339 | if (e->persistent) | ||
340 | m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT); | ||
341 | m->value = GNUNET_htonll (e->value); | ||
342 | GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1], | ||
343 | size, | ||
344 | 2, | ||
345 | e->subsystem->service, | ||
346 | e->name)); | ||
347 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
348 | "Transmitting value for `%s:%s' (%d): %llu\n", | ||
349 | e->subsystem->service, | ||
350 | e->name, | ||
351 | e->persistent, | ||
352 | (unsigned long long) e->value); | ||
353 | GNUNET_MQ_send (ce->mq, env); | ||
354 | } | ||
355 | |||
356 | |||
357 | /** | ||
358 | * Callback called when a client connects to the service. | ||
359 | * | ||
360 | * @param cls closure for the service | ||
361 | * @param c the new client that connected to the service | ||
362 | * @param mq the message queue used to send messages to the client | ||
363 | * @return @a c | ||
364 | */ | ||
365 | static void * | ||
366 | client_connect_cb (void *cls, | ||
367 | struct GNUNET_SERVICE_Client *c, | ||
368 | struct GNUNET_MQ_Handle *mq) | ||
369 | { | ||
370 | struct ClientEntry *ce; | ||
371 | |||
372 | ce = GNUNET_new (struct ClientEntry); | ||
373 | ce->client = c; | ||
374 | ce->mq = mq; | ||
375 | client_count++; | ||
376 | GNUNET_notification_context_add (nc, mq); | ||
377 | return ce; | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * Check integrity of GET-message. | ||
383 | * | ||
384 | * @param cls identification of the client | ||
385 | * @param message the actual message | ||
386 | * @return #GNUNET_OK if @a message is well-formed | ||
387 | */ | ||
388 | static int | ||
389 | check_get (void *cls, const struct GNUNET_MessageHeader *message) | ||
390 | { | ||
391 | const char *service; | ||
392 | const char *name; | ||
393 | size_t size; | ||
394 | |||
395 | size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); | ||
396 | if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], | ||
397 | size, | ||
398 | 2, | ||
399 | &service, | ||
400 | &name)) | ||
401 | { | ||
402 | GNUNET_break (0); | ||
403 | return GNUNET_SYSERR; | ||
404 | } | ||
405 | return GNUNET_OK; | ||
406 | } | ||
407 | |||
408 | |||
409 | /** | ||
410 | * Handle GET-message. | ||
411 | * | ||
412 | * @param cls identification of the client | ||
413 | * @param message the actual message | ||
414 | */ | ||
415 | static void | ||
416 | handle_get (void *cls, const struct GNUNET_MessageHeader *message) | ||
417 | { | ||
418 | struct ClientEntry *ce = cls; | ||
419 | struct GNUNET_MQ_Envelope *env; | ||
420 | struct GNUNET_MessageHeader *end; | ||
421 | const char *service; | ||
422 | const char *name; | ||
423 | size_t slen; | ||
424 | size_t nlen; | ||
425 | struct SubsystemEntry *se; | ||
426 | struct StatsEntry *pos; | ||
427 | size_t size; | ||
428 | |||
429 | size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); | ||
430 | GNUNET_assert (size == | ||
431 | GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], | ||
432 | size, | ||
433 | 2, | ||
434 | &service, | ||
435 | &name)); | ||
436 | slen = strlen (service); | ||
437 | nlen = strlen (name); | ||
438 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
439 | "Received request for statistics on `%s:%s'\n", | ||
440 | slen ? service : "*", | ||
441 | nlen ? name : "*"); | ||
442 | for (se = sub_head; NULL != se; se = se->next) | ||
443 | { | ||
444 | if (! ((0 == slen) || (0 == strcmp (service, se->service)))) | ||
445 | continue; | ||
446 | for (pos = se->stat_head; NULL != pos; pos = pos->next) | ||
447 | { | ||
448 | if (! ((0 == nlen) || (0 == strcmp (name, pos->name)))) | ||
449 | continue; | ||
450 | transmit (ce, pos); | ||
451 | } | ||
452 | } | ||
453 | env = GNUNET_MQ_msg (end, GNUNET_MESSAGE_TYPE_STATISTICS_END); | ||
454 | GNUNET_MQ_send (ce->mq, env); | ||
455 | GNUNET_SERVICE_client_continue (ce->client); | ||
456 | } | ||
457 | |||
458 | |||
459 | /** | ||
460 | * Notify all clients listening about a change to a value. | ||
461 | * | ||
462 | * @param se value that changed | ||
463 | */ | ||
464 | static void | ||
465 | notify_change (struct StatsEntry *se) | ||
466 | { | ||
467 | struct GNUNET_MQ_Envelope *env; | ||
468 | struct GNUNET_STATISTICS_WatchValueMessage *wvm; | ||
469 | struct WatchEntry *pos; | ||
470 | |||
471 | for (pos = se->we_head; NULL != pos; pos = pos->next) | ||
472 | { | ||
473 | if (GNUNET_YES == pos->last_value_set) | ||
474 | { | ||
475 | if (pos->last_value == se->value) | ||
476 | continue; | ||
477 | } | ||
478 | else | ||
479 | { | ||
480 | pos->last_value_set = GNUNET_YES; | ||
481 | } | ||
482 | env = GNUNET_MQ_msg (wvm, GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE); | ||
483 | wvm->flags = | ||
484 | htonl (se->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0); | ||
485 | wvm->wid = htonl (pos->wid); | ||
486 | wvm->reserved = htonl (0); | ||
487 | wvm->value = GNUNET_htonll (se->value); | ||
488 | GNUNET_MQ_send (pos->ce->mq, env); | ||
489 | pos->last_value = se->value; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | |||
494 | /** | ||
495 | * Find the subsystem entry of the given name for the specified client. | ||
496 | * | ||
497 | * @param ce client looking for the subsystem, may contain a hint | ||
498 | * to find the entry faster, can be NULL | ||
499 | * @param service name of the subsystem to look for | ||
500 | * @return subsystem entry, never NULL (subsystem entry is created if necessary) | ||
501 | */ | ||
502 | static struct SubsystemEntry * | ||
503 | find_subsystem_entry (struct ClientEntry *ce, const char *service) | ||
504 | { | ||
505 | size_t slen; | ||
506 | struct SubsystemEntry *se; | ||
507 | |||
508 | if (NULL != ce) | ||
509 | se = ce->subsystem; | ||
510 | else | ||
511 | se = NULL; | ||
512 | if ((NULL == se) || (0 != strcmp (service, se->service))) | ||
513 | { | ||
514 | for (se = sub_head; NULL != se; se = se->next) | ||
515 | if (0 == strcmp (service, se->service)) | ||
516 | break; | ||
517 | if (NULL != ce) | ||
518 | ce->subsystem = se; | ||
519 | } | ||
520 | if (NULL != se) | ||
521 | return se; | ||
522 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
523 | "Allocating new subsystem entry `%s'\n", | ||
524 | service); | ||
525 | slen = strlen (service) + 1; | ||
526 | se = GNUNET_malloc (sizeof(struct SubsystemEntry) + slen); | ||
527 | GNUNET_memcpy (&se[1], service, slen); | ||
528 | se->service = (const char *) &se[1]; | ||
529 | GNUNET_CONTAINER_DLL_insert (sub_head, sub_tail, se); | ||
530 | if (NULL != ce) | ||
531 | ce->subsystem = se; | ||
532 | return se; | ||
533 | } | ||
534 | |||
535 | |||
536 | /** | ||
537 | * Find the statistics entry of the given subsystem. | ||
538 | * | ||
539 | * @param se subsystem to look in | ||
540 | * @param name name of the entry to look for | ||
541 | * @return statistics entry, or NULL if not found | ||
542 | */ | ||
543 | static struct StatsEntry * | ||
544 | find_stat_entry (struct SubsystemEntry *se, const char *name) | ||
545 | { | ||
546 | struct StatsEntry *pos; | ||
547 | |||
548 | for (pos = se->stat_head; NULL != pos; pos = pos->next) | ||
549 | if (0 == strcmp (name, pos->name)) | ||
550 | return pos; | ||
551 | return NULL; | ||
552 | } | ||
553 | |||
554 | |||
555 | /** | ||
556 | * Check format of SET-message. | ||
557 | * | ||
558 | * @param cls the `struct ClientEntry` | ||
559 | * @param msg the actual message | ||
560 | * @return #GNUNET_OK if message is well-formed | ||
561 | */ | ||
562 | static int | ||
563 | check_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg) | ||
564 | { | ||
565 | const char *service; | ||
566 | const char *name; | ||
567 | size_t msize; | ||
568 | |||
569 | msize = ntohs (msg->header.size) - sizeof(*msg); | ||
570 | if (msize != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], | ||
571 | msize, | ||
572 | 2, | ||
573 | &service, | ||
574 | &name)) | ||
575 | { | ||
576 | GNUNET_break (0); | ||
577 | return GNUNET_SYSERR; | ||
578 | } | ||
579 | return GNUNET_OK; | ||
580 | } | ||
581 | |||
582 | |||
583 | /** | ||
584 | * Handle SET-message. | ||
585 | * | ||
586 | * @param cls the `struct ClientEntry` | ||
587 | * @param msg the actual message | ||
588 | */ | ||
589 | static void | ||
590 | handle_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg) | ||
591 | { | ||
592 | struct ClientEntry *ce = cls; | ||
593 | const char *service; | ||
594 | const char *name; | ||
595 | size_t nlen; | ||
596 | uint16_t msize; | ||
597 | uint16_t size; | ||
598 | struct SubsystemEntry *se; | ||
599 | struct StatsEntry *pos; | ||
600 | uint32_t flags; | ||
601 | uint64_t value; | ||
602 | int64_t delta; | ||
603 | int changed; | ||
604 | int initial_set; | ||
605 | |||
606 | msize = ntohs (msg->header.size); | ||
607 | size = msize - sizeof(struct GNUNET_STATISTICS_SetMessage); | ||
608 | GNUNET_assert (size == GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], | ||
609 | size, | ||
610 | 2, | ||
611 | &service, | ||
612 | &name)); | ||
613 | se = find_subsystem_entry (ce, service); | ||
614 | flags = ntohl (msg->flags); | ||
615 | value = GNUNET_ntohll (msg->value); | ||
616 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
617 | "Received request to update statistic on `%s:%s' (%u) to/by %llu\n", | ||
618 | service, | ||
619 | name, | ||
620 | (unsigned int) flags, | ||
621 | (unsigned long long) value); | ||
622 | pos = find_stat_entry (se, name); | ||
623 | if (NULL != pos) | ||
624 | { | ||
625 | initial_set = 0; | ||
626 | if (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) | ||
627 | { | ||
628 | changed = (pos->value != value); | ||
629 | pos->value = value; | ||
630 | } | ||
631 | else | ||
632 | { | ||
633 | delta = (int64_t) value; | ||
634 | if ((delta < 0) && (pos->value < -delta)) | ||
635 | { | ||
636 | changed = (0 != pos->value); | ||
637 | pos->value = 0; | ||
638 | } | ||
639 | else | ||
640 | { | ||
641 | changed = (0 != delta); | ||
642 | GNUNET_break ((delta <= 0) || (pos->value + delta > pos->value)); | ||
643 | pos->value += delta; | ||
644 | } | ||
645 | } | ||
646 | if (GNUNET_NO == pos->set) | ||
647 | { | ||
648 | pos->set = GNUNET_YES; | ||
649 | initial_set = 1; | ||
650 | } | ||
651 | pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); | ||
652 | if (pos != se->stat_head) | ||
653 | { | ||
654 | /* move to front for faster setting next time! */ | ||
655 | GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); | ||
656 | GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); | ||
657 | } | ||
658 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
659 | "Statistic `%s:%s' updated to value %llu (%d).\n", | ||
660 | service, | ||
661 | name, | ||
662 | (unsigned long long) pos->value, | ||
663 | pos->persistent); | ||
664 | if ((changed) || (1 == initial_set)) | ||
665 | notify_change (pos); | ||
666 | GNUNET_SERVICE_client_continue (ce->client); | ||
667 | return; | ||
668 | } | ||
669 | /* not found, create a new entry */ | ||
670 | nlen = strlen (name) + 1; | ||
671 | pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen); | ||
672 | GNUNET_memcpy (&pos[1], name, nlen); | ||
673 | pos->name = (const char *) &pos[1]; | ||
674 | pos->subsystem = se; | ||
675 | if ((0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) || | ||
676 | (0 < (int64_t) GNUNET_ntohll (msg->value))) | ||
677 | { | ||
678 | pos->value = GNUNET_ntohll (msg->value); | ||
679 | pos->set = GNUNET_YES; | ||
680 | } | ||
681 | else | ||
682 | { | ||
683 | pos->set = GNUNET_NO; | ||
684 | } | ||
685 | pos->uid = uidgen++; | ||
686 | pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); | ||
687 | GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); | ||
688 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
689 | "New statistic on `%s:%s' with value %llu created.\n", | ||
690 | service, | ||
691 | name, | ||
692 | (unsigned long long) pos->value); | ||
693 | if (NULL != ce) | ||
694 | GNUNET_SERVICE_client_continue (ce->client); | ||
695 | } | ||
696 | |||
697 | |||
698 | /** | ||
699 | * Check integrity of WATCH-message. | ||
700 | * | ||
701 | * @param cls the `struct ClientEntry *` | ||
702 | * @param message the actual message | ||
703 | * @return #GNUNET_OK if message is well-formed | ||
704 | */ | ||
705 | static int | ||
706 | check_watch (void *cls, const struct GNUNET_MessageHeader *message) | ||
707 | { | ||
708 | size_t size; | ||
709 | const char *service; | ||
710 | const char *name; | ||
711 | |||
712 | size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); | ||
713 | if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], | ||
714 | size, | ||
715 | 2, | ||
716 | &service, | ||
717 | &name)) | ||
718 | { | ||
719 | GNUNET_break (0); | ||
720 | return GNUNET_SYSERR; | ||
721 | } | ||
722 | return GNUNET_OK; | ||
723 | } | ||
724 | |||
725 | |||
726 | /** | ||
727 | * Handle WATCH-message. | ||
728 | * | ||
729 | * @param cls the `struct ClientEntry *` | ||
730 | * @param message the actual message | ||
731 | */ | ||
732 | static void | ||
733 | handle_watch (void *cls, const struct GNUNET_MessageHeader *message) | ||
734 | { | ||
735 | struct ClientEntry *ce = cls; | ||
736 | const char *service; | ||
737 | const char *name; | ||
738 | uint16_t msize; | ||
739 | uint16_t size; | ||
740 | struct SubsystemEntry *se; | ||
741 | struct StatsEntry *pos; | ||
742 | struct WatchEntry *we; | ||
743 | size_t nlen; | ||
744 | |||
745 | if (NULL == nc) | ||
746 | { | ||
747 | GNUNET_SERVICE_client_drop (ce->client); | ||
748 | return; | ||
749 | } | ||
750 | GNUNET_SERVICE_client_mark_monitor (ce->client); | ||
751 | msize = ntohs (message->size); | ||
752 | size = msize - sizeof(struct GNUNET_MessageHeader); | ||
753 | GNUNET_assert (size == | ||
754 | GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], | ||
755 | size, | ||
756 | 2, | ||
757 | &service, | ||
758 | &name)); | ||
759 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
760 | "Received request to watch statistic on `%s:%s'\n", | ||
761 | service, | ||
762 | name); | ||
763 | se = find_subsystem_entry (ce, service); | ||
764 | pos = find_stat_entry (se, name); | ||
765 | if (NULL == pos) | ||
766 | { | ||
767 | nlen = strlen (name) + 1; | ||
768 | pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen); | ||
769 | GNUNET_memcpy (&pos[1], name, nlen); | ||
770 | pos->name = (const char *) &pos[1]; | ||
771 | pos->subsystem = se; | ||
772 | GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); | ||
773 | pos->uid = uidgen++; | ||
774 | pos->set = GNUNET_NO; | ||
775 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
776 | "New statistic on `%s:%s' with value %llu created.\n", | ||
777 | service, | ||
778 | name, | ||
779 | (unsigned long long) pos->value); | ||
780 | } | ||
781 | we = GNUNET_new (struct WatchEntry); | ||
782 | we->ce = ce; | ||
783 | we->last_value_set = GNUNET_NO; | ||
784 | we->wid = ce->max_wid++; | ||
785 | GNUNET_CONTAINER_DLL_insert (pos->we_head, pos->we_tail, we); | ||
786 | if (0 != pos->value) | ||
787 | notify_change (pos); | ||
788 | GNUNET_SERVICE_client_continue (ce->client); | ||
789 | } | ||
790 | |||
791 | |||
792 | /** | ||
793 | * Handle DISCONNECT-message. Sync to disk and send | ||
794 | * back a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM | ||
795 | * message. | ||
796 | * | ||
797 | * @param cls the `struct ClientEntry *` | ||
798 | * @param message the actual message | ||
799 | */ | ||
800 | static void | ||
801 | handle_disconnect (void *cls, const struct GNUNET_MessageHeader *message) | ||
802 | { | ||
803 | struct ClientEntry *ce = cls; | ||
804 | struct GNUNET_MQ_Envelope *env; | ||
805 | struct GNUNET_MessageHeader *msg; | ||
806 | |||
807 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM); | ||
808 | GNUNET_MQ_send (ce->mq, env); | ||
809 | GNUNET_SERVICE_client_continue (ce->client); | ||
810 | } | ||
811 | |||
812 | |||
813 | /** | ||
814 | * Actually perform the shutdown. | ||
815 | */ | ||
816 | static void | ||
817 | do_shutdown () | ||
818 | { | ||
819 | struct WatchEntry *we; | ||
820 | struct StatsEntry *pos; | ||
821 | struct SubsystemEntry *se; | ||
822 | |||
823 | if (NULL == nc) | ||
824 | return; | ||
825 | save (); | ||
826 | GNUNET_notification_context_destroy (nc); | ||
827 | nc = NULL; | ||
828 | GNUNET_assert (0 == client_count); | ||
829 | while (NULL != (se = sub_head)) | ||
830 | { | ||
831 | GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se); | ||
832 | while (NULL != (pos = se->stat_head)) | ||
833 | { | ||
834 | GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); | ||
835 | while (NULL != (we = pos->we_head)) | ||
836 | { | ||
837 | GNUNET_break (0); | ||
838 | GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we); | ||
839 | GNUNET_free (we); | ||
840 | } | ||
841 | GNUNET_free (pos); | ||
842 | } | ||
843 | GNUNET_free (se); | ||
844 | } | ||
845 | } | ||
846 | |||
847 | |||
848 | /** | ||
849 | * Task run during shutdown. | ||
850 | * | ||
851 | * @param cls unused | ||
852 | */ | ||
853 | static void | ||
854 | shutdown_task (void *cls) | ||
855 | { | ||
856 | in_shutdown = GNUNET_YES; | ||
857 | if (0 != client_count) | ||
858 | return; | ||
859 | do_shutdown (); | ||
860 | } | ||
861 | |||
862 | |||
863 | /** | ||
864 | * A client disconnected. Remove all of its data structure entries. | ||
865 | * | ||
866 | * @param cls closure, NULL | ||
867 | * @param client identification of the client | ||
868 | * @param app_cls the `struct ClientEntry *` | ||
869 | */ | ||
870 | static void | ||
871 | client_disconnect_cb (void *cls, | ||
872 | struct GNUNET_SERVICE_Client *client, | ||
873 | void *app_cls) | ||
874 | { | ||
875 | struct ClientEntry *ce = app_cls; | ||
876 | struct WatchEntry *we; | ||
877 | struct WatchEntry *wen; | ||
878 | struct StatsEntry *pos; | ||
879 | struct SubsystemEntry *se; | ||
880 | |||
881 | client_count--; | ||
882 | for (se = sub_head; NULL != se; se = se->next) | ||
883 | { | ||
884 | for (pos = se->stat_head; NULL != pos; pos = pos->next) | ||
885 | { | ||
886 | wen = pos->we_head; | ||
887 | while (NULL != (we = wen)) | ||
888 | { | ||
889 | wen = we->next; | ||
890 | if (we->ce != ce) | ||
891 | continue; | ||
892 | GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we); | ||
893 | GNUNET_free (we); | ||
894 | } | ||
895 | } | ||
896 | } | ||
897 | GNUNET_free (ce); | ||
898 | if ((0 == client_count) && (GNUNET_YES == in_shutdown)) | ||
899 | do_shutdown (); | ||
900 | } | ||
901 | |||
902 | |||
903 | /** | ||
904 | * We've read a `struct GNUNET_STATISTICS_SetMessage *` from | ||
905 | * disk. Check that it is well-formed, and if so pass it to | ||
906 | * the handler for set messages. | ||
907 | * | ||
908 | * @param cls NULL | ||
909 | * @param message the message found on disk | ||
910 | * @return #GNUNET_OK on success, | ||
911 | * #GNUNET_NO to stop further processing (no error) | ||
912 | * #GNUNET_SYSERR to stop further processing with error | ||
913 | */ | ||
914 | static int | ||
915 | inject_message (void *cls, const struct GNUNET_MessageHeader *message) | ||
916 | { | ||
917 | uint16_t msize = ntohs (message->size); | ||
918 | const struct GNUNET_STATISTICS_SetMessage *sm; | ||
919 | |||
920 | sm = (const struct GNUNET_STATISTICS_SetMessage *) message; | ||
921 | if ((sizeof(struct GNUNET_STATISTICS_SetMessage) > msize) || | ||
922 | (GNUNET_OK != check_set (NULL, sm))) | ||
923 | { | ||
924 | GNUNET_break (0); | ||
925 | return GNUNET_SYSERR; | ||
926 | } | ||
927 | handle_set (NULL, sm); | ||
928 | return GNUNET_OK; | ||
929 | } | ||
930 | |||
931 | |||
932 | /** | ||
933 | * Load persistent values from disk. Disk format is exactly the same | ||
934 | * format that we also use for setting the values over the network. | ||
935 | */ | ||
936 | static void | ||
937 | load () | ||
938 | { | ||
939 | char *fn; | ||
940 | struct GNUNET_BIO_ReadHandle *rh; | ||
941 | uint64_t fsize; | ||
942 | char *buf; | ||
943 | struct GNUNET_MessageStreamTokenizer *mst; | ||
944 | |||
945 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
946 | "STATISTICS", | ||
947 | "DATABASE", | ||
948 | &fn)) | ||
949 | { | ||
950 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
951 | "STATISTICS", | ||
952 | "DATABASE"); | ||
953 | return; | ||
954 | } | ||
955 | if ((GNUNET_OK != | ||
956 | GNUNET_DISK_file_size (fn, &fsize, GNUNET_NO, GNUNET_YES)) || | ||
957 | (0 == fsize)) | ||
958 | { | ||
959 | GNUNET_free (fn); | ||
960 | return; | ||
961 | } | ||
962 | buf = GNUNET_malloc (fsize); | ||
963 | rh = GNUNET_BIO_read_open_file (fn); | ||
964 | if (! rh) | ||
965 | { | ||
966 | GNUNET_free (buf); | ||
967 | GNUNET_free (fn); | ||
968 | return; | ||
969 | } | ||
970 | if (GNUNET_OK != GNUNET_BIO_read (rh, fn, buf, fsize)) | ||
971 | { | ||
972 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", fn); | ||
973 | GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); | ||
974 | GNUNET_free (buf); | ||
975 | GNUNET_free (fn); | ||
976 | return; | ||
977 | } | ||
978 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
979 | _ ("Loading %llu bytes of statistics from `%s'\n"), | ||
980 | (unsigned long long) fsize, | ||
981 | fn); | ||
982 | mst = GNUNET_MST_create (&inject_message, NULL); | ||
983 | GNUNET_break ( | ||
984 | GNUNET_OK == | ||
985 | GNUNET_MST_from_buffer (mst, buf, (size_t) fsize, GNUNET_YES, GNUNET_NO)); | ||
986 | GNUNET_MST_destroy (mst); | ||
987 | GNUNET_free (buf); | ||
988 | GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); | ||
989 | GNUNET_free (fn); | ||
990 | } | ||
991 | |||
992 | |||
993 | /** | ||
994 | * Process statistics requests. | ||
995 | * | ||
996 | * @param cls closure | ||
997 | * @param c configuration to use | ||
998 | * @param service the initialized service | ||
999 | */ | ||
1000 | static void | ||
1001 | run (void *cls, | ||
1002 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
1003 | struct GNUNET_SERVICE_Handle *service) | ||
1004 | { | ||
1005 | cfg = c; | ||
1006 | nc = GNUNET_notification_context_create (16); | ||
1007 | load (); | ||
1008 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
1009 | } | ||
1010 | |||
1011 | |||
1012 | /** | ||
1013 | * Define "main" method using service macro. | ||
1014 | */ | ||
1015 | GNUNET_SERVICE_MAIN ( | ||
1016 | "statistics", | ||
1017 | GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, | ||
1018 | &run, | ||
1019 | &client_connect_cb, | ||
1020 | &client_disconnect_cb, | ||
1021 | NULL, | ||
1022 | GNUNET_MQ_hd_var_size (set, | ||
1023 | GNUNET_MESSAGE_TYPE_STATISTICS_SET, | ||
1024 | struct GNUNET_STATISTICS_SetMessage, | ||
1025 | NULL), | ||
1026 | GNUNET_MQ_hd_var_size (get, | ||
1027 | GNUNET_MESSAGE_TYPE_STATISTICS_GET, | ||
1028 | struct GNUNET_MessageHeader, | ||
1029 | NULL), | ||
1030 | GNUNET_MQ_hd_var_size (watch, | ||
1031 | GNUNET_MESSAGE_TYPE_STATISTICS_WATCH, | ||
1032 | struct GNUNET_MessageHeader, | ||
1033 | NULL), | ||
1034 | GNUNET_MQ_hd_fixed_size (disconnect, | ||
1035 | GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT, | ||
1036 | struct GNUNET_MessageHeader, | ||
1037 | NULL), | ||
1038 | GNUNET_MQ_handler_end ()); | ||
1039 | |||
1040 | |||
1041 | #if defined(__linux__) && defined(__GLIBC__) | ||
1042 | #include <malloc.h> | ||
1043 | |||
1044 | /** | ||
1045 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
1046 | */ | ||
1047 | void __attribute__ ((constructor)) | ||
1048 | GNUNET_STATISTICS_memory_init () | ||
1049 | { | ||
1050 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
1051 | mallopt (M_TOP_PAD, 1 * 1024); | ||
1052 | malloc_trim (0); | ||
1053 | } | ||
1054 | |||
1055 | |||
1056 | #endif | ||
1057 | |||
1058 | |||
1059 | /* end of gnunet-service-statistics.c */ | ||
diff --git a/src/service/statistics/meson.build b/src/service/statistics/meson.build new file mode 100644 index 000000000..9c4d8c649 --- /dev/null +++ b/src/service/statistics/meson.build | |||
@@ -0,0 +1,35 @@ | |||
1 | libgnunetstatistics_src = ['statistics_api.c'] | ||
2 | |||
3 | gnunetservicestatistics_src = ['gnunet-service-statistics.c'] | ||
4 | |||
5 | configure_file(input : 'statistics.conf.in', | ||
6 | output : 'statistics.conf', | ||
7 | configuration : cdata, | ||
8 | install: true, | ||
9 | install_dir: pkgcfgdir) | ||
10 | |||
11 | |||
12 | if get_option('monolith') | ||
13 | foreach p : libgnunetstatistics_src + gnunetservicestatistics_src | ||
14 | gnunet_src += 'statistics/' + p | ||
15 | endforeach | ||
16 | endif | ||
17 | |||
18 | libgnunetstatistics = library('gnunetstatistics', | ||
19 | libgnunetstatistics_src, | ||
20 | soversion: '2', | ||
21 | version: '2.0.0', | ||
22 | dependencies: libgnunetutil_dep, | ||
23 | include_directories: [incdir, configuration_inc], | ||
24 | install: true, | ||
25 | install_dir: get_option('libdir')) | ||
26 | libgnunetstatistics_dep = declare_dependency(link_with : libgnunetstatistics) | ||
27 | pkg.generate(libgnunetstatistics, url: 'https://www.gnunet.org', | ||
28 | description : 'Provides API of GNUnet statistics service') | ||
29 | |||
30 | executable ('gnunet-service-statistics', | ||
31 | gnunetservicestatistics_src, | ||
32 | dependencies: [libgnunetstatistics_dep, libgnunetutil_dep], | ||
33 | include_directories: [incdir, configuration_inc], | ||
34 | install: true, | ||
35 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
diff --git a/src/service/statistics/statistics.conf.in b/src/service/statistics/statistics.conf.in new file mode 100644 index 000000000..36aca538f --- /dev/null +++ b/src/service/statistics/statistics.conf.in | |||
@@ -0,0 +1,21 @@ | |||
1 | [statistics] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | @JAVAPORT@PORT = 2088 | ||
4 | HOSTNAME = localhost | ||
5 | BINARY = gnunet-service-statistics | ||
6 | ACCEPT_FROM = 127.0.0.1; | ||
7 | ACCEPT_FROM6 = ::1; | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics.sock | ||
9 | UNIX_MATCH_UID = NO | ||
10 | UNIX_MATCH_GID = YES | ||
11 | DATABASE = $GNUNET_DATA_HOME/statistics.dat | ||
12 | # DISABLE_SOCKET_FORWARDING = NO | ||
13 | # USERNAME = | ||
14 | # MAXBUF = | ||
15 | # TIMEOUT = | ||
16 | # DISABLEV6 = | ||
17 | # BINDTO = | ||
18 | # REJECT_FROM = | ||
19 | # REJECT_FROM6 = | ||
20 | # PREFIX = | ||
21 | |||
diff --git a/src/service/statistics/statistics.h b/src/service/statistics/statistics.h new file mode 100644 index 000000000..6eb75cc99 --- /dev/null +++ b/src/service/statistics/statistics.h | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001-2014 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @author Christian Grothoff | ||
23 | * @file statistics/statistics.h | ||
24 | */ | ||
25 | #ifndef STATISTICS_H | ||
26 | #define STATISTICS_H | ||
27 | |||
28 | #include "gnunet_common.h" | ||
29 | |||
30 | |||
31 | GNUNET_NETWORK_STRUCT_BEGIN | ||
32 | |||
33 | /** | ||
34 | * Statistics message. Contains how long the system is up | ||
35 | * and one value. | ||
36 | * | ||
37 | * The struct is be followed by the service name and | ||
38 | * name of the statistic, both 0-terminated. | ||
39 | */ | ||
40 | struct GNUNET_STATISTICS_ReplyMessage | ||
41 | { | ||
42 | /** | ||
43 | * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE | ||
44 | */ | ||
45 | struct GNUNET_MessageHeader header; | ||
46 | |||
47 | /** | ||
48 | * Unique numerical identifier for the value (will | ||
49 | * not change during the same client-session). Highest | ||
50 | * bit will be set for persistent values (see | ||
51 | * #GNUNET_STATISTICS_PERSIST_BIT). | ||
52 | */ | ||
53 | uint32_t uid GNUNET_PACKED; | ||
54 | |||
55 | /** | ||
56 | * The value. | ||
57 | */ | ||
58 | uint64_t value GNUNET_PACKED; | ||
59 | }; | ||
60 | |||
61 | /** | ||
62 | * Flag for the `struct GNUNET_STATISTICS_ReplyMessage` UID only. | ||
63 | * Note that other messages use #GNUNET_STATISTICS_SETFLAG_PERSISTENT. | ||
64 | */ | ||
65 | #define GNUNET_STATISTICS_PERSIST_BIT ((uint32_t) (1LLU << 31)) | ||
66 | |||
67 | /** | ||
68 | * The value being set is an absolute change. | ||
69 | */ | ||
70 | #define GNUNET_STATISTICS_SETFLAG_ABSOLUTE 0 | ||
71 | |||
72 | /** | ||
73 | * The value being set is a relative change. | ||
74 | */ | ||
75 | #define GNUNET_STATISTICS_SETFLAG_RELATIVE 1 | ||
76 | |||
77 | /** | ||
78 | * The value being set is to be persistent (note that | ||
79 | * this bit can be combined with #GNUNET_STATISTICS_SETFLAG_RELATIVE). | ||
80 | * This value must not be used for the `uid` member of | ||
81 | * `struct GNUNET_STATISTICS_ReplyMessage`! | ||
82 | */ | ||
83 | #define GNUNET_STATISTICS_SETFLAG_PERSISTENT 2 | ||
84 | |||
85 | |||
86 | /** | ||
87 | * Message to set a statistic. Followed | ||
88 | * by the subsystem name and the name of | ||
89 | * the statistic (each 0-terminated). | ||
90 | */ | ||
91 | struct GNUNET_STATISTICS_SetMessage | ||
92 | { | ||
93 | /** | ||
94 | * Type: #GNUNET_MESSAGE_TYPE_STATISTICS_SET | ||
95 | */ | ||
96 | struct GNUNET_MessageHeader header; | ||
97 | |||
98 | /** | ||
99 | * 0 for absolute value, 1 for relative value; 2 to make persistent | ||
100 | * (see GNUNET_STATISTICS_SETFLAG_*). | ||
101 | */ | ||
102 | uint32_t flags GNUNET_PACKED; | ||
103 | |||
104 | /** | ||
105 | * Value. Note that if this is a relative value, it will | ||
106 | * be signed even though the type given here is unsigned. | ||
107 | */ | ||
108 | uint64_t value GNUNET_PACKED; | ||
109 | }; | ||
110 | |||
111 | |||
112 | /** | ||
113 | * Message transmitted if a watched value changes. | ||
114 | */ | ||
115 | struct GNUNET_STATISTICS_WatchValueMessage | ||
116 | { | ||
117 | /** | ||
118 | * Type: #GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE | ||
119 | */ | ||
120 | struct GNUNET_MessageHeader header; | ||
121 | |||
122 | /** | ||
123 | * 0 for absolute value, 1 for relative value; 2 to make persistent | ||
124 | * (see GNUNET_STATISTICS_SETFLAG_*). | ||
125 | */ | ||
126 | uint32_t flags GNUNET_PACKED; | ||
127 | |||
128 | /** | ||
129 | * Unique watch identification number (watch | ||
130 | * requests are enumerated in the order they | ||
131 | * are received, the first request having | ||
132 | * a wid of zero). | ||
133 | */ | ||
134 | uint32_t wid GNUNET_PACKED; | ||
135 | |||
136 | /** | ||
137 | * Reserved (always 0). | ||
138 | */ | ||
139 | uint32_t reserved GNUNET_PACKED; | ||
140 | |||
141 | /** | ||
142 | * Value. Note that if this is a relative value, it will | ||
143 | * be signed even though the type given here is unsigned. | ||
144 | */ | ||
145 | uint64_t value GNUNET_PACKED; | ||
146 | }; | ||
147 | GNUNET_NETWORK_STRUCT_END | ||
148 | |||
149 | #endif | ||
diff --git a/src/service/statistics/statistics_api.c b/src/service/statistics/statistics_api.c new file mode 100644 index 000000000..88f127da8 --- /dev/null +++ b/src/service/statistics/statistics_api.c | |||
@@ -0,0 +1,1342 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file statistics/statistics_api.c | ||
23 | * @brief API of the statistics service | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_constants.h" | ||
29 | #include "gnunet_protocols.h" | ||
30 | #include "gnunet_statistics_service.h" | ||
31 | #include "statistics.h" | ||
32 | |||
33 | /** | ||
34 | * How long do we wait until a statistics request for setting | ||
35 | * a value times out? (The update will be lost if the | ||
36 | * service does not react within this timeframe). | ||
37 | */ | ||
38 | #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
39 | GNUNET_TIME_UNIT_SECONDS, 2) | ||
40 | |||
41 | #define LOG(kind, ...) GNUNET_log_from (kind, "statistics-api", __VA_ARGS__) | ||
42 | |||
43 | /** | ||
44 | * Types of actions. | ||
45 | */ | ||
46 | enum ActionType | ||
47 | { | ||
48 | /** | ||
49 | * Get a value. | ||
50 | */ | ||
51 | ACTION_GET, | ||
52 | |||
53 | /** | ||
54 | * Set a value. | ||
55 | */ | ||
56 | ACTION_SET, | ||
57 | |||
58 | /** | ||
59 | * Update a value. | ||
60 | */ | ||
61 | ACTION_UPDATE, | ||
62 | |||
63 | /** | ||
64 | * Watch a value. | ||
65 | */ | ||
66 | ACTION_WATCH | ||
67 | }; | ||
68 | |||
69 | |||
70 | /** | ||
71 | * Entry kept for each value we are watching. | ||
72 | */ | ||
73 | struct GNUNET_STATISTICS_WatchEntry | ||
74 | { | ||
75 | /** | ||
76 | * What subsystem is this action about? (never NULL) | ||
77 | */ | ||
78 | char *subsystem; | ||
79 | |||
80 | /** | ||
81 | * What value is this action about? (never NULL) | ||
82 | */ | ||
83 | char *name; | ||
84 | |||
85 | /** | ||
86 | * Function to call | ||
87 | */ | ||
88 | GNUNET_STATISTICS_Iterator proc; | ||
89 | |||
90 | /** | ||
91 | * Closure for @e proc | ||
92 | */ | ||
93 | void *proc_cls; | ||
94 | }; | ||
95 | |||
96 | |||
97 | /** | ||
98 | * Linked list of things we still need to do. | ||
99 | */ | ||
100 | struct GNUNET_STATISTICS_GetHandle | ||
101 | { | ||
102 | /** | ||
103 | * This is a doubly linked list. | ||
104 | */ | ||
105 | struct GNUNET_STATISTICS_GetHandle *next; | ||
106 | |||
107 | /** | ||
108 | * This is a doubly linked list. | ||
109 | */ | ||
110 | struct GNUNET_STATISTICS_GetHandle *prev; | ||
111 | |||
112 | /** | ||
113 | * Main statistics handle. | ||
114 | */ | ||
115 | struct GNUNET_STATISTICS_Handle *sh; | ||
116 | |||
117 | /** | ||
118 | * What subsystem is this action about? (can be NULL) | ||
119 | */ | ||
120 | char *subsystem; | ||
121 | |||
122 | /** | ||
123 | * What value is this action about? (can be NULL) | ||
124 | */ | ||
125 | char *name; | ||
126 | |||
127 | /** | ||
128 | * Continuation to call once action is complete. | ||
129 | */ | ||
130 | GNUNET_STATISTICS_Callback cont; | ||
131 | |||
132 | /** | ||
133 | * Function to call (for GET actions only). | ||
134 | */ | ||
135 | GNUNET_STATISTICS_Iterator proc; | ||
136 | |||
137 | /** | ||
138 | * Closure for @e proc and @e cont. | ||
139 | */ | ||
140 | void *cls; | ||
141 | |||
142 | /** | ||
143 | * Timeout for this action. | ||
144 | */ | ||
145 | struct GNUNET_TIME_Absolute timeout; | ||
146 | |||
147 | /** | ||
148 | * Associated value. | ||
149 | */ | ||
150 | uint64_t value; | ||
151 | |||
152 | /** | ||
153 | * Flag for SET/UPDATE actions. | ||
154 | */ | ||
155 | int make_persistent; | ||
156 | |||
157 | /** | ||
158 | * Has the current iteration been aborted; for GET actions. | ||
159 | */ | ||
160 | int aborted; | ||
161 | |||
162 | /** | ||
163 | * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH? | ||
164 | */ | ||
165 | enum ActionType type; | ||
166 | |||
167 | /** | ||
168 | * Size of the message that we will be transmitting. | ||
169 | */ | ||
170 | uint16_t msize; | ||
171 | }; | ||
172 | |||
173 | |||
174 | /** | ||
175 | * Handle for the service. | ||
176 | */ | ||
177 | struct GNUNET_STATISTICS_Handle | ||
178 | { | ||
179 | /** | ||
180 | * Name of our subsystem. | ||
181 | */ | ||
182 | char *subsystem; | ||
183 | |||
184 | /** | ||
185 | * Configuration to use. | ||
186 | */ | ||
187 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
188 | |||
189 | /** | ||
190 | * Message queue to the service. | ||
191 | */ | ||
192 | struct GNUNET_MQ_Handle *mq; | ||
193 | |||
194 | /** | ||
195 | * Head of the linked list of pending actions (first action | ||
196 | * to be performed). | ||
197 | */ | ||
198 | struct GNUNET_STATISTICS_GetHandle *action_head; | ||
199 | |||
200 | /** | ||
201 | * Tail of the linked list of actions (for fast append). | ||
202 | */ | ||
203 | struct GNUNET_STATISTICS_GetHandle *action_tail; | ||
204 | |||
205 | /** | ||
206 | * Action we are currently busy with (action request has been | ||
207 | * transmitted, we're now receiving the response from the | ||
208 | * service). | ||
209 | */ | ||
210 | struct GNUNET_STATISTICS_GetHandle *current; | ||
211 | |||
212 | /** | ||
213 | * Array of watch entries. | ||
214 | */ | ||
215 | struct GNUNET_STATISTICS_WatchEntry **watches; | ||
216 | |||
217 | /** | ||
218 | * Task doing exponential back-off trying to reconnect. | ||
219 | */ | ||
220 | struct GNUNET_SCHEDULER_Task *backoff_task; | ||
221 | |||
222 | /** | ||
223 | * Task for running #do_destroy(). | ||
224 | */ | ||
225 | struct GNUNET_SCHEDULER_Task *destroy_task; | ||
226 | |||
227 | /** | ||
228 | * Time for next connect retry. | ||
229 | */ | ||
230 | struct GNUNET_TIME_Relative backoff; | ||
231 | |||
232 | /** | ||
233 | * Maximum heap size observed so far (if available). | ||
234 | */ | ||
235 | uint64_t peak_heap_size; | ||
236 | |||
237 | /** | ||
238 | * Maximum resident set side observed so far (if available). | ||
239 | */ | ||
240 | uint64_t peak_rss; | ||
241 | |||
242 | /** | ||
243 | * Size of the @e watches array. | ||
244 | */ | ||
245 | unsigned int watches_size; | ||
246 | |||
247 | /** | ||
248 | * Should this handle auto-destruct once all actions have | ||
249 | * been processed? | ||
250 | */ | ||
251 | int do_destroy; | ||
252 | |||
253 | /** | ||
254 | * Are we currently receiving from the service? | ||
255 | */ | ||
256 | int receiving; | ||
257 | }; | ||
258 | |||
259 | |||
260 | /** | ||
261 | * Obtain statistics about this process's memory consumption and | ||
262 | * report those as well (if they changed). | ||
263 | */ | ||
264 | static void | ||
265 | update_memory_statistics (struct GNUNET_STATISTICS_Handle *h) | ||
266 | { | ||
267 | #if ENABLE_HEAP_STATISTICS | ||
268 | uint64_t current_heap_size = 0; | ||
269 | uint64_t current_rss = 0; | ||
270 | |||
271 | if (GNUNET_NO != h->do_destroy) | ||
272 | return; | ||
273 | #if HAVE_MALLINFO2 | ||
274 | { | ||
275 | struct mallinfo2 mi; | ||
276 | |||
277 | mi = mallinfo2 (); | ||
278 | current_heap_size = mi.uordblks + mi.fordblks; | ||
279 | } | ||
280 | #endif | ||
281 | #if HAVE_GETRUSAGE | ||
282 | { | ||
283 | struct rusage ru; | ||
284 | |||
285 | if (0 == getrusage (RUSAGE_SELF, &ru)) | ||
286 | { | ||
287 | current_rss = 1024LL * ru.ru_maxrss; | ||
288 | } | ||
289 | } | ||
290 | #endif | ||
291 | if (current_heap_size > h->peak_heap_size) | ||
292 | { | ||
293 | h->peak_heap_size = current_heap_size; | ||
294 | GNUNET_STATISTICS_set (h, | ||
295 | "# peak heap size", | ||
296 | current_heap_size, | ||
297 | GNUNET_NO); | ||
298 | } | ||
299 | if (current_rss > h->peak_rss) | ||
300 | { | ||
301 | h->peak_rss = current_rss; | ||
302 | GNUNET_STATISTICS_set (h, | ||
303 | "# peak resident set size", | ||
304 | current_rss, | ||
305 | GNUNET_NO); | ||
306 | } | ||
307 | #endif | ||
308 | } | ||
309 | |||
310 | |||
311 | /** | ||
312 | * Reconnect at a later time, respecting back-off. | ||
313 | * | ||
314 | * @param h statistics handle | ||
315 | */ | ||
316 | static void | ||
317 | reconnect_later (struct GNUNET_STATISTICS_Handle *h); | ||
318 | |||
319 | |||
320 | /** | ||
321 | * Schedule the next action to be performed. | ||
322 | * | ||
323 | * @param cls statistics handle to reconnect | ||
324 | */ | ||
325 | static void | ||
326 | schedule_action (void *cls); | ||
327 | |||
328 | |||
329 | /** | ||
330 | * Transmit request to service that we want to watch | ||
331 | * the development of a particular value. | ||
332 | * | ||
333 | * @param h statistics handle | ||
334 | * @param watch watch entry of the value to watch | ||
335 | */ | ||
336 | static void | ||
337 | schedule_watch_request (struct GNUNET_STATISTICS_Handle *h, | ||
338 | struct GNUNET_STATISTICS_WatchEntry *watch) | ||
339 | { | ||
340 | struct GNUNET_STATISTICS_GetHandle *ai; | ||
341 | size_t slen; | ||
342 | size_t nlen; | ||
343 | size_t nsize; | ||
344 | |||
345 | slen = strlen (watch->subsystem) + 1; | ||
346 | nlen = strlen (watch->name) + 1; | ||
347 | nsize = sizeof(struct GNUNET_MessageHeader) + slen + nlen; | ||
348 | if (nsize >= GNUNET_MAX_MESSAGE_SIZE) | ||
349 | { | ||
350 | GNUNET_break (0); | ||
351 | return; | ||
352 | } | ||
353 | ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); | ||
354 | ai->sh = h; | ||
355 | ai->subsystem = GNUNET_strdup (watch->subsystem); | ||
356 | ai->name = GNUNET_strdup (watch->name); | ||
357 | ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
358 | ai->msize = nsize; | ||
359 | ai->type = ACTION_WATCH; | ||
360 | ai->proc = watch->proc; | ||
361 | ai->cls = watch->proc_cls; | ||
362 | GNUNET_CONTAINER_DLL_insert_tail (h->action_head, | ||
363 | h->action_tail, | ||
364 | ai); | ||
365 | schedule_action (h); | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * Free memory associated with the given action item. | ||
371 | * | ||
372 | * @param gh action item to free | ||
373 | */ | ||
374 | static void | ||
375 | free_action_item (struct GNUNET_STATISTICS_GetHandle *gh) | ||
376 | { | ||
377 | GNUNET_free (gh->subsystem); | ||
378 | GNUNET_free (gh->name); | ||
379 | GNUNET_free (gh); | ||
380 | } | ||
381 | |||
382 | |||
383 | /** | ||
384 | * Disconnect from the statistics service. | ||
385 | * | ||
386 | * @param h statistics handle to disconnect from | ||
387 | */ | ||
388 | static void | ||
389 | do_disconnect (struct GNUNET_STATISTICS_Handle *h) | ||
390 | { | ||
391 | struct GNUNET_STATISTICS_GetHandle *c; | ||
392 | |||
393 | h->receiving = GNUNET_NO; | ||
394 | if (NULL != (c = h->current)) | ||
395 | { | ||
396 | h->current = NULL; | ||
397 | if ((NULL != c->cont) && | ||
398 | (GNUNET_YES != c->aborted)) | ||
399 | { | ||
400 | c->cont (c->cls, | ||
401 | GNUNET_SYSERR); | ||
402 | c->cont = NULL; | ||
403 | } | ||
404 | free_action_item (c); | ||
405 | } | ||
406 | if (NULL != h->mq) | ||
407 | { | ||
408 | GNUNET_MQ_destroy (h->mq); | ||
409 | h->mq = NULL; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | |||
414 | /** | ||
415 | * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message. | ||
416 | * | ||
417 | * @param cls statistics handle | ||
418 | * @param smsg message received from the service, never NULL | ||
419 | * @return #GNUNET_OK if the message was well-formed | ||
420 | */ | ||
421 | static int | ||
422 | check_statistics_value (void *cls, | ||
423 | const struct GNUNET_STATISTICS_ReplyMessage *smsg) | ||
424 | { | ||
425 | const char *service; | ||
426 | const char *name; | ||
427 | uint16_t size; | ||
428 | |||
429 | size = ntohs (smsg->header.size); | ||
430 | size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage); | ||
431 | if (size != | ||
432 | GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], | ||
433 | size, | ||
434 | 2, | ||
435 | &service, | ||
436 | &name)) | ||
437 | { | ||
438 | GNUNET_break (0); | ||
439 | return GNUNET_SYSERR; | ||
440 | } | ||
441 | return GNUNET_OK; | ||
442 | } | ||
443 | |||
444 | |||
445 | /** | ||
446 | * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message. | ||
447 | * | ||
448 | * @param cls statistics handle | ||
449 | * @param smsg message received from the service, never NULL | ||
450 | * @return #GNUNET_OK if the message was well-formed | ||
451 | */ | ||
452 | static void | ||
453 | handle_statistics_value (void *cls, | ||
454 | const struct GNUNET_STATISTICS_ReplyMessage *smsg) | ||
455 | { | ||
456 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
457 | const char *service; | ||
458 | const char *name; | ||
459 | uint16_t size; | ||
460 | |||
461 | if (h->current->aborted) | ||
462 | return; /* iteration aborted, don't bother */ | ||
463 | |||
464 | size = ntohs (smsg->header.size); | ||
465 | size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage); | ||
466 | GNUNET_assert (size == | ||
467 | GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], | ||
468 | size, | ||
469 | 2, | ||
470 | &service, | ||
471 | &name)); | ||
472 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
473 | "Received valid statistic on `%s:%s': %llu\n", | ||
474 | service, name, | ||
475 | (unsigned long long) GNUNET_ntohll (smsg->value)); | ||
476 | if (GNUNET_OK != | ||
477 | h->current->proc (h->current->cls, | ||
478 | service, | ||
479 | name, | ||
480 | GNUNET_ntohll (smsg->value), | ||
481 | (0 != | ||
482 | (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)) )) | ||
483 | { | ||
484 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
485 | "Processing of remaining statistics aborted by client.\n"); | ||
486 | h->current->aborted = GNUNET_YES; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | |||
491 | /** | ||
492 | * We have received a watch value from the service. Process it. | ||
493 | * | ||
494 | * @param cls statistics handle | ||
495 | * @param wvm the watch value message | ||
496 | */ | ||
497 | static void | ||
498 | handle_statistics_watch_value (void *cls, | ||
499 | const struct | ||
500 | GNUNET_STATISTICS_WatchValueMessage *wvm) | ||
501 | { | ||
502 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
503 | struct GNUNET_STATISTICS_WatchEntry *w; | ||
504 | uint32_t wid; | ||
505 | |||
506 | GNUNET_break (0 == ntohl (wvm->reserved)); | ||
507 | wid = ntohl (wvm->wid); | ||
508 | if (wid >= h->watches_size) | ||
509 | { | ||
510 | do_disconnect (h); | ||
511 | reconnect_later (h); | ||
512 | return; | ||
513 | } | ||
514 | w = h->watches[wid]; | ||
515 | if (NULL == w) | ||
516 | return; | ||
517 | (void) w->proc (w->proc_cls, | ||
518 | w->subsystem, | ||
519 | w->name, | ||
520 | GNUNET_ntohll (wvm->value), | ||
521 | 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT)); | ||
522 | } | ||
523 | |||
524 | |||
525 | /** | ||
526 | * Generic error handler, called with the appropriate error code and | ||
527 | * the same closure specified at the creation of the message queue. | ||
528 | * Not every message queue implementation supports an error handler. | ||
529 | * | ||
530 | * @param cls closure with the `struct GNUNET_STATISTICS_Handle *` | ||
531 | * @param error error code | ||
532 | */ | ||
533 | static void | ||
534 | mq_error_handler (void *cls, | ||
535 | enum GNUNET_MQ_Error error) | ||
536 | { | ||
537 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
538 | |||
539 | if (GNUNET_NO != h->do_destroy) | ||
540 | { | ||
541 | h->do_destroy = GNUNET_NO; | ||
542 | if (NULL != h->destroy_task) | ||
543 | { | ||
544 | GNUNET_SCHEDULER_cancel (h->destroy_task); | ||
545 | h->destroy_task = NULL; | ||
546 | } | ||
547 | GNUNET_STATISTICS_destroy (h, | ||
548 | GNUNET_NO); | ||
549 | return; | ||
550 | } | ||
551 | do_disconnect (h); | ||
552 | reconnect_later (h); | ||
553 | } | ||
554 | |||
555 | |||
556 | /** | ||
557 | * Task used to destroy the statistics handle. | ||
558 | * | ||
559 | * @param cls the `struct GNUNET_STATISTICS_Handle` | ||
560 | */ | ||
561 | static void | ||
562 | do_destroy (void *cls) | ||
563 | { | ||
564 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
565 | |||
566 | h->destroy_task = NULL; | ||
567 | h->do_destroy = GNUNET_NO; | ||
568 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
569 | "Running final destruction\n"); | ||
570 | GNUNET_STATISTICS_destroy (h, | ||
571 | GNUNET_NO); | ||
572 | } | ||
573 | |||
574 | |||
575 | /** | ||
576 | * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM | ||
577 | * message. We receive this message at the end of the shutdown when | ||
578 | * the service confirms that all data has been written to disk. | ||
579 | * | ||
580 | * @param cls our `struct GNUNET_STATISTICS_Handle *` | ||
581 | * @param msg the message | ||
582 | */ | ||
583 | static void | ||
584 | handle_disconnect_confirm (void *cls, | ||
585 | const struct GNUNET_MessageHeader *msg) | ||
586 | { | ||
587 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
588 | |||
589 | if (GNUNET_SYSERR != h->do_destroy) | ||
590 | { | ||
591 | /* not in shutdown, why do we get 'TEST'? */ | ||
592 | GNUNET_break (0); | ||
593 | do_disconnect (h); | ||
594 | reconnect_later (h); | ||
595 | return; | ||
596 | } | ||
597 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
598 | "Received DISCONNNECT_CONFIRM message from statistics, can complete disconnect\n"); | ||
599 | if (NULL != h->destroy_task) | ||
600 | GNUNET_SCHEDULER_cancel (h->destroy_task); | ||
601 | h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy, | ||
602 | h); | ||
603 | } | ||
604 | |||
605 | |||
606 | /** | ||
607 | * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive | ||
608 | * this message in response to a query to indicate that there are no | ||
609 | * further matching results. | ||
610 | * | ||
611 | * @param cls our `struct GNUNET_STATISTICS_Handle *` | ||
612 | * @param msg the message | ||
613 | */ | ||
614 | static void | ||
615 | handle_statistics_end (void *cls, | ||
616 | const struct GNUNET_MessageHeader *msg) | ||
617 | { | ||
618 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
619 | struct GNUNET_STATISTICS_GetHandle *c; | ||
620 | |||
621 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
622 | "Received end of statistics marker\n"); | ||
623 | if (NULL == (c = h->current)) | ||
624 | { | ||
625 | GNUNET_break (0); | ||
626 | do_disconnect (h); | ||
627 | reconnect_later (h); | ||
628 | return; | ||
629 | } | ||
630 | h->backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
631 | h->current = NULL; | ||
632 | schedule_action (h); | ||
633 | if (NULL != c->cont) | ||
634 | { | ||
635 | c->cont (c->cls, | ||
636 | GNUNET_OK); | ||
637 | c->cont = NULL; | ||
638 | } | ||
639 | free_action_item (c); | ||
640 | } | ||
641 | |||
642 | |||
643 | /** | ||
644 | * Try to (re)connect to the statistics service. | ||
645 | * | ||
646 | * @param h statistics handle to reconnect | ||
647 | * @return #GNUNET_YES on success, #GNUNET_NO on failure. | ||
648 | */ | ||
649 | static int | ||
650 | try_connect (struct GNUNET_STATISTICS_Handle *h) | ||
651 | { | ||
652 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
653 | GNUNET_MQ_hd_fixed_size (disconnect_confirm, | ||
654 | GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM, | ||
655 | struct GNUNET_MessageHeader, | ||
656 | h), | ||
657 | GNUNET_MQ_hd_fixed_size (statistics_end, | ||
658 | GNUNET_MESSAGE_TYPE_STATISTICS_END, | ||
659 | struct GNUNET_MessageHeader, | ||
660 | h), | ||
661 | GNUNET_MQ_hd_var_size (statistics_value, | ||
662 | GNUNET_MESSAGE_TYPE_STATISTICS_VALUE, | ||
663 | struct GNUNET_STATISTICS_ReplyMessage, | ||
664 | h), | ||
665 | GNUNET_MQ_hd_fixed_size (statistics_watch_value, | ||
666 | GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE, | ||
667 | struct GNUNET_STATISTICS_WatchValueMessage, | ||
668 | h), | ||
669 | GNUNET_MQ_handler_end () | ||
670 | }; | ||
671 | struct GNUNET_STATISTICS_GetHandle *gh; | ||
672 | struct GNUNET_STATISTICS_GetHandle *gn; | ||
673 | |||
674 | if (NULL != h->backoff_task) | ||
675 | return GNUNET_NO; | ||
676 | if (NULL != h->mq) | ||
677 | return GNUNET_YES; | ||
678 | h->mq = GNUNET_CLIENT_connect (h->cfg, | ||
679 | "statistics", | ||
680 | handlers, | ||
681 | &mq_error_handler, | ||
682 | h); | ||
683 | if (NULL == h->mq) | ||
684 | { | ||
685 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
686 | "Failed to connect to statistics service!\n"); | ||
687 | return GNUNET_NO; | ||
688 | } | ||
689 | gn = h->action_head; | ||
690 | while (NULL != (gh = gn)) | ||
691 | { | ||
692 | gn = gh->next; | ||
693 | if (gh->type == ACTION_WATCH) | ||
694 | { | ||
695 | GNUNET_CONTAINER_DLL_remove (h->action_head, | ||
696 | h->action_tail, | ||
697 | gh); | ||
698 | free_action_item (gh); | ||
699 | } | ||
700 | } | ||
701 | for (unsigned int i = 0; i < h->watches_size; i++) | ||
702 | if (NULL != h->watches[i]) | ||
703 | schedule_watch_request (h, | ||
704 | h->watches[i]); | ||
705 | return GNUNET_YES; | ||
706 | } | ||
707 | |||
708 | |||
709 | /** | ||
710 | * We've waited long enough, reconnect now. | ||
711 | * | ||
712 | * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect | ||
713 | */ | ||
714 | static void | ||
715 | reconnect_task (void *cls) | ||
716 | { | ||
717 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
718 | |||
719 | h->backoff_task = NULL; | ||
720 | schedule_action (h); | ||
721 | } | ||
722 | |||
723 | |||
724 | /** | ||
725 | * Reconnect at a later time, respecting back-off. | ||
726 | * | ||
727 | * @param h statistics handle | ||
728 | */ | ||
729 | static void | ||
730 | reconnect_later (struct GNUNET_STATISTICS_Handle *h) | ||
731 | { | ||
732 | int loss; | ||
733 | struct GNUNET_STATISTICS_GetHandle *gh; | ||
734 | |||
735 | GNUNET_assert (NULL == h->backoff_task); | ||
736 | if (GNUNET_YES == h->do_destroy) | ||
737 | { | ||
738 | /* So we are shutting down and the service is not reachable. | ||
739 | * Chances are that it's down for good and we are not going to connect to | ||
740 | * it anymore. | ||
741 | * Give up and don't sync the rest of the data. | ||
742 | */loss = GNUNET_NO; | ||
743 | for (gh = h->action_head; NULL != gh; gh = gh->next) | ||
744 | if ((gh->make_persistent) && | ||
745 | (ACTION_SET == gh->type)) | ||
746 | loss = GNUNET_YES; | ||
747 | if (GNUNET_YES == loss) | ||
748 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
749 | _ ("Could not save some persistent statistics\n")); | ||
750 | if (NULL != h->destroy_task) | ||
751 | GNUNET_SCHEDULER_cancel (h->destroy_task); | ||
752 | h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy, | ||
753 | h); | ||
754 | return; | ||
755 | } | ||
756 | h->backoff_task | ||
757 | = GNUNET_SCHEDULER_add_delayed (h->backoff, | ||
758 | &reconnect_task, | ||
759 | h); | ||
760 | h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff); | ||
761 | } | ||
762 | |||
763 | |||
764 | /** | ||
765 | * Transmit a GET request (and if successful, start to receive | ||
766 | * the response). | ||
767 | * | ||
768 | * @param handle statistics handle | ||
769 | */ | ||
770 | static void | ||
771 | transmit_get (struct GNUNET_STATISTICS_Handle *handle) | ||
772 | { | ||
773 | struct GNUNET_STATISTICS_GetHandle *c; | ||
774 | struct GNUNET_MessageHeader *hdr; | ||
775 | struct GNUNET_MQ_Envelope *env; | ||
776 | size_t slen1; | ||
777 | size_t slen2; | ||
778 | |||
779 | GNUNET_assert (NULL != (c = handle->current)); | ||
780 | slen1 = strlen (c->subsystem) + 1; | ||
781 | slen2 = strlen (c->name) + 1; | ||
782 | env = GNUNET_MQ_msg_extra (hdr, | ||
783 | slen1 + slen2, | ||
784 | GNUNET_MESSAGE_TYPE_STATISTICS_GET); | ||
785 | GNUNET_assert (slen1 + slen2 == | ||
786 | GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], | ||
787 | slen1 + slen2, | ||
788 | 2, | ||
789 | c->subsystem, | ||
790 | c->name)); | ||
791 | GNUNET_MQ_notify_sent (env, | ||
792 | &schedule_action, | ||
793 | handle); | ||
794 | GNUNET_MQ_send (handle->mq, | ||
795 | env); | ||
796 | } | ||
797 | |||
798 | |||
799 | /** | ||
800 | * Transmit a WATCH request (and if successful, start to receive | ||
801 | * the response). | ||
802 | * | ||
803 | * @param handle statistics handle | ||
804 | */ | ||
805 | static void | ||
806 | transmit_watch (struct GNUNET_STATISTICS_Handle *handle) | ||
807 | { | ||
808 | struct GNUNET_MessageHeader *hdr; | ||
809 | struct GNUNET_MQ_Envelope *env; | ||
810 | size_t slen1; | ||
811 | size_t slen2; | ||
812 | |||
813 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
814 | "Transmitting watch request for `%s'\n", | ||
815 | handle->current->name); | ||
816 | slen1 = strlen (handle->current->subsystem) + 1; | ||
817 | slen2 = strlen (handle->current->name) + 1; | ||
818 | env = GNUNET_MQ_msg_extra (hdr, | ||
819 | slen1 + slen2, | ||
820 | GNUNET_MESSAGE_TYPE_STATISTICS_WATCH); | ||
821 | GNUNET_assert (slen1 + slen2 == | ||
822 | GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], | ||
823 | slen1 + slen2, | ||
824 | 2, | ||
825 | handle->current->subsystem, | ||
826 | handle->current->name)); | ||
827 | GNUNET_MQ_notify_sent (env, | ||
828 | &schedule_action, | ||
829 | handle); | ||
830 | GNUNET_MQ_send (handle->mq, | ||
831 | env); | ||
832 | GNUNET_assert (NULL == handle->current->cont); | ||
833 | free_action_item (handle->current); | ||
834 | handle->current = NULL; | ||
835 | schedule_action (handle); | ||
836 | } | ||
837 | |||
838 | |||
839 | /** | ||
840 | * Transmit a SET/UPDATE request. | ||
841 | * | ||
842 | * @param handle statistics handle | ||
843 | */ | ||
844 | static void | ||
845 | transmit_set (struct GNUNET_STATISTICS_Handle *handle) | ||
846 | { | ||
847 | struct GNUNET_STATISTICS_SetMessage *r; | ||
848 | struct GNUNET_MQ_Envelope *env; | ||
849 | size_t slen; | ||
850 | size_t nlen; | ||
851 | |||
852 | slen = strlen (handle->current->subsystem) + 1; | ||
853 | nlen = strlen (handle->current->name) + 1; | ||
854 | env = GNUNET_MQ_msg_extra (r, | ||
855 | slen + nlen, | ||
856 | GNUNET_MESSAGE_TYPE_STATISTICS_SET); | ||
857 | r->flags = 0; | ||
858 | r->value = GNUNET_htonll (handle->current->value); | ||
859 | if (handle->current->make_persistent) | ||
860 | r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT); | ||
861 | if (handle->current->type == ACTION_UPDATE) | ||
862 | r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE); | ||
863 | GNUNET_assert (slen + nlen == | ||
864 | GNUNET_STRINGS_buffer_fill ((char *) &r[1], | ||
865 | slen + nlen, | ||
866 | 2, | ||
867 | handle->current->subsystem, | ||
868 | handle->current->name)); | ||
869 | GNUNET_assert (NULL == handle->current->cont); | ||
870 | free_action_item (handle->current); | ||
871 | handle->current = NULL; | ||
872 | update_memory_statistics (handle); | ||
873 | GNUNET_MQ_notify_sent (env, | ||
874 | &schedule_action, | ||
875 | handle); | ||
876 | GNUNET_MQ_send (handle->mq, | ||
877 | env); | ||
878 | } | ||
879 | |||
880 | |||
881 | /** | ||
882 | * Get handle for the statistics service. | ||
883 | * | ||
884 | * @param subsystem name of subsystem using the service | ||
885 | * @param cfg services configuration in use | ||
886 | * @return handle to use | ||
887 | */ | ||
888 | struct GNUNET_STATISTICS_Handle * | ||
889 | GNUNET_STATISTICS_create (const char *subsystem, | ||
890 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
891 | { | ||
892 | struct GNUNET_STATISTICS_Handle *h; | ||
893 | |||
894 | if (GNUNET_YES == | ||
895 | GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
896 | "statistics", | ||
897 | "DISABLE")) | ||
898 | return NULL; | ||
899 | h = GNUNET_new (struct GNUNET_STATISTICS_Handle); | ||
900 | h->cfg = cfg; | ||
901 | h->subsystem = GNUNET_strdup (subsystem); | ||
902 | h->backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
903 | return h; | ||
904 | } | ||
905 | |||
906 | |||
907 | /** | ||
908 | * Destroy a handle (free all state associated with | ||
909 | * it). | ||
910 | * | ||
911 | * @param h statistics handle to destroy | ||
912 | * @param sync_first set to #GNUNET_YES if pending SET requests should | ||
913 | * be completed | ||
914 | */ | ||
915 | void | ||
916 | GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, | ||
917 | int sync_first) | ||
918 | { | ||
919 | struct GNUNET_STATISTICS_GetHandle *pos; | ||
920 | struct GNUNET_STATISTICS_GetHandle *next; | ||
921 | |||
922 | if (NULL == h) | ||
923 | return; | ||
924 | GNUNET_assert (GNUNET_NO == h->do_destroy); /* Don't call twice. */ | ||
925 | if ((sync_first) && | ||
926 | (NULL != h->mq) && | ||
927 | (0 != GNUNET_MQ_get_length (h->mq))) | ||
928 | { | ||
929 | if ((NULL != h->current) && | ||
930 | (ACTION_GET == h->current->type)) | ||
931 | h->current->aborted = GNUNET_YES; | ||
932 | next = h->action_head; | ||
933 | while (NULL != (pos = next)) | ||
934 | { | ||
935 | next = pos->next; | ||
936 | if ((ACTION_GET == pos->type) || | ||
937 | (ACTION_WATCH == pos->type)) | ||
938 | { | ||
939 | GNUNET_CONTAINER_DLL_remove (h->action_head, | ||
940 | h->action_tail, | ||
941 | pos); | ||
942 | free_action_item (pos); | ||
943 | } | ||
944 | } | ||
945 | h->do_destroy = GNUNET_YES; | ||
946 | schedule_action (h); | ||
947 | GNUNET_assert (NULL == h->destroy_task); | ||
948 | h->destroy_task | ||
949 | = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (h->backoff, | ||
950 | 5), | ||
951 | &do_destroy, | ||
952 | h); | ||
953 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
954 | "Deferring destruction\n"); | ||
955 | return; /* do not finish destruction just yet */ | ||
956 | } | ||
957 | /* do clean up all */ | ||
958 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
959 | "Cleaning all up\n"); | ||
960 | while (NULL != (pos = h->action_head)) | ||
961 | { | ||
962 | GNUNET_CONTAINER_DLL_remove (h->action_head, | ||
963 | h->action_tail, | ||
964 | pos); | ||
965 | free_action_item (pos); | ||
966 | } | ||
967 | do_disconnect (h); | ||
968 | if (NULL != h->backoff_task) | ||
969 | { | ||
970 | GNUNET_SCHEDULER_cancel (h->backoff_task); | ||
971 | h->backoff_task = NULL; | ||
972 | } | ||
973 | if (NULL != h->destroy_task) | ||
974 | { | ||
975 | GNUNET_break (0); | ||
976 | GNUNET_SCHEDULER_cancel (h->destroy_task); | ||
977 | h->destroy_task = NULL; | ||
978 | } | ||
979 | for (unsigned int i = 0; i < h->watches_size; i++) | ||
980 | { | ||
981 | if (NULL == h->watches[i]) | ||
982 | continue; | ||
983 | GNUNET_free (h->watches[i]->subsystem); | ||
984 | GNUNET_free (h->watches[i]->name); | ||
985 | GNUNET_free (h->watches[i]); | ||
986 | } | ||
987 | GNUNET_array_grow (h->watches, | ||
988 | h->watches_size, | ||
989 | 0); | ||
990 | GNUNET_free (h->subsystem); | ||
991 | GNUNET_free (h); | ||
992 | } | ||
993 | |||
994 | |||
995 | /** | ||
996 | * Schedule the next action to be performed. | ||
997 | * | ||
998 | * @param cls statistics handle | ||
999 | */ | ||
1000 | static void | ||
1001 | schedule_action (void *cls) | ||
1002 | { | ||
1003 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
1004 | |||
1005 | if (NULL != h->backoff_task) | ||
1006 | return; /* action already pending */ | ||
1007 | if (GNUNET_YES != try_connect (h)) | ||
1008 | { | ||
1009 | reconnect_later (h); | ||
1010 | return; | ||
1011 | } | ||
1012 | if (0 < GNUNET_MQ_get_length (h->mq)) | ||
1013 | return; /* Wait for queue to be reduced more */ | ||
1014 | /* schedule next action */ | ||
1015 | while (NULL == h->current) | ||
1016 | { | ||
1017 | h->current = h->action_head; | ||
1018 | if (NULL == h->current) | ||
1019 | { | ||
1020 | struct GNUNET_MessageHeader *hdr; | ||
1021 | struct GNUNET_MQ_Envelope *env; | ||
1022 | |||
1023 | if (GNUNET_YES != h->do_destroy) | ||
1024 | return; /* nothing to do */ | ||
1025 | /* let service know that we're done */ | ||
1026 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1027 | "Notifying service that we are done\n"); | ||
1028 | h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */ | ||
1029 | env = GNUNET_MQ_msg (hdr, | ||
1030 | GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT); | ||
1031 | GNUNET_MQ_notify_sent (env, | ||
1032 | &schedule_action, | ||
1033 | h); | ||
1034 | GNUNET_MQ_send (h->mq, | ||
1035 | env); | ||
1036 | return; | ||
1037 | } | ||
1038 | GNUNET_CONTAINER_DLL_remove (h->action_head, | ||
1039 | h->action_tail, | ||
1040 | h->current); | ||
1041 | switch (h->current->type) | ||
1042 | { | ||
1043 | case ACTION_GET: | ||
1044 | transmit_get (h); | ||
1045 | break; | ||
1046 | |||
1047 | case ACTION_SET: | ||
1048 | case ACTION_UPDATE: | ||
1049 | transmit_set (h); | ||
1050 | break; | ||
1051 | |||
1052 | case ACTION_WATCH: | ||
1053 | transmit_watch (h); | ||
1054 | break; | ||
1055 | |||
1056 | default: | ||
1057 | GNUNET_assert (0); | ||
1058 | break; | ||
1059 | } | ||
1060 | } | ||
1061 | } | ||
1062 | |||
1063 | |||
1064 | struct GNUNET_STATISTICS_GetHandle * | ||
1065 | GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle, | ||
1066 | const char *subsystem, | ||
1067 | const char *name, | ||
1068 | GNUNET_STATISTICS_Callback cont, | ||
1069 | GNUNET_STATISTICS_Iterator proc, | ||
1070 | void *cls) | ||
1071 | { | ||
1072 | size_t slen1; | ||
1073 | size_t slen2; | ||
1074 | struct GNUNET_STATISTICS_GetHandle *ai; | ||
1075 | |||
1076 | if (NULL == handle) | ||
1077 | return NULL; | ||
1078 | GNUNET_assert (NULL != proc); | ||
1079 | GNUNET_assert (GNUNET_NO == handle->do_destroy); | ||
1080 | if (NULL == subsystem) | ||
1081 | subsystem = ""; | ||
1082 | if (NULL == name) | ||
1083 | name = ""; | ||
1084 | slen1 = strlen (subsystem) + 1; | ||
1085 | slen2 = strlen (name) + 1; | ||
1086 | GNUNET_assert (slen1 + slen2 + sizeof(struct GNUNET_MessageHeader) < | ||
1087 | GNUNET_MAX_MESSAGE_SIZE); | ||
1088 | ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); | ||
1089 | ai->sh = handle; | ||
1090 | ai->subsystem = GNUNET_strdup (subsystem); | ||
1091 | ai->name = GNUNET_strdup (name); | ||
1092 | ai->cont = cont; | ||
1093 | ai->proc = proc; | ||
1094 | ai->cls = cls; | ||
1095 | ai->type = ACTION_GET; | ||
1096 | ai->msize = slen1 + slen2 + sizeof(struct GNUNET_MessageHeader); | ||
1097 | GNUNET_CONTAINER_DLL_insert_tail (handle->action_head, | ||
1098 | handle->action_tail, | ||
1099 | ai); | ||
1100 | schedule_action (handle); | ||
1101 | return ai; | ||
1102 | } | ||
1103 | |||
1104 | |||
1105 | void | ||
1106 | GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh) | ||
1107 | { | ||
1108 | if (NULL == gh) | ||
1109 | return; | ||
1110 | gh->cont = NULL; | ||
1111 | if (gh->sh->current == gh) | ||
1112 | { | ||
1113 | gh->aborted = GNUNET_YES; | ||
1114 | return; | ||
1115 | } | ||
1116 | GNUNET_CONTAINER_DLL_remove (gh->sh->action_head, | ||
1117 | gh->sh->action_tail, | ||
1118 | gh); | ||
1119 | GNUNET_free (gh->name); | ||
1120 | GNUNET_free (gh->subsystem); | ||
1121 | GNUNET_free (gh); | ||
1122 | } | ||
1123 | |||
1124 | |||
1125 | /** | ||
1126 | * Watch statistics from the peer (be notified whenever they change). | ||
1127 | * | ||
1128 | * @param handle identification of the statistics service | ||
1129 | * @param subsystem limit to the specified subsystem, never NULL | ||
1130 | * @param name name of the statistic value, never NULL | ||
1131 | * @param proc function to call on each value | ||
1132 | * @param proc_cls closure for @a proc | ||
1133 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
1134 | */ | ||
1135 | int | ||
1136 | GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle, | ||
1137 | const char *subsystem, | ||
1138 | const char *name, | ||
1139 | GNUNET_STATISTICS_Iterator proc, | ||
1140 | void *proc_cls) | ||
1141 | { | ||
1142 | struct GNUNET_STATISTICS_WatchEntry *w; | ||
1143 | |||
1144 | if (NULL == handle) | ||
1145 | return GNUNET_SYSERR; | ||
1146 | w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry); | ||
1147 | w->subsystem = GNUNET_strdup (subsystem); | ||
1148 | w->name = GNUNET_strdup (name); | ||
1149 | w->proc = proc; | ||
1150 | w->proc_cls = proc_cls; | ||
1151 | GNUNET_array_append (handle->watches, | ||
1152 | handle->watches_size, | ||
1153 | w); | ||
1154 | schedule_watch_request (handle, | ||
1155 | w); | ||
1156 | return GNUNET_OK; | ||
1157 | } | ||
1158 | |||
1159 | |||
1160 | /** | ||
1161 | * Stop watching statistics from the peer. | ||
1162 | * | ||
1163 | * @param handle identification of the statistics service | ||
1164 | * @param subsystem limit to the specified subsystem, never NULL | ||
1165 | * @param name name of the statistic value, never NULL | ||
1166 | * @param proc function to call on each value | ||
1167 | * @param proc_cls closure for @a proc | ||
1168 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch) | ||
1169 | */ | ||
1170 | int | ||
1171 | GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle, | ||
1172 | const char *subsystem, | ||
1173 | const char *name, | ||
1174 | GNUNET_STATISTICS_Iterator proc, | ||
1175 | void *proc_cls) | ||
1176 | { | ||
1177 | struct GNUNET_STATISTICS_WatchEntry *w; | ||
1178 | |||
1179 | if (NULL == handle) | ||
1180 | return GNUNET_SYSERR; | ||
1181 | for (unsigned int i = 0; i < handle->watches_size; i++) | ||
1182 | { | ||
1183 | w = handle->watches[i]; | ||
1184 | if (NULL == w) | ||
1185 | continue; | ||
1186 | if ((w->proc == proc) && | ||
1187 | (w->proc_cls == proc_cls) && | ||
1188 | (0 == strcmp (w->name, | ||
1189 | name)) && | ||
1190 | (0 == strcmp (w->subsystem, | ||
1191 | subsystem))) | ||
1192 | { | ||
1193 | GNUNET_free (w->name); | ||
1194 | GNUNET_free (w->subsystem); | ||
1195 | GNUNET_free (w); | ||
1196 | handle->watches[i] = NULL; | ||
1197 | return GNUNET_OK; | ||
1198 | } | ||
1199 | } | ||
1200 | return GNUNET_SYSERR; | ||
1201 | } | ||
1202 | |||
1203 | |||
1204 | /** | ||
1205 | * Queue a request to change a statistic. | ||
1206 | * | ||
1207 | * @param h statistics handle | ||
1208 | * @param name name of the value | ||
1209 | * @param make_persistent should the value be kept across restarts? | ||
1210 | * @param value new value or change | ||
1211 | * @param type type of the action (#ACTION_SET or #ACTION_UPDATE) | ||
1212 | */ | ||
1213 | static void | ||
1214 | add_setter_action (struct GNUNET_STATISTICS_Handle *h, | ||
1215 | const char *name, | ||
1216 | int make_persistent, | ||
1217 | uint64_t value, | ||
1218 | enum ActionType type) | ||
1219 | { | ||
1220 | struct GNUNET_STATISTICS_GetHandle *ai; | ||
1221 | size_t slen; | ||
1222 | size_t nlen; | ||
1223 | size_t nsize; | ||
1224 | int64_t delta; | ||
1225 | |||
1226 | slen = strlen (h->subsystem) + 1; | ||
1227 | nlen = strlen (name) + 1; | ||
1228 | nsize = sizeof(struct GNUNET_STATISTICS_SetMessage) + slen + nlen; | ||
1229 | if (nsize >= GNUNET_MAX_MESSAGE_SIZE) | ||
1230 | { | ||
1231 | GNUNET_break (0); | ||
1232 | return; | ||
1233 | } | ||
1234 | for (ai = h->action_head; NULL != ai; ai = ai->next) | ||
1235 | { | ||
1236 | if (! ((0 == strcmp (ai->subsystem, | ||
1237 | h->subsystem)) && | ||
1238 | (0 == strcmp (ai->name, | ||
1239 | name)) && | ||
1240 | ((ACTION_UPDATE == ai->type) || | ||
1241 | (ACTION_SET == ai->type)))) | ||
1242 | continue; | ||
1243 | if (ACTION_SET == ai->type) | ||
1244 | { | ||
1245 | if (ACTION_UPDATE == type) | ||
1246 | { | ||
1247 | delta = (int64_t) value; | ||
1248 | if (delta > 0) | ||
1249 | { | ||
1250 | /* update old set by new delta */ | ||
1251 | ai->value += delta; | ||
1252 | } | ||
1253 | else | ||
1254 | { | ||
1255 | /* update old set by new delta, but never go negative */ | ||
1256 | if (ai->value < -delta) | ||
1257 | ai->value = 0; | ||
1258 | else | ||
1259 | ai->value += delta; | ||
1260 | } | ||
1261 | } | ||
1262 | else | ||
1263 | { | ||
1264 | /* new set overrides old set */ | ||
1265 | ai->value = value; | ||
1266 | } | ||
1267 | } | ||
1268 | else | ||
1269 | { | ||
1270 | if (ACTION_UPDATE == type) | ||
1271 | { | ||
1272 | /* make delta cumulative */ | ||
1273 | delta = (int64_t) value; | ||
1274 | ai->value += delta; | ||
1275 | } | ||
1276 | else | ||
1277 | { | ||
1278 | /* drop old 'update', use new 'set' instead */ | ||
1279 | ai->value = value; | ||
1280 | ai->type = type; | ||
1281 | } | ||
1282 | } | ||
1283 | ai->timeout | ||
1284 | = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT); | ||
1285 | ai->make_persistent | ||
1286 | = make_persistent; | ||
1287 | return; | ||
1288 | } | ||
1289 | /* no existing entry matches, create a fresh one */ | ||
1290 | ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); | ||
1291 | ai->sh = h; | ||
1292 | ai->subsystem = GNUNET_strdup (h->subsystem); | ||
1293 | ai->name = GNUNET_strdup (name); | ||
1294 | ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT); | ||
1295 | ai->make_persistent = make_persistent; | ||
1296 | ai->msize = nsize; | ||
1297 | ai->value = value; | ||
1298 | ai->type = type; | ||
1299 | GNUNET_CONTAINER_DLL_insert_tail (h->action_head, | ||
1300 | h->action_tail, | ||
1301 | ai); | ||
1302 | schedule_action (h); | ||
1303 | } | ||
1304 | |||
1305 | |||
1306 | void | ||
1307 | GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle, | ||
1308 | const char *name, | ||
1309 | uint64_t value, | ||
1310 | int make_persistent) | ||
1311 | { | ||
1312 | if (NULL == handle) | ||
1313 | return; | ||
1314 | GNUNET_assert (GNUNET_NO == handle->do_destroy); | ||
1315 | add_setter_action (handle, | ||
1316 | name, | ||
1317 | make_persistent, | ||
1318 | value, | ||
1319 | ACTION_SET); | ||
1320 | } | ||
1321 | |||
1322 | |||
1323 | void | ||
1324 | GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle, | ||
1325 | const char *name, | ||
1326 | int64_t delta, | ||
1327 | int make_persistent) | ||
1328 | { | ||
1329 | if (NULL == handle) | ||
1330 | return; | ||
1331 | if (0 == delta) | ||
1332 | return; | ||
1333 | GNUNET_assert (GNUNET_NO == handle->do_destroy); | ||
1334 | add_setter_action (handle, | ||
1335 | name, | ||
1336 | make_persistent, | ||
1337 | (uint64_t) delta, | ||
1338 | ACTION_UPDATE); | ||
1339 | } | ||
1340 | |||
1341 | |||
1342 | /* end of statistics_api.c */ | ||
diff --git a/src/service/statistics/test_gnunet_statistics.py.in b/src/service/statistics/test_gnunet_statistics.py.in new file mode 100644 index 000000000..bf6ba6ef2 --- /dev/null +++ b/src/service/statistics/test_gnunet_statistics.py.in | |||
@@ -0,0 +1,171 @@ | |||
1 | #!@PYTHONEXE@ | ||
2 | |||
3 | import os | ||
4 | import sys | ||
5 | import shutil | ||
6 | import re | ||
7 | import subprocess | ||
8 | import time | ||
9 | |||
10 | raw_tmp = True | ||
11 | if os.name == "nt": | ||
12 | tmp = os.getenv("TEMP") | ||
13 | elif None != os.environ.get("TMPDIR"): | ||
14 | tmp = os.getenv("TMPDIR") | ||
15 | elif None != os.environ.get("TMP"): | ||
16 | tmp = os.getenv("TMP") | ||
17 | else: | ||
18 | raw_tmp = False | ||
19 | tmp = subprocess.check_output(["gnunet-config", "-f", "-s", "PATHS", "-o", "GNUNET_TMP"], | ||
20 | text=True) | ||
21 | |||
22 | if os.name == 'nt': | ||
23 | st = './gnunet-statistics.exe' | ||
24 | arm = 'gnunet-arm.exe' | ||
25 | else: | ||
26 | st = './gnunet-statistics' | ||
27 | arm = 'gnunet-arm' | ||
28 | |||
29 | run_st = [st, '-c', 'test_statistics_api_data.conf'] | ||
30 | run_arm = [arm, '-c', 'test_statistics_api_data.conf'] | ||
31 | debug = os.getenv('DEBUG') | ||
32 | if debug: | ||
33 | run_arm += [debug.split(' ')] | ||
34 | |||
35 | |||
36 | if raw_tmp: | ||
37 | cleanup_path = "gnunet/test-gnunet-statistics" | ||
38 | else: | ||
39 | cleanup_path = "test-gnunet-statistics" | ||
40 | |||
41 | |||
42 | def cleanup(): | ||
43 | shutil.rmtree(os.path.join(tmp, cleanup_path), True) | ||
44 | |||
45 | |||
46 | def sub_run(args, want_stdo=True, want_stde=False, nofail=False): | ||
47 | if want_stdo: | ||
48 | stdo = subprocess.PIPE | ||
49 | else: | ||
50 | stdo = None | ||
51 | if want_stde: | ||
52 | stde = subprocess.PIPE | ||
53 | else: | ||
54 | stde = None | ||
55 | p = subprocess.Popen(args, stdout=stdo, stderr=stde) | ||
56 | stdo, stde = p.communicate() | ||
57 | if not nofail: | ||
58 | if p.returncode != 0: | ||
59 | sys.exit(p.returncode) | ||
60 | return (p.returncode, stdo, stde) | ||
61 | |||
62 | |||
63 | def fail(result): | ||
64 | print(result) | ||
65 | r_arm(['-e'], want_stdo=False) | ||
66 | sys.exit(1) | ||
67 | |||
68 | |||
69 | def r_arm(extra_args, **kw): | ||
70 | rc, stdo, stde = sub_run(run_arm + extra_args, **kw) | ||
71 | if rc != 0: | ||
72 | fail("FAIL: error running {}".format(run_arm)) | ||
73 | return (rc, stdo, stde) | ||
74 | |||
75 | |||
76 | def r_st(extra_args, normal=True, **kw): | ||
77 | rc, stdo, stde = sub_run(run_st + extra_args, **kw) | ||
78 | if normal: | ||
79 | if rc != 0: | ||
80 | fail("FAIL: error running {}".format(run_st)) | ||
81 | else: | ||
82 | if rc == 0: | ||
83 | fail("FAIL: expected error while running {}".format(run_st)) | ||
84 | return (rc, stdo, stde) | ||
85 | |||
86 | |||
87 | def restart(): | ||
88 | print("Restarting service...") | ||
89 | t = r_arm(['-k', 'statistics']) | ||
90 | time.sleep(1) | ||
91 | t = r_arm(['-i', 'statistics']) | ||
92 | time.sleep(1) | ||
93 | |||
94 | |||
95 | cleanup() | ||
96 | |||
97 | print("Preparing: Starting service...") | ||
98 | t = r_arm(['-s'], want_stdo=False) | ||
99 | time.sleep(1) | ||
100 | t = r_arm(['-i', 'statistics'], want_stdo=False) | ||
101 | time.sleep(1) | ||
102 | |||
103 | print("TEST: Bad argument checking...", end='') | ||
104 | r_st(['-x'], normal=False, nofail=True, want_stdo=False, want_stde=True) | ||
105 | print("PASS") | ||
106 | |||
107 | print("TEST: Set value...", end='') | ||
108 | r_st(['-n', 'test', '-s', 'subsystem', b'42'], nofail=True, want_stdo=False) | ||
109 | print("PASS") | ||
110 | |||
111 | print("TEST: Set another value...", end='') | ||
112 | r_st(['-n', 'other', '-s', 'osystem', b'43'], nofail=True, want_stdo=False) | ||
113 | print("PASS") | ||
114 | |||
115 | #print("TEST: Viewing all stats...", end='') | ||
116 | #rc, stdo, stde = r_st([], nofail=True, want_stdo=True) | ||
117 | #if len(stdo.splitlines()) != 2: | ||
118 | # fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
119 | #print("PASS") | ||
120 | |||
121 | print("TEST: Viewing stats by name...", end='') | ||
122 | rc, stdo, stde = r_st(['-n', 'other'], nofail=True, want_stdo=True) | ||
123 | if len([x for x in stdo.splitlines() if re.search(b'43', x)]) != 1: | ||
124 | fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
125 | print("PASS") | ||
126 | |||
127 | print("TEST: Viewing stats by subsystem...", end='') | ||
128 | rc, stdo, stde = r_st(['-s', 'subsystem'], nofail=True, want_stdo=True) | ||
129 | if len([x for x in stdo.splitlines() if re.search(b'42', x)]) != 1: | ||
130 | fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
131 | print("PASS") | ||
132 | |||
133 | print("TEST: Set persistent value...", end='') | ||
134 | rc, stdo, stde = r_st(['-n', 'lasting', '-s', 'subsystem', '40', '-p'], | ||
135 | nofail=True, | ||
136 | want_stdo=False) | ||
137 | rc, stdo, stde = r_st([], nofail=True, want_stdo=True) | ||
138 | if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 1: | ||
139 | fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
140 | print("PASS") | ||
141 | |||
142 | restart() | ||
143 | |||
144 | print("TEST: Checking persistence...", end='') | ||
145 | rc, stdo, stde = r_st([], nofail=True, want_stdo=True) | ||
146 | if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 1: | ||
147 | fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
148 | print("PASS") | ||
149 | |||
150 | print("TEST: Removing persistence...", end='') | ||
151 | rc, stdo, stde = r_st(['-n', 'lasting', '-s', 'subsystem', '40'], | ||
152 | nofail=True, | ||
153 | want_stdo=False) | ||
154 | rc, stdo, stde = r_st([], nofail=True, want_stdo=True) | ||
155 | if len([x for x in stdo.splitlines() if re.search(b'!', x)]) != 0: | ||
156 | fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
157 | print("PASS") | ||
158 | |||
159 | restart() | ||
160 | |||
161 | print("TEST: Checking removed persistence...", end='') | ||
162 | rc, stdo, stde = r_st([], nofail=True, want_stdo=True) | ||
163 | if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 0: | ||
164 | fail("FAIL: unexpected output:\n{}".format(stdo)) | ||
165 | print("PASS") | ||
166 | |||
167 | print("Stopping service...") | ||
168 | t = r_arm(['-e'], want_stdo=False) | ||
169 | time.sleep(1) | ||
170 | |||
171 | cleanup() | ||
diff --git a/src/service/statistics/test_statistics_api.c b/src/service/statistics/test_statistics_api.c new file mode 100644 index 000000000..c9e568870 --- /dev/null +++ b/src/service/statistics/test_statistics_api.c | |||
@@ -0,0 +1,253 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2012, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file statistics/test_statistics_api.c | ||
22 | * @brief testcase for statistics_api.c | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_statistics_service.h" | ||
28 | |||
29 | |||
30 | static struct GNUNET_STATISTICS_Handle *h; | ||
31 | |||
32 | static struct GNUNET_STATISTICS_GetHandle *g; | ||
33 | |||
34 | |||
35 | static void | ||
36 | do_shutdown () | ||
37 | { | ||
38 | if (NULL != g) | ||
39 | { | ||
40 | GNUNET_STATISTICS_get_cancel (g); | ||
41 | g = NULL; | ||
42 | } | ||
43 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
44 | h = NULL; | ||
45 | } | ||
46 | |||
47 | |||
48 | static int | ||
49 | check_1 (void *cls, | ||
50 | const char *subsystem, | ||
51 | const char *name, | ||
52 | uint64_t value, | ||
53 | int is_persistent) | ||
54 | { | ||
55 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
56 | "Received value %llu for `%s:%s\n", | ||
57 | (unsigned long long) value, | ||
58 | subsystem, | ||
59 | name); | ||
60 | GNUNET_assert (0 == strcmp (name, "test-1")); | ||
61 | GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); | ||
62 | GNUNET_assert (value == 1); | ||
63 | GNUNET_assert (is_persistent == GNUNET_NO); | ||
64 | return GNUNET_OK; | ||
65 | } | ||
66 | |||
67 | |||
68 | static int | ||
69 | check_2 (void *cls, | ||
70 | const char *subsystem, | ||
71 | const char *name, | ||
72 | uint64_t value, | ||
73 | int is_persistent) | ||
74 | { | ||
75 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
76 | "Received value %llu for `%s:%s\n", | ||
77 | (unsigned long long) value, | ||
78 | subsystem, | ||
79 | name); | ||
80 | GNUNET_assert (0 == strcmp (name, "test-2")); | ||
81 | GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); | ||
82 | GNUNET_assert (value == 2); | ||
83 | GNUNET_assert (is_persistent == GNUNET_NO); | ||
84 | return GNUNET_OK; | ||
85 | } | ||
86 | |||
87 | |||
88 | static int | ||
89 | check_3 (void *cls, | ||
90 | const char *subsystem, | ||
91 | const char *name, | ||
92 | uint64_t value, | ||
93 | int is_persistent) | ||
94 | { | ||
95 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
96 | "Received value %llu for `%s:%s\n", | ||
97 | (unsigned long long) value, | ||
98 | subsystem, | ||
99 | name); | ||
100 | GNUNET_assert (0 == strcmp (name, "test-3")); | ||
101 | GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); | ||
102 | GNUNET_assert (value == 3); | ||
103 | GNUNET_assert (is_persistent == GNUNET_YES); | ||
104 | return GNUNET_OK; | ||
105 | } | ||
106 | |||
107 | |||
108 | static void | ||
109 | next_fin (void *cls, | ||
110 | int success) | ||
111 | { | ||
112 | int *ok = cls; | ||
113 | |||
114 | g = NULL; | ||
115 | GNUNET_SCHEDULER_shutdown (); | ||
116 | GNUNET_assert (success == GNUNET_OK); | ||
117 | *ok = 0; | ||
118 | } | ||
119 | |||
120 | |||
121 | static void | ||
122 | next (void *cls, int success) | ||
123 | { | ||
124 | g = NULL; | ||
125 | GNUNET_assert (success == GNUNET_OK); | ||
126 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
127 | "Issuing GET request\n"); | ||
128 | GNUNET_break (NULL != | ||
129 | GNUNET_STATISTICS_get (h, NULL, "test-2", | ||
130 | &next_fin, | ||
131 | &check_2, cls)); | ||
132 | } | ||
133 | |||
134 | |||
135 | static void | ||
136 | run (void *cls, | ||
137 | char *const *args, | ||
138 | const char *cfgfile, | ||
139 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
140 | { | ||
141 | h = GNUNET_STATISTICS_create ("test-statistics-api", cfg); | ||
142 | if (NULL == h) | ||
143 | { | ||
144 | GNUNET_break (0); | ||
145 | return; | ||
146 | } | ||
147 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
148 | NULL); | ||
149 | GNUNET_STATISTICS_set (h, "test-1", 1, GNUNET_NO); | ||
150 | GNUNET_STATISTICS_set (h, "test-2", 2, GNUNET_NO); | ||
151 | GNUNET_STATISTICS_set (h, "test-3", 2, GNUNET_NO); | ||
152 | GNUNET_STATISTICS_update (h, "test-3", 1, GNUNET_YES); | ||
153 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
154 | "Issuing GET request\n"); | ||
155 | GNUNET_break (NULL != | ||
156 | (g = GNUNET_STATISTICS_get (h, NULL, "test-1", | ||
157 | &next, | ||
158 | &check_1, cls))); | ||
159 | } | ||
160 | |||
161 | |||
162 | static void | ||
163 | run_more (void *cls, | ||
164 | char *const *args, | ||
165 | const char *cfgfile, | ||
166 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
167 | { | ||
168 | h = GNUNET_STATISTICS_create ("test-statistics-api", | ||
169 | cfg); | ||
170 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
171 | NULL); | ||
172 | GNUNET_break (NULL != | ||
173 | (g = GNUNET_STATISTICS_get (h, NULL, | ||
174 | "test-3", | ||
175 | &next_fin, | ||
176 | &check_3, cls))); | ||
177 | } | ||
178 | |||
179 | |||
180 | int | ||
181 | main (int argc, char *argv_ign[]) | ||
182 | { | ||
183 | int ok = 1; | ||
184 | char *const argv[] = { "test-statistics-api", | ||
185 | "-c", | ||
186 | "test_statistics_api_data.conf", | ||
187 | "-L", "WARNING", | ||
188 | NULL }; | ||
189 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
190 | GNUNET_GETOPT_OPTION_END | ||
191 | }; | ||
192 | struct GNUNET_OS_Process *proc; | ||
193 | char *binary; | ||
194 | |||
195 | GNUNET_log_setup ("test_statistics_api", | ||
196 | "WARNING", | ||
197 | NULL); | ||
198 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); | ||
199 | proc = | ||
200 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
201 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
202 | NULL, NULL, NULL, | ||
203 | binary, | ||
204 | "gnunet-service-statistics", | ||
205 | "-c", "test_statistics_api_data.conf", NULL); | ||
206 | GNUNET_assert (NULL != proc); | ||
207 | GNUNET_PROGRAM_run (5, argv, | ||
208 | "test-statistics-api", "nohelp", | ||
209 | options, &run, | ||
210 | &ok); | ||
211 | if (0 != GNUNET_OS_process_kill (proc, | ||
212 | GNUNET_TERM_SIG)) | ||
213 | { | ||
214 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
215 | ok = 1; | ||
216 | } | ||
217 | GNUNET_OS_process_wait (proc); | ||
218 | GNUNET_OS_process_destroy (proc); | ||
219 | proc = NULL; | ||
220 | if (ok != 0) | ||
221 | { | ||
222 | GNUNET_free (binary); | ||
223 | return ok; | ||
224 | } | ||
225 | ok = 1; | ||
226 | /* restart to check persistence! */ | ||
227 | proc = | ||
228 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
229 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
230 | NULL, NULL, NULL, | ||
231 | binary, | ||
232 | "gnunet-service-statistics", | ||
233 | "-c", "test_statistics_api_data.conf", | ||
234 | NULL); | ||
235 | GNUNET_PROGRAM_run (5, argv, | ||
236 | "test-statistics-api", "nohelp", | ||
237 | options, | ||
238 | &run_more, &ok); | ||
239 | if (0 != GNUNET_OS_process_kill (proc, | ||
240 | GNUNET_TERM_SIG)) | ||
241 | { | ||
242 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
243 | ok = 1; | ||
244 | } | ||
245 | GNUNET_OS_process_wait (proc); | ||
246 | GNUNET_OS_process_destroy (proc); | ||
247 | proc = NULL; | ||
248 | GNUNET_free (binary); | ||
249 | return ok; | ||
250 | } | ||
251 | |||
252 | |||
253 | /* end of test_statistics_api.c */ | ||
diff --git a/src/service/statistics/test_statistics_api_data.conf b/src/service/statistics/test_statistics_api_data.conf new file mode 100644 index 000000000..f7884f368 --- /dev/null +++ b/src/service/statistics/test_statistics_api_data.conf | |||
@@ -0,0 +1,5 @@ | |||
1 | @INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf | ||
2 | @INLINE@ ../../../contrib/conf/gnunet/no_autostart_above_core.conf | ||
3 | |||
4 | [PATHS] | ||
5 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-statistics/ | ||
diff --git a/src/service/statistics/test_statistics_api_loop.c b/src/service/statistics/test_statistics_api_loop.c new file mode 100644 index 000000000..ad273287d --- /dev/null +++ b/src/service/statistics/test_statistics_api_loop.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file statistics/test_statistics_api_loop.c | ||
22 | * @brief testcase for statistics_api.c | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "gnunet_util_lib.h" | ||
26 | #include "gnunet_statistics_service.h" | ||
27 | |||
28 | #define ROUNDS (1024 * 1024) | ||
29 | |||
30 | static struct GNUNET_STATISTICS_Handle *h; | ||
31 | |||
32 | |||
33 | static int | ||
34 | check_1 (void *cls, | ||
35 | const char *subsystem, | ||
36 | const char *name, | ||
37 | uint64_t value, | ||
38 | int is_persistent) | ||
39 | { | ||
40 | GNUNET_assert (0 == strcmp (name, "test-0")); | ||
41 | GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api-loop")); | ||
42 | GNUNET_assert (is_persistent == GNUNET_NO); | ||
43 | return GNUNET_OK; | ||
44 | } | ||
45 | |||
46 | |||
47 | static void | ||
48 | next (void *cls, | ||
49 | int success) | ||
50 | { | ||
51 | int *ok = cls; | ||
52 | |||
53 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
54 | GNUNET_assert (success == GNUNET_OK); | ||
55 | *ok = 0; | ||
56 | } | ||
57 | |||
58 | |||
59 | static void | ||
60 | run (void *cls, | ||
61 | char *const *args, | ||
62 | const char *cfgfile, | ||
63 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
64 | { | ||
65 | unsigned int i; | ||
66 | char name[128]; | ||
67 | |||
68 | h = GNUNET_STATISTICS_create ("test-statistics-api-loop", cfg); | ||
69 | for (i = 0; i < ROUNDS; i++) | ||
70 | { | ||
71 | GNUNET_snprintf (name, sizeof(name), "test-%d", i % 32); | ||
72 | GNUNET_STATISTICS_set (h, name, i, GNUNET_NO); | ||
73 | GNUNET_snprintf (name, sizeof(name), "test-%d", i % 16); | ||
74 | GNUNET_STATISTICS_update (h, name, 1, GNUNET_NO); | ||
75 | } | ||
76 | i = 0; | ||
77 | GNUNET_break (NULL != | ||
78 | GNUNET_STATISTICS_get (h, NULL, "test-0", | ||
79 | &next, | ||
80 | &check_1, cls)); | ||
81 | } | ||
82 | |||
83 | |||
84 | int | ||
85 | main (int argc, char *argv_ign[]) | ||
86 | { | ||
87 | int ok = 1; | ||
88 | |||
89 | char *const argv[] = { "test-statistics-api", | ||
90 | "-c", | ||
91 | "test_statistics_api_data.conf", | ||
92 | NULL }; | ||
93 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
94 | GNUNET_GETOPT_OPTION_END | ||
95 | }; | ||
96 | struct GNUNET_OS_Process *proc; | ||
97 | char *binary; | ||
98 | |||
99 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); | ||
100 | proc = | ||
101 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
102 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
103 | NULL, NULL, NULL, | ||
104 | binary, | ||
105 | "gnunet-service-statistics", | ||
106 | "-c", "test_statistics_api_data.conf", NULL); | ||
107 | GNUNET_assert (NULL != proc); | ||
108 | GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, | ||
109 | &ok); | ||
110 | if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) | ||
111 | { | ||
112 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
113 | ok = 1; | ||
114 | } | ||
115 | GNUNET_OS_process_wait (proc); | ||
116 | GNUNET_OS_process_destroy (proc); | ||
117 | proc = NULL; | ||
118 | GNUNET_free (binary); | ||
119 | return ok; | ||
120 | } | ||
121 | |||
122 | |||
123 | /* end of test_statistics_api_loop.c */ | ||
diff --git a/src/service/statistics/test_statistics_api_watch.c b/src/service/statistics/test_statistics_api_watch.c new file mode 100644 index 000000000..2d9d08305 --- /dev/null +++ b/src/service/statistics/test_statistics_api_watch.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2011, 2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file statistics/test_statistics_api_watch.c | ||
22 | * @brief testcase for statistics_api.c watch functions | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_statistics_service.h" | ||
28 | |||
29 | |||
30 | static int ok; | ||
31 | |||
32 | static struct GNUNET_STATISTICS_Handle *h; | ||
33 | |||
34 | static struct GNUNET_STATISTICS_Handle *h2; | ||
35 | |||
36 | static struct GNUNET_SCHEDULER_Task *shutdown_task; | ||
37 | |||
38 | |||
39 | static void | ||
40 | force_shutdown (void *cls) | ||
41 | { | ||
42 | fprintf (stderr, "Timeout, failed to receive notifications: %d\n", ok); | ||
43 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
44 | GNUNET_STATISTICS_destroy (h2, GNUNET_NO); | ||
45 | ok = 7; | ||
46 | } | ||
47 | |||
48 | |||
49 | static void | ||
50 | normal_shutdown (void *cls) | ||
51 | { | ||
52 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
53 | GNUNET_STATISTICS_destroy (h2, GNUNET_NO); | ||
54 | } | ||
55 | |||
56 | |||
57 | static int | ||
58 | watch_1 (void *cls, | ||
59 | const char *subsystem, | ||
60 | const char *name, | ||
61 | uint64_t value, | ||
62 | int is_persistent) | ||
63 | { | ||
64 | GNUNET_assert (value == 42); | ||
65 | GNUNET_assert (0 == strcmp (name, "test-1")); | ||
66 | ok &= ~1; | ||
67 | if (0 == ok) | ||
68 | { | ||
69 | GNUNET_SCHEDULER_cancel (shutdown_task); | ||
70 | GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); | ||
71 | } | ||
72 | return GNUNET_OK; | ||
73 | } | ||
74 | |||
75 | |||
76 | static int | ||
77 | watch_2 (void *cls, | ||
78 | const char *subsystem, | ||
79 | const char *name, | ||
80 | uint64_t value, | ||
81 | int is_persistent) | ||
82 | { | ||
83 | GNUNET_assert (value == 43); | ||
84 | GNUNET_assert (0 == strcmp (name, "test-2")); | ||
85 | ok &= ~2; | ||
86 | if (0 == ok) | ||
87 | { | ||
88 | GNUNET_SCHEDULER_cancel (shutdown_task); | ||
89 | GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); | ||
90 | } | ||
91 | return GNUNET_OK; | ||
92 | } | ||
93 | |||
94 | |||
95 | static void | ||
96 | run (void *cls, | ||
97 | char *const *args, | ||
98 | const char *cfgfile, | ||
99 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
100 | { | ||
101 | h = GNUNET_STATISTICS_create ("dummy", cfg); | ||
102 | GNUNET_assert (GNUNET_OK == | ||
103 | GNUNET_STATISTICS_watch (h, "test-statistics-api-watch", | ||
104 | "test-1", &watch_1, NULL)); | ||
105 | GNUNET_assert (GNUNET_OK == | ||
106 | GNUNET_STATISTICS_watch (h, "test-statistics-api-watch", | ||
107 | "test-2", &watch_2, NULL)); | ||
108 | h2 = GNUNET_STATISTICS_create ("test-statistics-api-watch", cfg); | ||
109 | GNUNET_STATISTICS_set (h2, "test-1", 42, GNUNET_NO); | ||
110 | GNUNET_STATISTICS_set (h2, "test-2", 43, GNUNET_NO); | ||
111 | shutdown_task = | ||
112 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
113 | &force_shutdown, | ||
114 | NULL); | ||
115 | } | ||
116 | |||
117 | |||
118 | int | ||
119 | main (int argc, char *argv_ign[]) | ||
120 | { | ||
121 | char *const argv[] = { "test-statistics-api", | ||
122 | "-c", | ||
123 | "test_statistics_api_data.conf", | ||
124 | NULL }; | ||
125 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
126 | GNUNET_GETOPT_OPTION_END | ||
127 | }; | ||
128 | struct GNUNET_OS_Process *proc; | ||
129 | char *binary; | ||
130 | |||
131 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); | ||
132 | proc = | ||
133 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
134 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
135 | NULL, NULL, NULL, | ||
136 | binary, | ||
137 | "gnunet-service-statistics", | ||
138 | "-c", "test_statistics_api_data.conf", NULL); | ||
139 | GNUNET_assert (NULL != proc); | ||
140 | ok = 3; | ||
141 | GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, | ||
142 | NULL); | ||
143 | if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) | ||
144 | { | ||
145 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
146 | ok = 1; | ||
147 | } | ||
148 | GNUNET_OS_process_wait (proc); | ||
149 | GNUNET_OS_process_destroy (proc); | ||
150 | proc = NULL; | ||
151 | GNUNET_free (binary); | ||
152 | return ok; | ||
153 | } | ||
154 | |||
155 | |||
156 | /* end of test_statistics_api_watch.c */ | ||
diff --git a/src/service/statistics/test_statistics_api_watch_zero_value.c b/src/service/statistics/test_statistics_api_watch_zero_value.c new file mode 100644 index 000000000..cb2694f8f --- /dev/null +++ b/src/service/statistics/test_statistics_api_watch_zero_value.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2011, 2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file statistics/test_statistics_api_watch_zero_value.c | ||
22 | * @brief testcase for statistics_api.c watch functions with initial 0 value | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "gnunet_util_lib.h" | ||
26 | #include "gnunet_statistics_service.h" | ||
27 | |||
28 | static int ok; | ||
29 | |||
30 | static int ok2; | ||
31 | |||
32 | static struct GNUNET_STATISTICS_Handle *h; | ||
33 | |||
34 | static struct GNUNET_STATISTICS_Handle *h2; | ||
35 | |||
36 | static struct GNUNET_SCHEDULER_Task *shutdown_task; | ||
37 | |||
38 | |||
39 | static void | ||
40 | force_shutdown (void *cls) | ||
41 | { | ||
42 | fprintf (stderr, "Timeout, failed to receive notifications: %d\n", ok); | ||
43 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
44 | GNUNET_STATISTICS_destroy (h2, GNUNET_NO); | ||
45 | ok = 7; | ||
46 | } | ||
47 | |||
48 | |||
49 | static void | ||
50 | normal_shutdown (void *cls) | ||
51 | { | ||
52 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
53 | GNUNET_STATISTICS_destroy (h2, GNUNET_NO); | ||
54 | } | ||
55 | |||
56 | |||
57 | static int | ||
58 | watch_1 (void *cls, const char *subsystem, const char *name, uint64_t value, | ||
59 | int is_persistent) | ||
60 | { | ||
61 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
62 | "Received value `%s' `%s' %llu\n", | ||
63 | subsystem, | ||
64 | name, | ||
65 | (unsigned long long) value); | ||
66 | GNUNET_assert (0 == strcmp (name, "test-1")); | ||
67 | if ((0 == value) && (3 == ok)) | ||
68 | { | ||
69 | ok--; | ||
70 | GNUNET_STATISTICS_set (h, "test-1", 42, GNUNET_NO); | ||
71 | } | ||
72 | |||
73 | if ((42 == value) && (2 == ok)) | ||
74 | { | ||
75 | ok--; | ||
76 | GNUNET_STATISTICS_set (h, "test-1", 0, GNUNET_NO); | ||
77 | } | ||
78 | |||
79 | if ((0 == value) && (1 == ok)) | ||
80 | { | ||
81 | ok--; | ||
82 | } | ||
83 | if ((0 == ok) && (0 == ok2)) | ||
84 | { | ||
85 | GNUNET_SCHEDULER_cancel (shutdown_task); | ||
86 | GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); | ||
87 | } | ||
88 | |||
89 | return GNUNET_OK; | ||
90 | } | ||
91 | |||
92 | |||
93 | static int | ||
94 | watch_2 (void *cls, | ||
95 | const char *subsystem, | ||
96 | const char *name, | ||
97 | uint64_t value, | ||
98 | int is_persistent) | ||
99 | { | ||
100 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
101 | "Received value `%s' `%s' %llu\n", | ||
102 | subsystem, | ||
103 | name, | ||
104 | (unsigned long long) value); | ||
105 | |||
106 | GNUNET_assert (0 == strcmp (name, "test-2")); | ||
107 | if ((42 == value) && (1 == ok2)) | ||
108 | { | ||
109 | ok2 = 0; | ||
110 | if (0 == ok) | ||
111 | { | ||
112 | GNUNET_SCHEDULER_cancel (shutdown_task); | ||
113 | GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); | ||
114 | } | ||
115 | } | ||
116 | else | ||
117 | { | ||
118 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
119 | "Received unexpected value %llu\n", | ||
120 | (unsigned long long) value); | ||
121 | |||
122 | GNUNET_break (0); | ||
123 | GNUNET_SCHEDULER_cancel (shutdown_task); | ||
124 | GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); | ||
125 | } | ||
126 | |||
127 | return GNUNET_OK; | ||
128 | } | ||
129 | |||
130 | |||
131 | static void | ||
132 | run (void *cls, char *const *args, const char *cfgfile, | ||
133 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
134 | { | ||
135 | h = GNUNET_STATISTICS_create ("dummy", cfg); | ||
136 | h2 = GNUNET_STATISTICS_create ("dummy-2", cfg); | ||
137 | GNUNET_assert (GNUNET_OK == | ||
138 | GNUNET_STATISTICS_watch (h, "dummy", | ||
139 | "test-1", &watch_1, NULL)); | ||
140 | |||
141 | GNUNET_assert (GNUNET_OK == | ||
142 | GNUNET_STATISTICS_watch (h2, "dummy-2", | ||
143 | "test-2", &watch_2, NULL)); | ||
144 | |||
145 | /* Set initial value to 0 */ | ||
146 | GNUNET_STATISTICS_set (h, "test-1", 0, GNUNET_NO); | ||
147 | GNUNET_STATISTICS_set (h2, "test-2", 42, GNUNET_NO); | ||
148 | |||
149 | shutdown_task = | ||
150 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
151 | &force_shutdown, | ||
152 | NULL); | ||
153 | } | ||
154 | |||
155 | |||
156 | int | ||
157 | main (int argc, char *argv_ign[]) | ||
158 | { | ||
159 | char *const argv[] = { "test-statistics-api", | ||
160 | "-c", | ||
161 | "test_statistics_api_data.conf", | ||
162 | NULL }; | ||
163 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
164 | GNUNET_GETOPT_OPTION_END | ||
165 | }; | ||
166 | struct GNUNET_OS_Process *proc; | ||
167 | char *binary; | ||
168 | |||
169 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); | ||
170 | proc = | ||
171 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
172 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
173 | NULL, NULL, NULL, | ||
174 | binary, | ||
175 | "gnunet-service-statistics", | ||
176 | "-c", "test_statistics_api_data.conf", NULL); | ||
177 | GNUNET_assert (NULL != proc); | ||
178 | ok = 3; | ||
179 | ok2 = 1; | ||
180 | GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, | ||
181 | NULL); | ||
182 | if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) | ||
183 | { | ||
184 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
185 | ok = 1; | ||
186 | } | ||
187 | GNUNET_OS_process_wait (proc); | ||
188 | GNUNET_OS_process_destroy (proc); | ||
189 | proc = NULL; | ||
190 | GNUNET_free (binary); | ||
191 | if ((0 == ok) && (0 == ok2)) | ||
192 | return 0; | ||
193 | return 1; | ||
194 | } | ||
195 | |||
196 | |||
197 | /* end of test_statistics_api_watch_zero_value.c */ | ||