info: fail info_plugin_init when plugin_ipc_init fails
[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 const char * name = NULL;
86
87 static info_plugin_functions_t *functions = NULL;
88
89 static info_plugin_config_t *config = NULL;
90
91 static int ipc_socket = -1;
92
93 static info_plugin_outbuffer_t outbuffer;
94
95 static struct timer_entry *writetimer_entry = NULL;
96
97 static struct info_cache_t info_cache;
98
99 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
100
101 static char * skipMultipleSlashes(char * requ, size_t* len) {
102   char * r = requ;
103
104   if ((r[0] == '\0') // zero length
105       || (r[0] != '/') // does not start with a slash
106       || (r[1] != '/')) // does not have another slash
107   {
108     return r;
109   }
110
111   while (r[1] == '/') {
112     r++;
113     if (len) {
114       *len = *len - 1;
115     }
116   }
117   return r;
118 }
119
120 static unsigned long long SIW_ENTRIES_ALL[] = {
121 //
122     SIW_NEIGHBORS,//
123     SIW_LINKS, //
124     SIW_ROUTES, //
125     SIW_HNA, //
126     SIW_MID, //
127     SIW_TOPOLOGY, //
128     SIW_GATEWAYS, //
129     SIW_INTERFACES, //
130     SIW_2HOP, //
131     SIW_SGW, //
132     SIW_RUNTIME_ALL,//
133     SIW_NEIGHBORS_FREIFUNK, //
134     //
135     SIW_VERSION,//
136     SIW_CONFIG, //
137     SIW_PLUGINS, //
138     SIW_STARTUP_ALL, //
139     //
140     SIW_ALL, //
141     //
142     SIW_OLSRD_CONF, //
143     //
144     SIW_NETJSON_NETWORK_ROUTES,
145     SIW_NETJSON_NETWORK_GRAPH,
146     SIW_NETJSON_DEVICE_CONFIGURATION,
147     SIW_NETJSON_DEVICE_MONITORING,
148     SIW_NETJSON_NETWORK_COLLECTION
149     };
150
151 long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw) {
152   long timeout = plugin_config->cache_timeout;
153   if (timeout <= 0) {
154     return timeout;
155   }
156
157   switch (siw) {
158     case SIW_NEIGHBORS:
159     case SIW_LINKS:
160     case SIW_ROUTES:
161     case SIW_HNA:
162     case SIW_MID:
163     case SIW_TOPOLOGY:
164     case SIW_GATEWAYS:
165     case SIW_INTERFACES:
166     case SIW_2HOP:
167     case SIW_SGW:
168
169     case SIW_NETJSON_NETWORK_ROUTES:
170     case SIW_NETJSON_NETWORK_GRAPH:
171     case SIW_NETJSON_DEVICE_CONFIGURATION:
172     case SIW_NETJSON_DEVICE_MONITORING:
173     case SIW_NETJSON_NETWORK_COLLECTION:
174       return timeout;
175
176     case SIW_VERSION:
177     case SIW_CONFIG:
178     case SIW_PLUGINS:
179       return LONG_MAX;
180
181     default:
182       /* not cached */
183       return false;
184   }
185 }
186
187 static void info_plugin_cache_init(bool init) {
188   unsigned int i;
189
190   if (!functions->cache_timeout) {
191     return;
192   }
193
194   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
195     unsigned long long siw = SIW_ENTRIES_ALL[i];
196     struct info_cache_entry_t * entry;
197
198     if (functions->cache_timeout(config, siw) <= 0) {
199       continue;
200     }
201
202     entry = info_cache_get_entry(&info_cache, siw);
203     if (!entry) {
204       continue;
205     }
206
207     if (init) {
208       entry->initialised = false;
209     } else {
210       if (entry->initialised) {
211         abuf_free(&entry->buf);
212         entry->initialised = false;
213       }
214       entry->timestamp = 0;
215     }
216   }
217 }
218
219 static INLINE void info_plugin_cache_init_entry(struct info_cache_entry_t * entry) {
220   if (!entry->initialised) {
221     entry->timestamp = 0;
222     abuf_init(&entry->buf, AUTOBUFCHUNK);
223     entry->initialised = true;
224   }
225 }
226
227 static unsigned int determine_single_action(char *requ) {
228   unsigned int i;
229   unsigned long long siw_mask = !functions->supported_commands_mask ? SIW_EVERYTHING : functions->supported_commands_mask();
230
231   if (!functions->is_command || !siw_mask)
232     return 0;
233
234   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
235     unsigned long long siw = SIW_ENTRIES_ALL[i];
236     if ((siw & siw_mask) && functions->is_command(requ, siw))
237       return siw;
238   }
239
240   return 0;
241 }
242
243 static unsigned int determine_action(char *requ) {
244   if (!functions->is_command) {
245     return 0;
246   }
247
248   if (!requ || (requ[0] == '\0')) {
249     /* no more text */
250     return 0;
251   }
252
253   /* requ is guaranteed to be at least 1 character long */
254
255   if (!functions->supportsCompositeCommands) {
256     /* no support for composite commands */
257     return determine_single_action(requ);
258   }
259
260   /* composite commands */
261
262   {
263     unsigned int action = 0;
264
265     char * requestSegment = requ;
266     while (requestSegment) {
267       requestSegment = skipMultipleSlashes(requestSegment, NULL);
268       if (requestSegment[0] == '\0') {
269         /* there is no more text */
270         requestSegment = NULL;
271         continue;
272       }
273
274       /* there is more text */
275
276       {
277         unsigned int r = 0;
278         char * requestSegmentTail = strchr(&requestSegment[1], '/');
279
280         if (!requestSegmentTail) {
281           /* there isn't another slash, process everything that is left */
282           r = determine_single_action(requestSegment);
283         } else {
284           /* there is another slash, process everything before the slash */
285           char savedCharacter = *requestSegmentTail;
286           *requestSegmentTail = '\0';
287           r = determine_single_action(requestSegment);
288           *requestSegmentTail = savedCharacter;
289         }
290
291         if (!r) {
292           /* not found */
293           return 0;
294         }
295
296         action |= r;
297
298         /* process everything that is left in the next iteration */
299         requestSegment = requestSegmentTail;
300       }
301     }
302
303     return action;
304   }
305 }
306
307 static void write_data(void *fullyWritten) {
308   fd_set set;
309   int result, i, max;
310   struct timeval tv;
311
312   if (outbuffer.count <= 0) {
313     /* exit early if there is nothing to send */
314     return;
315   }
316
317   FD_ZERO(&set);
318   max = 0;
319   for (i = 0; i < outbuffer.count; i++) {
320     if (outbuffer.socket[i] < 0) {
321       continue;
322     }
323
324     /* And we cast here since we get a warning on Win32 */
325     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
326
327     if (outbuffer.socket[i] > max) {
328       max = outbuffer.socket[i];
329     }
330   }
331
332   tv.tv_sec = 0;
333   tv.tv_usec = 0;
334
335   result = select(max + 1, NULL, &set, NULL, &tv);
336   if (result <= 0) {
337     /* exit early if any of the sockets is not ready for writing */
338     return;
339   }
340
341   for (i = 0; i < MAX_CLIENTS; i++) {
342     if (outbuffer.socket[i] < 0) {
343       continue;
344     }
345
346     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
347     if (result > 0) {
348       outbuffer.written[i] += result;
349     }
350
351     if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
352       continue;
353     }
354
355     if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
356       /* close this socket and cleanup*/
357       close(outbuffer.socket[i]);
358       outbuffer.socket[i] = -1;
359       free(outbuffer.buffer[i]);
360       outbuffer.buffer[i] = NULL;
361       outbuffer.size[i] = 0;
362       outbuffer.written[i] = 0;
363
364       outbuffer.count--;
365       if (fullyWritten) {
366         bool * p = fullyWritten;
367         *p = true;
368       }
369     }
370   }
371
372   if (!outbuffer.count) {
373     olsr_stop_timer(writetimer_entry);
374   }
375 }
376
377 typedef struct {
378   unsigned long long siw;
379   printer_generic func;
380 } SiwLookupTableEntry;
381
382 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
383   unsigned int i;
384   unsigned int preLength;
385   unsigned int what = send_what;
386   cache_timeout_func cache_timeout_f = functions->cache_timeout;
387
388   if (functions->output_start) {
389     functions->output_start(abuf);
390   }
391
392   preLength = abuf->len;
393
394   for (i = 0; (i < funcsSize) && what; i++) {
395     unsigned long long siw = funcs[i].siw;
396     if (what & siw) {
397       printer_generic func = funcs[i].func;
398       if (func) {
399         long cache_timeout = 0;
400         struct info_cache_entry_t *cache_entry = NULL;
401
402         if (cache_timeout_f) {
403           cache_timeout = cache_timeout_f(config, siw);
404           cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
405         }
406
407         if (!cache_entry) {
408             func(abuf);
409         } else {
410           long long now = olsr_times();
411           long long age = abs(now - cache_entry->timestamp);
412           info_plugin_cache_init_entry(cache_entry);
413           if (!cache_entry->timestamp || (age >= cache_timeout)) {
414             /* cache is never used before or cache is too old */
415             cache_entry->buf.buf[0] = '\0';
416             cache_entry->buf.len = 0;
417             cache_entry->timestamp = now;
418             func(&cache_entry->buf);
419           }
420
421           abuf_concat(abuf, &cache_entry->buf);
422         }
423       }
424     }
425     what &= ~siw;
426   }
427
428   *outputLength = abuf->len - preLength;
429
430   if (functions->output_end) {
431     functions->output_end(abuf);
432   }
433 }
434
435 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
436   struct autobuf abuf;
437   unsigned int outputLength = 0;
438
439   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
440   int contentLengthIndex = 0;
441   int headerLength = 0;
442
443   abuf_init(&abuf, AUTOBUFCHUNK);
444
445   if (config->http_headers) {
446     http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
447     headerLength = abuf.len;
448   }
449
450   if (status == INFO_HTTP_OK) {
451     /* OK */
452
453     // only add if normal format
454     if (send_what & SIW_ALL) {
455       SiwLookupTableEntry funcs[] = {
456         { SIW_NEIGHBORS , functions->neighbors  }, //
457         { SIW_LINKS     , functions->links      }, //
458         { SIW_ROUTES    , functions->routes     }, //
459         { SIW_HNA       , functions->hna        }, //
460         { SIW_MID       , functions->mid        }, //
461         { SIW_TOPOLOGY  , functions->topology   }, //
462         { SIW_GATEWAYS  , functions->gateways   }, //
463         { SIW_INTERFACES, functions->interfaces }, //
464         { SIW_2HOP      , functions->twohop     }, //
465         { SIW_SGW       , functions->sgw        }, //
466         //
467         { SIW_VERSION   , functions->version    }, //
468         { SIW_CONFIG    , functions->config     }, //
469         { SIW_PLUGINS   , functions->plugins    } //
470       };
471
472       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
473     } else if (send_what & SIW_NETJSON) {
474       SiwLookupTableEntry funcs[] = {
475         { SIW_NETJSON_NETWORK_ROUTES      , functions->networkRoutes      }, //
476         { SIW_NETJSON_NETWORK_GRAPH       , functions->networkGraph       }, //
477         { SIW_NETJSON_DEVICE_CONFIGURATION, functions->deviceConfiguration}, //
478         { SIW_NETJSON_DEVICE_MONITORING   , functions->deviceMonitoring   }, //
479         { SIW_NETJSON_NETWORK_COLLECTION  , functions->networkCollection  } //
480       };
481
482       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
483     } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
484       /* this outputs the olsrd.conf text directly, not normal format */
485       unsigned int preLength = abuf.len;
486       functions->olsrd_conf(&abuf);
487       outputLength = abuf.len - preLength;
488     }
489
490     if (!abuf.len || !outputLength) {
491       status = INFO_HTTP_NOCONTENT;
492       abuf.buf[0] = '\0';
493       abuf.len = 0;
494       if (config->http_headers) {
495         http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
496         headerLength = abuf.len;
497       }
498     }
499   }
500
501   if (status != INFO_HTTP_OK) {
502     if (functions->output_error) {
503       functions->output_error(&abuf, status, req, config->http_headers);
504     } else if (status == INFO_HTTP_NOCONTENT) {
505       /* wget can't handle output of zero length */
506       abuf_puts(&abuf, "\n");
507     }
508   }
509
510   if (config->http_headers) {
511     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
512   }
513
514   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
515   outbuffer.buffer[outbuffer.count] = abuf.buf;
516   outbuffer.size[outbuffer.count] = abuf.len;
517   outbuffer.written[outbuffer.count] = 0;
518   outbuffer.socket[outbuffer.count] = the_socket;
519   abuf.buf = NULL;
520   abuf.len = 0;
521   abuf.size = 0;
522
523   outbuffer.count++;
524
525   {
526     bool fullyWritten = false;
527     write_data(&fullyWritten);
528
529     if (!fullyWritten && (outbuffer.count == 1)) {
530       writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
531     }
532   }
533
534   abuf_free(&abuf);
535 }
536
537 static char * skipLeadingWhitespace(char * requ, size_t *len) {
538   while (isspace(*requ) && (*requ != '\0')) {
539     *len = *len - 1;
540     requ++;
541   }
542   return requ;
543 }
544
545 static char * stripEOLs(char * requ, size_t *len) {
546   while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
547     *len = *len - 1;
548     requ[*len] = '\0';
549   }
550   return requ;
551 }
552
553 static char * stripTrailingSlashes(char * requ, size_t *len) {
554   while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
555     *len = *len - 1;
556     requ[*len] = '\0';
557   }
558   return requ;
559 }
560
561 static char * cutAtFirstEOL(char * requ, size_t *len) {
562   char * s = requ;
563   size_t l = 0;
564   while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
565     s++;
566     l++;
567   }
568   if ((*s == '\n') || (*s == '\r')) {
569     *s = '\0';
570   }
571   *len = l;
572   return requ;
573 }
574
575 static char * parseRequest(char * requ, size_t *len) {
576   char * req = requ;
577
578   if (!req || !*len) {
579     return requ;
580   }
581
582   req = skipLeadingWhitespace(req, len);
583   req = stripEOLs(req, len);
584
585   /* HTTP request: GET whitespace URI whitespace HTTP/1.1 */
586   if (*len < (3 + 1 + 1 + 1 + 8)) {
587     return req;
588   }
589
590   if (strncasecmp(req, "GET", 3) || !isspace(req[3])) {
591     /* does not start with 'GET ' */
592     return req;
593   }
594
595   /* skip 'GET ' and further leading whitespace */
596   req = skipLeadingWhitespace(&req[4], len);
597   if (!*len) return req;
598
599   /* cut req at the first '\n' */
600   req = cutAtFirstEOL(req, len);
601   if (!*len) return req;
602
603   /* strip req of trailing EOL and whitespace */
604   req = stripEOLs(req, len);
605   if (*len < 9) return req;
606
607   if (!isspace(req[*len - 9]) //
608       || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
609       || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
610     return req;
611   }
612   *len = *len - 8;
613   req[*len] = '\0';
614   if (!*len) return req;
615
616   /* strip req of trailing EOL and whitespace */
617   req = stripEOLs(req, len);
618   req = stripTrailingSlashes(req, len);
619
620   return req;
621 }
622
623 static void drain_request(int ipc_connection) {
624   static char drain_buffer[AUTOBUFCHUNK];
625
626   /* input was much too long: read until the end for graceful connection termination
627    * because wget can't handle the premature connection termination that is allowed
628    * by the INFO_HTTP_REQUEST_ENTITY_TOO_LARGE HTTP status code
629    */
630   while (recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), 0) == sizeof(drain_buffer)) {}
631 }
632
633 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
634 #ifndef NODEBUG
635   char addr[INET6_ADDRSTRLEN];
636 #endif /* NODEBUG */
637
638   int ipc_connection = -1;
639   union olsr_sockaddr sock_addr;
640   socklen_t sock_addr_len = sizeof(sock_addr);
641   bool hostDenied = false;
642   struct timeval timeout;
643   fd_set read_fds;
644   char req_buffer[1024];
645   char * req = req_buffer;
646   ssize_t rx_count = 0;
647   unsigned int send_what = 0;
648   unsigned int http_status = INFO_HTTP_OK;
649
650   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) < 0) {
651 #ifndef NODEBUG
652     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
653 #endif /* NODEBUG */
654     /* the caller will retry later */
655     return;
656   }
657
658   if (outbuffer.count >= MAX_CLIENTS) {
659     /* limit the number of replies that are in-flight */
660     close(ipc_connection);
661     return;
662   }
663
664   if (olsr_cnf->ip_version == AF_INET) {
665     hostDenied = //
666         (ntohl(config->accept_ip.v4.s_addr) != INADDR_ANY) //
667         && !ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) //
668         && (!config->allow_localhost //
669             || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK));
670   } else {
671     hostDenied = //
672         !ip6equal(&config->accept_ip.v6, &in6addr_any) //
673         && !ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) //
674         && (!config->allow_localhost //
675             || !ip6equal(&config->accept_ip.v6, &in6addr_loopback));
676   }
677
678 #ifndef NODEBUG
679   if (!inet_ntop( //
680       olsr_cnf->ip_version, //
681       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
682       addr, //
683       sizeof(addr))) {
684     addr[0] = '\0';
685   }
686 #endif /* NODEBUG */
687
688   if (hostDenied) {
689 #ifndef NODEBUG
690     olsr_printf(1, "(%s) Connect from host %s is not allowed!\n", name, addr);
691 #endif /* NODEBUG */
692     drain_request(ipc_connection);
693     send_info(req, send_what, ipc_connection, INFO_HTTP_FORBIDDEN);
694     return;
695   }
696
697 #ifndef NODEBUG
698   olsr_printf(2, "(%s) Connect from host %s is allowed\n", name, addr);
699 #endif /* NODEBUG */
700
701   timeout.tv_sec = timeout.tv_usec = 0;
702
703   FD_ZERO(&read_fds);
704 #ifndef _WIN32
705   FD_SET(ipc_connection, &read_fds);
706 #else
707   FD_SET((unsigned int ) ipc_connection, &read_fds);
708 #endif
709
710   /* On success, select() and pselect() return the number of file descriptors
711    * contained in the three returned descriptor sets (that is, the total number
712    * of bits that are set in readfds, writefds, exceptfds) which may be zero if
713    * the timeout expires before anything interesting happens. On error, -1 is
714    * returned, and errno is set to indicate the error; the file descriptor sets
715    * are unmodified, and timeout becomes undefined.
716    */
717
718   if (select(ipc_connection + 1, &read_fds, NULL, NULL, &timeout) < 0) {
719 #ifndef NODEBUG
720     olsr_printf(1, "(%s) select()=%s\n", name, strerror(errno));
721 #endif /* NODEBUG */
722     drain_request(ipc_connection);
723     send_info(req, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
724     return;
725   }
726
727   *req = '\0';
728   rx_count = recv(ipc_connection, req, sizeof(req_buffer), 0); /* Win32 needs the cast here */
729
730   /* Upon successful completion, recv() shall return the length of the message
731    * in bytes. If no messages are available to be received and the peer has
732    * performed an orderly shutdown, recv() shall return 0. Otherwise, −1 shall
733    * be returned and errno set to indicate the error.
734    */
735
736   if (rx_count < 0) {
737 #ifndef NODEBUG
738     olsr_printf(1, "(%s) s < 0\n", name);
739 #endif /* NODEBUG */
740     *req = '\0';
741     drain_request(ipc_connection);
742     send_info(req, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
743     return;
744   }
745
746   /* rx_count >= 0 */
747
748   if (!rx_count) {
749 #ifndef NODEBUG
750     olsr_printf(1, "(%s) s == 0\n", name);
751 #endif /* NODEBUG */
752     *req = '\0';
753     drain_request(ipc_connection);
754     send_info(req, send_what, ipc_connection, INFO_HTTP_NOCONTENT);
755     return;
756   }
757
758   /* rx_count > 0 */
759
760   if (rx_count >= (ssize_t) sizeof(req_buffer)) {
761 #ifndef NODEBUG
762     olsr_printf(1, "(%s) s > %ld\n", name, (long int) sizeof(req_buffer));
763 #endif /* NODEBUG */
764     req[sizeof(req_buffer) - 1] = '\0';
765     drain_request(ipc_connection);
766     send_info(req, send_what, ipc_connection, INFO_HTTP_REQUEST_ENTITY_TOO_LARGE);
767     return;
768   }
769
770   /* 0 < rx_count < sizeof(requ) */
771
772   req[rx_count] = '\0';
773   req = parseRequest(req, (size_t*) &rx_count);
774   req = skipMultipleSlashes(req, (size_t*) &rx_count);
775   if ((req[0] == '\0') //
776       || ((req[0] == '/') && (req[1] == '\0'))) {
777     /* empty or '/' */
778     send_what = SIW_EVERYTHING;
779   } else {
780     send_what = determine_action(req);
781   }
782
783   if (!send_what) {
784     http_status = INFO_HTTP_NOTFOUND;
785   }
786
787   send_info(req, send_what, ipc_connection, http_status);
788 }
789
790 static int plugin_ipc_init(void) {
791   union olsr_sockaddr sock_addr;
792   uint32_t yes = 1;
793   socklen_t sock_addr_len;
794
795   /* Init ipc socket */
796   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
797 #ifndef NODEBUG
798     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
799 #endif /* NODEBUG */
800     goto error_out;
801   }
802
803   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
804 #ifndef NODEBUG
805     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
806 #endif /* NODEBUG */
807     goto error_out;
808   }
809
810 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
811   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
812     perror("SO_NOSIGPIPE failed");
813     goto error_out;
814   }
815 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
816
817 #if defined __linux__ && defined IPV6_V6ONLY
818   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
819       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
820     perror("IPV6_V6ONLY failed");
821     goto error_out;
822   }
823 #endif /* defined __linux__ && defined IPV6_V6ONLY */
824
825   /* complete the socket structure */
826   memset(&sock_addr, 0, sizeof(sock_addr));
827   if (olsr_cnf->ip_version == AF_INET) {
828     sock_addr.in4.sin_family = AF_INET;
829     sock_addr_len = sizeof(struct sockaddr_in);
830 #ifdef SIN6_LEN
831     sock_addr.in4.sin_len = sock_addr_len;
832 #endif /* SIN6_LEN */
833     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
834     sock_addr.in4.sin_port = htons(config->ipc_port);
835   } else {
836     sock_addr.in6.sin6_family = AF_INET6;
837     sock_addr_len = sizeof(struct sockaddr_in6);
838 #ifdef SIN6_LEN
839     sock_addr.in6.sin6_len = sock_addr_len;
840 #endif /* SIN6_LEN */
841     sock_addr.in6.sin6_addr = config->listen_ip.v6;
842     sock_addr.in6.sin6_port = htons(config->ipc_port);
843   }
844
845   /* bind the socket to the port number */
846   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
847 #ifndef NODEBUG
848     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
849 #endif /* NODEBUG */
850     goto error_out;
851   }
852
853   /* show that we are willing to listen */
854   if (listen(ipc_socket, 1) == -1) {
855 #ifndef NODEBUG
856     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
857 #endif /* NODEBUG */
858     goto error_out;
859   }
860
861   /* Register with olsrd */
862   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
863
864 #ifndef NODEBUG
865   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
866 #endif /* NODEBUG */
867
868   return 1;
869
870   error_out: //
871   if (ipc_socket >= 0) {
872     close(ipc_socket);
873     ipc_socket = -1;
874   }
875   return 0;
876 }
877
878 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
879   int i;
880
881   assert(plugin_name);
882   assert(plugin_functions);
883   assert(plugin_config);
884
885   name = plugin_name;
886   functions = plugin_functions;
887   config = plugin_config;
888
889   memset(&outbuffer, 0, sizeof(outbuffer));
890   for (i = 0; i < MAX_CLIENTS; ++i) {
891     outbuffer.socket[i] = -1;
892   }
893
894   ipc_socket = -1;
895
896   if (functions->init) {
897     functions->init(name);
898   }
899
900   info_plugin_cache_init(true);
901
902   return plugin_ipc_init();
903 }
904
905 void info_plugin_exit(void) {
906   int i;
907
908   if (ipc_socket != -1) {
909     close(ipc_socket);
910     ipc_socket = -1;
911   }
912   for (i = 0; i < MAX_CLIENTS; ++i) {
913     if (outbuffer.buffer[i]) {
914       free(outbuffer.buffer[i]);
915       outbuffer.buffer[i] = NULL;
916     }
917     outbuffer.size[i] = 0;
918     outbuffer.written[i] = 0;
919     if (outbuffer.socket[i]) {
920       close(outbuffer.socket[i]);
921       outbuffer.socket[i] = -1;
922     }
923   }
924   outbuffer.count = 0;
925
926   info_plugin_cache_init(false);
927 }