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