I started to clean up the plugin parameter handling:
[olsrd.git] / lib / txtinfo / src / olsrd_txtinfo.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto@olsr.org)
4  *                     includes code by Bruno Randolf
5  *                     includes code by Andreas Lopatic
6  *                     includes code by Sven-Ola Tuecke
7  *                     includes code by Lorenz Schori
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without 
11  * modification, are permitted provided that the following conditions 
12  * are met:
13  *
14  * * Redistributions of source code must retain the above copyright 
15  *   notice, this list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above copyright 
17  *   notice, this list of conditions and the following disclaimer in 
18  *   the documentation and/or other materials provided with the 
19  *   distribution.
20  * * Neither the name of olsr.org, olsrd nor the names of its 
21  *   contributors may be used to endorse or promote products derived 
22  *   from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  * Visit http://www.olsr.org for more information.
38  *
39  * If you find this software useful feel free to make a donation
40  * to the project. For more information see the website or contact
41  * the copyright holders.
42  *
43  * $Id: olsrd_txtinfo.c,v 1.7 2007/07/15 19:29:37 bernd67 Exp $
44  */
45
46 /*
47  * Dynamic linked library for the olsr.org olsr daemon
48  */
49
50  
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #if !defined WIN32
54 #include <sys/select.h>
55 #endif
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <sys/time.h>
59 #include <time.h>
60 #include <math.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <stdarg.h>
65 #include <unistd.h>
66 #include <errno.h>
67
68 #include "olsr.h"
69 #include "olsr_types.h"
70 #include "neighbor_table.h"
71 #include "two_hop_neighbor_table.h"
72 #include "mpr_selector_set.h"
73 #include "tc_set.h"
74 #include "hna_set.h"
75 #include "mid_set.h"
76 #include "link_set.h"
77 #include "socket_parser.h"
78
79 #include "olsrd_txtinfo.h"
80 #include "olsrd_plugin.h"
81
82
83 #ifdef WIN32
84 #define close(x) closesocket(x)
85 #endif
86
87
88 static int ipc_socket;
89 static int ipc_open;
90 static int ipc_connection;
91 static int ipc_socket_up;
92
93
94 /* IPC initialization function */
95 static int plugin_ipc_init(void);
96
97 static void  send_info(int neighonly);
98
99 static void ipc_action(int);
100
101 static void ipc_print_neigh_link(void);
102
103 static void ipc_print_routes(void);
104
105 static void ipc_print_topology(void);
106
107 static void ipc_print_hna(void);
108
109 static void ipc_print_mid(void);
110
111 #define TXT_IPC_BUFSIZE 256
112 static int ipc_sendf(const char* format, ...) __attribute__((format(printf, 1, 2)));
113
114 /**
115  *Do initialization here
116  *
117  *This function is called by the my_init
118  *function in uolsrd_plugin.c
119  */
120 int
121 olsrd_plugin_init(void)
122 {
123     /* Initial IPC value */
124     ipc_open = 0;
125     ipc_socket_up = 0;
126
127     plugin_ipc_init();
128     return 1;
129 }
130
131
132 /**
133  * destructor - called at unload
134  */
135 void olsr_plugin_exit(void)
136 {
137     if(ipc_open)
138         close(ipc_socket);
139 }
140
141
142
143 static int
144 plugin_ipc_init(void)
145 {
146     struct sockaddr_in sin;
147     olsr_u32_t yes = 1;
148
149     /* Init ipc socket */
150     if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
151 #ifndef NODEBUG
152         olsr_printf(1, "(TXTINFO) socket()=%s\n", strerror(errno));
153 #endif
154         return 0;
155     } else {
156         if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) {
157 #ifndef NODEBUG
158             olsr_printf(1, "(TXTINFO) setsockopt()=%s\n", strerror(errno));
159 #endif
160             return 0;
161         }
162
163 #if defined __FreeBSD__ && defined SO_NOSIGPIPE
164         if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) {
165             perror("SO_REUSEADDR failed");
166             return 0;
167         }
168 #endif
169         /* Bind the socket */
170
171         /* complete the socket structure */
172         memset(&sin, 0, sizeof(sin));
173         sin.sin_family = AF_INET;
174         sin.sin_addr.s_addr = INADDR_ANY;
175         sin.sin_port = htons(ipc_port);
176       
177         /* bind the socket to the port number */
178         if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
179 #ifndef NODEBUG
180             olsr_printf(1, "(TXTINFO) bind()=%s\n", strerror(errno));
181 #endif
182             return 0;
183         }
184
185         /* show that we are willing to listen */
186         if (listen(ipc_socket, 1) == -1) {
187 #ifndef NODEBUG
188             olsr_printf(1, "(TXTINFO) listen()=%s\n", strerror(errno));
189 #endif
190             return 0;
191         }
192
193         /* Register with olsrd */
194         add_olsr_socket(ipc_socket, &ipc_action);
195         
196 #ifndef NODEBUG
197         olsr_printf(2, "(TXTINFO) listening on port %d\n",ipc_port);
198 #endif
199         ipc_socket_up = 1;
200     }
201     return 1;
202 }
203
204
205 static void ipc_action(int fd)
206 {
207     struct sockaddr_in pin;
208     char *addr;  
209     fd_set rfds;
210     struct timeval tv;
211     int neighonly = 0;
212
213     socklen_t addrlen = sizeof(struct sockaddr_in);
214
215     if(ipc_open)
216         return;
217
218     if ((ipc_connection = accept(fd, (struct sockaddr *)  &pin, &addrlen)) == -1) {
219 #ifndef NODEBUG
220         olsr_printf(1, "(TXTINFO) accept()=%s\n", strerror(errno));
221 #endif
222         return;
223     }
224
225     tv.tv_sec = tv.tv_usec = 0;
226     addr = inet_ntoa(pin.sin_addr);
227     if (ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.v4)) {
228         olsr_printf(1, "(TXTINFO) From host(%s) not allowed!\n", addr);
229         close(ipc_connection);
230         return;
231     }
232     ipc_open = 1;
233 #ifndef NODEBUG
234     olsr_printf(2, "(TXTINFO) Connect from %s\n",addr);
235 #endif
236       
237     /* purge read buffer to prevent blocking on linux*/
238     FD_ZERO(&rfds);
239     FD_SET((unsigned int)ipc_connection, &rfds); /* Win32 needs the cast here */
240     if(select(ipc_connection+1, &rfds, NULL, NULL, &tv)) {
241         char requ[128];
242         ssize_t s = recv(ipc_connection, (void*)&requ, sizeof(requ), 0); /* Win32 needs the cast here */
243         if (0 < s) {
244             requ[s] = 0;
245             /* To print out neighbours only on the Freifunk Status
246              * page the normal output is somewhat lengthy. The
247              * header parsing is sufficient for standard wget.
248              */
249             neighonly = (0 != strstr(requ, "/neighbours"));
250         }
251     }
252
253     send_info(neighonly);
254           
255     close(ipc_connection);
256     ipc_open = 0;
257 }
258
259 static void ipc_print_neigh_link(void)
260 {
261     struct neighbor_entry *neigh;
262     struct neighbor_2_list_entry *list_2;
263     struct link_entry *link = NULL;
264     int index, thop_cnt;
265
266     ipc_sendf("Table: Links\nLocal IP\tremote IP\tHysteresis\tLinkQuality\tlost\ttotal\tNLQ\tETX\n");
267
268     /* Link set */
269     link = link_set;
270     while(link) {
271         ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%d\t%d\t%0.2f\t%0.2f\t\n",
272                    olsr_ip_to_string(&link->local_iface_addr),
273                    olsr_ip_to_string(&link->neighbor_iface_addr),
274                    link->L_link_quality, 
275                    link->loss_link_quality,
276                    link->lost_packets, 
277                    link->total_packets,
278                    link->neigh_link_quality, 
279                    (link->loss_link_quality * link->neigh_link_quality) ? 1.0 / (link->loss_link_quality * link->neigh_link_quality) : 0.0);
280         link = link->next;
281     }
282     ipc_sendf("\nTable: Neighbors\nIP address\tSYM\tMPR\tMPRS\tWillingness\t2 Hop Neighbors\n");
283
284     /* Neighbors */
285     for(index = 0; index < HASHSIZE; index++) {
286         for(neigh = neighbortable[index].next;
287             neigh != &neighbortable[index];
288             neigh = neigh->next) {
289             ipc_sendf("%s\t%s\t%s\t%s\t%d\t", 
290                       olsr_ip_to_string(&neigh->neighbor_main_addr),
291                       (neigh->status == SYM) ? "YES" : "NO",
292                       neigh->is_mpr ? "YES" : "NO",
293                       olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO",
294                       neigh->willingness);
295             thop_cnt = 0;
296
297             for(list_2 = neigh->neighbor_2_list.next;
298                 list_2 != &neigh->neighbor_2_list;
299                 list_2 = list_2->next)
300                 {
301                     //size += sprintf(&buf[size], "<option>%s</option>\n", olsr_ip_to_string(&list_2->neighbor_2->neighbor_2_addr));
302                     thop_cnt ++;
303                 }
304             ipc_sendf("%d\n", thop_cnt);
305         }
306     }
307     ipc_sendf("\n");
308 }
309
310 static void ipc_print_routes(void)
311 {
312     int size = 0, index;
313     struct rt_entry *routes;
314
315     ipc_sendf("Table: Routes\nDestination\tGateway\tMetric\tETX\tInterface\tType\n");
316
317     /* Neighbors */
318     for(index = 0;index < HASHSIZE; index++) {
319         for(routes = routingtable[index].next;
320             routes != &routingtable[index];
321             routes = routes->next) {
322             size = 0;
323             ipc_sendf( "%s\t%s\t%d\t%.2f\t%s\tHOST\n",
324                        olsr_ip_to_string(&routes->rt_dst),
325                        olsr_ip_to_string(&routes->rt_router),
326                        routes->rt_metric,
327                        routes->rt_etx,
328                        routes->rt_if->int_name);
329         }
330     }
331
332     /* HNA */
333     for(index = 0;index < HASHSIZE;index++) {
334         for(routes = hna_routes[index].next;
335             routes != &hna_routes[index];
336             routes = routes->next) {
337             ipc_sendf("%s\t%s\t%d\t%s\t\tHNA\n",
338                       olsr_ip_to_string(&routes->rt_dst),
339                       olsr_ip_to_string(&routes->rt_router),
340                       routes->rt_metric,
341                       routes->rt_if->int_name);
342         }
343     }
344     ipc_sendf("\n");
345
346 }
347
348 static void ipc_print_topology(void)
349 {
350     int index;
351     struct tc_entry *entry;
352     struct topo_dst *dst_entry;
353
354     ipc_sendf("Table: Topology\nDestination IP\tLast hop IP\tLQ\tILQ\tETX\n");
355
356     /* Topology */  
357     for(index = 0; index < HASHSIZE; index++) {
358         /* For all TC entries */
359         entry = tc_table[index].next;
360         while(entry != &tc_table[index]) {
361             /* For all destination entries of that TC entry */
362             dst_entry = entry->destinations.next;
363             while(dst_entry != &entry->destinations) {
364                 ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%0.2f\n", 
365                            olsr_ip_to_string(&dst_entry->T_dest_addr),
366                            olsr_ip_to_string(&entry->T_last_addr), 
367                            dst_entry->link_quality,
368                            dst_entry->inverse_link_quality,
369                            (dst_entry->link_quality * dst_entry->inverse_link_quality) ? 1.0 / (dst_entry->link_quality * dst_entry->inverse_link_quality) : 0.0);
370                 
371                 dst_entry = dst_entry->next;
372             }
373             entry = entry->next;
374         }
375     }
376     ipc_sendf("\n");
377 }
378
379 static void ipc_print_hna(void)
380 {
381     int size;
382     olsr_u8_t index;
383     struct hna_entry *tmp_hna;
384     struct hna_net *tmp_net;
385     struct hna4_entry *hna4;
386     struct hna6_entry *hna6;
387
388     size = 0;
389
390     ipc_sendf("Table: HNA\nNetwork\tNetmask\tGateway\n");
391
392     /* Announced HNA entries */
393     for(hna4 = olsr_cnf->hna4_entries; hna4; hna4 = hna4->next) {
394         ipc_sendf("%s\t%s\t%s\n",
395                   olsr_ip_to_string(&hna4->net),
396                   olsr_ip_to_string(&hna4->netmask),
397                   olsr_ip_to_string(&olsr_cnf->main_addr));
398     }
399     for(hna6 = olsr_cnf->hna6_entries; hna6; hna6 = hna6->next) {
400         ipc_sendf("%s\t%d\t%s\n",
401                   olsr_ip_to_string(&hna6->net),
402                   hna6->prefix_len,
403                   olsr_ip_to_string(&olsr_cnf->main_addr));
404     }
405
406     /* HNA entries */
407     for(index = 0; index < HASHSIZE; index++) {
408         tmp_hna = hna_set[index].next;
409         /* Check all entrys */
410         while(tmp_hna != &hna_set[index]) {
411             /* Check all networks */
412             tmp_net = tmp_hna->networks.next;
413               
414             while(tmp_net != &tmp_hna->networks) {
415                 if (AF_INET == olsr_cnf->ip_version) {
416                     ipc_sendf("%s\t%s\t%s\n",
417                               olsr_ip_to_string(&tmp_net->A_network_addr),
418                               olsr_ip_to_string((union olsr_ip_addr *)&tmp_net->A_netmask.v4),
419                               olsr_ip_to_string(&tmp_hna->A_gateway_addr));
420                 } else {
421                     ipc_sendf("%s\t%d\t%s\n",
422                               olsr_ip_to_string(&tmp_net->A_network_addr),
423                               tmp_net->A_netmask.v6,
424                               olsr_ip_to_string(&tmp_hna->A_gateway_addr));
425                 }
426                 tmp_net = tmp_net->next;
427             }
428               
429             tmp_hna = tmp_hna->next;
430         }
431     }
432     ipc_sendf("\n");
433
434 }
435
436 static void ipc_print_mid(void)
437 {
438     int index;
439     unsigned short is_first;
440     struct mid_entry *entry;
441     struct mid_address *alias;
442
443     ipc_sendf("Table: MID\nIP\tAliases\n");
444
445     /* MID */
446     for(index = 0; index < HASHSIZE; index++) {
447         entry = mid_set[index].next;
448         
449         while( entry != &mid_set[index] ) {
450             ipc_sendf( olsr_ip_to_string( &entry->main_addr ) );
451             alias = entry->aliases;
452             is_first = 1;
453
454             while( alias ) {
455                 ipc_sendf( "%s%s",
456                            ( is_first ? "\t" : ";" ),
457                            olsr_ip_to_string( &alias->alias )
458                            );
459
460                 alias = alias->next_alias;
461                 is_first = 0;
462             }
463             entry = entry->next;
464             ipc_sendf("\n");
465         }
466     }
467     ipc_sendf("\n");
468 }
469
470
471 static void  send_info(int neighonly)
472 {
473     /* Print minimal http header */
474     ipc_sendf("HTTP/1.0 200 OK\n");
475     ipc_sendf("Content-type: text/plain\n\n");
476
477     /* Print tables to IPC socket */
478         
479     /* links + Neighbors */
480     ipc_print_neigh_link();
481         
482     /* topology */
483     if (!neighonly) ipc_print_topology();
484         
485     /* hna */
486     if (!neighonly) ipc_print_hna();
487
488     /* mid */
489     if (!neighonly) ipc_print_mid();
490
491     /* routes */
492     if (!neighonly) ipc_print_routes();
493 }
494
495 /*
496  * In a bigger mesh, there are probs with the fixed
497  * bufsize. Because the Content-Length header is
498  * optional, the sprintf() is changed to a more
499  * scalable solution here.
500  */
501  
502 static int  ipc_sendf(const char* format, ...)
503 {
504     char txtnetbuf[TXT_IPC_BUFSIZE];
505
506     va_list arg;
507     int rv;
508 #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
509     int flags = 0;
510 #else
511     int flags = MSG_NOSIGNAL;
512 #endif
513     va_start(arg, format);
514     rv = vsnprintf(txtnetbuf, sizeof(txtnetbuf), format, arg);
515     va_end(arg);
516     if(ipc_socket_up) {
517         if (0 > send(ipc_connection, txtnetbuf, rv, flags)) {
518 #ifndef NODEBUG
519             olsr_printf(1, "(TXTINFO) Failed sending data to client!\n");
520 #endif
521             close(ipc_connection);
522             return - 1;
523         }
524     }
525     return rv;
526 }
527
528 /*
529  * Local Variables:
530  * mode: c
531  * style: linux
532  * c-basic-offset: 4
533  * indent-tabs-mode: nil
534  * End:
535  */