2 * The olsr.org Optimized Link-State Routing daemon (olsrd)
4 * (c) by the OLSR project
6 * See our Git repository to find out who worked on this file
7 * and thus is a copyright holder on it.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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
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.
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.
38 * Visit http://www.olsr.org for more information.
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.
46 #include <arpa/inet.h>
52 #include "olsrd_info.h"
54 #include "scheduler.h"
56 #include "http_headers.h"
59 #define close(x) closesocket(x)
65 * There is the problem that writing to a network socket can block,
66 * and the olsrd scheduler does not care about write events.
68 * There was a case that olsrd just froze for minutes when people used
69 * jsoninfo/txtinfo with large topologies over bad WiFi.
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.
78 int socket[MAX_CLIENTS];
79 char *buffer[MAX_CLIENTS];
80 size_t size[MAX_CLIENTS];
81 size_t written[MAX_CLIENTS];
83 } info_plugin_outbuffer_t;
85 static const char * name;
87 static info_plugin_functions_t *functions = NULL;
89 static info_plugin_config_t *config = NULL;
91 static int ipc_socket = -1;
93 static info_plugin_outbuffer_t outbuffer;
95 static struct timer_entry *writetimer_entry = NULL;
97 static struct info_cache_t info_cache;
99 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
101 static char * skipMultipleSlashes(char * requ, size_t* len) {
104 if (!r // null pointer
105 || (len && !*len) // zero length
106 || (r[0] == '\0') // zero length
107 || (r[0] != '/') // does not start with a slash
108 || (r[1] != '/')) // does not have another slash
113 while (r[1] == '/') {
122 static unsigned long long SIW_ENTRIES_ALL[] = {
136 SIW_NEIGHBORS_FREIFUNK, //
147 SIW_NETJSON_NETWORK_ROUTES, //
148 SIW_NETJSON_NETWORK_GRAPH, //
149 SIW_NETJSON_DEVICE_CONFIGURATION, //
150 SIW_NETJSON_DEVICE_MONITORING, //
151 SIW_NETJSON_NETWORK_COLLECTION //
154 long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw) {
155 long timeout = !plugin_config ? 0 : plugin_config->cache_timeout;
171 case SIW_PUD_POSITION:
173 case SIW_NETJSON_NETWORK_ROUTES:
174 case SIW_NETJSON_NETWORK_GRAPH:
175 case SIW_NETJSON_DEVICE_CONFIGURATION:
176 case SIW_NETJSON_DEVICE_MONITORING:
177 case SIW_NETJSON_NETWORK_COLLECTION:
191 static void info_plugin_cache_init(bool init) {
194 if (!functions->cache_timeout) {
198 for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
199 unsigned long long siw = SIW_ENTRIES_ALL[i];
200 struct info_cache_entry_t * entry = info_cache_get_entry(&info_cache, siw);
206 entry->timestamp = 0;
207 abuf_init(&entry->buf, 0);
209 abuf_free(&entry->buf);
210 entry->timestamp = 0;
215 static INLINE void info_plugin_cache_init_entry(struct info_cache_entry_t * entry) {
216 if (!entry->buf.buf) {
217 entry->timestamp = 0;
218 abuf_init(&entry->buf, AUTOBUFCHUNK);
219 assert(!entry->timestamp);
220 assert(!entry->buf.len);
221 assert(entry->buf.size == AUTOBUFCHUNK);
222 assert(entry->buf.buf);
224 assert(entry->timestamp >= 0);
225 assert(entry->buf.len >= 0);
226 assert(entry->buf.size >= AUTOBUFCHUNK);
227 assert(entry->buf.buf);
231 static unsigned int determine_single_action(char *requ) {
233 unsigned long long siw_mask = !functions->supported_commands_mask ? SIW_EVERYTHING : functions->supported_commands_mask();
235 if (!functions->is_command || !siw_mask)
238 for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
239 unsigned long long siw = SIW_ENTRIES_ALL[i];
240 if ((siw & siw_mask) && functions->is_command(requ, siw))
247 static unsigned int determine_action(char *requ) {
248 if (!functions->is_command) {
252 if (!requ || (requ[0] == '\0')) {
257 /* requ is guaranteed to be at least 1 character long */
259 if (!functions->supportsCompositeCommands) {
260 /* no support for composite commands */
261 return determine_single_action(requ);
264 /* composite commands */
267 unsigned int action = 0;
269 char * requestSegment = requ;
270 while (requestSegment) {
271 requestSegment = skipMultipleSlashes(requestSegment, NULL);
272 if (requestSegment[0] == '\0') {
273 /* there is no more text */
274 requestSegment = NULL;
278 /* there is more text */
282 char * requestSegmentTail = strchr(&requestSegment[1], '/');
284 if (!requestSegmentTail) {
285 /* there isn't another slash, process everything that is left */
286 r = determine_single_action(requestSegment);
288 /* there is another slash, process everything before the slash */
289 char savedCharacter = *requestSegmentTail;
290 *requestSegmentTail = '\0';
291 r = determine_single_action(requestSegment);
292 *requestSegmentTail = savedCharacter;
302 /* process everything that is left in the next iteration */
303 requestSegment = requestSegmentTail;
311 static void send_status_no_retries(const char * req, bool add_headers, int the_socket, unsigned int status) {
314 abuf_init(&abuf, AUTOBUFCHUNK);
317 http_header_build_result(status, &abuf);
318 } else if (status != INFO_HTTP_OK) {
319 if (functions->output_error) {
320 functions->output_error(&abuf, status, req, add_headers);
321 } else if (status == INFO_HTTP_NOCONTENT) {
322 /* wget can't handle output of zero length */
323 abuf_puts(&abuf, "\n");
327 (void) send(the_socket, abuf.buf, abuf.len, 0);
332 static void write_data(void *unused __attribute__((unused))) {
337 if (outbuffer.count <= 0) {
338 /* exit early if there is nothing to send */
344 for (i = 0; i < MAX_CLIENTS; i++) {
345 if (outbuffer.socket[i] < 0) {
349 /* And we cast here since we get a warning on Win32 */
350 FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
352 if (outbuffer.socket[i] > max) {
353 max = outbuffer.socket[i];
360 result = select(max + 1, NULL, &set, NULL, &tv);
362 /* exit early if any of the sockets is not ready for writing */
366 for (i = 0; i < MAX_CLIENTS; i++) {
367 if (outbuffer.socket[i] < 0) {
371 result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
373 outbuffer.written[i] += result;
376 #if EWOULDBLOCK == EAGAIN
377 if ((result < 0) && (errno == EAGAIN)) {
379 if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
384 if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
385 /* close this socket and cleanup*/
386 close(outbuffer.socket[i]);
387 outbuffer.socket[i] = -1;
388 free(outbuffer.buffer[i]);
389 outbuffer.buffer[i] = NULL;
390 outbuffer.size[i] = 0;
391 outbuffer.written[i] = 0;
397 if (!outbuffer.count) {
398 olsr_stop_timer(writetimer_entry);
403 unsigned long long siw;
404 printer_generic func;
405 } SiwLookupTableEntry;
407 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
409 unsigned int preLength;
410 unsigned int what = send_what;
411 cache_timeout_func cache_timeout_f = functions->cache_timeout;
413 if (functions->output_start) {
414 functions->output_start(abuf);
417 preLength = abuf->len;
419 for (i = 0; (i < funcsSize) && what; i++) {
420 unsigned long long siw = funcs[i].siw;
422 printer_generic func = funcs[i].func;
424 long cache_timeout = 0;
425 struct info_cache_entry_t *cache_entry = NULL;
427 if (cache_timeout_f) {
428 cache_timeout = cache_timeout_f(config, siw);
429 cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
438 info_plugin_cache_init_entry(cache_entry);
441 age = llabs(now - cache_entry->timestamp);
442 if (!cache_entry->timestamp || (age >= cache_timeout)) {
443 /* cache is never used before or cache is too old */
444 cache_entry->buf.buf[0] = '\0';
445 cache_entry->buf.len = 0;
446 cache_entry->timestamp = now;
447 func(&cache_entry->buf);
450 abuf_concat(abuf, &cache_entry->buf);
457 *outputLength = abuf->len - preLength;
459 if (functions->output_end) {
460 functions->output_end(abuf);
464 static void send_info(const char * req, bool add_headers, unsigned int send_what, int the_socket, unsigned int status) {
466 unsigned int outputLength = 0;
467 unsigned int send_index = 0;
468 bool first_reply = false;
470 const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
471 int contentLengthIndex = 0;
472 int headerLength = 0;
474 assert(outbuffer.count <= MAX_CLIENTS);
476 abuf_init(&abuf, AUTOBUFCHUNK);
479 http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
480 headerLength = abuf.len;
483 if (status == INFO_HTTP_OK) {
486 // only add if normal format
487 if (send_what & SIW_ALL) {
488 SiwLookupTableEntry funcs[] = {
489 { SIW_NEIGHBORS , functions->neighbors }, //
490 { SIW_LINKS , functions->links }, //
491 { SIW_ROUTES , functions->routes }, //
492 { SIW_HNA , functions->hna }, //
493 { SIW_MID , functions->mid }, //
494 { SIW_TOPOLOGY , functions->topology }, //
495 { SIW_GATEWAYS , functions->gateways }, //
496 { SIW_INTERFACES , functions->interfaces }, //
497 { SIW_2HOP , functions->twohop }, //
498 { SIW_SGW , functions->sgw }, //
499 { SIW_PUD_POSITION, functions->pudPosition }, //
501 { SIW_VERSION , functions->version }, //
502 { SIW_CONFIG , functions->config }, //
503 { SIW_PLUGINS , functions->plugins } //
506 send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
507 } else if (send_what & SIW_NETJSON) {
508 SiwLookupTableEntry funcs[] = {
509 { SIW_NETJSON_NETWORK_ROUTES , functions->networkRoutes }, //
510 { SIW_NETJSON_NETWORK_GRAPH , functions->networkGraph }, //
511 { SIW_NETJSON_DEVICE_CONFIGURATION, functions->deviceConfiguration}, //
512 { SIW_NETJSON_DEVICE_MONITORING , functions->deviceMonitoring }, //
513 { SIW_NETJSON_NETWORK_COLLECTION , functions->networkCollection } //
516 send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
517 } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
518 /* this outputs the olsrd.conf text directly, not normal format */
519 unsigned int preLength = abuf.len;
520 functions->olsrd_conf(&abuf);
521 outputLength = abuf.len - preLength;
524 if (!abuf.len || !outputLength) {
525 status = INFO_HTTP_NOCONTENT;
529 http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
530 headerLength = abuf.len;
535 if (status != INFO_HTTP_OK) {
536 if (functions->output_error) {
537 functions->output_error(&abuf, status, req, add_headers);
538 } else if (status == INFO_HTTP_NOCONTENT) {
539 /* wget can't handle output of zero length */
540 abuf_puts(&abuf, "\n");
545 http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
549 * Determine the last available outbuffer slot.
550 * Search from the end towards the start to avoid starvation of
551 * older replies that are still in-flight (since the send function
552 * iterates from the start towards the end).
554 send_index = MAX_CLIENTS;
557 if (!outbuffer.buffer[send_index]) {
561 assert(send_index < MAX_CLIENTS);
562 assert(!outbuffer.buffer[send_index]);
564 /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
565 outbuffer.buffer[send_index] = abuf.buf;
566 outbuffer.size[send_index] = abuf.len;
567 outbuffer.written[send_index] = 0;
568 outbuffer.socket[send_index] = the_socket;
573 first_reply = !outbuffer.count;
579 if (first_reply && outbuffer.buffer[send_index]) {
580 writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
584 static char * skipLeadingWhitespace(char * requ, size_t *len) {
585 if (!requ || !len || !*len) {
589 while (isspace(*requ) && (*requ != '\0')) {
596 static char * stripTrailingWhitespace(char * requ, size_t *len) {
597 if (!requ || !len || !*len) {
601 while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
608 static char * stripTrailingSlashes(char * requ, size_t *len) {
609 if (!requ || !len || !*len) {
613 while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
620 static char * cutAtFirstEOL(char * requ, size_t *len) {
624 if (!requ || !len || !*len) {
628 while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
632 if ((*s == '\n') || (*s == '\r')) {
639 static char * parseRequest(char * req, size_t *len) {
640 if (!req || !len || !*len) {
644 /* HTTP request: GET whitespace URI whitespace HTTP/1.[01] */
645 if (*len < (3 + 1 + 1 + 1 + 8) //
646 || strncasecmp(req, "GET", 3) //
647 || !isspace(req[3]) //
648 || !isspace(req[*len - 9]) //
649 || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
650 || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
651 /* too short or does not start with 'GET ' nor ends with ' HTTP/1.[01]'*/
659 /* strip ' HTTP/1.[01]' */
666 static char * checkCommandPrefixes(char * req, size_t *len, bool *add_headers) {
669 if (!req || !len || !*len || !add_headers) {
677 if ((l >= (SIW_PREFIX_HTTP_LEN + 1)) && !strncasecmp(req, SIW_PREFIX_HTTP "/", SIW_PREFIX_HTTP_LEN + 1)) {
678 *len = l - SIW_PREFIX_HTTP_LEN;
679 req = &req[SIW_PREFIX_HTTP_LEN];
686 if ((l == SIW_PREFIX_HTTP_LEN) && !strcasecmp(req, SIW_PREFIX_HTTP)) {
695 if ((l >= (SIW_PREFIX_PLAIN_LEN + 1)) && !strncasecmp(req, SIW_PREFIX_PLAIN "/", SIW_PREFIX_PLAIN_LEN + 1)) {
696 *len = l - SIW_PREFIX_PLAIN_LEN;
697 req = &req[SIW_PREFIX_PLAIN_LEN];
698 *add_headers = false;
704 if ((l == SIW_PREFIX_PLAIN_LEN) && !strcasecmp(req, SIW_PREFIX_PLAIN)) {
707 *add_headers = false;
716 static void drain_request(int ipc_connection) {
717 static char drain_buffer[AUTOBUFCHUNK];
722 r = recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), MSG_PEEK);
724 r = recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), 0);
727 r = recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), MSG_DONTWAIT);
729 } while ((r > 0) && (r <= (ssize_t) sizeof(drain_buffer)));
732 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
734 char addr[INET6_ADDRSTRLEN];
737 int ipc_connection = -1;
738 union olsr_sockaddr sock_addr;
739 socklen_t sock_addr_len = sizeof(sock_addr);
740 bool hostDenied = false;
741 struct timeval timeout;
743 char req_buffer[1024]; /* maximum size is the size of an IP packet */
744 char * req = req_buffer;
745 ssize_t rx_count = 0;
746 unsigned int send_what = 0;
747 unsigned int http_status = INFO_HTTP_OK;
748 bool add_headers = config->http_headers;
752 if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) < 0) {
754 olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
756 /* the caller will retry later */
760 /* Wait at most this much time for the request to arrive on the connection */
762 timeout.tv_usec = (outbuffer.count >= MAX_CLIENTS) ? 0 : (20 * 1000); /* 20 msec */
766 FD_SET(ipc_connection, &read_fds);
768 FD_SET((unsigned int ) ipc_connection, &read_fds); /* Win32 needs the cast here */
771 /* On success, select() and pselect() return the number of file descriptors
772 * contained in the three returned descriptor sets (that is, the total number
773 * of bits that are set in readfds, writefds, exceptfds) which may be zero if
774 * the timeout expires before anything interesting happens. On error, -1 is
775 * returned, and errno is set to indicate the error; the file descriptor sets
776 * are unmodified, and timeout becomes undefined.
779 if (select(ipc_connection + 1, &read_fds, NULL, NULL, &timeout) < 0) {
780 /* ipc_connection is not ready for reading */
782 olsr_printf(1, "(%s) select()=%s\n", name, strerror(errno));
784 drain_request(ipc_connection);
785 if (outbuffer.count >= MAX_CLIENTS) {
786 send_status_no_retries(req, add_headers, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
788 send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
793 rx_count = recv(ipc_connection, req, sizeof(req_buffer), 0);
795 /* Upon successful completion, recv() shall return the length of the message
796 * in bytes. If no messages are available to be received and the peer has
797 * performed an orderly shutdown, recv() shall return 0. Otherwise, −1 shall
798 * be returned and errno set to indicate the error.
801 /* ensure proper request termination */
804 } else if (rx_count < (ssize_t) sizeof(req_buffer)) {
805 req[rx_count] = '\0';
807 req[sizeof(req_buffer) - 1] = '\0';
810 /* sanitise the request */
812 req = cutAtFirstEOL(req, (size_t*) &rx_count);
814 req = stripTrailingWhitespace(req, (size_t*) &rx_count);
815 req = skipLeadingWhitespace(req, (size_t*) &rx_count);
817 /* detect http requests */
818 req = parseRequest(req, (size_t*) &rx_count);
820 req = stripTrailingWhitespace(req, (size_t*) &rx_count);
821 req = stripTrailingSlashes(req, (size_t*) &rx_count);
822 req = skipLeadingWhitespace(req, (size_t*) &rx_count);
823 req = skipMultipleSlashes(req, (size_t*) &rx_count);
825 req = checkCommandPrefixes(req, (size_t*) &rx_count, &add_headers);
827 req = skipMultipleSlashes(req, (size_t*) &rx_count);
830 if (outbuffer.count >= MAX_CLIENTS) {
831 /* limit the number of replies that are in-flight */
832 drain_request(ipc_connection);
833 send_status_no_retries(req, add_headers, ipc_connection, INFO_HTTP_SERVICE_UNAVAILABLE);
837 if (olsr_cnf->ip_version == AF_INET) {
839 (ntohl(config->accept_ip.v4.s_addr) != INADDR_ANY) //
840 && !ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) //
841 && (!config->allow_localhost //
842 || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK));
845 !ip6equal(&config->accept_ip.v6, &in6addr_any) //
846 && !ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) //
847 && (!config->allow_localhost //
848 || !ip6equal(&config->accept_ip.v6, &in6addr_loopback));
853 olsr_cnf->ip_version, //
854 (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
863 olsr_printf(1, "(%s) Connect from host %s is not allowed!\n", name, addr);
865 drain_request(ipc_connection);
866 send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_FORBIDDEN);
871 olsr_printf(1, "(%s) Connect from host %s is allowed\n", name, addr);
876 olsr_printf(1, "(%s) rx_count < 0\n", name);
878 drain_request(ipc_connection);
879 send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
887 olsr_printf(1, "(%s) rx_count == 0\n", name);
889 drain_request(ipc_connection);
890 send_info(req, add_headers, SIW_EVERYTHING, ipc_connection, INFO_HTTP_OK);
896 if (rx_count >= (ssize_t) sizeof(req_buffer)) {
898 olsr_printf(1, "(%s) rx_count > %ld\n", name, (long int) sizeof(req_buffer));
901 /* input was much too long: read until the end for graceful connection termination
902 * because wget can't handle the premature connection termination that is allowed
903 * by the INFO_HTTP_REQUEST_ENTITY_TOO_LARGE HTTP status code
905 drain_request(ipc_connection);
906 send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_REQUEST_ENTITY_TOO_LARGE);
910 /* 0 < rx_count < sizeof(requ) */
913 || ((rx_count == 1) && (*req == '/'))) {
915 send_what = SIW_EVERYTHING;
917 send_what = determine_action(req);
921 http_status = INFO_HTTP_NOTFOUND;
924 send_info(req, add_headers, send_what, ipc_connection, http_status);
927 static int plugin_ipc_init(void) {
928 union olsr_sockaddr sock_addr;
930 socklen_t sock_addr_len;
932 /* Init ipc socket */
933 if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
935 olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
940 if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
942 olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
947 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
948 if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
949 perror("SO_NOSIGPIPE failed");
952 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
954 #if defined __linux__ && defined IPV6_V6ONLY
955 if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
956 && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
957 perror("IPV6_V6ONLY failed");
960 #endif /* defined __linux__ && defined IPV6_V6ONLY */
962 /* complete the socket structure */
963 memset(&sock_addr, 0, sizeof(sock_addr));
964 if (olsr_cnf->ip_version == AF_INET) {
965 sock_addr.in4.sin_family = AF_INET;
966 sock_addr_len = sizeof(struct sockaddr_in);
968 sock_addr.in4.sin_len = sock_addr_len;
969 #endif /* SIN6_LEN */
970 sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
971 sock_addr.in4.sin_port = htons(config->ipc_port);
973 sock_addr.in6.sin6_family = AF_INET6;
974 sock_addr_len = sizeof(struct sockaddr_in6);
976 sock_addr.in6.sin6_len = sock_addr_len;
977 #endif /* SIN6_LEN */
978 sock_addr.in6.sin6_addr = config->listen_ip.v6;
979 sock_addr.in6.sin6_port = htons(config->ipc_port);
982 /* bind the socket to the port number */
983 if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
985 olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
990 /* show that we are willing to listen */
991 if (listen(ipc_socket, 1) == -1) {
993 olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
998 /* Register with olsrd */
999 add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
1002 olsr_printf(1, "(%s) listening on port %d\n", name, config->ipc_port);
1003 #endif /* NODEBUG */
1008 if (ipc_socket >= 0) {
1015 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
1018 assert(plugin_name);
1019 assert(plugin_functions);
1020 assert(plugin_config);
1023 functions = plugin_functions;
1024 config = plugin_config;
1026 memset(&outbuffer, 0, sizeof(outbuffer));
1027 for (i = 0; i < MAX_CLIENTS; ++i) {
1028 outbuffer.socket[i] = -1;
1033 if (functions->init) {
1034 functions->init(name);
1037 info_plugin_cache_init(true);
1039 return plugin_ipc_init();
1042 void info_plugin_exit(void) {
1045 if (ipc_socket != -1) {
1049 for (i = 0; i < MAX_CLIENTS; ++i) {
1050 if (outbuffer.buffer[i]) {
1051 free(outbuffer.buffer[i]);
1052 outbuffer.buffer[i] = NULL;
1054 outbuffer.size[i] = 0;
1055 outbuffer.written[i] = 0;
1056 if (outbuffer.socket[i]) {
1057 close(outbuffer.socket[i]);
1058 outbuffer.socket[i] = -1;
1061 outbuffer.count = 0;
1063 info_plugin_cache_init(false);