gnunet-service-statistics.c 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2009, 2010, 2012, 2014, 2016 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file statistics/gnunet-service-statistics.c
  18. * @brief program that tracks statistics
  19. * @author Christian Grothoff
  20. */
  21. #include "platform.h"
  22. #include "gnunet_bio_lib.h"
  23. #include "gnunet_container_lib.h"
  24. #include "gnunet_disk_lib.h"
  25. #include "gnunet_getopt_lib.h"
  26. #include "gnunet_protocols.h"
  27. #include "gnunet_service_lib.h"
  28. #include "gnunet_statistics_service.h"
  29. #include "gnunet_strings_lib.h"
  30. #include "gnunet_time_lib.h"
  31. #include "statistics.h"
  32. /**
  33. * Watch entry.
  34. */
  35. struct WatchEntry
  36. {
  37. /**
  38. * Watch entries are kept in a linked list.
  39. */
  40. struct WatchEntry *next;
  41. /**
  42. * Watch entries are kept in a linked list.
  43. */
  44. struct WatchEntry *prev;
  45. /**
  46. * For which client is this watch entry?
  47. */
  48. struct ClientEntry *ce;
  49. /**
  50. * Last value we communicated to the client for this watch entry.
  51. */
  52. uint64_t last_value;
  53. /**
  54. * Unique watch number for this client and this watched value.
  55. */
  56. uint32_t wid;
  57. /**
  58. * Is last_value valid
  59. * #GNUNET_NO : last_value is n/a, #GNUNET_YES: last_value is valid
  60. */
  61. int last_value_set;
  62. };
  63. /**
  64. * We keep the statistics organized by subsystem for faster
  65. * lookup during SET operations.
  66. */
  67. struct SubsystemEntry;
  68. /**
  69. * Entry in the statistics list.
  70. */
  71. struct StatsEntry
  72. {
  73. /**
  74. * This is a linked list.
  75. */
  76. struct StatsEntry *next;
  77. /**
  78. * This is a linked list.
  79. */
  80. struct StatsEntry *prev;
  81. /**
  82. * Subsystem this entry belongs to.
  83. */
  84. struct SubsystemEntry *subsystem;
  85. /**
  86. * Name for the value stored by this entry, allocated at the end of
  87. * this struct.
  88. */
  89. const char *name;
  90. /**
  91. * Watch context for changes to this value, or NULL for none.
  92. */
  93. struct WatchEntry *we_head;
  94. /**
  95. * Watch context for changes to this value, or NULL for none.
  96. */
  97. struct WatchEntry *we_tail;
  98. /**
  99. * Our value.
  100. */
  101. uint64_t value;
  102. /**
  103. * Unique ID.
  104. */
  105. uint32_t uid;
  106. /**
  107. * Is this value persistent?
  108. */
  109. int persistent;
  110. /**
  111. * Is this value set?
  112. * #GNUNET_NO: value is n/a, #GNUNET_YES: value is valid
  113. */
  114. int set;
  115. };
  116. /**
  117. * We keep the statistics organized by subsystem for faster
  118. * lookup during SET operations.
  119. */
  120. struct SubsystemEntry
  121. {
  122. /**
  123. * Subsystems are kept in a DLL.
  124. */
  125. struct SubsystemEntry *next;
  126. /**
  127. * Subsystems are kept in a DLL.
  128. */
  129. struct SubsystemEntry *prev;
  130. /**
  131. * Head of list of values kept for this subsystem.
  132. */
  133. struct StatsEntry *stat_head;
  134. /**
  135. * Tail of list of values kept for this subsystem.
  136. */
  137. struct StatsEntry *stat_tail;
  138. /**
  139. * Name of the subsystem this entry is for, allocated at
  140. * the end of this struct, do not free().
  141. */
  142. const char *service;
  143. };
  144. /**
  145. * Client entry.
  146. */
  147. struct ClientEntry
  148. {
  149. /**
  150. * Corresponding server handle.
  151. */
  152. struct GNUNET_SERVICE_Client *client;
  153. /**
  154. * Corresponding message queue.
  155. */
  156. struct GNUNET_MQ_Handle *mq;
  157. /**
  158. * Which subsystem is this client writing to (SET/UPDATE)?
  159. */
  160. struct SubsystemEntry *subsystem;
  161. /**
  162. * Maximum watch ID used by this client so far.
  163. */
  164. uint32_t max_wid;
  165. };
  166. /**
  167. * Our configuration.
  168. */
  169. static const struct GNUNET_CONFIGURATION_Handle *cfg;
  170. /**
  171. * Head of linked list of subsystems with active statistics.
  172. */
  173. static struct SubsystemEntry *sub_head;
  174. /**
  175. * Tail of linked list of subsystems with active statistics.
  176. */
  177. static struct SubsystemEntry *sub_tail;
  178. /**
  179. * Number of connected clients.
  180. */
  181. static unsigned int client_count;
  182. /**
  183. * Our notification context.
  184. */
  185. static struct GNUNET_NotificationContext *nc;
  186. /**
  187. * Counter used to generate unique values.
  188. */
  189. static uint32_t uidgen;
  190. /**
  191. * Set to #GNUNET_YES if we are shutting down as soon as possible.
  192. */
  193. static int in_shutdown;
  194. /**
  195. * Write persistent statistics to disk.
  196. */
  197. static void
  198. save ()
  199. {
  200. struct SubsystemEntry *se;
  201. struct StatsEntry *pos;
  202. char *fn;
  203. struct GNUNET_BIO_WriteHandle *wh;
  204. uint16_t size;
  205. unsigned long long total;
  206. size_t nlen;
  207. size_t slen;
  208. struct GNUNET_STATISTICS_SetMessage *msg;
  209. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
  210. "STATISTICS",
  211. "DATABASE",
  212. &fn))
  213. {
  214. GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
  215. "STATISTICS",
  216. "DATABASE");
  217. return;
  218. }
  219. (void) GNUNET_DISK_directory_create_for_file (fn);
  220. wh = GNUNET_BIO_write_open_file (fn);
  221. total = 0;
  222. while (NULL != (se = sub_head))
  223. {
  224. GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se);
  225. slen = strlen (se->service) + 1;
  226. while (NULL != (pos = se->stat_head))
  227. {
  228. GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos);
  229. if ((pos->persistent) && (NULL != wh))
  230. {
  231. nlen = strlen (pos->name) + 1;
  232. size = sizeof(struct GNUNET_STATISTICS_SetMessage) + nlen + slen;
  233. GNUNET_assert (size < UINT16_MAX);
  234. msg = GNUNET_malloc (size);
  235. msg->header.size = htons ((uint16_t) size);
  236. msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
  237. GNUNET_assert (nlen + slen ==
  238. GNUNET_STRINGS_buffer_fill ((char *) &msg[1],
  239. nlen + slen,
  240. 2,
  241. se->service,
  242. pos->name));
  243. msg->flags =
  244. htonl (pos->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0);
  245. msg->value = GNUNET_htonll (pos->value);
  246. if (GNUNET_OK != GNUNET_BIO_write (wh, "statistics-save-msg", msg,
  247. size))
  248. {
  249. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
  250. if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
  251. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
  252. wh = NULL;
  253. }
  254. else
  255. {
  256. total += size;
  257. }
  258. GNUNET_free (msg);
  259. }
  260. GNUNET_free (pos);
  261. }
  262. GNUNET_free (se);
  263. }
  264. if (NULL != wh)
  265. {
  266. if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
  267. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
  268. if (0 == total)
  269. GNUNET_break (0 == unlink (fn));
  270. else
  271. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  272. _ ("Wrote %llu bytes of statistics to `%s'\n"),
  273. total,
  274. fn);
  275. }
  276. GNUNET_free (fn);
  277. }
  278. /**
  279. * Transmit the given stats value.
  280. *
  281. * @param client receiver of the value
  282. * @param e value to transmit
  283. */
  284. static void
  285. transmit (struct ClientEntry *ce, const struct StatsEntry *e)
  286. {
  287. struct GNUNET_MQ_Envelope *env;
  288. struct GNUNET_STATISTICS_ReplyMessage *m;
  289. size_t size;
  290. size = strlen (e->subsystem->service) + 1 + strlen (e->name) + 1;
  291. GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE);
  292. env = GNUNET_MQ_msg_extra (m, size, GNUNET_MESSAGE_TYPE_STATISTICS_VALUE);
  293. m->uid = htonl (e->uid);
  294. if (e->persistent)
  295. m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT);
  296. m->value = GNUNET_htonll (e->value);
  297. GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1],
  298. size,
  299. 2,
  300. e->subsystem->service,
  301. e->name));
  302. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  303. "Transmitting value for `%s:%s' (%d): %llu\n",
  304. e->subsystem->service,
  305. e->name,
  306. e->persistent,
  307. (unsigned long long) e->value);
  308. GNUNET_MQ_send (ce->mq, env);
  309. }
  310. /**
  311. * Callback called when a client connects to the service.
  312. *
  313. * @param cls closure for the service
  314. * @param c the new client that connected to the service
  315. * @param mq the message queue used to send messages to the client
  316. * @return @a c
  317. */
  318. static void *
  319. client_connect_cb (void *cls,
  320. struct GNUNET_SERVICE_Client *c,
  321. struct GNUNET_MQ_Handle *mq)
  322. {
  323. struct ClientEntry *ce;
  324. ce = GNUNET_new (struct ClientEntry);
  325. ce->client = c;
  326. ce->mq = mq;
  327. client_count++;
  328. GNUNET_notification_context_add (nc, mq);
  329. return ce;
  330. }
  331. /**
  332. * Check integrity of GET-message.
  333. *
  334. * @param cls identification of the client
  335. * @param message the actual message
  336. * @return #GNUNET_OK if @a message is well-formed
  337. */
  338. static int
  339. check_get (void *cls, const struct GNUNET_MessageHeader *message)
  340. {
  341. const char *service;
  342. const char *name;
  343. size_t size;
  344. size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader);
  345. if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
  346. size,
  347. 2,
  348. &service,
  349. &name))
  350. {
  351. GNUNET_break (0);
  352. return GNUNET_SYSERR;
  353. }
  354. return GNUNET_OK;
  355. }
  356. /**
  357. * Handle GET-message.
  358. *
  359. * @param cls identification of the client
  360. * @param message the actual message
  361. */
  362. static void
  363. handle_get (void *cls, const struct GNUNET_MessageHeader *message)
  364. {
  365. struct ClientEntry *ce = cls;
  366. struct GNUNET_MQ_Envelope *env;
  367. struct GNUNET_MessageHeader *end;
  368. const char *service;
  369. const char *name;
  370. size_t slen;
  371. size_t nlen;
  372. struct SubsystemEntry *se;
  373. struct StatsEntry *pos;
  374. size_t size;
  375. size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader);
  376. GNUNET_assert (size ==
  377. GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
  378. size,
  379. 2,
  380. &service,
  381. &name));
  382. slen = strlen (service);
  383. nlen = strlen (name);
  384. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  385. "Received request for statistics on `%s:%s'\n",
  386. slen ? service : "*",
  387. nlen ? name : "*");
  388. for (se = sub_head; NULL != se; se = se->next)
  389. {
  390. if (! ((0 == slen) || (0 == strcmp (service, se->service))))
  391. continue;
  392. for (pos = se->stat_head; NULL != pos; pos = pos->next)
  393. {
  394. if (! ((0 == nlen) || (0 == strcmp (name, pos->name))))
  395. continue;
  396. transmit (ce, pos);
  397. }
  398. }
  399. env = GNUNET_MQ_msg (end, GNUNET_MESSAGE_TYPE_STATISTICS_END);
  400. GNUNET_MQ_send (ce->mq, env);
  401. GNUNET_SERVICE_client_continue (ce->client);
  402. }
  403. /**
  404. * Notify all clients listening about a change to a value.
  405. *
  406. * @param se value that changed
  407. */
  408. static void
  409. notify_change (struct StatsEntry *se)
  410. {
  411. struct GNUNET_MQ_Envelope *env;
  412. struct GNUNET_STATISTICS_WatchValueMessage *wvm;
  413. struct WatchEntry *pos;
  414. for (pos = se->we_head; NULL != pos; pos = pos->next)
  415. {
  416. if (GNUNET_YES == pos->last_value_set)
  417. {
  418. if (pos->last_value == se->value)
  419. continue;
  420. }
  421. else
  422. {
  423. pos->last_value_set = GNUNET_YES;
  424. }
  425. env = GNUNET_MQ_msg (wvm, GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE);
  426. wvm->flags =
  427. htonl (se->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0);
  428. wvm->wid = htonl (pos->wid);
  429. wvm->reserved = htonl (0);
  430. wvm->value = GNUNET_htonll (se->value);
  431. GNUNET_MQ_send (pos->ce->mq, env);
  432. pos->last_value = se->value;
  433. }
  434. }
  435. /**
  436. * Find the subsystem entry of the given name for the specified client.
  437. *
  438. * @param ce client looking for the subsystem, may contain a hint
  439. * to find the entry faster, can be NULL
  440. * @param service name of the subsystem to look for
  441. * @return subsystem entry, never NULL (subsystem entry is created if necessary)
  442. */
  443. static struct SubsystemEntry *
  444. find_subsystem_entry (struct ClientEntry *ce, const char *service)
  445. {
  446. size_t slen;
  447. struct SubsystemEntry *se;
  448. if (NULL != ce)
  449. se = ce->subsystem;
  450. else
  451. se = NULL;
  452. if ((NULL == se) || (0 != strcmp (service, se->service)))
  453. {
  454. for (se = sub_head; NULL != se; se = se->next)
  455. if (0 == strcmp (service, se->service))
  456. break;
  457. if (NULL != ce)
  458. ce->subsystem = se;
  459. }
  460. if (NULL != se)
  461. return se;
  462. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  463. "Allocating new subsystem entry `%s'\n",
  464. service);
  465. slen = strlen (service) + 1;
  466. se = GNUNET_malloc (sizeof(struct SubsystemEntry) + slen);
  467. GNUNET_memcpy (&se[1], service, slen);
  468. se->service = (const char *) &se[1];
  469. GNUNET_CONTAINER_DLL_insert (sub_head, sub_tail, se);
  470. if (NULL != ce)
  471. ce->subsystem = se;
  472. return se;
  473. }
  474. /**
  475. * Find the statistics entry of the given subsystem.
  476. *
  477. * @param subsystem subsystem to look in
  478. * @param name name of the entry to look for
  479. * @return statistis entry, or NULL if not found
  480. */
  481. static struct StatsEntry *
  482. find_stat_entry (struct SubsystemEntry *se, const char *name)
  483. {
  484. struct StatsEntry *pos;
  485. for (pos = se->stat_head; NULL != pos; pos = pos->next)
  486. if (0 == strcmp (name, pos->name))
  487. return pos;
  488. return NULL;
  489. }
  490. /**
  491. * Check format of SET-message.
  492. *
  493. * @param cls the `struct ClientEntry`
  494. * @param message the actual message
  495. * @return #GNUNET_OK if message is well-formed
  496. */
  497. static int
  498. check_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg)
  499. {
  500. const char *service;
  501. const char *name;
  502. size_t msize;
  503. msize = ntohs (msg->header.size) - sizeof(*msg);
  504. if (msize != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
  505. msize,
  506. 2,
  507. &service,
  508. &name))
  509. {
  510. GNUNET_break (0);
  511. return GNUNET_SYSERR;
  512. }
  513. return GNUNET_OK;
  514. }
  515. /**
  516. * Handle SET-message.
  517. *
  518. * @param cls the `struct ClientEntry`
  519. * @param message the actual message
  520. */
  521. static void
  522. handle_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg)
  523. {
  524. struct ClientEntry *ce = cls;
  525. const char *service;
  526. const char *name;
  527. size_t nlen;
  528. uint16_t msize;
  529. uint16_t size;
  530. struct SubsystemEntry *se;
  531. struct StatsEntry *pos;
  532. uint32_t flags;
  533. uint64_t value;
  534. int64_t delta;
  535. int changed;
  536. int initial_set;
  537. msize = ntohs (msg->header.size);
  538. size = msize - sizeof(struct GNUNET_STATISTICS_SetMessage);
  539. GNUNET_assert (size == GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
  540. size,
  541. 2,
  542. &service,
  543. &name));
  544. se = find_subsystem_entry (ce, service);
  545. flags = ntohl (msg->flags);
  546. value = GNUNET_ntohll (msg->value);
  547. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  548. "Received request to update statistic on `%s:%s' (%u) to/by %llu\n",
  549. service,
  550. name,
  551. (unsigned int) flags,
  552. (unsigned long long) value);
  553. pos = find_stat_entry (se, name);
  554. if (NULL != pos)
  555. {
  556. initial_set = 0;
  557. if (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE))
  558. {
  559. changed = (pos->value != value);
  560. pos->value = value;
  561. }
  562. else
  563. {
  564. delta = (int64_t) value;
  565. if ((delta < 0) && (pos->value < -delta))
  566. {
  567. changed = (0 != pos->value);
  568. pos->value = 0;
  569. }
  570. else
  571. {
  572. changed = (0 != delta);
  573. GNUNET_break ((delta <= 0) || (pos->value + delta > pos->value));
  574. pos->value += delta;
  575. }
  576. }
  577. if (GNUNET_NO == pos->set)
  578. {
  579. pos->set = GNUNET_YES;
  580. initial_set = 1;
  581. }
  582. pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
  583. if (pos != se->stat_head)
  584. {
  585. /* move to front for faster setting next time! */
  586. GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos);
  587. GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos);
  588. }
  589. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  590. "Statistic `%s:%s' updated to value %llu (%d).\n",
  591. service,
  592. name,
  593. (unsigned long long) pos->value,
  594. pos->persistent);
  595. if ((changed) || (1 == initial_set))
  596. notify_change (pos);
  597. GNUNET_SERVICE_client_continue (ce->client);
  598. return;
  599. }
  600. /* not found, create a new entry */
  601. nlen = strlen (name) + 1;
  602. pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen);
  603. GNUNET_memcpy (&pos[1], name, nlen);
  604. pos->name = (const char *) &pos[1];
  605. pos->subsystem = se;
  606. if ((0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) ||
  607. (0 < (int64_t) GNUNET_ntohll (msg->value)))
  608. {
  609. pos->value = GNUNET_ntohll (msg->value);
  610. pos->set = GNUNET_YES;
  611. }
  612. else
  613. {
  614. pos->set = GNUNET_NO;
  615. }
  616. pos->uid = uidgen++;
  617. pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
  618. GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos);
  619. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  620. "New statistic on `%s:%s' with value %llu created.\n",
  621. service,
  622. name,
  623. (unsigned long long) pos->value);
  624. if (NULL != ce)
  625. GNUNET_SERVICE_client_continue (ce->client);
  626. }
  627. /**
  628. * Check integrity of WATCH-message.
  629. *
  630. * @param cls the `struct ClientEntry *`
  631. * @param message the actual message
  632. * @return #GNUNET_OK if message is well-formed
  633. */
  634. static int
  635. check_watch (void *cls, const struct GNUNET_MessageHeader *message)
  636. {
  637. size_t size;
  638. const char *service;
  639. const char *name;
  640. size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader);
  641. if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
  642. size,
  643. 2,
  644. &service,
  645. &name))
  646. {
  647. GNUNET_break (0);
  648. return GNUNET_SYSERR;
  649. }
  650. return GNUNET_OK;
  651. }
  652. /**
  653. * Handle WATCH-message.
  654. *
  655. * @param cls the `struct ClientEntry *`
  656. * @param message the actual message
  657. */
  658. static void
  659. handle_watch (void *cls, const struct GNUNET_MessageHeader *message)
  660. {
  661. struct ClientEntry *ce = cls;
  662. const char *service;
  663. const char *name;
  664. uint16_t msize;
  665. uint16_t size;
  666. struct SubsystemEntry *se;
  667. struct StatsEntry *pos;
  668. struct WatchEntry *we;
  669. size_t nlen;
  670. if (NULL == nc)
  671. {
  672. GNUNET_SERVICE_client_drop (ce->client);
  673. return;
  674. }
  675. GNUNET_SERVICE_client_mark_monitor (ce->client);
  676. msize = ntohs (message->size);
  677. size = msize - sizeof(struct GNUNET_MessageHeader);
  678. GNUNET_assert (size ==
  679. GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
  680. size,
  681. 2,
  682. &service,
  683. &name));
  684. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  685. "Received request to watch statistic on `%s:%s'\n",
  686. service,
  687. name);
  688. se = find_subsystem_entry (ce, service);
  689. pos = find_stat_entry (se, name);
  690. if (NULL == pos)
  691. {
  692. nlen = strlen (name) + 1;
  693. pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen);
  694. GNUNET_memcpy (&pos[1], name, nlen);
  695. pos->name = (const char *) &pos[1];
  696. pos->subsystem = se;
  697. GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos);
  698. pos->uid = uidgen++;
  699. pos->set = GNUNET_NO;
  700. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  701. "New statistic on `%s:%s' with value %llu created.\n",
  702. service,
  703. name,
  704. (unsigned long long) pos->value);
  705. }
  706. we = GNUNET_new (struct WatchEntry);
  707. we->ce = ce;
  708. we->last_value_set = GNUNET_NO;
  709. we->wid = ce->max_wid++;
  710. GNUNET_CONTAINER_DLL_insert (pos->we_head, pos->we_tail, we);
  711. if (0 != pos->value)
  712. notify_change (pos);
  713. GNUNET_SERVICE_client_continue (ce->client);
  714. }
  715. /**
  716. * Handle DISCONNECT-message. Sync to disk and send
  717. * back a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM
  718. * message.
  719. *
  720. * @param cls the `struct ClientEntry *`
  721. * @param message the actual message
  722. */
  723. static void
  724. handle_disconnect (void *cls, const struct GNUNET_MessageHeader *message)
  725. {
  726. struct ClientEntry *ce = cls;
  727. struct GNUNET_MQ_Envelope *env;
  728. struct GNUNET_MessageHeader *msg;
  729. env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM);
  730. GNUNET_MQ_send (ce->mq, env);
  731. GNUNET_SERVICE_client_continue (ce->client);
  732. }
  733. /**
  734. * Actually perform the shutdown.
  735. */
  736. static void
  737. do_shutdown ()
  738. {
  739. struct WatchEntry *we;
  740. struct StatsEntry *pos;
  741. struct SubsystemEntry *se;
  742. if (NULL == nc)
  743. return;
  744. save ();
  745. GNUNET_notification_context_destroy (nc);
  746. nc = NULL;
  747. GNUNET_assert (0 == client_count);
  748. while (NULL != (se = sub_head))
  749. {
  750. GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se);
  751. while (NULL != (pos = se->stat_head))
  752. {
  753. GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos);
  754. while (NULL != (we = pos->we_head))
  755. {
  756. GNUNET_break (0);
  757. GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we);
  758. GNUNET_free (we);
  759. }
  760. GNUNET_free (pos);
  761. }
  762. GNUNET_free (se);
  763. }
  764. }
  765. /**
  766. * Task run during shutdown.
  767. *
  768. * @param cls unused
  769. */
  770. static void
  771. shutdown_task (void *cls)
  772. {
  773. in_shutdown = GNUNET_YES;
  774. if (0 != client_count)
  775. return;
  776. do_shutdown ();
  777. }
  778. /**
  779. * A client disconnected. Remove all of its data structure entries.
  780. *
  781. * @param cls closure, NULL
  782. * @param client identification of the client
  783. * @param app_cls the `struct ClientEntry *`
  784. */
  785. static void
  786. client_disconnect_cb (void *cls,
  787. struct GNUNET_SERVICE_Client *client,
  788. void *app_cls)
  789. {
  790. struct ClientEntry *ce = app_cls;
  791. struct WatchEntry *we;
  792. struct WatchEntry *wen;
  793. struct StatsEntry *pos;
  794. struct SubsystemEntry *se;
  795. client_count--;
  796. for (se = sub_head; NULL != se; se = se->next)
  797. {
  798. for (pos = se->stat_head; NULL != pos; pos = pos->next)
  799. {
  800. wen = pos->we_head;
  801. while (NULL != (we = wen))
  802. {
  803. wen = we->next;
  804. if (we->ce != ce)
  805. continue;
  806. GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we);
  807. GNUNET_free (we);
  808. }
  809. }
  810. }
  811. GNUNET_free (ce);
  812. if ((0 == client_count) && (GNUNET_YES == in_shutdown))
  813. do_shutdown ();
  814. }
  815. /**
  816. * We've read a `struct GNUNET_STATISTICS_SetMessage *` from
  817. * disk. Check that it is well-formed, and if so pass it to
  818. * the handler for set messages.
  819. *
  820. * @param cls NULL
  821. * @param message the message found on disk
  822. * @return #GNUNET_OK on success,
  823. * #GNUNET_NO to stop further processing (no error)
  824. * #GNUNET_SYSERR to stop further processing with error
  825. */
  826. static int
  827. inject_message (void *cls, const struct GNUNET_MessageHeader *message)
  828. {
  829. uint16_t msize = ntohs (message->size);
  830. const struct GNUNET_STATISTICS_SetMessage *sm;
  831. sm = (const struct GNUNET_STATISTICS_SetMessage *) message;
  832. if ((sizeof(struct GNUNET_STATISTICS_SetMessage) > msize) ||
  833. (GNUNET_OK != check_set (NULL, sm)))
  834. {
  835. GNUNET_break (0);
  836. return GNUNET_SYSERR;
  837. }
  838. handle_set (NULL, sm);
  839. return GNUNET_OK;
  840. }
  841. /**
  842. * Load persistent values from disk. Disk format is exactly the same
  843. * format that we also use for setting the values over the network.
  844. */
  845. static void
  846. load ()
  847. {
  848. char *fn;
  849. struct GNUNET_BIO_ReadHandle *rh;
  850. uint64_t fsize;
  851. char *buf;
  852. struct GNUNET_MessageStreamTokenizer *mst;
  853. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
  854. "STATISTICS",
  855. "DATABASE",
  856. &fn))
  857. {
  858. GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
  859. "STATISTICS",
  860. "DATABASE");
  861. return;
  862. }
  863. if ((GNUNET_OK !=
  864. GNUNET_DISK_file_size (fn, &fsize, GNUNET_NO, GNUNET_YES)) ||
  865. (0 == fsize))
  866. {
  867. GNUNET_free (fn);
  868. return;
  869. }
  870. buf = GNUNET_malloc (fsize);
  871. rh = GNUNET_BIO_read_open_file (fn);
  872. if (! rh)
  873. {
  874. GNUNET_free (buf);
  875. GNUNET_free (fn);
  876. return;
  877. }
  878. if (GNUNET_OK != GNUNET_BIO_read (rh, fn, buf, fsize))
  879. {
  880. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", fn);
  881. GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL));
  882. GNUNET_free (buf);
  883. GNUNET_free (fn);
  884. return;
  885. }
  886. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  887. _ ("Loading %llu bytes of statistics from `%s'\n"),
  888. (unsigned long long) fsize,
  889. fn);
  890. mst = GNUNET_MST_create (&inject_message, NULL);
  891. GNUNET_break (
  892. GNUNET_OK ==
  893. GNUNET_MST_from_buffer (mst, buf, (size_t) fsize, GNUNET_YES, GNUNET_NO));
  894. GNUNET_MST_destroy (mst);
  895. GNUNET_free (buf);
  896. GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL));
  897. GNUNET_free (fn);
  898. }
  899. /**
  900. * Process statistics requests.
  901. *
  902. * @param cls closure
  903. * @param c configuration to use
  904. * @param service the initialized service
  905. */
  906. static void
  907. run (void *cls,
  908. const struct GNUNET_CONFIGURATION_Handle *c,
  909. struct GNUNET_SERVICE_Handle *service)
  910. {
  911. cfg = c;
  912. nc = GNUNET_notification_context_create (16);
  913. load ();
  914. GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
  915. }
  916. /**
  917. * Define "main" method using service macro.
  918. */
  919. GNUNET_SERVICE_MAIN (
  920. "statistics",
  921. GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
  922. &run,
  923. &client_connect_cb,
  924. &client_disconnect_cb,
  925. NULL,
  926. GNUNET_MQ_hd_var_size (set,
  927. GNUNET_MESSAGE_TYPE_STATISTICS_SET,
  928. struct GNUNET_STATISTICS_SetMessage,
  929. NULL),
  930. GNUNET_MQ_hd_var_size (get,
  931. GNUNET_MESSAGE_TYPE_STATISTICS_GET,
  932. struct GNUNET_MessageHeader,
  933. NULL),
  934. GNUNET_MQ_hd_var_size (watch,
  935. GNUNET_MESSAGE_TYPE_STATISTICS_WATCH,
  936. struct GNUNET_MessageHeader,
  937. NULL),
  938. GNUNET_MQ_hd_fixed_size (disconnect,
  939. GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT,
  940. struct GNUNET_MessageHeader,
  941. NULL),
  942. GNUNET_MQ_handler_end ());
  943. #if defined(__linux__) && defined(__GLIBC__)
  944. #include <malloc.h>
  945. /**
  946. * MINIMIZE heap size (way below 128k) since this process doesn't need much.
  947. */
  948. void __attribute__ ((constructor))
  949. GNUNET_STATISTICS_memory_init ()
  950. {
  951. mallopt (M_TRIM_THRESHOLD, 4 * 1024);
  952. mallopt (M_TOP_PAD, 1 * 1024);
  953. malloc_trim (0);
  954. }
  955. #endif
  956. /* end of gnunet-service-statistics.c */