info: stop evaluating send_what when all information has been processed
[olsrd.git] / lib / info / olsrd_info.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2015
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 #include <arpa/inet.h>
43 #include <unistd.h>
44 #include <assert.h>
45 #include <ctype.h>
46 #include <stdbool.h>
47
48 #include "olsrd_info.h"
49 #include "olsr.h"
50 #include "scheduler.h"
51 #include "ipcalc.h"
52 #include "http_headers.h"
53
54 #ifdef _WIN32
55 #define close(x) closesocket(x)
56 #endif /* _WIN32 */
57
58 #define MAX_CLIENTS 3
59
60 /*
61  * There is the problem that writing to a network socket can block,
62  * and the olsrd scheduler does not care about write events.
63  *
64  * There was a case that olsrd just froze for minutes when people used
65  * jsoninfo/txtinfo with large topologies over bad WiFi.
66  *
67  * This is the solution that was chosen at that time, unwilling to
68  * rewrite the whole scheduler:
69  * A timer was added and each time it expires each non-empty buffer
70  * in this structure will try to write data into a "non-blocking"
71  * socket until all data is sent, so that no blocking occurs.
72  */
73 typedef struct {
74   int socket[MAX_CLIENTS];
75   char *buffer[MAX_CLIENTS];
76   size_t size[MAX_CLIENTS];
77   size_t written[MAX_CLIENTS];
78   int count;
79 } info_plugin_outbuffer_t;
80
81 static char sink_buffer[AUTOBUFCHUNK];
82
83 static const char * name = NULL;
84
85 static info_plugin_functions_t *functions = NULL;
86
87 static info_plugin_config_t *config = NULL;
88
89 static int ipc_socket = -1;
90
91 static info_plugin_outbuffer_t outbuffer;
92
93 static struct timer_entry *writetimer_entry = NULL;
94
95 static struct info_cache_t info_cache;
96
97 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
98
99 static char * skipMultipleSlashes(char * requ) {
100   char * r = requ;
101
102   if ((r[0] == '\0') // zero length
103       || (r[0] != '/') // does not start with a slash
104       || (r[1] != '/')) // does not have another slash
105   {
106     return r;
107   }
108
109   while (r[1] == '/') {
110     r++;
111   }
112   return r;
113 }
114
115 static unsigned long long SIW_ENTRIES_ALL[] = {
116 //
117     SIW_NEIGHBORS,//
118     SIW_LINKS, //
119     SIW_ROUTES, //
120     SIW_HNA, //
121     SIW_MID, //
122     SIW_TOPOLOGY, //
123     SIW_GATEWAYS, //
124     SIW_INTERFACES, //
125     SIW_2HOP, //
126     SIW_SGW, //
127     SIW_RUNTIME_ALL,//
128     SIW_NEIGHBORS_FREIFUNK, //
129     //
130     SIW_VERSION,//
131     SIW_CONFIG, //
132     SIW_PLUGINS, //
133     SIW_STARTUP_ALL, //
134     //
135     SIW_ALL, //
136     //
137     SIW_OLSRD_CONF //
138     };
139
140 long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw) {
141   long timeout = plugin_config->cache_timeout;
142   if (timeout <= 0) {
143     return timeout;
144   }
145
146   switch (siw) {
147     case SIW_NEIGHBORS:
148     case SIW_LINKS:
149     case SIW_ROUTES:
150     case SIW_HNA:
151     case SIW_MID:
152     case SIW_TOPOLOGY:
153     case SIW_GATEWAYS:
154     case SIW_INTERFACES:
155     case SIW_2HOP:
156     case SIW_SGW:
157       return timeout;
158
159     case SIW_VERSION:
160     case SIW_CONFIG:
161     case SIW_PLUGINS:
162       return LONG_MAX;
163
164     default:
165       /* not cached */
166       return false;
167   }
168 }
169
170 static void info_plugin_cache_init(bool init) {
171   unsigned int i;
172
173   if (!functions->cache_timeout) {
174     return;
175   }
176
177   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
178     unsigned long long siw = SIW_ENTRIES_ALL[i];
179     struct info_cache_entry_t * entry;
180
181     if (functions->cache_timeout(config, siw) <= 0) {
182       continue;
183     }
184
185     entry = info_cache_get_entry(&info_cache, siw);
186     if (!entry) {
187       continue;
188     }
189
190     if (init) {
191       entry->initialised = false;
192     } else {
193       if (entry->initialised) {
194         abuf_free(&entry->buf);
195         entry->initialised = false;
196       }
197       entry->timestamp = 0;
198     }
199   }
200 }
201
202 static INLINE void info_plugin_cache_init_entry(struct info_cache_entry_t * entry) {
203   if (!entry->initialised) {
204     entry->timestamp = 0;
205     abuf_init(&entry->buf, AUTOBUFCHUNK);
206     entry->initialised = true;
207   }
208 }
209
210 static unsigned int determine_single_action(char *requ) {
211   unsigned int i;
212
213   if (!functions->is_command)
214     return 0;
215
216   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
217     unsigned long long siw = SIW_ENTRIES_ALL[i];
218     if (functions->is_command(requ, siw))
219       return siw;
220   }
221
222   return 0;
223 }
224
225 static unsigned int determine_action(char *requ) {
226   if (!functions->is_command) {
227     return 0;
228   }
229
230   if (!requ || (requ[0] == '\0')) {
231     /* no more text */
232     return 0;
233   }
234
235   /* requ is guaranteed to be at least 1 character long */
236
237   if (!functions->supportsCompositeCommands) {
238     /* no support for composite commands */
239     return determine_single_action(requ);
240   }
241
242   /* composite commands */
243
244   {
245     unsigned int action = 0;
246
247     char * requestSegment = requ;
248     while (requestSegment) {
249       requestSegment = skipMultipleSlashes(requestSegment);
250       if (requestSegment[0] == '\0') {
251         /* there is no more text */
252         requestSegment = NULL;
253         continue;
254       }
255
256       /* there is more text */
257
258       {
259         unsigned int r = 0;
260         char * requestSegmentTail = strchr(&requestSegment[1], '/');
261
262         if (!requestSegmentTail) {
263           /* there isn't another slash, process everything that is left */
264           r = determine_single_action(requestSegment);
265         } else {
266           /* there is another slash, process everything before the slash */
267           char savedCharacter = *requestSegmentTail;
268           *requestSegmentTail = '\0';
269           r = determine_single_action(requestSegment);
270           *requestSegmentTail = savedCharacter;
271         }
272
273         if (!r) {
274           /* not found */
275           return 0;
276         }
277
278         action |= r;
279
280         /* process everything that is left in the next iteration */
281         requestSegment = requestSegmentTail;
282       }
283     }
284
285     return action;
286   }
287 }
288
289 static void write_data(void *fullyWritten) {
290   fd_set set;
291   int result, i, max;
292   struct timeval tv;
293
294   if (outbuffer.count <= 0) {
295     /* exit early if there is nothing to send */
296     return;
297   }
298
299   FD_ZERO(&set);
300   max = 0;
301   for (i = 0; i < outbuffer.count; i++) {
302     if (outbuffer.socket[i] < 0) {
303       continue;
304     }
305
306     /* And we cast here since we get a warning on Win32 */
307     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
308
309     if (outbuffer.socket[i] > max) {
310       max = outbuffer.socket[i];
311     }
312   }
313
314   tv.tv_sec = 0;
315   tv.tv_usec = 0;
316
317   result = select(max + 1, NULL, &set, NULL, &tv);
318   if (result <= 0) {
319     /* exit early if any of the sockets is not ready for writing */
320     return;
321   }
322
323   for (i = 0; i < MAX_CLIENTS; i++) {
324     if (outbuffer.socket[i] < 0) {
325       continue;
326     }
327
328     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
329     if (result > 0) {
330       outbuffer.written[i] += result;
331     }
332
333     if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
334       continue;
335     }
336
337     if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
338       /* close this socket and cleanup*/
339       close(outbuffer.socket[i]);
340       outbuffer.socket[i] = -1;
341       free(outbuffer.buffer[i]);
342       outbuffer.buffer[i] = NULL;
343       outbuffer.size[i] = 0;
344       outbuffer.written[i] = 0;
345
346       outbuffer.count--;
347       if (fullyWritten) {
348         bool * p = fullyWritten;
349         *p = true;
350       }
351     }
352   }
353
354   if (!outbuffer.count) {
355     olsr_stop_timer(writetimer_entry);
356   }
357 }
358
359 typedef struct {
360   unsigned long long siw;
361   printer_generic func;
362 } SiwLookupTableEntry;
363
364 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
365   unsigned int i;
366   unsigned int preLength;
367   unsigned int what = send_what;
368   cache_timeout_func cache_timeout_f = functions->cache_timeout;
369
370   if (functions->output_start) {
371     functions->output_start(abuf);
372   }
373
374   preLength = abuf->len;
375
376   for (i = 0; (i < funcsSize) && what; i++) {
377     unsigned long long siw = funcs[i].siw;
378     if (what & siw) {
379       printer_generic func = funcs[i].func;
380       if (func) {
381         long cache_timeout = 0;
382         struct info_cache_entry_t *cache_entry = NULL;
383
384         if (cache_timeout_f) {
385           cache_timeout = cache_timeout_f(config, siw);
386           cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
387         }
388
389         if (!cache_entry) {
390             func(abuf);
391         } else {
392           long long now = olsr_times();
393           long long age = abs(now - cache_entry->timestamp);
394           info_plugin_cache_init_entry(cache_entry);
395           if (!cache_entry->timestamp || (age >= cache_timeout)) {
396             /* cache is never used before or cache is too old */
397             cache_entry->buf.buf[0] = '\0';
398             cache_entry->buf.len = 0;
399             cache_entry->timestamp = now;
400             func(&cache_entry->buf);
401           }
402
403           abuf_concat(abuf, &cache_entry->buf);
404         }
405       }
406     }
407     what &= ~siw;
408   }
409
410   *outputLength = abuf->len - preLength;
411
412   if (functions->output_end) {
413     functions->output_end(abuf);
414   }
415 }
416
417 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
418   struct autobuf abuf;
419   unsigned int outputLength = 0;
420
421   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
422   int contentLengthIndex = 0;
423   int headerLength = 0;
424
425   abuf_init(&abuf, 2 * AUTOBUFCHUNK);
426
427   if (config->http_headers) {
428     http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
429     headerLength = abuf.len;
430   }
431
432   if (status == INFO_HTTP_OK) {
433     /* OK */
434
435     // only add if normal format
436     if (send_what & SIW_ALL) {
437       SiwLookupTableEntry funcs[] = {
438         { SIW_NEIGHBORS , functions->neighbors  }, //
439         { SIW_LINKS     , functions->links      }, //
440         { SIW_ROUTES    , functions->routes     }, //
441         { SIW_HNA       , functions->hna        }, //
442         { SIW_MID       , functions->mid        }, //
443         { SIW_TOPOLOGY  , functions->topology   }, //
444         { SIW_GATEWAYS  , functions->gateways   }, //
445         { SIW_INTERFACES, functions->interfaces }, //
446         { SIW_2HOP      , functions->twohop     }, //
447         { SIW_SGW       , functions->sgw        }, //
448         //
449         { SIW_VERSION   , functions->version    }, //
450         { SIW_CONFIG    , functions->config     }, //
451         { SIW_PLUGINS   , functions->plugins    } //
452       };
453
454       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
455     } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
456       /* this outputs the olsrd.conf text directly, not normal format */
457       unsigned int preLength = abuf.len;
458       functions->olsrd_conf(&abuf);
459       outputLength = abuf.len - preLength;
460     }
461
462     if (!abuf.len || !outputLength) {
463       status = INFO_HTTP_NOCONTENT;
464       abuf.buf[0] = '\0';
465       abuf.len = 0;
466       if (config->http_headers) {
467         http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
468         headerLength = abuf.len;
469       }
470     }
471   }
472
473   if (status != INFO_HTTP_OK) {
474     if (functions->output_error) {
475       functions->output_error(&abuf, status, req, config->http_headers);
476     } else if (status == INFO_HTTP_NOCONTENT) {
477       /* wget can't handle output of zero length */
478       abuf_puts(&abuf, "\n");
479     }
480   }
481
482   if (config->http_headers) {
483     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
484   }
485
486   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
487   outbuffer.buffer[outbuffer.count] = abuf.buf;
488   outbuffer.size[outbuffer.count] = abuf.len;
489   outbuffer.written[outbuffer.count] = 0;
490   outbuffer.socket[outbuffer.count] = the_socket;
491   abuf.buf = NULL;
492   abuf.len = 0;
493   abuf.size = 0;
494
495   outbuffer.count++;
496
497   {
498     bool fullyWritten = false;
499     write_data(&fullyWritten);
500
501     if (!fullyWritten && (outbuffer.count == 1)) {
502       writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
503     }
504   }
505
506   abuf_free(&abuf);
507 }
508
509 static char * skipLeadingWhitespace(char * requ, size_t *len) {
510   while (isspace(*requ) && (*requ != '\0')) {
511     *len = *len - 1;
512     requ++;
513   }
514   return requ;
515 }
516
517 static char * stripEOLs(char * requ, size_t *len) {
518   while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
519     *len = *len - 1;
520     requ[*len] = '\0';
521   }
522   return requ;
523 }
524
525 static char * stripTrailingSlashes(char * requ, size_t *len) {
526   while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
527     *len = *len - 1;
528     requ[*len] = '\0';
529   }
530   return requ;
531 }
532
533 static char * cutAtFirstEOL(char * requ, size_t *len) {
534   char * s = requ;
535   size_t l = 0;
536   while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
537     s++;
538     l++;
539   }
540   if ((*s == '\n') || (*s == '\r')) {
541     *s = '\0';
542   }
543   *len = l;
544   return requ;
545 }
546
547 static char * parseRequest(char * requ, size_t *len) {
548   char * req = requ;
549
550   if (!req || !*len) {
551     return requ;
552   }
553
554   req = skipLeadingWhitespace(req, len);
555   req = stripEOLs(req, len);
556
557   /* HTTP request: GET whitespace URI whitespace HTTP/1.1 */
558   if (*len < (3 + 1 + 1 + 1 + 8)) {
559     return req;
560   }
561
562   if (strncasecmp(req, "GET", 3) || !isspace(req[3])) {
563     /* does not start with 'GET ' */
564     return req;
565   }
566
567   /* skip 'GET ' and further leading whitespace */
568   req = skipLeadingWhitespace(&req[4], len);
569   if (!*len) return req;
570
571   /* cut req at the first '\n' */
572   req = cutAtFirstEOL(req, len);
573   if (!*len) return req;
574
575   /* strip req of trailing EOL and whitespace */
576   req = stripEOLs(req, len);
577   if (*len < 9) return req;
578
579   if (!isspace(req[*len - 9]) //
580       || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
581       || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
582     return req;
583   }
584   *len = *len - 8;
585   req[*len] = '\0';
586   if (!*len) return req;
587
588   /* strip req of trailing EOL and whitespace */
589   req = stripEOLs(req, len);
590   req = stripTrailingSlashes(req, len);
591
592   return req;
593 }
594
595 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
596 #ifndef NODEBUG
597   char addr[INET6_ADDRSTRLEN];
598 #endif /* NODEBUG */
599
600   char * req = NULL;
601   unsigned int http_status = INFO_HTTP_OK;
602   union olsr_sockaddr sock_addr;
603   socklen_t sock_addr_len = sizeof(sock_addr);
604   fd_set rfds;
605   struct timeval tv;
606   unsigned int send_what = 0;
607   int ipc_connection = -1;
608
609   if (outbuffer.count >= MAX_CLIENTS) {
610     return;
611   }
612
613   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) == -1) {
614 #ifndef NODEBUG
615     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
616 #endif /* NODEBUG */
617     return;
618   }
619
620 #ifndef NODEBUG
621   if (!inet_ntop( //
622       olsr_cnf->ip_version, //
623       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
624       addr, //
625       sizeof(addr))) {
626     addr[0] = '\0';
627   }
628 #endif /* NODEBUG */
629
630   tv.tv_sec = tv.tv_usec = 0;
631   if (olsr_cnf->ip_version == AF_INET) {
632     if (!ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) && (config->accept_ip.v4.s_addr != INADDR_ANY) //
633         && (!config->allow_localhost || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK))) {
634 #ifndef NODEBUG
635       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
636 #endif /* NODEBUG */
637       close(ipc_connection);
638       return;
639     }
640   } else {
641     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
642     if (!ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) && !ip6equal(&config->accept_ip.v6, &in6addr_any)) {
643 #ifndef NODEBUG
644       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
645 #endif /* NODEBUG */
646       close(ipc_connection);
647       return;
648     }
649   }
650
651 #ifndef NODEBUG
652   olsr_printf(2, "(%s) Connect from %s\n", name, addr);
653 #endif /* NODEBUG */
654
655   /* purge read buffer to prevent blocking on linux */
656   FD_ZERO(&rfds);
657   FD_SET((unsigned int ) ipc_connection, &rfds); /* Win32 needs the cast here */
658   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
659     char requ[1024];
660     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
661
662     if (s >= (ssize_t) (sizeof(requ) - 1)) {
663       /* input was much too long, just skip the rest */
664       while (recv(ipc_connection, (void *) &sink_buffer, sizeof(sink_buffer), 0) == sizeof(sink_buffer))
665         ;
666       s = -1;
667     }
668
669     if (0 <= s) {
670       req = requ;
671       req[s] = '\0';
672       req = parseRequest(req, (size_t*)&s);
673       req = skipMultipleSlashes(req);
674       if ((req[0] == '\0') || ((req[0] == '/') && (req[1] == '\0'))) {
675         /* empty or '/' */
676         send_what = SIW_EVERYTHING;
677       } else {
678         send_what = determine_action(req);
679       }
680     }
681
682     if (!send_what) {
683       http_status = INFO_HTTP_NOTFOUND;
684     }
685   }
686
687   send_info(req ? req : "", send_what, ipc_connection, http_status);
688 }
689
690 static int plugin_ipc_init(void) {
691   union olsr_sockaddr sock_addr;
692   uint32_t yes = 1;
693   socklen_t sock_addr_len;
694
695   /* Init ipc socket */
696   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
697 #ifndef NODEBUG
698     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
699 #endif /* NODEBUG */
700     goto error_out;
701   }
702
703   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
704 #ifndef NODEBUG
705     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
706 #endif /* NODEBUG */
707     goto error_out;
708   }
709
710 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
711   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
712     perror("SO_NOSIGPIPE failed");
713     goto error_out;
714   }
715 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
716
717 #if defined __linux__ && defined IPV6_V6ONLY
718   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
719       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
720     perror("IPV6_V6ONLY failed");
721     goto error_out;
722   }
723 #endif /* defined __linux__ && defined IPV6_V6ONLY */
724
725   /* complete the socket structure */
726   memset(&sock_addr, 0, sizeof(sock_addr));
727   if (olsr_cnf->ip_version == AF_INET) {
728     sock_addr.in4.sin_family = AF_INET;
729     sock_addr_len = sizeof(struct sockaddr_in);
730 #ifdef SIN6_LEN
731     sock_addr.in4.sin_len = sock_addr_len;
732 #endif /* SIN6_LEN */
733     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
734     sock_addr.in4.sin_port = htons(config->ipc_port);
735   } else {
736     sock_addr.in6.sin6_family = AF_INET6;
737     sock_addr_len = sizeof(struct sockaddr_in6);
738 #ifdef SIN6_LEN
739     sock_addr.in6.sin6_len = sock_addr_len;
740 #endif /* SIN6_LEN */
741     sock_addr.in6.sin6_addr = config->listen_ip.v6;
742     sock_addr.in6.sin6_port = htons(config->ipc_port);
743   }
744
745   /* bind the socket to the port number */
746   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
747 #ifndef NODEBUG
748     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
749 #endif /* NODEBUG */
750     goto error_out;
751   }
752
753   /* show that we are willing to listen */
754   if (listen(ipc_socket, 1) == -1) {
755 #ifndef NODEBUG
756     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
757 #endif /* NODEBUG */
758     goto error_out;
759   }
760
761   /* Register with olsrd */
762   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
763
764 #ifndef NODEBUG
765   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
766 #endif /* NODEBUG */
767
768   return 1;
769
770   error_out: //
771   if (ipc_socket >= 0) {
772     close(ipc_socket);
773     ipc_socket = -1;
774   }
775   return 0;
776 }
777
778 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
779   int i;
780
781   assert(plugin_name);
782   assert(plugin_functions);
783   assert(plugin_config);
784
785   name = plugin_name;
786   functions = plugin_functions;
787   config = plugin_config;
788
789   memset(&outbuffer, 0, sizeof(outbuffer));
790   for (i = 0; i < MAX_CLIENTS; ++i) {
791     outbuffer.socket[i] = -1;
792   }
793
794   ipc_socket = -1;
795
796   if (functions->init) {
797     functions->init(name);
798   }
799
800   info_plugin_cache_init(true);
801
802   plugin_ipc_init();
803   return 1;
804 }
805
806 void info_plugin_exit(void) {
807   int i;
808
809   if (ipc_socket != -1) {
810     close(ipc_socket);
811     ipc_socket = -1;
812   }
813   for (i = 0; i < MAX_CLIENTS; ++i) {
814     if (outbuffer.buffer[i]) {
815       free(outbuffer.buffer[i]);
816       outbuffer.buffer[i] = NULL;
817     }
818     outbuffer.size[i] = 0;
819     outbuffer.written[i] = 0;
820     if (outbuffer.socket[i]) {
821       close(outbuffer.socket[i]);
822       outbuffer.socket[i] = -1;
823     }
824   }
825   outbuffer.count = 0;
826
827   info_plugin_cache_init(false);
828 }