287388dd169ccb0d88c4d39b38a800dffe4f63d5
[olsrd.git] / lib / debuginfo / src / olsrd_debuginfo.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /*
43  * Dynamic linked library for the olsr.org olsr daemon
44  */
45 #include "olsr.h"
46 #include "ipcalc.h"
47 #include "neighbor_table.h"
48 #include "tc_set.h"
49 #include "hna_set.h"
50 #include "mid_set.h"
51 #include "routing_table.h"
52 #include "log.h"
53 #include "misc.h"
54 #include "olsr_ip_prefix_list.h"
55 #include "parser.h"
56 #include "olsr_comport_txt.h"
57 #include "olsrd_debuginfo.h"
58 #include "olsr_types.h"
59 #include "defs.h"
60
61 #include "common/autobuf.h"
62
63 #define PLUGIN_NAME    "OLSRD debuginfo plugin"
64 #define PLUGIN_VERSION "0.1"
65 #define PLUGIN_AUTHOR   "Henning Rogge"
66 #define MOD_DESC PLUGIN_NAME " " PLUGIN_VERSION " by " PLUGIN_AUTHOR
67 #define PLUGIN_INTERFACE_VERSION 5
68
69 struct debuginfo_cmd {
70   const char *name;
71   olsr_txthandler handler;
72   struct olsr_txtcommand *cmd;
73 };
74
75 static void debuginfo_new(void) __attribute__ ((constructor));
76 static void debuginfo_delete(void) __attribute__ ((destructor));
77
78 static enum olsr_txtcommand_result debuginfo_msgstat(struct comport_connection *con,
79     const char *cmd, const char *param);
80 static enum olsr_txtcommand_result debuginfo_pktstat(struct comport_connection *con,
81     const char *cmd, const char *param);
82 static enum olsr_txtcommand_result debuginfo_cookies(struct comport_connection *con,
83     const char *cmd, const char *param);
84
85 static void update_statistics_ptr(void *);
86 static void olsr_msg_statistics(union olsr_message *, struct interface *, union olsr_ip_addr *, enum duplicate_status);
87 static char *olsr_packet_statistics(char *packet, struct interface *interface, union olsr_ip_addr *, int *length);
88 static void update_statistics_ptr(void *data __attribute__ ((unused)));
89
90 /* plugin configuration */
91 static struct ip_acl allowed_nets;
92 static uint32_t traffic_interval, traffic_slots, current_slot;
93
94 /* plugin parameters */
95 static const struct olsrd_plugin_parameters plugin_parameters[] = {
96   {.name = IP_ACL_ACCEPT_PARAP,.set_plugin_parameter = &ip_acl_add_plugin_accept,.data = &allowed_nets},
97   {.name = IP_ACL_REJECT_PARAM,.set_plugin_parameter = &ip_acl_add_plugin_reject,.data = &allowed_nets},
98   {.name = IP_ACL_CHECKFIRST_PARAM,.set_plugin_parameter = &ip_acl_add_plugin_checkFirst,.data = &allowed_nets},
99   {.name = IP_ACL_DEFAULTPOLICY_PARAM,.set_plugin_parameter = &ip_acl_add_plugin_defaultPolicy,.data = &allowed_nets},
100   {.name = "stat_interval", .set_plugin_parameter = &set_plugin_int, .data = &traffic_interval},
101   {.name = "stat_slots", .set_plugin_parameter = &set_plugin_int, .data = &traffic_slots},
102 };
103
104 /* command callbacks and names */
105 static struct debuginfo_cmd commands[] = {
106     {"msgstat", &debuginfo_msgstat, NULL},
107     {"pktstat", &debuginfo_pktstat, NULL},
108     {"cookies", &debuginfo_cookies, NULL}
109 };
110
111 /* variables for statistics */
112 static struct avl_tree stat_msg_tree, stat_pkt_tree;
113
114 static struct debug_msgtraffic_count total_msg_traffic;
115 static struct debug_pkttraffic_count total_pkt_traffic;
116
117 static struct olsr_cookie_info *statistics_timer = NULL;
118 static struct olsr_cookie_info *statistics_msg_mem = NULL;
119 static struct olsr_cookie_info *statistics_pkt_mem = NULL;
120
121 static union olsr_ip_addr total_ip_addr;
122
123 int
124 olsrd_plugin_interface_version(void)
125 {
126   return PLUGIN_INTERFACE_VERSION;
127 }
128
129 void
130 olsrd_get_plugin_parameters(const struct olsrd_plugin_parameters **params, int *size)
131 {
132   *params = plugin_parameters;
133   *size = ARRAYSIZE(plugin_parameters);
134 }
135
136
137 /**
138  *Constructor
139  */
140 static void
141 debuginfo_new(void)
142 {
143   /* Print plugin info to stdout */
144   OLSR_INFO(LOG_PLUGINS, "%s\n", MOD_DESC);
145
146   ip_acl_init(&allowed_nets);
147
148   traffic_interval = 5; /* seconds */
149   traffic_slots = 12;      /* number of 5000 second slots */
150   current_slot = 0;
151
152   memset(&total_ip_addr, 255, sizeof(total_ip_addr));
153
154   /* always allow localhost */
155   if (olsr_cnf->ip_version == AF_INET) {
156     union olsr_ip_addr ip;
157
158     ip.v4.s_addr = ntohl(INADDR_LOOPBACK);
159     ip_acl_add(&allowed_nets, &ip, 32, false);
160   } else {
161     ip_acl_add(&allowed_nets, (const union olsr_ip_addr *)&in6addr_loopback, 128, false);
162     ip_acl_add(&allowed_nets, (const union olsr_ip_addr *)&in6addr_v4mapped_loopback, 128, false);
163   }
164 }
165
166 /**
167  *Destructor
168  */
169 static void
170 debuginfo_delete(void)
171 {
172   size_t i;
173
174   for (i=0; i<ARRAYSIZE(commands); i++) {
175     olsr_com_remove_normal_txtcommand(commands[i].cmd);
176   }
177   olsr_parser_remove_function(&olsr_msg_statistics);
178   olsr_preprocessor_remove_function(&olsr_packet_statistics);
179   ip_acl_flush(&allowed_nets);
180 }
181
182 int
183 olsrd_plugin_init(void)
184 {
185   size_t i;
186
187   for (i=0; i<ARRAYSIZE(commands); i++) {
188     commands[i].cmd = olsr_com_add_normal_txtcommand(commands[i].name, commands[i].handler);
189     commands[i].cmd->acl = &allowed_nets;
190   }
191
192   statistics_timer = olsr_alloc_cookie("debuginfo timer", OLSR_COOKIE_TYPE_TIMER);
193   olsr_start_timer(traffic_interval * 1000, 0, true, &update_statistics_ptr, NULL, statistics_timer);
194
195   statistics_msg_mem = olsr_alloc_cookie("debuginfo msgstat", OLSR_COOKIE_TYPE_MEMORY);
196   olsr_cookie_set_memory_size(statistics_msg_mem,
197       sizeof(struct debug_msgtraffic) + sizeof(struct debug_msgtraffic_count) * traffic_slots);
198
199   statistics_pkt_mem = olsr_alloc_cookie("debuginfo pktstat", OLSR_COOKIE_TYPE_MEMORY);
200   olsr_cookie_set_memory_size(statistics_pkt_mem,
201       sizeof(struct debug_pkttraffic) + sizeof(struct debug_pkttraffic_count) * traffic_slots);
202
203   memset(&total_msg_traffic, 0, sizeof(total_msg_traffic));
204   memset(&total_pkt_traffic, 0, sizeof(total_pkt_traffic));
205   avl_init(&stat_msg_tree, avl_comp_default);
206   avl_init(&stat_pkt_tree, avl_comp_default);
207
208   olsr_parser_add_function(&olsr_msg_statistics, PROMISCUOUS);
209   olsr_preprocessor_add_function(&olsr_packet_statistics);
210   return 1;
211 }
212
213 static struct debug_msgtraffic *get_msgtraffic_entry(union olsr_ip_addr *ip) {
214   struct debug_msgtraffic *tr;
215   tr = (struct debug_msgtraffic *) avl_find(&stat_msg_tree, ip);
216   if (tr == NULL) {
217     tr = olsr_cookie_malloc(statistics_msg_mem);
218
219     memcpy(&tr->ip, ip, sizeof(union olsr_ip_addr));
220     tr->node.key = &tr->ip;
221
222     avl_insert(&stat_msg_tree, &tr->node, false);
223   }
224   return tr;
225 }
226
227 static struct debug_pkttraffic *get_pkttraffic_entry(union olsr_ip_addr *ip, struct interface *in) {
228   struct debug_pkttraffic *tr;
229   tr = (struct debug_pkttraffic *) avl_find(&stat_pkt_tree, ip);
230   if (tr == NULL) {
231     tr = olsr_cookie_malloc(statistics_pkt_mem);
232
233     memcpy(&tr->ip, ip, sizeof(union olsr_ip_addr));
234     tr->node.key = &tr->ip;
235
236     tr->int_name = strdup(in ? in->int_name : "---");
237
238     avl_insert(&stat_pkt_tree, &tr->node, false);
239   }
240   return tr;
241 }
242
243 static void
244 update_statistics_ptr(void *data __attribute__ ((unused)))
245 {
246   struct debug_msgtraffic *msg;
247   struct debug_pkttraffic *pkt;
248   uint32_t last_slot, i;
249
250   last_slot = current_slot;
251   current_slot++;
252   if (current_slot == traffic_slots) {
253     current_slot = 0;
254   }
255
256   /* move data from "current" template to slot array */
257   OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES(msg) {
258     /* subtract old values from node count and total count */
259     for (i=0; i<DTR_MSG_COUNT; i++) {
260       msg->total.data[i] -= msg->traffic[current_slot].data[i];
261     }
262
263     /* copy new traffic into slot */
264     msg->traffic[current_slot] = msg->current;
265
266     /* add new values to node count and total count */
267     for (i=0; i<DTR_MSG_COUNT; i++) {
268       msg->total.data[i] += msg->current.data[i];
269     }
270
271     /* erase new traffic */
272     memset(&msg->current, 0, sizeof(msg->current));
273
274     if (msg->total.data[DTR_MESSAGES] == 0) {
275       /* no traffic left, cleanup ! */
276
277       avl_delete(&stat_msg_tree, &msg->node);
278       olsr_cookie_free(statistics_msg_mem, msg);
279     }
280   } OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES_END()
281
282   OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES(pkt) {
283     /* subtract old values from node count and total count */
284     for (i=0; i<DTR_PKT_COUNT; i++) {
285       pkt->total.data[i] -= pkt->traffic[current_slot].data[i];
286     }
287
288     /* copy new traffic into slot */
289     pkt->traffic[current_slot] = pkt->current;
290
291     /* add new values to node count and total count */
292     for (i=0; i<DTR_PKT_COUNT; i++) {
293       pkt->total.data[i] += pkt->current.data[i];
294     }
295
296     /* erase new traffic */
297     memset(&pkt->current, 0, sizeof(pkt->current));
298
299     if (pkt->total.data[DTR_PACKETS] == 0) {
300       /* no traffic left, cleanup ! */
301
302       avl_delete(&stat_pkt_tree, &pkt->node);
303       free(pkt->int_name);
304       olsr_cookie_free(statistics_pkt_mem, pkt);
305     }
306   } OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES_END()
307 }
308
309 /* update message statistics */
310 static void
311 olsr_msg_statistics(union olsr_message *msg, struct interface *input_if __attribute__ ((unused)),
312     union olsr_ip_addr *from_addr __attribute__ ((unused)), enum duplicate_status status  __attribute__ ((unused)))
313 {
314   int msgtype, msgsize;
315   union olsr_ip_addr origaddr;
316   enum debug_msgtraffic_type type;
317   struct debug_msgtraffic *tr;
318 #if !defined REMOVE_LOG_DEBUG
319   struct ipaddr_str buf;
320 #endif
321
322   memset(&origaddr, 0, sizeof(origaddr));
323   if (olsr_cnf->ip_version == AF_INET) {
324     msgtype = msg->v4.olsr_msgtype;
325     msgsize = ntohs(msg->v4.olsr_msgsize);
326     origaddr.v4.s_addr = msg->v4.originator;
327   } else {
328     msgtype = msg->v6.olsr_msgtype;
329     msgsize = ntohs(msg->v6.olsr_msgsize);
330     origaddr.v6 = msg->v6.originator;
331   }
332
333   switch (msgtype) {
334   case HELLO_MESSAGE:
335     type = DTR_HELLO;
336     break;
337   case TC_MESSAGE:
338     type = DTR_TC;
339     break;
340   case MID_MESSAGE:
341     type = DTR_MID;
342     break;
343   case HNA_MESSAGE:
344     type = DTR_HNA;
345     break;
346   case LQ_HELLO_MESSAGE:
347     type = DTR_HELLO;
348     break;
349   case LQ_TC_MESSAGE:
350     type = DTR_TC;
351     break;
352   default:
353     type = DTR_OTHER;
354     break;
355   }
356
357   /* input data for specific node */
358   tr = get_msgtraffic_entry(&origaddr);
359   tr->current.data[type]++;
360   tr->current.data[DTR_MESSAGES]++;
361   tr->current.data[DTR_MSG_TRAFFIC] += msgsize;
362
363   OLSR_DEBUG(LOG_PLUGINS, "Added message type %d to statistics of %s: %d\n",
364       type, olsr_ip_to_string(&buf, &tr->ip), tr->current.data[type]);
365
366   /* input data for total traffic handling */
367   tr = get_msgtraffic_entry(&total_ip_addr);
368   tr->current.data[type]++;
369   tr->current.data[DTR_MESSAGES]++;
370   tr->current.data[DTR_MSG_TRAFFIC] += msgsize;
371 }
372
373 /* update traffic statistics */
374 static char *
375 olsr_packet_statistics(char *packet __attribute__ ((unused)),
376                        struct interface *interface,
377                        union olsr_ip_addr *ip, int *length)
378 {
379   struct debug_pkttraffic *tr;
380   tr = get_pkttraffic_entry(ip, interface);
381   tr->current.data[DTR_PACK_TRAFFIC] += *length;
382   tr->current.data[DTR_PACKETS] ++;
383
384   tr = get_pkttraffic_entry(&total_ip_addr, NULL);
385   tr->current.data[DTR_PACK_TRAFFIC] += *length;
386   tr->current.data[DTR_PACKETS] ++;
387   return packet;
388 }
389
390 static const char *debuginfo_print_trafficip(struct ipaddr_str *buf, union olsr_ip_addr *ip) {
391   static const char *total = "Total";
392   if (olsr_ipcmp(ip, &total_ip_addr) == 0) {
393     return total;
394   }
395   return olsr_ip_to_string(buf, ip);
396 }
397
398 static bool debuginfo_print_msgstat(struct autobuf *buf, union olsr_ip_addr *ip, struct debug_msgtraffic_count *cnt) {
399   struct ipaddr_str strbuf;
400   return abuf_appendf(buf, "%-*s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t\n",
401       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, debuginfo_print_trafficip(&strbuf, ip),
402       cnt->data[DTR_HELLO],
403       cnt->data[DTR_TC],
404       cnt->data[DTR_MID],
405       cnt->data[DTR_HNA],
406       cnt->data[DTR_OTHER],
407       cnt->data[DTR_MESSAGES],
408       cnt->data[DTR_MSG_TRAFFIC]) < 0;
409 }
410
411 static enum olsr_txtcommand_result
412 debuginfo_msgstat(struct comport_connection *con,
413     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused)))
414 {
415   struct debug_msgtraffic *tr;
416
417   if (abuf_appendf(&con->out, "Slot size: %d seconds\tSlot count: %d\n", traffic_interval, traffic_slots) < 0) {
418     return ABUF_ERROR;
419   }
420   if (abuf_appendf(&con->out,
421       "Table: Statistics (without duplicates)\n%-*s\tHello\tTC\tMID\tHNA\tOther\tTotal\tBytes\n",
422       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, "IP"
423       ) < 0) {
424     return ABUF_ERROR;
425   }
426
427   if (param == NULL || strcasecmp(param, "node") == 0) {
428     OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES(tr) {
429       if (debuginfo_print_msgstat(&con->out, &tr->ip, &tr->traffic[current_slot])) {
430         return ABUF_ERROR;
431       }
432     } OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES_END()
433   }
434   else {
435     uint32_t mult = 1, divisor = 1;
436     struct debug_msgtraffic_count cnt;
437     int i;
438
439     if (strcasecmp(param, "total") == 0) {
440       divisor = 1;
441     }
442     else if (strcasecmp(param, "average") == 0) {
443       divisor = traffic_slots;
444     }
445     else if (strcasecmp(param, "avgsec") == 0) {
446       divisor = traffic_slots * traffic_interval;
447       mult = 1;
448     }
449     else if (strcasecmp(param, "avgmin") == 0) {
450       divisor = traffic_slots * traffic_interval;
451       mult = 60;
452     }
453     else if (strcasecmp(param, "avghour") == 0) {
454       divisor = traffic_slots * traffic_interval;
455       mult = 3600;
456     }
457     else {
458       abuf_appendf(&con->out, "Error, unknown parameter %s for msgstat\n", param);
459       return CONTINUE;
460     }
461
462     OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES(tr) {
463       for (i=0; i<DTR_MSG_COUNT; i++) {
464         cnt.data[i] = (tr->total.data[i] * mult) / divisor;
465       }
466       if (debuginfo_print_msgstat(&con->out, &tr->ip, &cnt)) {
467         return ABUF_ERROR;
468       }
469     } OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES_END()
470   }
471
472   return CONTINUE;
473 }
474
475 static bool debuginfo_print_pktstat(struct autobuf *buf, union olsr_ip_addr *ip, char *int_name, struct debug_pkttraffic_count *cnt) {
476   struct ipaddr_str strbuf;
477   return abuf_appendf(buf, "%-*s\t%s\t%d\t%d\n",
478       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, debuginfo_print_trafficip(&strbuf, ip),
479       int_name,
480       cnt->data[DTR_PACKETS],
481       cnt->data[DTR_PACK_TRAFFIC]) < 0;
482 }
483
484 static enum olsr_txtcommand_result
485 debuginfo_pktstat(struct comport_connection *con,
486     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused)))
487 {
488   struct debug_pkttraffic *tr;
489
490   if (abuf_appendf(&con->out, "Slot size: %d seconds\tSlot count: %d\n", traffic_interval, traffic_slots) < 0) {
491     return ABUF_ERROR;
492   }
493   if (abuf_appendf(&con->out,
494       "Table: Statistics (without duplicates)\n%-*s\tInterf.\tPackets\tBytes\n",
495       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, "IP"
496       ) < 0) {
497     return ABUF_ERROR;
498   }
499
500   if (param == NULL || strcasecmp(param, "node") == 0) {
501     OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES(tr) {
502       if (debuginfo_print_pktstat(&con->out, &tr->ip, tr->int_name, &tr->traffic[current_slot])) {
503         return ABUF_ERROR;
504       }
505     } OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES_END()
506   }
507   else {
508     uint32_t mult = 1, divisor = 1;
509     struct debug_pkttraffic_count cnt;
510     int i;
511
512     if (strcasecmp(param, "total") == 0) {
513       divisor = 1;
514     }
515     else if (strcasecmp(param, "average") == 0) {
516       divisor = traffic_slots;
517     }
518     else if (strcasecmp(param, "avgsec") == 0) {
519       divisor = traffic_slots * traffic_interval;
520       mult = 1;
521     }
522     else if (strcasecmp(param, "avgmin") == 0) {
523       divisor = traffic_slots * traffic_interval;
524       mult = 60;
525     }
526     else if (strcasecmp(param, "avghour") == 0) {
527       divisor = traffic_slots * traffic_interval;
528       mult = 3600;
529     }
530     else {
531       abuf_appendf(&con->out, "Error, unknown parameter %s for msgstat\n", param);
532       return CONTINUE;
533     }
534
535     OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES(tr) {
536       for (i=0; i<DTR_PKT_COUNT; i++) {
537         cnt.data[i] = (tr->total.data[i] * mult) / divisor;
538       }
539       if (debuginfo_print_pktstat(&con->out, &tr->ip, tr->int_name, &cnt)) {
540         return ABUF_ERROR;
541       }
542     } OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES_END()
543   }
544
545   return CONTINUE;
546 }
547
548 static INLINE bool debuginfo_print_cookies_mem(struct autobuf *buf, const char *format) {
549   struct olsr_cookie_info *c;
550
551   OLSR_FOR_ALL_COOKIES(c) {
552     if (c == NULL || c->ci_type != OLSR_COOKIE_TYPE_MEMORY) {
553       continue;
554     }
555     if (abuf_appendf(buf, format,
556         c->ci_name == NULL ? "Unknown" : c->ci_name,
557         (unsigned long)c->ci_size, c->ci_usage, c->ci_free_list_usage) < 0) {
558       return true;
559     }
560   } OLSR_FOR_ALL_COOKIES_END()
561   return false;
562 }
563
564 static INLINE bool debuginfo_print_cookies_timer(struct autobuf *buf, const char *format) {
565   struct olsr_cookie_info *c;
566
567   OLSR_FOR_ALL_COOKIES(c) {
568     if (c == NULL || c->ci_type != OLSR_COOKIE_TYPE_TIMER) {
569       continue;
570     }
571     if (abuf_appendf(buf, format, c->ci_name == NULL ? "Unknown" : c->ci_name,
572                        c->ci_usage, c->ci_changes) < 0) {
573       return true;
574     }
575   } OLSR_FOR_ALL_COOKIES_END()
576   return false;
577 }
578
579 static enum olsr_txtcommand_result
580 debuginfo_cookies(struct comport_connection *con,
581     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused)))
582 {
583   if (abuf_puts(&con->out, "Memory cookies:\n") < 0) {
584     return ABUF_ERROR;
585   }
586
587   if (debuginfo_print_cookies_mem(&con->out, "%-25s (MEMORY) size: %lu usage: %u freelist: %u\n")) {
588     return ABUF_ERROR;
589   }
590
591   if (abuf_puts(&con->out, "\nTimer cookies:\n") < 0) {
592     return ABUF_ERROR;
593   }
594
595   if (debuginfo_print_cookies_timer(&con->out, "%-25s (TIMER) usage: %u changes: %u\n")) {
596     return ABUF_ERROR;
597   }
598   return CONTINUE;
599 }
600
601 /*
602  * Local Variables:
603  * mode: c
604  * style: linux
605  * c-basic-offset: 4
606  * indent-tabs-mode: nil
607  * End:
608  */