txtinfo: move ipc_print_* functions into a 'printers' unit
[olsrd.git] / lib / txtinfo / src / olsrd_txtinfo.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004
4  *
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
46 #include <arpa/inet.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "ipcalc.h"
51 #include "olsr.h"
52 #include "scheduler.h"
53 #include "../../info/http_headers.h"
54 #include "txtinfo_printers.h"
55
56 #include "olsrd_txtinfo.h"
57
58 #ifdef _WIN32
59 #define close(x) closesocket(x)
60 #endif /* _WIN32 */
61
62 /* defines to make txtinfo and jsoninfo look alike */
63 #define PLUGIN_NAME "TXTINFO"
64 #define info_accept_ip txtinfo_accept_ip
65 #define info_listen_ip txtinfo_listen_ip
66 #define info_ipv6_only txtinfo_ipv6_only
67 #ifdef TXTINFO_ALLOW_LOCALHOST
68 #define INFO_ALLOW_LOCALHOST TXTINFO_ALLOW_LOCALHOST
69 #endif
70
71 static int ipc_socket;
72
73 /* IPC initialization function */
74 static int plugin_ipc_init(void);
75
76 static void send_info(unsigned int /*send_what*/, int /*socket*/);
77
78 static void ipc_action(int, void *, unsigned int);
79
80 #define TXT_IPC_BUFSIZE 256
81
82 /* these provide all of the runtime status info */
83 #define SIW_NEIGHBORS 0x0001
84 #define SIW_LINKS 0x0002
85 #define SIW_ROUTES 0x0004
86 #define SIW_HNA 0x0008
87 #define SIW_MID 0x0010
88 #define SIW_TOPOLOGY 0x0020
89 #define SIW_GATEWAYS 0x0040
90 #define SIW_INTERFACES 0x0080
91 #define SIW_2HOP 0x0100
92 #define SIW_SGW 0x0200
93 #define SIW_RUNTIME_ALL (SIW_NEIGHBORS | SIW_LINKS | SIW_ROUTES | SIW_HNA | SIW_MID | SIW_TOPOLOGY | SIW_GATEWAYS | SIW_INTERFACES | SIW_2HOP | SIW_SGW)
94
95 /* these only change at olsrd startup */
96 #define SIW_VERSION 0x0400
97 #define SIW_CONFIG 0x0800
98 #define SIW_PLUGINS 0x1000
99 #define SIW_STARTUP_ALL (SIW_VERSION | SIW_CONFIG | SIW_PLUGINS)
100
101 /* this is everything in normal format */
102 #define SIW_ALL (SIW_RUNTIME_ALL | SIW_STARTUP_ALL)
103
104 /* this data is not normal format but olsrd.conf format */
105 #define SIW_OLSRD_CONF 0x2000
106
107 #define MAX_CLIENTS 3
108
109 static char *outbuffer[MAX_CLIENTS];
110 static size_t outbuffer_size[MAX_CLIENTS];
111 static size_t outbuffer_written[MAX_CLIENTS];
112 static int outbuffer_socket[MAX_CLIENTS];
113 static int outbuffer_count = 0;
114
115 static struct timer_entry *writetimer_entry;
116
117 static void determine_action(unsigned int *send_what, char *requ) {
118   if (strstr(requ, "/con"))
119     *send_what |= SIW_OLSRD_CONF;
120   else if (strstr(requ, "/all"))
121     *send_what = SIW_ALL;
122   else {
123     // these are the two overarching categories
124     if (strstr(requ, "/runtime"))
125       *send_what |= SIW_RUNTIME_ALL;
126     if (strstr(requ, "/startup"))
127       *send_what |= SIW_STARTUP_ALL;
128
129     // these are the individual sections
130     if (strstr(requ, "/nei"))
131       *send_what |= SIW_NEIGHBORS;
132     if (strstr(requ, "/lin"))
133       *send_what |= SIW_LINKS;
134     if (strstr(requ, "/rou"))
135       *send_what |= SIW_ROUTES;
136     if (strstr(requ, "/hna"))
137       *send_what |= SIW_HNA;
138     if (strstr(requ, "/mid"))
139       *send_what |= SIW_MID;
140     if (strstr(requ, "/top"))
141       *send_what |= SIW_TOPOLOGY;
142     if (strstr(requ, "/gat"))
143       *send_what |= SIW_GATEWAYS;
144     if (strstr(requ, "/int"))
145       *send_what |= SIW_INTERFACES;
146     if (strstr(requ, "/2ho"))
147       *send_what |= SIW_2HOP;
148     if (strstr(requ, "/sgw"))
149       *send_what |= SIW_SGW;
150
151     // specials
152     if (strstr(requ, "/ver"))
153       *send_what |= SIW_VERSION;
154     if (strstr(requ, "/config"))
155       *send_what |= SIW_CONFIG;
156     if (strstr(requ, "/plugins"))
157       *send_what |= SIW_PLUGINS;
158
159     /* To print out neighbours only on the Freifunk Status
160      * page the normal output is somewhat lengthy. The
161      * header parsing is sufficient for standard wget.
162      */
163     if (strstr(requ, "/neighbours"))
164       *send_what = SIW_NEIGHBORS | SIW_LINKS;
165   }
166 }
167
168 static void plugin_init(void) {
169   /* nothing to do */
170 }
171
172 /**
173  *Do initialization here
174  *
175  *This function is called by the my_init
176  *function in uolsrd_plugin.c
177  */
178 int olsrd_plugin_init(void) {
179   /* Initial IPC value */
180   ipc_socket = -1;
181
182   plugin_init();
183
184   plugin_ipc_init();
185   return 1;
186 }
187
188 /**
189  * destructor - called at unload
190  */
191 void olsr_plugin_exit(void) {
192   if (ipc_socket != -1)
193     close(ipc_socket);
194 }
195
196 static int plugin_ipc_init(void) {
197   union olsr_sockaddr sst;
198   uint32_t yes = 1;
199   socklen_t addrlen;
200
201   /* Init ipc socket */
202   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
203 #ifndef NODEBUG
204     olsr_printf(1, "("PLUGIN_NAME") socket()=%s\n", strerror(errno));
205 #endif /* NODEBUG */
206     return 0;
207   } else {
208     if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
209 #ifndef NODEBUG
210       olsr_printf(1, "("PLUGIN_NAME") setsockopt()=%s\n", strerror(errno));
211 #endif /* NODEBUG */
212       return 0;
213     }
214 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
215     if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
216       perror("SO_REUSEADDR failed");
217       return 0;
218     }
219 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
220 #if defined linux && defined IPV6_V6ONLY
221     if (info_ipv6_only && olsr_cnf->ip_version == AF_INET6) {
222       if (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0) {
223         perror("IPV6_V6ONLY failed");
224         return 0;
225       }
226     }
227 #endif /* defined linux && defined IPV6_V6ONLY */
228     /* Bind the socket */
229
230     /* complete the socket structure */
231     memset(&sst, 0, sizeof(sst));
232     if (olsr_cnf->ip_version == AF_INET) {
233       sst.in4.sin_family = AF_INET;
234       addrlen = sizeof(struct sockaddr_in);
235 #ifdef SIN6_LEN
236       sst.in4.sin_len = addrlen;
237 #endif /* SIN6_LEN */
238       sst.in4.sin_addr.s_addr = info_listen_ip.v4.s_addr;
239       sst.in4.sin_port = htons(ipc_port);
240     } else {
241       sst.in6.sin6_family = AF_INET6;
242       addrlen = sizeof(struct sockaddr_in6);
243 #ifdef SIN6_LEN
244       sst.in6.sin6_len = addrlen;
245 #endif /* SIN6_LEN */
246       sst.in6.sin6_addr = info_listen_ip.v6;
247       sst.in6.sin6_port = htons(ipc_port);
248     }
249
250     /* bind the socket to the port number */
251     if (bind(ipc_socket, &sst.in, addrlen) == -1) {
252 #ifndef NODEBUG
253       olsr_printf(1, "("PLUGIN_NAME") bind()=%s\n", strerror(errno));
254 #endif /* NODEBUG */
255       return 0;
256     }
257
258     /* show that we are willing to listen */
259     if (listen(ipc_socket, 1) == -1) {
260 #ifndef NODEBUG
261       olsr_printf(1, "("PLUGIN_NAME") listen()=%s\n", strerror(errno));
262 #endif /* NODEBUG */
263       return 0;
264     }
265
266     /* Register with olsrd */
267     add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
268
269 #ifndef NODEBUG
270     olsr_printf(2, "("PLUGIN_NAME") listening on port %d\n", ipc_port);
271 #endif /* NODEBUG */
272   }
273   return 1;
274 }
275
276 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
277   union olsr_sockaddr pin;
278
279   char addr[INET6_ADDRSTRLEN];
280   fd_set rfds;
281   struct timeval tv;
282   unsigned int send_what = 0;
283   int ipc_connection;
284
285   socklen_t addrlen = sizeof(pin);
286
287   if (outbuffer_count >= MAX_CLIENTS) {
288     return;
289   }
290
291   if ((ipc_connection = accept(fd, &pin.in, &addrlen)) == -1) {
292 #ifndef NODEBUG
293     olsr_printf(1, "("PLUGIN_NAME") accept()=%s\n", strerror(errno));
294 #endif /* NODEBUG */
295     return;
296   }
297
298   tv.tv_sec = tv.tv_usec = 0;
299   if (olsr_cnf->ip_version == AF_INET) {
300     if (inet_ntop(olsr_cnf->ip_version, &pin.in4.sin_addr, addr, INET6_ADDRSTRLEN) == NULL)
301       addr[0] = '\0';
302     if (!ip4equal(&pin.in4.sin_addr, &info_accept_ip.v4) && info_accept_ip.v4.s_addr != INADDR_ANY) {
303 #ifdef INFO_ALLOW_LOCALHOST
304       if (ntohl(pin.in4.sin_addr.s_addr) != INADDR_LOOPBACK) {
305 #endif /* INFO_ALLOW_LOCALHOST */
306         olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
307         close(ipc_connection);
308         return;
309 #ifdef INFO_ALLOW_LOCALHOST
310       }
311 #endif /* INFO_ALLOW_LOCALHOST */
312     }
313   } else {
314     if (inet_ntop(olsr_cnf->ip_version, &pin.in6.sin6_addr, addr, INET6_ADDRSTRLEN) == NULL)
315       addr[0] = '\0';
316     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
317     if (!ip6equal(&in6addr_any, &info_accept_ip.v6) && !ip6equal(&pin.in6.sin6_addr, &info_accept_ip.v6)) {
318       olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
319       close(ipc_connection);
320       return;
321     }
322   }
323
324 #ifndef NODEBUG
325   olsr_printf(2, "("PLUGIN_NAME") Connect from %s\n", addr);
326 #endif /* NODEBUG */
327
328   /* purge read buffer to prevent blocking on linux */
329   FD_ZERO(&rfds);
330   FD_SET((unsigned int )ipc_connection, &rfds); /* Win32 needs the cast here */
331   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
332     char requ[1024];
333     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
334
335     if (s == sizeof(requ) - 1) {
336       /* input was much too long, just skip the rest */
337       char dummy[1024];
338
339       while (recv(ipc_connection, (void *) &dummy, sizeof(dummy), 0) == sizeof(dummy))
340         ;
341     }
342
343     if (0 < s) {
344       requ[s] = 0;
345       determine_action(&send_what, requ);
346     }
347
348     if (!send_what)
349       send_what = SIW_ALL;
350   }
351
352   send_info(send_what, ipc_connection);
353 }
354
355 static void info_write_data(void *foo __attribute__ ((unused))) {
356   fd_set set;
357   int result, i, j, max;
358   struct timeval tv;
359
360   FD_ZERO(&set);
361   max = 0;
362   for (i = 0; i < outbuffer_count; i++) {
363     /* And we cast here since we get a warning on Win32 */
364     FD_SET((unsigned int )(outbuffer_socket[i]), &set);
365
366     if (outbuffer_socket[i] > max) {
367       max = outbuffer_socket[i];
368     }
369   }
370
371   tv.tv_sec = 0;
372   tv.tv_usec = 0;
373
374   result = select(max + 1, NULL, &set, NULL, &tv);
375   if (result <= 0) {
376     return;
377   }
378
379   for (i = 0; i < outbuffer_count; i++) {
380     if (FD_ISSET(outbuffer_socket[i], &set)) {
381       result = send(outbuffer_socket[i], outbuffer[i] + outbuffer_written[i], outbuffer_size[i] - outbuffer_written[i], 0);
382       if (result > 0) {
383         outbuffer_written[i] += result;
384       }
385
386       if (result <= 0 || outbuffer_written[i] == outbuffer_size[i]) {
387         /* close this socket and cleanup*/
388         close(outbuffer_socket[i]);
389         free(outbuffer[i]);
390
391         for (j = i + 1; j < outbuffer_count; j++) {
392           outbuffer[j - 1] = outbuffer[j];
393           outbuffer_size[j - 1] = outbuffer_size[j];
394           outbuffer_socket[j - 1] = outbuffer_socket[j];
395           outbuffer_written[j - 1] = outbuffer_written[j];
396         }
397         outbuffer_count--;
398       }
399     }
400   }
401   if (!outbuffer_count) {
402     olsr_stop_timer(writetimer_entry);
403   }
404 }
405
406 static void send_info(unsigned int send_what, int the_socket) {
407   struct autobuf abuf;
408
409   const char *content_type = "text/plain; charset=utf-8";
410   int contentLengthPlaceholderStart = 0;
411   int headerLength = 0;
412
413   abuf_init(&abuf, 2 * 4096);
414
415   if (http_headers) {
416     build_http_header(PLUGIN_NAME, HTTP_200, content_type, &abuf, &contentLengthPlaceholderStart);
417     headerLength = abuf.len;
418   }
419
420   // only add if normal format
421   if (send_what & SIW_ALL) {
422     if (send_what & SIW_LINKS)
423       ipc_print_links(&abuf);
424     if (send_what & SIW_NEIGHBORS)
425       ipc_print_neighbors(&abuf, false);
426     if (send_what & SIW_TOPOLOGY)
427       ipc_print_topology(&abuf);
428     if (send_what & SIW_HNA)
429       ipc_print_hna(&abuf);
430     if (send_what & SIW_SGW)
431       ipc_print_sgw(&abuf);
432     if (send_what & SIW_MID)
433       ipc_print_mid(&abuf);
434     if (send_what & SIW_ROUTES)
435       ipc_print_routes(&abuf);
436     if (send_what & SIW_GATEWAYS)
437       ipc_print_gateways(&abuf);
438     if (send_what & SIW_CONFIG) {
439       /* not supported */
440     }
441     if (send_what & SIW_INTERFACES)
442       ipc_print_interfaces(&abuf);
443     if (send_what & SIW_2HOP)
444       ipc_print_neighbors(&abuf, true);
445     if (send_what & SIW_VERSION)
446       ipc_print_version(&abuf);
447     if (send_what & SIW_PLUGINS) {
448       /* not supported */
449     }
450   } else if (send_what & SIW_OLSRD_CONF) {
451     /* this outputs the olsrd.conf text directly, not normal format */
452     ipc_print_olsrd_conf(&abuf);
453   }
454
455   if (http_headers) {
456     http_header_adjust_content_length(&abuf, contentLengthPlaceholderStart, abuf.len - headerLength);
457   }
458
459   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
460   outbuffer[outbuffer_count] = abuf.buf;
461   outbuffer_size[outbuffer_count] = abuf.len;
462   outbuffer_written[outbuffer_count] = 0;
463   outbuffer_socket[outbuffer_count] = the_socket;
464   abuf.buf = NULL;
465   abuf.len = 0;
466   abuf.size = 0;
467
468   outbuffer_count++;
469
470   if (outbuffer_count == 1) {
471     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &info_write_data, NULL, 0);
472   }
473
474   abuf_free(&abuf);
475 }
476
477 /*
478  * Local Variables:
479  * mode: c
480  * style: linux
481  * c-basic-offset: 2
482  * indent-tabs-mode: nil
483  * End:
484  */